Compare commits

..

37 Commits

Author SHA1 Message Date
selune 2a4707404a Settle appearance panel scrolling before targeting Search (#6079)
* claim: #6076

* test(render): settle appearance scroll before resolving Search (#6076)

Expanded starter swatches moved under the scenario click while the existing scroll animation was active. Advance 20 deterministic harness frames before resolving the Search handle. No wall-clock sleep, timeout change, assertion deletion or production change. Original native reproduction fails at the empty-state assertion; all 124 scenarios now pass with one pre-existing ignored adhoc entry.
2026-10-24 15:41:29 +02:00
selune cad0767a10 Merge main synchronization into render (#6098)
Resolves #6050 branch conflict while retaining both original nextest policies. Fresh quick run 40097215892 passed. Preserve main ancestry; no advanced experimental feature or full-green claim.
2026-10-24 15:17:44 +02:00
0xlillith 644a70960e Merge pinned main into render; preserve both independent nextest additions
Refs #6050, #5493. Manual resolution is confined to .config/nextest.toml and retains the existing CI gpu-path policy and main local-ci/windows-gpu policy unchanged. Parsed TOML semantics and automatic merge-tree equality for every other path are verified. cargo-nextest is not installed locally; native nextest parsing is NOT claimed. Fresh repository/compile checks remain required. No advanced experimental code is introduced.
2026-10-24 15:12:23 +02:00
0xlillith c2de6c636d claim: resolve the main/render synchronization conflict for #6050 2026-10-24 15:11:44 +02:00
0xlillith 6fbcb5f1ea feat(render): expose bounded original appearance recipes (#1722) 2026-10-24 14:10:09 +02:00
selune d94b0a1a7b GPU: match index-matched Fresnel in path and viewport (#6036)
Fixes #6035. Preserve actual GPU/f64 material-oracle checks in both translations. Scoped Vulkan, render, Clippy, WASM and fresh quick checks pass. No CI configuration or gate changes.
2026-10-24 13:51:31 +02:00
selune a29a45eca1 Render: isolate matched GGX visible-normal sampling (#1654)
Independent numerical constituent of #5493. Part of #1654; the CPU reflection consumer remains #1698, so this does not close its image acceptance. Original sampler, matched PDF and analytic/hostile-input tests are preserved from the reviewed donor. 189 render tests, strict Clippy, wasm32 and repository gates pass; fresh quick run 40086624771 passes. No CI configuration, saved-data or kernel change.
2026-10-24 12:41:21 +02:00
selune f443a388cc Merge pull request #5980 from 0xlillith/sync/render-main-ancestry-polish-20261024
Restore main ancestry after #5976 (merge commit only; no file changes)
2026-10-24 11:17:59 +02:00
0xlillith e4cceb3db1 Restore main ancestry after the accepted render synchronization
History-only follow-up to #5976 under #5493. The accepted squash d8c2abdb has the correct tree but omits main ancestry. Normal merge of the accepted original sync head restores that ancestry with ZERO file changes. Both source/merge trees are verified identical. Must be merged with Create a merge commit, never squash/rebase; no shared branch rewrite or direct main push.
2026-10-24 10:57:00 +02:00
selune d8c2abdb2b Sync main into render and preserve GPU CLI help (#5976)
* CLI: --help on each subcommand prints its usage and exits 0 (#4538) (#4545)

* claim: #4538

* CLI: --help on each subcommand prints its usage and exits 0 (#4538)

Every command now answers `--help` and `-h` with its own usage on stdout and
exit 0, the way the top level has since #1828. Before, each subcommand
answered differently: `fuzz --help` started a fuzz run with the default seeds
and ran until it was stopped, `recipe --help` took the flag for a file name,
and `snapshot`, `render`, `oracle`, `run`, `eval`, `script` and `crashes`
answered with an error on stderr and exit 1.

The help is read before the command runs, so `--help` never starts work: no
fuzz run, no MCP server, no file read.

`fuzz` also refuses an argument it doesn't take, as `snapshot` already did: a
flag it doesn't know and a stray word are errors, not arguments to ignore
while a hundred seeds run.

The usage lives in one place now: each command's lines are a constant, and the
top-level usage is composed from them, so the two cannot drift apart. The
top-level usage is unchanged, byte for byte.

# Conflicts:
#	apps/solidcraft-cli/src/main.rs

* Renaming a parameter follows it into sheet-metal and plastic rules and canvases (#4420) (#4423)

* claim: #4420

* Renaming a parameter follows it into sheet-metal and plastic rules and canvases (#4420)

Document::rename_param rewrote other parameters, feature inputs, dimensions,
configurations and joints, but not the expressions of sheet metal and plastic rules or
the plane a canvas lies on, so after a rename the sheet metal and plastic features
using them failed with "unknown parameter" and the canvas left the view. It renames
them there now, and param_users lists them (a rule's field, a canvas's input), so a
parameter they use can't be deleted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Parameters: rules' features in drives and the graph; shared rule field lists (#4420)

A sheet metal or plastic rule that uses a parameter makes the sheet metal (or plastic)
features rebuild when it changes, so parameters.users lists them in drives and
parameters.graph links them. SheetRule::fields/fields_mut and PlasticRule::fields_mut
replace the hand-written copies of the rules' fields in params.rs, eval.rs and
sheet.manage_rules. Suggested by @CamWight.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fmt (#4420)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Mirror and pattern: a pattern or mirror feature as the source (#395) (#3998)

* claim: #395

* Mirror and pattern: a pattern or mirror feature as the source (#395)

A pattern or mirror of features refused another pattern or mirror as a
source ("Pat cannot be patterned or mirrored yet"). Such a source now
stands for what it copied: its own sources are replayed at each of its
transforms moved by each of ours, to any depth up to eight. Copies of
a feature picked directly keep the instance numbers they had, so
existing designs keep their face names; copies made through a nested
pattern or mirror are numbered after them, so one feature reached two
ways never names two copies alike. A pattern or mirror of bodies as
the source is still refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Expression completion: keep longer names visible when the typed word is a unit (#3663) (#3664)

* claim: #3663

* Expression completion: keep longer names listed when the typed word is a unit (#3663)

Typing `rad` into an expression box showed nothing because `rad` is the radians unit and
a word that is itself a known token closed the popup. The popup now stays open on such a
word. A complete name (`width` with `width_extra` defined) opens it unarmed: the longer
names are listed with no row selected, Enter submits what was typed and Tab moves on; Up
or Down picks a row, after which Enter or Tab complete it as before. A unit means
something only after a number, so a unit typed anywhere else (`rad` on the way to
`radius`) is a prefix like any other: the popup is armed and Enter completes `radius`
instead of submitting one radian.

A complete token is extended only by names that start with it (`in` lists `inner`, not
`rounding`), a typed unit offers no other units (`in` does not list `inch`), and a unit
right after a number (`2 in`, the form every dialog starts its sizes in) opens no popup at
all, so nothing sits over a dialog's OK button.

Fixes #3663

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Homero Thompson <vdirienzo@gmail.com>
Co-authored-by: SS-EN <84559909+SS-EN@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Martin Pålsson <martin.palsson@outlook.com>
Co-authored-by: Daniel <daniel.owsianski@gmail.com>
2026-10-24 04:51:48 -04:00
0xlillith 81d3e4cb14 Merge pinned main into render, retaining its GPU CLI help
Periodic normal main-to-render synchronization under #5493. Preserve all four main commits and both parents. Resolve the single CLI help conflict by keeping main subcommand help dispatch and the renderer progressive/lighting/environment options; add a regression checking both top-level and render help. No renderer/kernel/format/workflow source is otherwise edited. Formatting/diff checks pass; fresh quick CI gates the merge. Current private combined execution retains its older exact source pin.
2026-10-24 10:49:29 +02:00
selune 5c0e48ed2b feat(render): add bounded linear film accumulation (#1696) (#1697)
Extract the independent FilmAccumulator from owned donor 3b892eab5e. Keep the existing job cancellation and caller-owned admission/work contracts. PNG resolution, primary camera coverage and progressive consumer wiring remain separate. Add analytic statistics, atomic refusal, allocation/range boundaries and real 4096/1048576-count tests. No kernel, document schema, workflow or capability change.
2026-10-24 08:48:08 +02:00
selune ce1c8db7fb feat(render): reconcile definition-local visual mapping (#1214) (#1223)
Retain the original mapping implementation, all eight analytic/hostile-input tests and mapping contract byte-for-byte from #1223. Reconcile only module registration and the current roadmap on render. Actual current render library 166/0; strict all-target render Clippy, wasm32 and repository gates pass. No persistence, sampler, new capability, kernel/vendor change or external code is added. Historical mixed-base full-CI failures are not relabelled.
2026-10-24 08:31:59 +02:00
selune f0995dab1c Merge pull request #5871 from 0xlillith/sync/render-main-ancestry-20261024
History-only: retain main ancestry in render (merge, not squash)
2026-10-24 08:12:54 +02:00
0xlillith 600f12ea0e Restore pinned main ancestry after the render synchronization squash 2026-10-24 08:10:54 +02:00
selune d4ec5e6811 Sync pinned main into render (#5869)
* Viewport: show reference geometry through bodies (Preferences, off by default) (#2697)

A new Graphics preference, "Show reference geometry through bodies", draws
inactive sketch contours, origin and construction planes and their axes over
shaded bodies. It is off by default, so the viewport looks as before: reference
geometry is depth-tested and a section leaves it whole (#1229); Create Sketch
and dialogs picking a plane still draw the planes through the model.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* ui-egui tests: offscreen GPU tests start their devices one at a time (#3989) (#3990)

* claim: #3989

* ui-egui tests: offscreen GPU tests start their devices one at a time

Every offscreen test started a wgpu instance of its own, and starting one
makes the Vulkan loader scan its drivers. With several test threads two
tests doing that at once crashed the test process with SIGSEGV inside the
loader (loader_icd_scan). offscreen_device now holds a process-wide lock
while it starts the instance, adapter and device; each test keeps its own
device and the rendering still runs side by side.

* DXF: write and read a Bézier at its own degree (#5269) (#5281)

* claim: #5269

* DXF: write and read a Bézier at its own degree (#5269)

The DXF writer and reader held a spline's degree to 7, the value MAX_BEZIER_DEGREE had before
#1642 raised it to 9. A G4 blend (one Bézier of degree 9) was written as a degree-7 SPLINE over
its first 8 control points, so a flat pattern's cut-out stopped 4.444 mm short in the file.

- seg_shape takes its limit from MAX_BEZIER_DEGREE: a Bézier is written whole, at its own degree.
- read_dxf reads SPLINE degrees 1 to MAX_BEZIER_DEGREE, the degrees a sketch spline takes.
- Tests: every Bézier degree 4 to 9, a degree-9 round trip, the reader's degrees, hostile SPLINE
  degrees and too few control points, the G4 cut-out exported and read back with its areas, and a
  degree-9 SPLINE with 2 to 10 points inserted into a sketch.

Fixes #5269

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Renaming a plane keeps its projections, canvases and hidden state (#4353) (#4354)

* claim: #4353

* Renaming a plane keeps its projections, canvases and hidden state (#4353)

timeline.rename only renamed the feature in other features' references. A sketch's
projection of a construction plane, a canvas placed on it and the hidden list keep it
by name too, so they lost it: the sketch warned that the plane no longer exists, the
canvas left the view and a hidden plane showed again. Document::rename_feature_keys
renames it in sketch links and canvases, as rename_body_keys does for bodies; the
selection follows in the command, and the hidden list in Session::refresh_from, so a
hidden plane stays hidden through undo and redo too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Browser: add Rename and Delete to feature menus (#3839, #4343) (#4342)

* claim: #3839

* fix(ui): add Rename and Delete to browser feature menus

* Browser: a second click on a Document Settings entry closes its window (#4150) (#4152)

* claim: #4150

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Browser: a second click on a Document Settings entry closes its window (#4150)

Each entry under Document Settings only opened its window. A second click on
Units closed the menu on the press (the row is outside it) and opened it again
on the release; Precision left Preferences open; Parameters and Design
Properties came back as a fresh copy. Now a click on an entry whose window is
open closes it the way the window's own x does:
- Units: context_menu::toggle_for with the row as the opener, the fix #1522
  made for the workspace button's menu. A right-click still opens it at the
  pointer.
- Precision: prefs::close(app, true), the Cancel/x path that puts back what
  changed since the window opened (now pub(crate)).
- Parameters and Design Properties: the window is dropped, as its x does. A
  Parameters value typed but not applied yet is dropped with it, as today's
  second click already drops it.

Fixes #4150

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Preserve expression-positioned sketch fillet corners (#4097) (#4106)

* claim: #4097

* Sketch fillet keeps a corner placed by an expression (#4097)

Filleting a corner whose position comes from parameter expressions deleted
the point and its expressions, so the sketch gained two degrees of freedom
and later parameter changes distorted the outline. keep_corner now also
keeps fixed or expression-positioned points as virtual sharp corners.

* Fix feature editor picks after upstream sketch resizing (#4083) (#4084)

* claim: #4083

* Feature editors refill named edges and faces where they are now (#4083)

After an upstream sketch change, Fillet, Chamfer, Shell and Draft evaluated
correctly, but their editors refilled the originally clicked points and
could then fail with "no edge at ...". Resolve the stored names through the
document's naming helpers first, keep the resolved index, and map the pick
into the displayed body's frame; the point lookup stays as the fallback.

* ui-egui tests: clear the texture deltas of the frames they run (#4039)

Nine tests dropped an egui frame's output with its texture delta still
unapplied (the expression completion editor's frames, the slot preview's
three, the browser's feature rows). epaint asserts against that in debug
builds, so they failed under the dev profile and passed only where debug
assertions are off. They clear the delta as the other tests do; one more
hover test that dropped its output the same way does too.

* Frames: profile library, members along frame sketch lines, and the Frame Member dialog (#4003) (#4004)

* claim: #4003

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Frames: the profile library (#4003)

The first slice of frames and weldments: the cross-sections structural members
will be made of, each SolidCraft's own parametric definition built in code
(no vendor or standard size tables are shipped).

- solidcraft_doc::frame: bar, round bar, rectangular and round tube, angle,
  channel, I-beam and a generic T-slot extrusion as exact line/arc regions,
  with defaults worked out from the profile's size, refusals that name the
  dimension that does not fit, anchors and placement, and exact section
  properties (area, centroid, Ixx/Iyy/Ixy by Green's theorem).
- frame.profile.list, frame.profile.properties (with mass per metre for a
  density) and frame.profile.insert (draws a profile into a sketch, locked by
  default, at a point, anchor and angle).
- Tests against hand-computed values (rectangles, circles, composite
  rectangles by the parallel-axis theorem, fillet spandrels), hostile input,
  extrusion volume, undo; catalog entries in every locale; docs/frames.md;
  ROADMAP M15.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Frames: drop the built-in T-slot profile (#4003)

Aluminium extrusions and other vendor sections come from the user's own
sketch (drawn or inserted from the vendor's DXF), so the library keeps the
plain steel shapes only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Frames: members along frame sketch lines (#4003)

frame.member lays one body per straight line of a frame sketch (2D lines or
3D sketch lines), its cross-section square to the line from end to end:

- profile: a built-in profile, or profile_sketch, a sketch whose material
  (nested loops alternating, so two circles make a tube) is the section in
  its own x and y around its origin; profiles picks override it.
- anchor (origin, centre, centroid or a bounding-box corner or edge middle),
  angle (an angle expression, so a parameter), mirror.
- Orientation: the profile's y as near world up as the line allows, x
  square to both; a vertical member's x is world X (frame::member_axes).
- Members follow sketch and parameter edits, undo as one step, save and
  reopen (format: pending/frame-members.rs), and are named after the profile
  (Rect Tube 40x40x3, (2)…), numbering on past names already taken.

Tests against hand-computed volumes and bounding boxes (rectangle, 3D
tower lines, parameter edit, sketch tube profile and picks, anchor/angle/
mirror), refusals, undo/redo and reopen; walk fixtures and goldens; docs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Frames: fixes from the adversarial review (#4003)

- Default member lines are what was drawn: projected and other linked curves
  and 3D wires (a face sketch's edges) and lines of no length are left out.
- A profile sketch's material starts from its drawn profiles, as
  solid.extrude's "all" does, so a section sketched on a face works; the
  even-odd test uses outlines and boxes worked out once, capped at 256.
- Member names are pinned to their lines (`names`, by line id; `body_name`
  is the base): deleting or adding a line, or deleting another feature, no
  longer renames other members, and frame.member stores every name it gives.
  body.rename renames a member on its line (also for later member features);
  a copy whose body_names were set still names its members by position.
- Built-in profile dimensions are expressions (ProfileSpec): they follow
  parameters, bare numbers take the design's units, and they are feature
  inputs. frame.profile.properties/insert read them the same way.
- Default root/toe radii shrink to the room a thick section leaves; radii
  given are checked by name; a radius must be 0 or at least 0.01 mm; error
  messages report the wall or flange value given, not twice it.
- A line within 1e-6 rad of vertical orients as vertical, so rounding never
  turns a post a quarter turn.
- A 2D frame sketch in a differently placed component is mapped into the
  member's frame, and member features recompute when placements change.
- Caps before lookups for a design's `lines`; a line listed twice is
  refused; a 3D wire that runs back on itself is not a line.

Regression tests for each, from the review's scenarios.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Frames: the Frame Member dialog on the ribbon (#4003)

- Frame Member (MODEL > Create) opens a dialog: the frame sketch's lines
  picked in the view (or All Lines), a built-in profile and its dimensions or
  a section picked in another sketch, anchor, angle, mirror and name, with a
  live preview. Edit Feature reopens it filled in.
- Lines picked in the view keep their sketch (a SKETCH_LINES input picks
  ExternalSketchCurve): line ids are each sketch's own, so two 3D sketches
  both have a `w2`, and the members went on the newer sketch's lines.
- timeline.redefine takes frame.member. Members keep their names through an
  edit unless it gives them a new body_name; frame.member takes `names`.
- A member whose section is a whole profile sketch keeps it when edited
  (the dialog shows the sketch instead of picking every region, which would
  fill a tube's hole).
- An original frame_member icon, open for review, on the ribbon, timeline
  and browser.
- The angle profile is listed as L-Angle, and web and flange as Web/Flange
  thickness, so they translate apart from the existing Angle, Web and Flange;
  the dialog's labels are in en, ja and zh. en-GB no longer repeats the frame
  command labels it spells the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Rectangular and circular pattern: copies that miss the body are reported (#3042) (#3919)

* claim: #3042

* Warn when cut pattern copies remove no material

A rectangular or circular pattern whose copies miss the body succeeded silently and those copies did nothing. The pattern now reports how many of its cut copies removed no material, and keeps the copies that land.

* Delete in a rolled-back history warns about the features after the marker (#3471) (#3698)

* claim: #3471

* Delete in a rolled-back history warns about the features after the marker (#3471)

With the timeline rolled back, the checks a delete runs first (the
confirmation's "these features will fail", timeline.dependents and
selection.delete's would_fail) compared models evaluated only up to the
marker. Features after it, broken by the delete, were not listed, so their
error marks turned up only once the timeline moved to the end.

newly_broken_in_history evaluates both sides over the whole history when
either has a marker (on copies; the model and document are unchanged) and is
used by the delete's report and by timeline.dependents.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* macOS: Fusion navigation preset does not support Shift + two-finger orbit (#3446)

* Macos: keep touchpad/key modifiers true to fusion

* fix: remove obsolete orbit snap reset from trackpad navigation

Upstream removed ViewportState::orbit_snap. The new trackpad orbit path retained one reference, causing E0609 in the PR merge build.

Validation: all seven trackpad tests pass; cargo check -p solidcraft-ui-egui --all-targets passes on the exact failing CI merge 66a2791b with this fix applied; rustfmt and diff checks pass.

Co-Authored-By: GitHub Copilot <noreply@github.com>

* fix: retain Fusion trackpad help in refactored preferences

Preserve upstream's new preferences pages and apply the translated Fusion trackpad tooltip in prefs/navigation.rs.

Validation: cargo build -p solidcraft passed. Tests and full CI were not run, as requested.

Co-Authored-By: GitHub Copilot <noreply@github.com>

* style: format Fusion trackpad preferences tooltip

Apply rustfmt's required wrapping for the relocated tooltip. No behavior changes.

Validation: cargo fmt --all -- --check and git diff --check passed.

Co-Authored-By: GitHub Copilot <noreply@github.com>

---------

Co-authored-by: GitHub Copilot <noreply@github.com>

* 2-point circle takes a typed diameter (#3278) (#3335)

* claim: #3278

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* 2-point circle takes a typed diameter (#3278)

After its first point the 2-point circle tool showed no value box, so the
circle could only be sized with the cursor. It now shows a Diameter box,
like the centre circle: the typed value puts the second point that far
along the cursor's direction, and Enter draws the circle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Phanoob <phanoob@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Reorder design tabs by dragging them (#3187) (#3188)

* claim: #3187

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Reorder design tabs by dragging them (#3187)

A tab dragged along the strip follows the pointer while the others make
room, and lands where it is released; Escape, or releasing down the
window, leaves the order as it was. With the custom title bar a drag that
starts mostly up or down (or on the only tab) still moves the window.
The control channel's ui.documents gains a move action (from, to).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Phanoob <phanoob@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Renaming a body keeps the evaluated timeline, off the window thread (#2467) (#2468)

* Renaming a body that later features change evaluates nothing (#2467)

Model::rename_body now keeps the results when later features change the
body (a pattern, a chamfer, a join). It still evaluates when a body of
that name comes and goes or isn't the same body throughout, when a later
body's name is worked out from it (X (2)), or when a message quotes it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Rename box: rename off the window thread, and locked bodies follow a rename (#2467)

The rename box commits body.rename through run_heavy, so a rename that
still evaluates later features (one names the body in its targets)
computes beside the window instead of freezing it. Locked bodies now
follow a body.rename from anywhere (the rename box, the control channel,
scripts), not only the rename box.

---------

Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Toolbar: a constraint dragged out of the small box becomes a full button (#5254) (#5456)

* claim: #5254

* Toolbar: a constraint dragged out of the small box becomes a full button (#5254)

The Constrain panel's small box of constraint icons was a fixed list, so
dragging one onto the full-size buttons saved a new order but it was still
drawn in the box. The toolbar settings now remember, per tab, which box
commands were moved out; those are drawn as full buttons, the box closes
up, and dropping one back on a box icon returns it. Reset Toolbar restores
the default box, and settings saved before this load unchanged.

* Sketch offset keeps its source lines free to turn (#4464) (#4466)

* claim: #4464

* Sketch offset keeps its source lines free to turn (#4464)

An offset adds a Parallel between each source line and its offset line.
Since offsets became associative, the offset's points are held during a
solve, so each Parallel pinned its source line to the offset line's
current direction: a closed chain lost its turning freedom (8 degrees of
freedom became 6), and Horizontal or an angle edit on a source line was
refused as conflicting.

The solver and the constructive reading now leave out a Parallel that
ties an offset line to the source it follows while the association lives;
the association already keeps them parallel. After Break the stored
Parallel drives again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Flat Pattern view draws perforations (#4440) (#4441)

* claim: #4440

* Flat Pattern view: draw perforation holes (#4440)

The flat view drew the outline, cut-outs, pockets and bend lines but not
perforation fields, though the flat-pattern data and the DXF export list
their holes. The view now reads the hole centres from the sheet through
SheetBody::perforation_holes, caches them per model state and body, and
draws them as circles in the cut-out style. Holes outside the viewport or
smaller than one pixel are skipped, and the cache is capped at one million
holes.

* "Don't Save" looks like a button in the Save changes prompt (#4380) (#4400)

* claim: #4380

* "Don't Save" looks like a button in the Save changes prompt (#4380)

The prompt's third action used the kit's low-emphasis text style, so it
didn't read as clickable and people thought Cancel was the only way out.
Add an opt-in secondary_left style to the confirm footer (a framed
secondary button from the theme tokens) and use it for Don't Save.

* Save prompt test: clear each frame's texture delta once it is applied

egui 0.36 panics when a TexturesDelta with unapplied deltas is dropped;
the test kept only the last frame's output and dropped the earlier ones.

* 3D mouse test: clear the frame's texture changes, so it passes in debug builds (#4382)

each_frame_hands_the_view_its_motion_through_the_drivers_mapping dropped
the output of a frame it ran (`let _ = ctx.run_ui(...)`). epaint panics
in debug builds when a TexturesDelta is dropped with deltas left ("Dropped
TexturesDelta with 1 unapplied deltas"), so the test failed under a plain
`cargo test` on every platform while the ci profile passed. It now clears
them, as the other UI tests do since #60.

Co-authored-by: Tyler Chiriboga <18270301+Tyler-Chiriboga@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Spanish (es-ES): catch up with en.json (#4292)

Co-authored-by: carlymx <24634598+carlymx@users.noreply.github.com>

* Esc ends the navigation bar's Pan, Orbit and Zoom modes (#4226) (#4233)

* claim: #4226

* Esc ends the navigation bar's Pan, Orbit and Zoom modes (#4226)

The nav bar toggled a mode on, but nothing cleared it on Esc, so a left drag
kept panning, orbiting or zooming. Esc now ends an active nav mode first;
the next Esc reaches the tool or dialog as before.

* Keep long Select Other labels inside the popup (#4209) (#4225)

* claim: #4209

* fix(ui): bound Select Other labels and expose full names (#4209)

* Deleting an inserted file removes its components, placements and joints (#4100) (#4113)

* claim: #4100

* Deleting an inserted file removes its components, placements and joints (#4100)

A STEP or mesh file inserted into a design makes a component tree. Deleting
its feature left that tree behind: the components, their occurrences, the
rigid joints that hold them and the bodies' component assignments. A later
body that took an imported body's name was then placed in the stale component.

Document::remove_component_tree removes a component subtree and everything
that refers to it: the features made inside it, its occurrences, the joints
on them with their motion links, joint origins, contact set members, motion
study keys and exploded moves, body component and offset entries, standard
part and link records, and configuration columns. Deleting an inserted
file's feature and component.delete both use it; component.delete now also
removes joints left pointing at a deleted occurrence. The body transfer
refusal moves with it, unchanged.

* Only an inserted file's feature takes its component tree when deleted

Mark the feature a STEP, IGES or mesh insert makes (`inserted`), and remove
the component tree on delete only for those and for a linked design's
feature. Deleting a copied component's independent base deletes just that
feature, as before. Older designs get the mark as they open (pending format
change), except copied components' independent bases.

* Rigid joint saved with the wrong occurrence when two occurrences share a component (#3857) (#3939)

* claim: #3857

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Joint dialog: a body of a further instance picks that occurrence, not the component's first (#3857)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>

* Cancel Sketch: leave a sketch without keeping its changes (#3838) (#3938)

* claim: #3838

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SBN24y2TcqRcm2zn8Gkj27

* Cancel Sketch: leave a sketch without keeping its changes (#3838)

sketch.cancel (Cancel Sketch, ribbon Exit group and the sketch's
right-click menu) puts the design back as it was when the open sketch
was created or opened for editing: a new sketch is removed with its
dimension parameters, an edited one returns to how it was. It is one
undo step, so Undo goes back into the sketch.

The session remembers the design and the undo history at the point a
sketch is entered. Cancel uses that only while the history still runs
through it (the same steps before, every step since taken in this
sketch); otherwise it refuses and points to Undo, so a stale entry can
never throw away later work.

The icon is the approved close glyph in the sketch accent (alias
sketch_cancel); ja and zh carry the new label.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SBN24y2TcqRcm2zn8Gkj27

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* 3MF import: name a Bambu Studio project's bodies after its objects (#3858) (#3859)

* claim: #3858 (3MF import: names from a Bambu Studio project)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* 3MF import: name a Bambu Studio project's bodies after its objects (#3858)

Bambu Studio leaves the name off the root model's objects and keeps it in
Metadata/model_settings.config (<config><object id="N"><metadata key="name">),
so every object of a project came in as Mesh1, Mesh2, ... A build item whose
root object has no name of its own now takes the object's name from those
settings. A name in the model still comes first, and part names, other
settings and objects of other model parts are left alone.

The settings are read only when a body needs a name, within what the model
parts left of the 1 GB budget. Settings that can't be read (malformed, nested
too deeply, too large) never fail the import: the bodies keep the generic
names, with a warning saying why.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Tyler Chiriboga <18270301+Tyler-Chiriboga@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Viewport: right-click keeps a multi-selection of faces and edges (#3733) (#3788)

* claim: #3733

* Viewport: right-click keeps a multi-selection of faces and edges (#3733)

---------

Co-authored-by: AhmetV <344941595+ahmetv10@users.noreply.github.com>

* Parameter import and image pickers offer their own file types (#3216) (#3314)

* claim: #3216

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Parameter import and canvas image pickers offer their file types (#3216)

Both opened the picker with no file type of their own, so it listed only
designs, CAD files and meshes (and All files). macOS lets you pick only
the types a filter names: CSV parameters, and PNG or JPEG images for a
canvas, were greyed out. They now ask for csv/json and png/jpg/jpeg,
which the picker lists first (#1801).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Phanoob <phanoob@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Return to the originating tab after finishing a sketch (#3250) (#3264)

* claim: #3250

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Return to the originating toolbar tab after finishing a sketch (#3250)

A sketch started from SHEET METAL or PLASTIC always ended on the SOLID
tab. Remember the tab that was active when the sketch began
(UiState::sketch_return_tab, not saved) and go back to it when the
sketch ends. Falls back to SOLID when that is not one of the normal
toolbar tabs. A sketch started from SOLID behaves as before.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Test: no one-element loop in the return-tab test (clippy single_element_loop)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>

* STEP import: let a cancelled import release the import slot (#3171) (#3189)

* claim: #3171

* Release cancelled file import slots (#3171)

* Dragging a part along its joint no longer highlights what's under the pointer (#5137) (#5140)

* claim: #5137

* Dragging a part along its joint no longer highlights what's under the pointer (#5137)

The viewport's hover pick was skipped during a box selection and camera
moves, but not while a jointed part was dragged, so faces and edges under
the pointer lit up on every frame of the drag. The pick now waits until
the joint drag ends.

* Browser: a body picked in the viewport is opened to and scrolled to (#3005) (#3081)

* claim: #3005

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Browser: a body picked in the viewport is shown in the Browser (#3005)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Merge main; viewport::select takes a toggle flag now

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Phanoob <phanoob@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Line tool snaps a diameter through the circle centre (#4326) (#4327)

A line drawn across a circle only snapped its end onto the curve, so a
diameter could miss the centre at any angle. Near the centre the line now
locks onto the radial line: on the rim it stays on the circle and the centre
is kept on the line, and on the centre point the end lands there.

* Flatpak: find the host's fonts for CJK text and the font list (#5124) (#5127)

* claim: #5124

* Flatpak: find the host's fonts for CJK text and the font list (#5124)

Inside the Flatpak sandbox the host's fonts are mounted at /run/host/fonts
and /run/host/user-fonts, which no font search looked at. Japanese and
Chinese text fell back to boxes, and the font list missed the host's
families.

- Search both folders for the CJK and Chinese fallbacks and for the
  installed-font list.
- The CJK fallback reads SOLIDCRAFT_FONT_DIRS (a path list, as the font
  list already does), with CRAFT_FONTS_DIR kept as a fallback.

* Crash reports: keep component.activate in the journal (#5079) (#5121)

* claim: #5079

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Crash journal keeps component.activate (#5079)

Activating a component changes only the session's active component and
occurrence path, not the design, so the journal dropped the command and a
crash report's replay built later parts under the previously active
component. The journal now also keeps a command that changes either.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Control channel and MCP on by default, with a setting to turn it off (#5561)

The app opens its localhost control channel on port 27878 (or the next free
one) unless Preferences > Assistant turns it off; --control and
SOLIDCRAFT_CONTROL_PORT still win. A current.port discovery file lets
solidcraft-cli mcp find the running app with no arguments.

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>

* Tests: moving an occurrence recomputes only the cuts and joins it touches (#4965) (#4982)

* claim: #4965

* Joins and cuts follow only the placements they depend on (#4965)

Tests that turning a joint between parts cuts no hole again, and that a
tool cutting another part cuts again when that part moves relative to it,
while an unrelated part moving keeps the result.

Booleans and Combine now hash only the placements of the bodies they take
as inputs, relative to the frame that evaluates them (#3397), so these
pin that behaviour for joins and cuts with named targets across parts.

* fix(ui): preview shortcut chords and wait for complete modifier input (#1810)

Keep physical modifier events out of the ordinary-key slot, preview held keys immediately, and confirm after release and a one-second settling interval. Preserve legacy control-channel modifier behavior while allowing explicit physical Control/Command input. Add regression coverage, a UI scenario, localized guidance and documentation.

Fixes #1809.

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>

* Move: scope box selection to the active component (#4459) (#4527)

* claim: #4459

* fix: scope Move box selection to active component (#4459)

* Delete removes a selected construction axis (#4692) (#4695)

* claim: #4692

* Delete removes a selected construction axis (#4692)

Delete on a construction axis skipped it with "the origin axes can't be
deleted". It now deletes the construction axis feature, like construction
planes; the origin axes X, Y and Z are still refused.

* Agent chat: end the agent's process group where `kill` is busybox' (#4587) (#4588)

* claim: #4587

* Agent chat: end the agent's process group with the system call (#4587)

The group was signalled with a `kill` program: `kill -s TERM -- -<pgid>`.
That works with procps' `kill` and with BSD's, but busybox' `kill` takes no
`--`: it prints its usage, exits 1 and signals nothing. The agent's helpers
(the CLI an adapter wraps, the MCP servers it starts) then stay alive after
the agent is ended.

The group is signalled with the system call instead, so no `kill` program and
no shell are needed and the behaviour is the same everywhere. `nix` (MIT) is
already in the tree through zip, and only on Unix: the browser build does not
take it.

The two tests that cover this now pass where they failed before:
`agent_chat::process::tests::dropping_the_process_ends_its_group` and
`agent_chat::process::tests::stopping_the_agent_leaves_no_helpers_running`.

* Extrude: picking a face, vertex or parallel plane off the profile switches the extent to To Object (#2855)

* Extrude: picking a face, vertex or parallel plane off the profile turns the extent to To Object

With profiles picked, a face off their plane (or curved), a vertex off it, or a
construction plane parallel to it is taken as the To Object target instead of
another profile: the extent switches without the Extent Type choice, and the
distance shows how far the target is. Coplanar faces still join the profiles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Extrude: automatic To Object is a Preferences setting, off by default

Preferences > Design > "Extrude to a picked target" (`extrude_auto_to_object`, default
false) turns the automatic To Object on. Off, picks in the Extrude dialog only add
profiles and the distance is not synced to a target, as before this change.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>

* Regenerate THIRD-PARTY-LICENSES.txt and docs/features.md [skip ci]

* FreeCAD/Rhino import: decode caps with room for detailed parts (#1783) (#3209)

* claim: #1783

* FreeCAD/Rhino import: decode caps with room for detailed parts (#1783)

cadmpeg decoded FreeCAD and Rhino files under its service profile, whose
million collection items and 2.56e8 work units a detailed part (a sprocket,
a threaded rod, a part with a few hundred faces) runs past: the import was
refused with "resource limit on CollectionItems: BudgetExceeded". Allow
8 million items and 2e9 work units, half of cadmpeg's desktop profile; the
input-size cap stays SolidCraft's STEP limit and every other cap stays at
the service profile's.

decoding_is_capped checks the two raised caps (and that they stay below the
desktop profile's) and that every other cap is still the service one.

Fixes #1783

* A spherical shell revolves into a solid with a void, and cuts and joins with hollow tools work (#451) (#4182)

* Assembly: ground the first part; drag free joint chains (#2804) (#2805)

* assembly(feat): ground first parts and drag free joint chains

Ground the first top-level part when it is created and let face dragging reposition free occurrences or any member of a floating joint chain in one undo step. Preserve exact repeated-placement frames, joint values, modifier selection and Escape cancellation. Keep the accumulated fixture, viewport layout and documentation integration fixes from the existing PR.

Signed-off-by: Karl Von Richter<karl@embeint.com>

* assembly(fix): update movable fixtures for first-part grounding

Accept the documented grounded override in component creation, explicitly free movement fixtures, and keep linked clipboard pastes movable even in an empty document. Cover first and reused linked pastes, default insertion grounding, and single-step undo in the existing link tests.

Signed-off-by: Karl Von Richter<karl@embeint.com>

* assembly(test): keep the motion-cost crank free

Unground the first component in the document-level joint-face measurement fixture so its motion samples can rotate after first-part grounding. Preserve the rotation and shared-measurement assertions that caught the Linux CI failure.

Signed-off-by: Karl Von Richter<karl@embeint.com>

* assembly(test): free the frame placement fixture

Create the frame-member placement fixture ungrounded before moving it above its root sketch. Keep its world-space member assertions while respecting first-part grounding in the frame tests added on main.

Signed-off-by: Karl Von Richter<karl@embeint.com>

---------

Signed-off-by: Karl Von Richter<karl@embeint.com>

* UI: integrate theme radii, borders, palettes and viewport refresh (#5045) (#5046)

* claim: #4922

* UI: let themes own corner radii

* UI: use four-point panel and window radii in SolidCraft 95

* UI: align the 95 radius assertion with four-point panels

* claim: #4953

* UI: let themes own structural and control borders

* UI: single Blueprint outlines and dotted secondary rules

* UI: separate container outlines and add theme-owned 95 bevels

* claim: #5007

* UI: apply theme palettes to icons, workspace tabs and Gruvbox settings

* claim: #5045

* Viewport: invalidate cached geometry colours when the theme palette changes

* UI: preserve legacy theme logo colours with the borderless mark

* Section Analysis: OK with no plane picked turns the section off (#2410) (#2411)

While a section is on, Section Analysis opened with no plane picked now
says "No plane picked: OK turns the section off." and OK runs
inspect.section {"clear": true}. With no section on, OK with no plane
still asks for a plane.

Fixes #2410.

* Regenerate THIRD-PARTY-LICENSES.txt and docs/features.md [skip ci]

* Close Change Parameters with Escape (#3372) (#3399)

* claim: #3372

* Fix Escape cancellation in Change Parameters

Consume Escape before parameter editors can commit pending text on focus loss, while preserving autocomplete and shortcut capture priority. Add 11 UI regressions covering draft cancellation, preview cleanup, document and history preservation, and reopening saved values.

* Preserve construction references when axes and points are renamed (#4419) (#4449)

* claim: #4419

* fix: preserve renamed construction references (#4419)

* Browser: route body picks into open command dialogs (#2560) (#4371)

* claim: #2560

* fix(ui): route Browser body picks into active dialogs

* Fix midpoint picks in sketch dimensions (#4872)

Retain MID snaps across both picks and materialize reusable midpoint
constraints for point-to-line and point-to-point dimensions. Let midpoint
pairs choose horizontal or vertical spans with pointer placement before
entering a value.

Cover projected model edges, overlapping geometry, helper reuse,
undo/cancel/reopen, and existing angular and line-circle workflows.

* Sketch slots: provide typed dimension inputs for every variant (#2423) (#4318)

* claim: #2423

* fix(sketch): add typed dimensions to every slot tool (#2423)

* Version 0.21.0 on main (released from rc.2's code) (#5774)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* docs(mcp): Windows setup for Claude Desktop (#3794)

* docs(mcp): Windows setup for Claude Desktop (paths, two servers, Store config file)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(mcp): say the Store build can keep the config elsewhere, not that the other path fails

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Regenerate THIRD-PARTY-LICENSES.txt and docs/features.md [skip ci]

* MCP: wait out long requests, honest batch outcomes, batch tool aliases (#2901)

Rebased onto main as one commit (was 120d26f7, df488f9a, db9d1073 plus
merges of main).

- A request the app reports as still running (#1618) is waited for on the
  same connection through `control.pending`; past the wait the error says it
  was not cancelled and will still apply (or only write its file), later calls
  are not sent behind it, and the new read-only `pending` tool returns its
  result once it lands.
- A failed batch says what really happened: `rolled_back` only when the
  design is back where the batch started (as #3288 / #3242 on main: the app's
  `rolled_back` is believed; for an older app, every completed step undone;
  only a rollback asked for is reported as one), plus `applied`, `not_run` and
  `failed.still_running`.
- A batch step may name a tool (set_parameter, list_commands,
  describe_command, execute, measure, inspect_design, undo, redo, export, save,
  open, new_design, crash_reports); screenshot, body_topology, batch and
  pending are refused before anything is sent.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* 3D mouse: driver choice, guided calibration, button mapping and a picture of the device (#1463) (#3825)

* Add a small test boat to steer around with the 3D mouse

A little hull in its own camera, drawn by the UI itself and not the CAD
viewport, so a 3D mouse can be tried out without opening a design. It
brings the model (generated by assets/models/make_test_boat.py) and the
tests for how the camera moves it.

Nothing uses it yet; Preferences picks it up with the rest of the 3D mouse
work.

* UI kit: keep a choice row from flipping with the scrollbar

A choice row picks segments or a dropdown by whether its labels fit. A page
scrollbar that comes and goes narrows the row by its own width, so the row
could flip back and forth between the two on a page that sits right at the
edge. It is now judged on the whole row and through a small helper,
fits_steadily, that only changes its answer once the miss is wider than the
scrollbar.

Also tests for that, for typed numbers in a slider's value box, and a
test hook to put up the issue-report input barrier by hand.

* 3D mouse: device model, guided calibration, buttons and a picture

The 3D mouse becomes a device we know something about instead of six raw
numbers.

The reader picks between spacenavd and the direct hidraw driver, falls back
from one to the other, and says so when it can't open the device. Guided
calibration asks whether the device is a spring cap or a trackball, adds a
touch step, and uses a settle filter so a ball let go of stops reading as
motion. Buttons get jobs (keys, held modifiers, commands), saved per
device, with defaults for the SpaceMouse Pro.

Preferences gets a picture of the device that moves as it does, next to the
test boat, and the same picture can sit in the 3D view at Left Viewport,
Right Viewport or Left Window, above the floating History card like the
legend and readouts.

These ride together because the crate's calibration types, the Preferences
page, the Linux reader and the button hook all use each other.

* 3D mouse: offer to calibrate an uncalibrated device

When a 3D mouse shows up that has not been calibrated, a card in the 3D
view offers to calibrate it, to swap drivers when the other one would work
better, to close, or to never ask again. It is made once per device, goes
away after 30 seconds in view, and gives way to a status or computing
toast instead of stacking on it.

The card is the shared toast, which now takes buttons: they group by
purpose, wrap inside the card at narrow widths, and do nothing while an
issue report holds CAD input. The picture of the device in the 3D view
keeps clear of the card.

* 3D mouse: translations and attribution

The 3D mouse strings (ui.3d_mouse_*, the calibration steps and the offer
card) in every shipped language, the Japanese catalog note, and the credit
for the test boat model in ATTRIBUTION.md.

This is last because the code before it only looks keys up: it builds and
runs without these, falling back to English until they land.

* 3D mouse: draw its preferences with the settings kit like the rest of Navigation

The 3D mouse section was a folded header with its own layout. It now
uses the same kit as the sections above it: a settings_section card,
settings_form rows with the shared label column, dividers between the
device, axes, picture and buttons groups, Text::Hint for the helper
lines, and settings_control_width for the dropdowns.

The section is open like every other one (the header still folds it),
so the device badge on the folded header is gone; the calibration row
already names the device. Every setting, string and key is unchanged.
Tests that unfolded the section no longer click it, and the hint lines
are looked up as hints instead of notes.

* 3D mouse: show each axis's live tilt in Preferences, like main's axis rows

Each view axis row is main's axis row again: a meter that follows the tilt of the motion driving it, with the dead zone marked, next to Invert, Speed and the "driven by" menu. The meter reads the calibrated travel when the device has a calibration and the raw reading over full scale when it does not, and asks for a frame whenever the reading changes. The helpers are back as they are on main, and the calibration state is a badge by the enable toggle.

* 3D mouse: line its preferences up with the rest of Navigation

- The two checkboxes sit side by side in the same two-column grid as Wheel and trackpad.
- Row labels stop repeating "3D mouse" and use sentence case: Calibration, Driver, Restore defaults, Reset view, Left viewport, Right viewport, Left window.
- Hints line up under the control column instead of the left edge.
- The six view axes are named Pan X, Pan Y, Zoom, Orbit X, Orbit Y and Roll so they fit the name column.
- Buttons are a divider and rows in a form like the other groups, without the bold sub-heading.
- The driver, dead zone, axes, picture and buttons sit behind a collapsed Advanced section, since most people never touch them.

* 3D mouse: tidy the Advanced test for clippy

* Timeline: suppressing a feature keeps later features' body names (#4302) (#4311)

* claim: #4302

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Timeline: suppressing a feature keeps later features' body names (#4302)

Bodies numbered by the counter (`BodyN`) take the next free number on every
recompute, so suppressing a feature that made one moved every later counted
body down one (`Body2` became `Body1`) and broke what named it, such as a
Move of `Body2`. timeline.suppress now stores the current names of later
features' counted bodies in their `body_names` first, as body.rename already
does for a renamed counted body (#1109). Engine only: no evaluation or
design-format change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Timeline: a body made while a feature is suppressed keeps its name (#4302)

Suppressing a feature freed its counted name (`Body1`), so a body made
meanwhile took it and became `Body3` once the feature was back. Suppress
now also stores the suppressed feature's own counted names, and the
evaluator adds the names stored on earlier suppressed features to the
state's `reserved_body_names` (the names a rename freed, #4957), which the
`BodyN` counter passes by. They are taken from the design before each
feature is computed and are part of the state digest, so a cached result
is only reused when they match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* cadmpeg 0.6.3: FreeCAD and Rhino import in the browser too (#4482)

cadmpeg 0.6.3 is pure Rust (ruzstd instead of the zstd C library, zip 9), so
solidcraft-io depends on it on every target and the wasm build reads .FCStd
and .3dm files like the desktop app. The browser's file pickers offer them.

0.6.3 replaces DecodeOptions::container_only with a decode depth; ask for
Geometry, what its STEP writer reads. zip 8 leaves the tree, and with it the
three cargo-deny advisory exceptions kept for it.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Pattern dialogs: Quantity fields get up/down step arrows (#4260) (#4264)

A new kit control, `ui_kit::count_field` / `count_row`, is an expression field for a
whole-number count with stacked step arrows at its right edge. An arrow steps the
evaluated count by one within the field's limits: a plain number is replaced, an
expression keeps its parameters and gains or adjusts a trailing `+ n`. The text
changes as typing changes it, so the live preview follows.

Used by the Quantity rows of the sketch Rectangular (1-500, both directions together
at most 500) and Circular (2-500) patterns, and the solid Rectangular, Circular and
Path patterns (1-1000).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Regenerate THIRD-PARTY-LICENSES.txt and docs/features.md [skip ci]

* Join: say so when the joined piece doesn't touch the body (#4434) (#4436)

* claim: #4434

* Join: say so when the joined piece doesn't touch the body (#4434)

A Join (extrude, revolve, sweep, loft, pipe, coil, primitives, thicken,
boundary fill, surface network) whose material meets none of its targets
still joins it to the first target as a separate piece. The result stays
as it was; the feature now warns: "the joined piece doesn't touch `Body1`,
so `Body1` is now in 2 separate pieces (New Body makes it a body of its
own)". History marks it and the command reply carries it.

The check compares pieces (outer shells, not voids) of the bodies the
feature changed or absorbed before and after it, so it holds for the
batched multi-profile path too, stays quiet for a join that bridges
pieces or closes a cavity, and leaves Combine (which joins the bodies it
is given) alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Hole: snap to face and round-edge centres, drag placed holes, section in the dialog (#2171) (#2912)

Placing a Single hole put it exactly where the click landed, with nothing to line it up by and no
way to move it but taking it out and picking again; the dialog was a list of size fields.

- Preferences › Design › Create Hole has two settings, both off by default, so a new installation
  places holes as before:
  - Snap to face and edge centres (`hole_snaps`): the planar face under the cursor offers its
    middle and the centres of its round edges (a boss, a rounded corner, a hole already there) as
    marks; a pick near one, or on or beside such an edge, takes that centre and the mark is named.
    Sketch points on the face still come first; Alt or Ctrl held places it where clicked.
  - Move holes by clicking or dragging (`hole_moves`): each placed hole has a handle that drags it
    (snapping when snaps are on) and takes it out when clicked, and a pick elsewhere on its face
    moves it there. Edit Feature shows the handle too, so a hole can be moved later (#1047).
- The Hole dialog draws a section of the hole as its options make it, with each size in a field
  beside the dimension it sets; a hovered or focused field lights its dimension. The layout is a
  new kit element, `ui_kit::figure_rows` (a caller-painted figure beside captioned fields).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Hole: place a Single hole by distances from reference edges (#37) (#2931)

A Single hole can take up to two straight edges of its face as references,
each with the distance of the hole's centre from that edge's line, measured
in the face's plane. `solid.hole` takes them as `references: [{edge, distance}]`;
the edges are found again by their persistent names, else their points, so
the hole follows them when the part changes, and each distance is a
parameter. A bad reference (no edge there, a curved edge, one square to the
face, parallel edges, a negative distance, a centre off the face) is refused.

In the Hole dialog, Reference 1 and 2 follow the face, each with its Distance
row; the canvas shows each distance as a dimension from its edge to the hole,
with a box just past the edge to type it in. Picking an edge measures its
distance (and its box takes the keyboard), typing one moves the hole, and
moving the hole measures them again. Edit Feature picks the edges again with
their distances.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Signed-off-by: Karl Von Richter<karl@embeint.com>
Co-authored-by: Frank <58932224+plexi32@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: raykholo <raykholo@users.noreply.github.com>
Co-authored-by: CamWight <91156642+CamWight@users.noreply.github.com>
Co-authored-by: SS-EN <84559909+SS-EN@users.noreply.github.com>
Co-authored-by: Colin Faulkingham <colin.faulkingham@gmail.com>
Co-authored-by: Karl Von Richter <karl@von-richter.net>
Co-authored-by: HiroKenEz <59478073+HiroKenEz@users.noreply.github.com>
Co-authored-by: Peppe Bergqvist <p@bergqvi.st>
Co-authored-by: GitHub Copilot <noreply@github.com>
Co-authored-by: phanoob <lesur.Stephane@wanadoo.fr>
Co-authored-by: Phanoob <phanoob@gmail.com>
Co-authored-by: Kai Evans <nemshilov@gmail.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
Co-authored-by: Tyler Chiriboga <tylerchiriboga.apps@gmail.com>
Co-authored-by: Tyler Chiriboga <18270301+Tyler-Chiriboga@users.noreply.github.com>
Co-authored-by: CaRLymx <carlymx@gmail.com>
Co-authored-by: carlymx <24634598+carlymx@users.noreply.github.com>
Co-authored-by: Loohi <78334654+Loohi@users.noreply.github.com>
Co-authored-by: leValdemar19 <valdemarnord@hotmail.com>
Co-authored-by: AhmetV <mihendiz10@gmail.com>
Co-authored-by: AhmetV <344941595+ahmetv10@users.noreply.github.com>
Co-authored-by: rlartot <94693724+rlartot@users.noreply.github.com>
Co-authored-by: Christian Svensson <blue@cmd.nu>
Co-authored-by: Thodoris Tsagkogeorgas <thodoristsagko@gmail.com>
Co-authored-by: ahmetalpcelik <80778880+ahmetalpcelik@users.noreply.github.com>
Co-authored-by: ealitt <ealitt@gmail.com>
Co-authored-by: Homero Thompson <vdirienzo@gmail.com>
Co-authored-by: Diogo Soares Rodrigues <dsrodrigues20@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Artem Sushchev <Suschev.Artem+github@gmail.com>
Co-authored-by: Nathan Dane <nathandane.dev@gmail.com>
Co-authored-by: Kai Evans <hi@kaievans.co>
Co-authored-by: Victor Santos Oliveira <56417388+Vittuu@users.noreply.github.com>
Co-authored-by: cronos <crono2250@users.noreply.github.com>
Co-authored-by: rbousigues88 <rbousigues@gmail.com>
Co-authored-by: The Nomad <nomad@positron3d.com>
Co-authored-by: Jackson Oursland <jacob.oursland@gmail.com>
Co-authored-by: Jacques <59315319+Jacquesvdberg92@users.noreply.github.com>
2026-10-24 02:09:20 -04:00
selune 0b3e9a9e1e fix(render): wrap complete backend diagnostics (#5714) (#5715)
Preserve full backend/job/decoded-image diagnostics in the existing shared panel without ellipsis. The painted-galley regression covers Floating/Compact and light/dark, complete text, wrapping and fixed panel width. Exact-tree Render UI 33/0, strict UI Clippy, WASM and repository gates pass. Real safe-mode window captures, camera restore, CPU export and normal exit are recorded; no backend behavior or unrelated UI policy changes.
2026-10-24 08:05:00 +02:00
selune 24c9d14663 GPU: adapt dispatches and retire lost devices (#5754) (#5755)
* claim: #5754

* fix(gpu): adapt dispatch size and retire lost path devices (#5754)

Start with a 64-item quantum and adapt bounded work toward 20 ms from observed completion, including denoiser tiles. Never exceed 16384 work items; hardware-specific shader cost means this is not a universal TDR guarantee. Publish device loss/uncaptured failure/poll failure through live executor availability without reinitializing automatically. Source-pinned required-Vulkan GPU suite: 105 passed, zero failed, three existing ignores, including actual device.destroy retirement and session availability. Strict GPU/engine all-target Clippy passes. Broader engine and repository/WASM continuation remains separately recorded. No geometry baseline, CI rule or published format history change.
2026-10-24 08:04:52 +02:00
selune 934d4ddae2 Render: defer path-host startup until first use (#5742) (#5743)
* claim: #5742

* fix(render): defer GPU path initialization until first use (#5742)

Retain the selected adapter factory during Design/safe-mode startup; instantiate once on first Render entry, including late viewport installation. Preserve the deferred/preparing/ready/failed state across document changes and retain selected fallback adapter policy. Exact tree passes 32 render-workspace and 1966 full UI tests (0 failures; 7 existing ignored), strict native all-target Clippy, wasm check and repository gates. Three wasm warnings remain in untouched upstream files. Fresh native desktop acceptance is separate and in progress; no whole-renderer/main sign-off implied.
2026-10-24 07:25:50 +02:00
selune 7a81a7fc15 library: transport PBR and engineering cards without lossy copies (#1086)
* wip(library): reconcile exact rich-catalog replacement semantics (#1062)

Source-only scoped extraction from original 0f801962 / #1086 on the refreshed #1060 card prerequisite. Preserve current asynchronous picker guards and source-ID handles. Scalar v1 and rich-v2-without-PBR clear existing PBR; supplied rich PBR is retained. No new saved concept or duplicate card descriptor. Formatting and source review completed; compile/native acceptance is still pending.

* test(library): preserve explicit downgrade and fallible UI regressions (#1062)

Request scalar v1 explicitly in the card downgrade refusal regression; rich v2 remains the default lossless writer. Propagate fallible harness actions with ? in the shared clicked-catalog helper, preserving all assertions. No production code, limits or ignore changes. Fresh grouped IO/engine/UI tests, scoped strict Clippy and WASM pass on this exact tree.
2026-10-24 07:13:19 +02:00
selune 04acdc251f GPU: preserve error scopes on cancelled preparation (#5722) (#5723)
* claim: #5722

* fix(gpu): unwind preparation error scopes on cancellation (#5722)

Move fallible CPU HDR packing before GPU scopes and drain the remaining scope stack in reverse order before propagating denoiser/resource allocation failures. Native regressions cover cancellation and allocation-error cleanup plus subsequent scope reuse. Exact candidate Vulkan-required GPU suite: 101 passed, 0 failed, 3 existing ignored; strict GPU all-target Clippy and repository assets/layers/lists/container checks pass. No driver exception, shader transport or test limit is suppressed.
2026-10-24 01:04:07 +02:00
selune 8cef71d2d2 Merge pinned main ancestry after #5705
The validated content already landed by squash. Restore the normal main-to-render ancestry requested in #5493 without changing its tree.
2026-10-23 23:35:47 +02:00
selune 47a1459060 Sync current main into render, preserving release and viewport changes (#5705)
* Regenerate THIRD-PARTY-LICENSES.txt and docs/features.md [skip ci]

* Viewport: whole-body highlights reuse the model's own triangles (#4554) (#5109)

Hovering or selecting whole bodies -- a browser row, a History group, a body
under the pointer in body-pick mode -- built a CPU copy of every body's
triangles into the highlight scene. On a 40-body 4.3 MB STEP assembly that was
147 bodies, 70 MB of triangle copies and a 519 ms frame.

The model already has the same geometry on the GPU. The highlight now names it
and the renderer draws it again in the highlight colour:

- bodies drawn from a display plan: the model's evaluated plan chunks, with a
  hover/selection tint uniform (the chunks stay resident in the model scene);
- bodies drawn as placements of shared triangles: the same placements of the
  model's shared triangles, coloured per instance;
- bodies the model builds on the CPU: their vertex range in the model scene's
  block.

See-through and faded bodies keep the CPU feedback, which colours them; faces
and edges highlighted on their own are unchanged. The highlight looks the same
(verified side by side).

On the assembly the highlight build drops from 519 ms to 9 ms and no longer
copies any triangles; selecting the whole assembly takes 9-20 ms.

Co-authored-by: Claude Code <noreply@anthropic.com>

* Offer to stand up SolidWorks files instead of turning them silently; add Turn Up Axis to the component menu (#5655)

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>

* Sketch on a picked origin plane uses the plane shown after standing an import up (#5659)

* Sketch on a picked origin plane uses the plane shown, also in a moved component

After a STEP file opens its component stays the active one, and standing it up
turns that component a quarter about X. Starting a sketch on XY picked in the
viewport then sent `{"plane": "XY"}`, which names the active component's own
XY: upright after the stand-up. The sketch landed on the world XZ, Look At
turned to face Y and the extrude went along Y.

The viewport and browser only show the design's origin planes, so a picked
origin plane now goes to sketch.create written out as that world plane while
the active component is moved; sketch.create maps it into the component's
frame once. The flip toward the viewer uses the same plane.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Stand-up axes tests: stand the file up through the offer when one is waiting

#5655 offers to stand up SolidWorks files instead of turning them silently.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* gpu tests: give the two test frames the new hover and selection tints (#5698)

#5109 added Frame::hover_tint and selection_tint; two test frames that landed alongside it didn't set them, so solidcraft-gpu's tests stopped compiling on main.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* Release 0.21.0-rc.2 (#5691)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jackson Oursland <jacob.oursland@gmail.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
2026-10-23 17:29:50 -04:00
selune ee5ca2430b feat(render): expose independent anisotropic GGX core (#1694) (#1699)
Extract the original numerical source and analytic tests from published #1698 at 463a3e0d into its existing #1699 constituent. Keep current GPU/shared jobs, format and kernel untouched. Add scoped numerical-contract documentation. Full render library 158 passed, strict scoped Clippy and wasm pass, as do assets/layers/lists/format; source tree pinned in the acceptance record. No runtime consumer or final combined renderer acceptance is claimed.
2026-10-23 23:06:31 +02:00
selune ac08c3f106 Merge pinned main into render (#5669)
Normal history-preserving main-to-render synchronization under #5493. Retain both material defaults and engineering-card bounds; initialize the new upstream test fixture with no optional card. The corrected source passed fresh quick checks. No rewrite of main/render, manual document format bump or final main promotion is performed.
2026-10-23 22:10:14 +02:00
0xlillith 6a197b6cf6 test(material): initialize optional cards in upstream density regression
Fix the E0063 found by quick run 40015318093 while combining main #4785 with render #1060. The new upstream fixture explicitly has no engineering card. Keep every density/name/default-material assertion and production implementation unchanged.
2026-10-23 22:06:32 +02:00
0xlillith e3d7cbe830 Merge pinned main into the shared render integration
Synchronize official main 17bac141c8 into render e3c9f22ead under #5493. Resolve only the independent DEFAULT_MATERIAL / MAX_USER_MATERIALS imports, keeping both rules. Preserve all main and renderer history. No manual document format bump or released migration edit; release folding is inherited from main. Fresh quick checks are required before this normal merge reaches render.
2026-10-23 21:56:13 +02:00
Christian Svensson e3c9f22ead GPU path tracing: use compatible Vulkan adapters (#3306)
Land the final Vulkan-only path-executor policy after #2191 under the #5493 constituent route. Fixes #3217. Fresh quick run 40012565783 passed. Preserve adapter/software policy and CPU fallback; no DXC dependency or workflow changes. Full native/hardware acceptance and final jyviko sign-off remain separate.

Co-Authored-By: Christian Svensson <blue@cmd.nu>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 21:40:28 +02:00
Christian Svensson c6cbde3706 Render: studio lighting, clear coat and glass on GPU (#2191)
Integrate the original studio renderer after the GPU executor and async UI foundations, under #5493. Fresh render-target quick run 40011408339 passes. Preserve attribution, bounded resource admission and documented glass/denoising limits. Historical native benchmarks retain their source pins. Full renderer-to-main CI and jyviko sign-off remain separate.

Co-Authored-By: Christian Svensson <blue@cmd.nu>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Codex <noreply@openai.com>
2026-10-23 21:33:49 +02:00
Christian Svensson b0d7572daf Render: asynchronous GPU setup and completed-image panel (#2780)
Integrate the original asynchronous setup, progress and completed-image UI after #2067. Refreshed source passes quick gate 40009901424. Original attribution retained. Final render-to-main full CI and jyviko sign-off remain separate.

Co-Authored-By: Christian Svensson <blue@cmd.nu>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 21:24:40 +02:00
Christian Svensson 30449418ac Render: progressive GPU path-tracing executor (#2067)
Land the unchanged contributor GPU foundation into render under the maintainer policy in #5493. Fresh quick gate 39993339605 passed. Original author and co-author attribution retained; no workflow file changes. Final render-to-main CI and jyviko sign-off remain separate.

Co-Authored-By: Christian Svensson <blue@cmd.nu>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 21:10:55 +02:00
selune 95a56ca00e Persist validated engineering cards independently of mass and optics (#1060)
Render constituent under #5493. Fixes #1048. One pending material-engineering-cards descriptor, immutable released migrations, independent assigned density and source conditions. Nine persistence and ten physical-card regressions plus the fresh quick gate pass. Additional native follow-up is reported separately; no full-stack acceptance is implied.
2026-10-23 20:49:32 +02:00
selune fbf4a5458d Preserve translated source-label aliases (#5619)
Render integration prerequisite for #5493. Fixes #5618. Retain stable-ID fallback and translated legacy captions; include controlled regression coverage.
2026-10-23 20:06:26 +02:00
selune 2f8677a640 Merge main into render for constituent reconciliation (#5493) 2026-10-23 19:46:29 +02:00
selune bf69db04dd Normalize UI container violation paths across platforms (#5599)
Report repository-relative paths with forward slashes so diagnostics and the existing nested-package regression assertions agree on Windows and Unix. Keep the container detection rules unchanged.

Validation: official ui-containers gate and all seven focused xtask tests passed; rustfmt and diff checks passed. Only xtask/dependencies were compiled; no application build or heavy tests.

(cherry picked from commit 44b58d8e10)

Scoped render-branch prerequisite from #4507, coordinated under #5493. No unrelated translation/kernel changes are included. Fresh Windows fmt, seven ui_containers regressions and the production container gate pass on render base 3a6153d18f. Full CI is reserved for final render-to-main acceptance under the maintainer policy.

Co-authored-by: Victor Santos Oliveira <56417388+Vittuu@users.noreply.github.com>
2026-10-23 13:21:05 -04:00
selune e9f2f251e4 Merge current main into render per #5493 integration policy 2026-10-23 19:18:26 +02:00
Jackson Oursland 3a6153d18f Add .vscode to .gitignore. (#5588) 2026-10-23 12:49:05 -04:00
1173 changed files with 4659 additions and 291723 deletions
+1
View File
@@ -42,6 +42,7 @@ filter = "binary(action_matrix) & test(=action_matrix_sample)"
threads-required = 12
priority = 100
# The slowest tests start first, so a shard does not end with one of them running alone on a
# core while the others sit idle (seed_81 alone takes about 20 minutes under CI load). Medians
# from the CI test timings (the junit artifacts), slowest first; a test over ~100 s belongs here.
-3
View File
@@ -1,3 +0,0 @@
# Preserve canonical source receipts and the runtime icon-discovery snapshot.
.gitattributes text eol=lf
/assets/icons/ui/design/requested_icons.txt text eol=lf
-1
View File
@@ -19,7 +19,6 @@ body:
label: Steps to reproduce
description: |
What you clicked or typed, in order. For a problem on a particular design, `solidcraft-cli script design.solidcraft --out repro.json` turns its history into a script that `solidcraft-cli eval repro.json` replays; attach it, trimmed to the failing step if you can.
Using an AI agent with SolidCraft's MCP server? The `solidcraft-reporting` skill in [`skills/`](https://github.com/tangency-labs/solidcraft/tree/main/skills) has it reproduce the problem in a fresh design and write these steps for you.
placeholder: |
1. Sketch a circle on the XY plane
2. Extrude it 10 mm
-6
View File
@@ -2,12 +2,6 @@
Describe the problem and resulting behavior. Link the issue when applicable.
Changes a default: no
<!-- Required line, replace "no" if this changes what a user gets out of the box (a default value, key binding, setting, tool behaviour):
Changes a default: yes — <what>, old behaviour kept behind preference <name>
Docs-only and bot PRs are exempt. -->
## Validation
What did you run, and what were the results? Include expected and measured values for geometry changes.
+1 -1
View File
@@ -295,7 +295,7 @@ jobs:
- name: Merge-of-main detection self-test
run: python3 -I scripts/ci/test_main_merge.py
- name: Claim, triage and kernel-paused bot tests
run: node --test scripts/claim/*.test.js scripts/triage/*.test.js scripts/kernel-paused/*.test.js scripts/pr-checks/*.test.js
run: node --test scripts/claim/*.test.js scripts/triage/*.test.js scripts/kernel-paused/*.test.js
# Fast feedback for a pull request that has not been approved for the full CI yet.
quick-compile:
+3 -9
View File
@@ -1,8 +1,6 @@
# Files generated from the code are rewritten here, on main, after each merge, so pull requests
# never edit them (two pull requests that each regenerated one would always conflict):
# THIRD-PARTY-LICENSES.txt (`cargo xtask licences`) and docs/features.md (`cargo xtask features`).
# Pending files (assets/locales/pending, attribution/pending) are folded into the catalogs and
# ATTRIBUTION.md by `cargo xtask fold-pending`.
name: Generated files
on:
@@ -19,8 +17,6 @@ on:
- "crates/engine/**"
- "apps/solidcraft-cli/**"
- "xtask/**"
- "assets/locales/pending/*.json"
- "attribution/pending/*.md"
- ".github/workflows/generated.yml"
workflow_dispatch:
@@ -52,15 +48,13 @@ jobs:
run: cargo xtask licences
- name: docs/features.md
run: cargo xtask features
- name: Pending locale strings and attribution rows
run: cargo xtask fold-pending
- name: Commit if changed
run: |
if [ -z "$(git status --porcelain)" ]; then echo "unchanged"; exit 0; fi
if git diff --quiet -- THIRD-PARTY-LICENSES.txt docs/features.md; then echo "unchanged"; exit 0; fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add -A THIRD-PARTY-LICENSES.txt docs/features.md assets/locales ATTRIBUTION.md attribution
git commit -m "Regenerate generated files and fold pending files [skip ci]"
git add THIRD-PARTY-LICENSES.txt docs/features.md
git commit -m "Regenerate THIRD-PARTY-LICENSES.txt and docs/features.md [skip ci]"
for _ in 1 2 3; do
git pull --rebase origin main && git push origin HEAD:main && exit 0
sleep 10
-2
View File
@@ -39,8 +39,6 @@ jobs:
run: python3 packaging/tests/nightly_test.py
- name: Windows executable icon validation regression
run: python3 packaging/tests/windows_icon_test.py
- name: AppImage CLI wrapper regression
run: python3 packaging/tests/apprun_test.py
- name: Flatpak desktop smoke validation regression
run: python3 packaging/tests/flatpak_test.py
- name: PowerShell
-69
View File
@@ -1,69 +0,0 @@
# Two quick checks on every pull request (CONTRIBUTING.md, "Sending a change"): no second open PR
# for an issue a lower-numbered one already closes, and the "Changes a default:" line is filled in.
# Neither is a required status check. The logic is in scripts/pr-checks/pr-checks.js (tested in ci.yml).
name: PR checks
on:
# pull_request, never pull_request_target: nothing here checks out or runs the PR's code, and the
# token is read-only for fork PRs. Only scripts/pr-checks from the PR's own merge commit is run.
pull_request:
types: [opened, edited, synchronize, reopened, ready_for_review, labeled, unlabeled]
permissions:
contents: read
pull-requests: read
concurrency:
group: pr-checks-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
duplicate:
name: Duplicate PR check
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
sparse-checkout: scripts/pr-checks
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const checks = require('./scripts/pr-checks/pr-checks.js');
const r = await checks.checkDuplicate({ github, context, core });
core.info(JSON.stringify(r));
defaults:
name: Defaults line check
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
sparse-checkout: scripts/pr-checks
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const checks = require('./scripts/pr-checks/pr-checks.js');
const r = await checks.checkDefaults({ github, context, core });
core.info(JSON.stringify(r));
# Labelling needs write access, which a fork PR's token never has, so it is its own job and best
# effort: a fork PR just doesn't get the `changes-default` label from here.
label:
name: Label changes-default
runs-on: ubuntu-24.04
timeout-minutes: 5
continue-on-error: true
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
sparse-checkout: scripts/pr-checks
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const checks = require('./scripts/pr-checks/pr-checks.js');
await checks.syncDefaultLabel({ github, context, core });
+8 -63
View File
@@ -4,12 +4,11 @@
# comes from [workspace.package] in Cargo.toml. An agent verifies the artifacts and publishes
# the draft separately; see docs/releasing.md. Tags and pushes to main do not trigger releases.
# At midnight Eastern, changed main commits build and publish a nightly pre-release.
# A version with a suffix (0.2.0-rc.1) is a pre-release.
# The macOS app is signed and notarized when the APPLE_* secrets exist, nothing else is signed yet.
# A version with a suffix (0.2.0-rc.1) is a pre-release. Nothing is signed yet.
#
# Linux x86_64, aarch64 AppImage (+ .zsync), .deb, .rpm, .flatpak, .snap, .tar.gz packaging/linux
# FreeBSD x86_64 .tar.gz (built in a FreeBSD VM) packaging/freebsd
# macOS universal .dmg, and the CLI as a .zip (both archs built in parallel) packaging/macos
# macOS universal .dmg, and the CLI as a .zip packaging/macos
# Windows x64, x86, arm64 .msi and a portable .zip; x64 also the plain .exe packaging/windows
# Web a .zip of the static site packaging/web
#
@@ -296,19 +295,11 @@ jobs:
path: dist/release/*
if-no-files-found: error
# The two halves of the universal binary build in parallel on their own runners (each alone
# took about half of the old single job's 45 minutes); `macos` below joins and packages them.
macos-build:
name: macOS build ${{ matrix.arch }}
macos:
name: macOS universal
needs: version
if: needs.version.outputs.build == 'true'
runs-on: macos-14
strategy:
fail-fast: false
matrix:
include:
- { arch: aarch64, target: aarch64-apple-darwin }
- { arch: x86_64, target: x86_64-apple-darwin }
env:
SOLIDCRAFT_VERSION: ${{ needs.version.outputs.version }}
SOLIDCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }}
@@ -316,58 +307,12 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
targets: aarch64-apple-darwin, x86_64-apple-darwin
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
key: release-macos-${{ matrix.arch }}
- name: Build
run: packaging/macos/package.sh --build-only ${{ matrix.target }}
# Not named release-*: the release job collects every release-* artifact as an asset.
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: macos-bin-${{ matrix.arch }}
path: |
target/${{ matrix.target }}/release/solidcraft
target/${{ matrix.target }}/release/solidcraft-cli
target/${{ matrix.target }}/release/solvecraft-cli
if-no-files-found: error
macos:
name: macOS universal
needs: [version, macos-build]
if: needs.version.outputs.build == 'true'
runs-on: macos-14
env:
SOLIDCRAFT_VERSION: ${{ needs.version.outputs.version }}
SOLIDCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: macos-bin-aarch64
path: target/aarch64-apple-darwin/release
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: macos-bin-x86_64
path: target/x86_64-apple-darwin/release
- name: Set up Developer ID signing and notarization
# Optional until the APPLE_* secrets exist (docs/releasing.md, "macOS signing"). Without
# them (forks, dry runs) the app keeps its ad-hoc signature and is not notarized.
env:
APPLE_CERTIFICATE_P12: ${{ secrets.APPLE_CERTIFICATE_P12 }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_API_KEY_P8: ${{ secrets.APPLE_API_KEY_P8 }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
run: packaging/macos/signing.sh --setup
- name: Join, sign, package and check (dmg, CLI zip; the dmg is mounted and checked)
run: |
# Artifacts don't keep the executable bit.
chmod +x target/*-apple-darwin/release/solidcraft target/*-apple-darwin/release/solidcraft-cli target/*-apple-darwin/release/solvecraft-cli
packaging/macos/package.sh --arch universal --skip-build ${MACOS_SIGN_IDENTITY:+--sign}
- name: Remove the signing keychain and API key
if: always()
run: packaging/macos/signing.sh --cleanup
key: release-macos-universal
- name: Build and package (dmg, CLI zip; the dmg is mounted and checked)
run: packaging/macos/package.sh --arch universal
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: release-macos
+1 -7
View File
@@ -35,8 +35,7 @@ People trust SolidCraft with their designs; a crash loses their work. **This out
method), the Windows-only `apps/solidcraft/src/native_crash.rs` (the exception filter that
records crashes inside native code such as graphics drivers), the Windows-only
`apps/solidcraft/src/native_frame.rs` (the window procedure that answers the resize-border hit
test of the window without OS decorations, and gives its class the icon the taskbar falls back
on) and the Windows-only
test of the window without OS decorations) and the Windows-only
`apps/solidcraft/src/space_mouse/windows_hid.rs` (asks Plug and Play which HID interfaces are 3D
mice, so no other device is sent anything).
- **Errors are `Result<T, E>`** through the crate's error type and `?`. An unfinished feature
@@ -156,9 +155,6 @@ People trust SolidCraft with their designs; a crash loses their work. **This out
- If someone already has it, comment on their PR or issue instead of opening a second one. Two
PRs for one change can't both land, and one of them is wasted work and CI.
- Keep the `Changes a default:` line in the PR description filled in, and don't open a second PR for
an issue a lower-numbered open PR already closes; `pr-checks.yml` fails both (CONTRIBUTING.md, "Sending a change").
## Claim = a draft PR
- Before working on issue #N, check nobody has it: `gh pr list -R tangency-labs/solidcraft --state open --search "#N"`.
- If none, branch from the latest `origin/main`, `git commit --allow-empty -m "claim: #N"`, push,
@@ -218,5 +214,3 @@ workflow; never regenerate them in a PR.
Keep them conflict-free (CONTRIBUTING.md, "Lists many changes add to"): message catalogs sorted
(`cargo xtask lists --fix`), `mod` lines in their sorted group, the language list English first
then by id, generated files left to `main`, and a design-format change as one new file.
New UI strings go in a new `assets/locales/pending/<slug>.json` and new ATTRIBUTION.md rows in a new
`attribution/pending/<slug>.md`, never into the catalogs or the table (see the README in each folder).
+15 -404
View File
@@ -14,12 +14,6 @@ Editable drawing code, native projects and shipped exports are collected under `
| File | Author | Source | Licence |
|---|---|---|---|
| assets/render/catalog/capabilities.json | SolidCraft contributors | Byte-identical runtime export of docs/render-materials/capabilities.json; generated and checked by tools/render-library/generate.py | MIT OR Apache-2.0 |
| assets/render/catalog/environments.json | SolidCraft contributors | Byte-identical runtime export of docs/render-materials/environments.json; generated and checked by tools/render-library/generate.py | MIT OR Apache-2.0 |
| assets/render/catalog/lighting.json | SolidCraft contributors | Byte-identical runtime export of docs/render-materials/lighting.json; generated and checked by tools/render-library/generate.py | MIT OR Apache-2.0 |
| assets/render/catalog/materials.json | SolidCraft contributors | Byte-identical runtime export of docs/render-materials/materials.json; generated and checked by tools/render-library/generate.py | MIT OR Apache-2.0 |
| assets/render/catalog/quality-presets.json | SolidCraft contributors | Byte-identical runtime export of docs/render-materials/quality-presets.json; generated and checked by tools/render-library/generate.py | MIT OR Apache-2.0 |
| assets/render/catalog/scene-presets.json | SolidCraft contributors | Byte-identical runtime export of docs/render-materials/scene-presets.json; generated and checked by tools/render-library/generate.py | MIT OR Apache-2.0 |
| examples/chebyshev-motion-trace.json | SolidCraft contributors | Original parametric Chebyshev lambda mechanism and associative-trace demo, constructed from analytic circle intersections; classical link proportions documented at https://doi.org/10.1002/rcs.70095; no external CAD geometry or artwork | MIT OR Apache-2.0 |
| examples/material-card.json | SolidCraft contributors | original analytic box and explicitly synthetic engineering card; invented illustrative constants, no external material dataset | MIT OR Apache-2.0 |
| examples/studio/author_sweep.py | SolidCraft contributors | original editable CAD scene and authoring metadata; no external assets | MIT OR Apache-2.0 |
@@ -33,12 +27,6 @@ Editable drawing code, native projects and shipped exports are collected under `
| examples/studio/stage1-render.json | SolidCraft contributors | original studio camera and lighting parameters | MIT OR Apache-2.0 |
| examples/render-studio.json | SolidCraft contributors | original procedural box/cylinder enclosure script for the integrated Render workspace | MIT OR Apache-2.0 |
| examples/render-opaque-studio.json | SolidCraft contributors | original procedural sphere study varying opaque metal fraction and roughness independently; no external assets or engineering data | MIT OR Apache-2.0 |
| crates/gpu/src/path/denoise.wgsl | SolidCraft contributors | original bounded edge-aware a-trous spatial filter; GPU2 donor d710e31, no external shader source | MIT OR Apache-2.0 |
| crates/gpu/src/path/denoise_guides.wgsl | SolidCraft contributors | original deterministic centre-ray material and geometry guide pass; GPU2 donor d710e31, no external shader source | MIT OR Apache-2.0 |
| crates/gpu/src/path/shadows.wgsl | SolidCraft contributors | original bounded area, sun and environment visibility transport; GPU2 donor d710e31, no external shader source | MIT OR Apache-2.0 |
| crates/gpu/src/path/textures.wgsl | SolidCraft contributors | original planar colour and normal map sampling; GPU2 donor d710e31, no external shader source | MIT OR Apache-2.0 |
| assets/appearances/starter_pack.rs | SolidCraft contributors | original illustrative optical recipes, authored scalar values and future procedural resource descriptors; no measured or external material dataset | MIT OR Apache-2.0 |
| examples/material-finishes.json | SolidCraft contributors | original procedural sphere study of six illustrative opaque finishes with generic material densities; no external assets or measured optical data | MIT OR Apache-2.0 |
| examples/render-path-studio.json | SolidCraft contributors | original procedural opaque enclosure, teal insert and metal fasteners for progressive rendering; no imported models | MIT OR Apache-2.0 |
| crates/gpu/src/path/path.wgsl | SolidCraft contributors | original software BVH traversal and path integration using the existing SolidCraft surface/studio equations; no external shader source | MIT OR Apache-2.0 |
| crates/gpu/src/pbr.rs | SolidCraft contributors | original WGSL translation of SolidCraft's opaque surface and fixed studio reference, embedded as Rust source; no external shader code | MIT OR Apache-2.0 |
@@ -51,10 +39,10 @@ Editable drawing code, native projects and shipped exports are collected under `
| assets/icons/app/macos/exports/icon.icns | SolidCraft contributors | macOS fallback compiled by Apple's actool from the approved assets/icons/app/macos/source/SolidCraft.icon composition | MIT OR Apache-2.0 |
| assets/models/test_boat.bin | 3DBenchy.com | 3DBenchy.com | Public Domain |
| assets/models/make_test_boat.py | SolidCraft contributors | original script that packs a model into the compact file above | MIT OR Apache-2.0 |
| docs/screenshots/class-a-blend.png | SolidCraft contributors | screenshot of SolidCraft itself: a 24 mm and a 10 mm box bridged by a `surface.blend` sheet (G1 at both edges), rendered by the app's `ui.render` in the dark theme with examples/demo/class_a_shots.py | MIT OR Apache-2.0 |
| docs/screenshots/class-a-crown.png | SolidCraft contributors | screenshot of SolidCraft itself: a patch crowned with `surface.patch crown=0.3` over a skew four-line rim, rendered by the app's `ui.render` in the dark theme with examples/demo/class_a_shots.py | MIT OR Apache-2.0 |
| docs/screenshots/class-a-boundary.png | SolidCraft contributors | screenshot of SolidCraft itself: a surface through two 3D sketch splines built by `surface.boundary`, rendered by the app's `ui.render` in the dark theme with examples/demo/class_a_shots.py | MIT OR Apache-2.0 |
| docs/screenshots/sample-plate.png | SolidCraft contributors | screenshot of SolidCraft 0.22.1 itself (`solidcraft --sample`, default dark theme, 1600x1000) | MIT OR Apache-2.0 |
| docs/screenshots/class-a-blend.png | SolidCraft contributors | screenshot of SolidCraft itself: two boxes bridged by `surface.blend` (a 40x10 sheet, `solidcraft-cli snapshot` of plan/evidence script ev_blend) | MIT OR Apache-2.0 |
| docs/screenshots/class-a-crown.png | SolidCraft contributors | screenshot of SolidCraft itself: a patch crowned with `surface.patch crown=0.3` over a saddle rim (`solidcraft-cli snapshot`) | MIT OR Apache-2.0 |
| docs/screenshots/class-a-boundary.png | SolidCraft contributors | screenshot of SolidCraft itself: a patch through two sketch lines built by `surface.boundary` with `crown: 0.4` (`solidcraft-cli snapshot`) | MIT OR Apache-2.0 |
| docs/screenshots/sample-plate.png | SolidCraft contributors | screenshot of SolidCraft itself (`solidcraft --sample`) | MIT OR Apache-2.0 |
| assets/samples/exports/0.png | SolidCraft contributors | original Sample Plate from `crates/engine/src/sample.rs`, exported by the SolidCraft thumbnail renderer | MIT OR Apache-2.0 |
| assets/samples/exports/1.png | SolidCraft contributors | original Sheet Metal Bracket from `crates/engine/src/sample.rs`, exported by the SolidCraft thumbnail renderer | MIT OR Apache-2.0 |
| assets/samples/exports/2.png | SolidCraft contributors | original Hinge Assembly from `crates/engine/src/sample.rs`, exported by the SolidCraft thumbnail renderer | MIT OR Apache-2.0 |
@@ -62,10 +50,10 @@ Editable drawing code, native projects and shipped exports are collected under `
| assets/samples/exports/4.png | SolidCraft contributors | original Bolted Flange from `crates/engine/src/sample.rs`, exported by the SolidCraft thumbnail renderer | MIT OR Apache-2.0 |
| assets/samples/exports/5.png | SolidCraft contributors | original Vent Plate from `crates/engine/src/sample.rs`, exported by the SolidCraft thumbnail renderer | MIT OR Apache-2.0 |
| assets/samples/exports/scripts.json | SolidCraft contributors | SHA-256 fingerprints of the original sample scripts, generated with the shipped thumbnails | MIT OR Apache-2.0 |
| docs/screenshots/demo.gif | SolidCraft contributors | original screen recording of the actual SolidCraft 0.22.1 English desktop UI (default dark theme, software renderer) modelling an original Raspberry Pi 4 case (no logos or branding; the engraved text is set in the system's DejaVu Sans Bold), generated with examples/demo/record_ui.sh and its original animated pointer overlay (6× playback, idle pauses cut) | MIT OR Apache-2.0 |
| docs/screenshots/demo.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.22.1 English desktop UI (default dark theme, software renderer) modelling an original Raspberry Pi 4 case (no logos or branding; the engraved text is set in the system's DejaVu Sans Bold), generated with examples/demo/record_ui.sh and its original animated pointer overlay (6× playback, idle pauses cut) | MIT OR Apache-2.0 |
| docs/screenshots/rover-build.gif | 0xlillith (model); SolidCraft contributors (recording) | screen recording of the actual SolidCraft 0.22.1 desktop UI (default dark theme, software renderer) replaying the timeline of the SELENE rover (`Rover-recorded.solidcraft` from https://github.com/0xlillith/solidcraft-rover, an original model by 0xlillith built through SolidCraft MCP); the model's lettering follows Ubuntu Light outlines (Canonical Ltd., Ubuntu Font Licence 1.0, as noted in that repository); the emblem is the SolidCraft project icon (15× playback, idle pauses cut, constant-rate camera orbit) | MIT OR Apache-2.0 |
| docs/screenshots/rover-build.mp4 | 0xlillith (model); SolidCraft contributors (recording) | screen recording of the actual SolidCraft 0.22.1 desktop UI (default dark theme, software renderer) replaying the timeline of the SELENE rover (`Rover-recorded.solidcraft` from https://github.com/0xlillith/solidcraft-rover, an original model by 0xlillith built through SolidCraft MCP); the model's lettering follows Ubuntu Light outlines (Canonical Ltd., Ubuntu Font Licence 1.0, as noted in that repository); the emblem is the SolidCraft project icon | MIT OR Apache-2.0 |
| docs/screenshots/demo-0.8.gif | SolidCraft contributors | original screen recording of the actual SolidCraft 0.8 English desktop UI modelling an original Raspberry Pi 4 case (no logos or branding; the engraved text is set in the system's DejaVu Sans Bold), generated with examples/demo/record_ui.sh and its original animated pointer overlay (9× playback) | MIT OR Apache-2.0 |
| docs/screenshots/demo-0.8.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.8 English desktop UI modelling an original Raspberry Pi 4 case (no logos or branding; the engraved text is set in the system's DejaVu Sans Bold), generated with examples/demo/record_ui.sh and its original animated pointer overlay (9× playback) | MIT OR Apache-2.0 |
| docs/screenshots/rover-build.gif | 0xlillith (model); SolidCraft contributors (recording) | screen recording of the actual SolidCraft desktop UI replaying the timeline of the SELENE rover (`Rover-recorded.solidcraft` from https://github.com/0xlillith/solidcraft-rover, an original model by 0xlillith built through SolidCraft MCP); the model's lettering follows Ubuntu Light outlines (Canonical Ltd., Ubuntu Font Licence 1.0, as noted in that repository); the emblem is the SolidCraft project icon (2× playback) | MIT OR Apache-2.0 |
| docs/screenshots/rover-build.mp4 | 0xlillith (model); SolidCraft contributors (recording) | screen recording of the actual SolidCraft desktop UI replaying the timeline of the SELENE rover (`Rover-recorded.solidcraft` from https://github.com/0xlillith/solidcraft-rover, an original model by 0xlillith built through SolidCraft MCP); the model's lettering follows Ubuntu Light outlines (Canonical Ltd., Ubuntu Font Licence 1.0, as noted in that repository); the emblem is the SolidCraft project icon | MIT OR Apache-2.0 |
| docs/design/ux-redesign/model-floating.png | SolidCraft contributors | original UX mockup (Model tab, floating layout) rendered from an HTML mockup that uses the original Technical Accent icon set; no third-party artwork, and not a screenshot of any other CAD product | MIT OR Apache-2.0 |
| docs/design/ux-redesign/model-compact.png | SolidCraft contributors | original UX mockup (Model tab, compact docked layout) rendered from an HTML mockup that uses the original Technical Accent icon set; no third-party artwork, and not a screenshot of any other CAD product | MIT OR Apache-2.0 |
| docs/design/ux-redesign/sketch.png | SolidCraft contributors | original UX mockup (Sketch tab) rendered from an HTML mockup that uses the original Technical Accent icon set; no third-party artwork, and not a screenshot of any other CAD product | MIT OR Apache-2.0 |
@@ -101,7 +89,6 @@ Editable drawing code, native projects and shipped exports are collected under `
| tests/step-conformance/tiny_pin.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/step-conformance/torus_ring_nurbs.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/step-conformance/washer.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
| tests/step-regressions/rod_doubled_edge.step | SolidCraft contributors | written by SolidCraft from the commands of #4598 (a bored rod moved 100 mm and exported); import regression fixture | MIT OR Apache-2.0 |
| tests/oracle/17-loft/reference.stl | SolidCraft contributors | triangulation of our own recipe (tests/oracle/17-loft/recipe.json) built with FreeCAD as an external tool by tools/freecad-oracle | MIT OR Apache-2.0 |
| tests/oracle/23-bracket-multifeature/reference.stl | SolidCraft contributors | triangulation of our own recipe (tests/oracle/23-bracket-multifeature/recipe.json) built with FreeCAD as an external tool by tools/freecad-oracle | MIT OR Apache-2.0 |
| tests/oracle/28-blind-pocket-fillets/reference.stl | SolidCraft contributors | triangulation of our own recipe (tests/oracle/28-blind-pocket-fillets/recipe.json) built with FreeCAD as an external tool by tools/freecad-oracle | MIT OR Apache-2.0 |
@@ -123,7 +110,7 @@ Editable drawing code, native projects and shipped exports are collected under `
| tests/oracle/63-hinge-knuckle/reference.stl | SolidCraft contributors | triangulation of our own recipe (tests/oracle/63-hinge-knuckle/recipe.json) built with FreeCAD as an external tool by tools/freecad-oracle | MIT OR Apache-2.0 |
| tests/oracle/65-sketch-gear-tooth/reference.stl | SolidCraft contributors | triangulation of our own recipe (tests/oracle/65-sketch-gear-tooth/recipe.json) built with FreeCAD as an external tool by tools/freecad-oracle | MIT OR Apache-2.0 |
| assets/appearances/library.rs | SolidCraft contributors | Original SolidCraft resource data or drawing code, collected without redesign | MIT OR Apache-2.0 |
| assets/appearances/opaque_finishes.rs | SolidCraft contributors | original illustrative opaque finish values; no external dataset, textures or measured engineering data | MIT OR Apache-2.0 |
| assets/appearances/starter_pack.rs | SolidCraft contributors | original illustrative optical recipes, authored scalar values and future procedural resource descriptors; no measured or external material dataset | MIT OR Apache-2.0 |
| assets/fonts/biz-udp-gothic/OFL.txt | The BIZ UDGothic Project Authors | Unmodified accompanying font licence | SIL OFL 1.1 |
| assets/fonts/ibm-plex-mono/OFL.txt | IBM Corp. | Unmodified accompanying font licence | SIL OFL 1.1 |
| assets/fonts/ibm-plex-sans/OFL.txt | IBM Corp. | Unmodified accompanying font licence | SIL OFL 1.1 |
@@ -158,7 +145,6 @@ Editable drawing code, native projects and shipped exports are collected under `
| assets/locales/ja.json | SolidCraft contributors | Original SolidCraft resource data or drawing code, collected without redesign | MIT OR Apache-2.0 |
| assets/locales/ko.json | SolidCraft contributors | Original SolidCraft resource data or drawing code, collected without redesign | MIT OR Apache-2.0 |
| assets/locales/ms.json | A.Hakim (a-hakim) | Malay translation of the English message catalog | MIT OR Apache-2.0 |
| assets/locales/nb.json | SolidCraft contributors | Original Norwegian Bokmål translation of SolidCraft's English catalog; no commercial CAD text used | MIT OR Apache-2.0 |
| assets/locales/nl.json | SolidCraft contributors | Original Dutch translation of SolidCraft's English catalog; no commercial CAD text used | MIT OR Apache-2.0 |
| assets/locales/pl.json | cyryllo | Polish translation of the English catalog, contributed upstream | MIT OR Apache-2.0 |
| assets/locales/ru.json | Alex Pro (del000) | Russian translation of the English catalog, contributed upstream | MIT OR Apache-2.0 |
@@ -200,8 +186,6 @@ Editable drawing code, native projects and shipped exports are collected under `
| assets/styles/ui/themes/graphite.json | SolidCraft contributors | original SolidCraft theme file | MIT OR Apache-2.0 |
| assets/styles/ui/themes/nosferatu.json | SolidCraft contributors | original SolidCraft theme file adapting the colours of a published palette (Dracula by Zeno Rocha and the Dracula Theme contributors, https://github.com/dracula/dracula-theme, MIT) | MIT OR Apache-2.0 |
| assets/styles/ui/themes/catppuccin-mocha.json | SolidCraft contributors | original SolidCraft theme file adapting the colours of a published palette (Catppuccin by the Catppuccin organisation, https://github.com/catppuccin/catppuccin, MIT) | MIT OR Apache-2.0 |
| assets/styles/ui/themes/okibi.json | SolidCraft contributors (Kai Evans) | original SolidCraft theme file mapping SolidCraft's UI colours to the Okibi palette (dark) of the Okibi design system by Kai Evans (https://kaievns.github.io/okibi-akiri-design-system-spec/) | MIT OR Apache-2.0 |
| assets/styles/ui/themes/akari.json | SolidCraft contributors (Kai Evans) | original SolidCraft theme file mapping SolidCraft's UI colours to the Akari palette (light) of the Okibi design system by Kai Evans (https://kaievns.github.io/okibi-akiri-design-system-spec/) | MIT OR Apache-2.0 |
| assets/styles/web/loading-logo.svg | SolidCraft contributors | Animation of the SolidCraft mark, built from the paths and gradients of assets/icons/app/macos/source/SolidCraft.icon/Assets/03-solid-blue-cutout.svg; original animation | MIT OR Apache-2.0 |
| assets/styles/web/loading.css | SolidCraft contributors | Original SolidCraft resource data or drawing code, collected without redesign | MIT OR Apache-2.0 |
| crates/io/src/native_cad/fixtures/block.FCStd | SolidCraft contributors | original 10 × 20 × 30 mm test box saved by FreeCAD 1.1.4 run as an external program (`make_block_fcstd.py` beside it); no third-party geometry | MIT OR Apache-2.0 |
@@ -516,7 +500,6 @@ Editable drawing code, native projects and shipped exports are collected under `
| docs/book/src/og.png | SolidCraft contributors | copy of assets/branding/web/source/og.png staged by cargo xtask book (gitignored) | MIT OR Apache-2.0 |
| docs/sponsors/blacksmith-ci-powered.png | Blacksmith (blacksmith.sh) | "CI powered by Blacksmith" sponsor banner supplied by Blacksmith for the sponsorship credit; used only to credit the sponsor, not part of the application | © Blacksmith, used with permission |
| assets/icons/ui/design/svg/model/path_flange.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
## Code adapted from sibling projects
@@ -576,6 +559,12 @@ copied into SolidCraft.
| Directory | Source | Licence | Changes |
|---|---|---|---|
| examples/studio/author_sweep.py | SolidCraft contributors | original editable CAD scene and authoring metadata; no external assets | MIT OR Apache-2.0 |
| examples/studio/author_scene.py | SolidCraft contributors | original editable CAD scene and authoring metadata; no external assets | MIT OR Apache-2.0 |
| examples/studio/materials.json | SolidCraft contributors | original editable CAD scene and authoring metadata; no external assets | MIT OR Apache-2.0 |
| examples/studio/coffee-exploded-sweep.json | SolidCraft contributors | original editable CAD scene and authoring metadata; no external assets | MIT OR Apache-2.0 |
| examples/studio/coffee-exploded-render.json | SolidCraft contributors | original editable CAD scene and authoring metadata; no external assets | MIT OR Apache-2.0 |
| examples/studio/coffee-exploded.json | SolidCraft contributors | original editable CAD scene and authoring metadata; no external assets | MIT OR Apache-2.0 |
| vendor/truck-meshalgo | truck-meshalgo 0.4.0, https://github.com/ricosjp/truck (RICOS Co. Ltd.) | Apache-2.0 | meshing: bounded parameter searches, collapsed-boundary projection, subdivision and provisional fixed-grid previews (vendor/README.md) |
| vendor/truck-stepio | truck-stepio 0.3.0, https://github.com/ricosjp/truck (RICOS Co. Ltd.) | Apache-2.0 | STEP writer: intersection-curve surfaces written at unique ids (vendor/README.md) |
| vendor/truck-shapeops | truck-shapeops 0.4.0, https://github.com/ricosjp/truck (RICOS Co. Ltd.) | Apache-2.0 | robust meshing and nearest-parameter fallback in booleans; degenerate intersection-curve rejection; face and edge history of booleans; trace macro; corner choice at vertices several intersection curves end on; reuse of an operand's meshes (vendor/README.md) |
@@ -666,381 +655,3 @@ The original studio generators produce local ignored PNG maps and an RGBE gradie
Generated image bytes are not source assets; their authorship, licences and expected
hashes are documented under `examples/studio/textures`. Rendered evidence belongs
on the results branch. No photographs, scans or external image assets are included.
## Additional render and showcase resources
| File | Author | Source | Licence |
|---|---|---|---|
| assets/render/starter/tools/check_all.py | SolidCraft contributors | original read-only authored/native resource validation orchestration and evidence receipts | MIT OR Apache-2.0 |
| assets/render/starter/tools/test_check_all.py | SolidCraft contributors | original validation-runner regression fixtures and subprocess tests | MIT OR Apache-2.0 |
| assets/render/starter/tools/requirements.txt | SolidCraft contributors | pinned offline authoring tool requirements; no application dependency changes | MIT OR Apache-2.0 |
| crates/render/examples/validate_starter.rs | SolidCraft contributors | original native resource conformance checker using public SolidCraft decoding, normalization, sampling and mip APIs | MIT OR Apache-2.0 |
| .gitattributes | SolidCraft contributors | original canonical line-ending policy for the existing runtime icon-discovery manifest; no artwork or icon-assignment changes | MIT OR Apache-2.0 |
| assets/render/.gitattributes | SolidCraft contributors | original canonical line-ending policy for SHA-256-pinned renderer authoring resources | MIT OR Apache-2.0 |
| docs/render-materials/.gitattributes | SolidCraft contributors | original canonical line-ending policy for SHA-256-pinned renderer authoring resources | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/assembly-bench-soft.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/automotive-turntable.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/black-metal-strips.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/cnc-overhead.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/esd-bench-neutral.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/fdm-neutral-inspection.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/garage-door.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/hangar-skylight.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/home-workbench.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/makerspace-worklights.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/mechanical-bay.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/neutral-comparison.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/neutral-side-softbox.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/paint-inspection-bands.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/printed-part-photo.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/rework-offset-strips.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/robot-led-editorial.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/showroom-ribbons.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/spacecraft-clean-bay.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/sun-free-overcast.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/vacuum-black.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/warm-table-window.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/white-enveloping-studio.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/exports/woodshop-window.hdr | SolidCraft contributors | original analytic relative-radiance HDR export; recipe, linear-sRGB primaries and SHA-256 in assets/render/starter/environments/manifest.json; not measured photometry | MIT OR Apache-2.0 |
| assets/render/starter/environments/manifest.json | SolidCraft contributors | original clean-room renderer resource recipe or manifest; authored/unbound scope and source receipts in assets/render/starter/README.md | MIT OR Apache-2.0 |
| assets/render/starter/environments/source/recipes.json | SolidCraft contributors | original clean-room renderer resource recipe or manifest; authored/unbound scope and source receipts in assets/render/starter/README.md | MIT OR Apache-2.0 |
| assets/render/starter/lighting/exports/rigs.json | SolidCraft contributors | original clean-room renderer resource recipe or manifest; authored/unbound scope and source receipts in assets/render/starter/README.md | MIT OR Apache-2.0 |
| assets/render/starter/lighting/recipe.json | SolidCraft contributors | original clean-room renderer resource recipe or manifest; authored/unbound scope and source receipts in assets/render/starter/README.md | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-001/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-001/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-001/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-001/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-001/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-002/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-002/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-002/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-002/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-002/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-003/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-003/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-003/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-003/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-003/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-004/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-004/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-004/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-004/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-004/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-005/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-005/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-005/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-005/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-005/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-006/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-006/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-006/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-006/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-006/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-007/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-007/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-007/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-007/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-007/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-008/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-008/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-008/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-008/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-008/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-009/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-009/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-009/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-009/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-009/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-010/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-010/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-010/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-010/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-010/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-011/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-011/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-011/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-011/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-011/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-012/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-012/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-012/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-012/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-012/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-013/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-013/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-013/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-013/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-013/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-014/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-014/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-014/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-014/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-014/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-015/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-015/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-015/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-015/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-015/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-016/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-016/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-016/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-016/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-016/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-017/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-017/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-017/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-017/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-017/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-018/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-018/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-018/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-018/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-018/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-019/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-019/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-019/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-019/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-019/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-020/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-020/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-020/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-020/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-020/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-021/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-021/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-021/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-021/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-021/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-022/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-022/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-022/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-022/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-022/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-023/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-023/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-023/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-023/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-023/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-024/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-024/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-024/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-024/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-024/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-025/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-025/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-025/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-025/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-025/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-026/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-026/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-026/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-026/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-026/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-027/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-027/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-027/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-027/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-027/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-028/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-028/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-028/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-028/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-028/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-029/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-029/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-029/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-029/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-029/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-030/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-030/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-030/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-030/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-030/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-031/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-031/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-031/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-031/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-031/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-032/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-032/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-032/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-032/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-032/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-033/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-033/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-033/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-033/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-033/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-034/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-034/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-034/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-034/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-034/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-035/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-035/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-035/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-035/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-035/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-036/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-036/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-036/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-036/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-036/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-037/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-037/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-037/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-037/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-037/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-038/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-038/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-038/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-038/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-038/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-039/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-039/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-039/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-039/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-039/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-040/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-040/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-040/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-040/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-040/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-041/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-041/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-041/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-041/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-041/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-042/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-042/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-042/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-042/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-042/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-043/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-043/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-043/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-043/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-043/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-044/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-044/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-044/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-044/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-044/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-045/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-045/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-045/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-045/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-045/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-046/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-046/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-046/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-046/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-046/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-047/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-047/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-047/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-047/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-047/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-048/base_color.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-048/height.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-048/metallic.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-048/normal_open_gl.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/exports/SURF-048/roughness.png | SolidCraft contributors | original deterministic periodic PBR map export; recipe, role, scale and SHA-256 in assets/render/starter/materials/manifest.json; not measured material data | MIT OR Apache-2.0 |
| assets/render/starter/materials/manifest.json | SolidCraft contributors | original clean-room renderer resource recipe or manifest; authored/unbound scope and source receipts in assets/render/starter/README.md | MIT OR Apache-2.0 |
| assets/render/starter/materials/source/recipes.json | SolidCraft contributors | original clean-room renderer resource recipe or manifest; authored/unbound scope and source receipts in assets/render/starter/README.md | MIT OR Apache-2.0 |
| assets/render/starter/pack.json | SolidCraft contributors | original clean-room renderer resource recipe or manifest; authored/unbound scope and source receipts in assets/render/starter/README.md | MIT OR Apache-2.0 |
| assets/render/starter/tools/environments.py | SolidCraft contributors | original clean-room offline resource authoring, numerical verification or validation tool; no application runtime dependency | MIT OR Apache-2.0 |
| assets/render/starter/tools/lighting.py | SolidCraft contributors | original clean-room offline resource authoring, numerical verification or validation tool; no application runtime dependency | MIT OR Apache-2.0 |
| assets/render/starter/tools/materials.py | SolidCraft contributors | original clean-room offline resource authoring, numerical verification or validation tool; no application runtime dependency | MIT OR Apache-2.0 |
| assets/render/starter/tools/test_environments.py | SolidCraft contributors | original clean-room offline resource authoring, numerical verification or validation tool; no application runtime dependency | MIT OR Apache-2.0 |
| assets/render/starter/tools/test_lighting.py | SolidCraft contributors | original clean-room offline resource authoring, numerical verification or validation tool; no application runtime dependency | MIT OR Apache-2.0 |
| assets/render/starter/tools/test_materials.py | SolidCraft contributors | original clean-room offline resource authoring, numerical verification or validation tool; no application runtime dependency | MIT OR Apache-2.0 |
| assets/render/starter/tools/validate_pack.py | SolidCraft contributors | original clean-room offline resource authoring, numerical verification or validation tool; no application runtime dependency | MIT OR Apache-2.0 |
| assets/render/selene/licenses/UFL.txt | Canonical Ltd. | exact Ubuntu Font Licence 1.0 notice retained from https://github.com/0xlillith/solidcraft-rover/blob/b32437915372d32b71419dc8f5143fe22ff1e869/UFL.txt for the modelled Ubuntu Light lettering | Ubuntu Font Licence 1.0 |
| assets/render/selene/materials/appearance-pack.json | SolidCraft contributors | original clean-room English appearance authoring data: 48 illustrative opaque PBR projections with separate advanced target specifications; no measured dataset or external assets | MIT OR Apache-2.0 |
| assets/render/selene/materials/appearance-swatches.json | SolidCraft contributors | original analytic command script for 48 opaque box swatches and a support base, derived only from the adjacent original appearance pack; no external artwork or engineering properties | MIT OR Apache-2.0 |
| assets/render/selene/models/SELENE-functional.solidcraft | SolidCraft contributors (native enhancements); 0xlillith (original rover); SychPL / Mateusz Sych (supplied showcase assembly); Canonical Ltd. (lettering outlines) | native-command derivative of the unchanged author-supplied source, with original bearing, suspension, gripper, electronics and presentation work; source and final hashes in assets/render/selene/SOURCE.md and VALIDATION.md | Model and showcase contributions: MIT OR Apache-2.0 (explicit SychPL grant https://github.com/tangency-labs/solidcraft/pull/5494#issuecomment-6375788360); lettering: Ubuntu Font Licence 1.0, notice retained in assets/render/selene/licenses/UFL.txt |
| assets/render/selene/motion/interference-pairs.json | SolidCraft contributors | original authored exact-body pair selection for bounded native clearance checks on the functional SELENE derivative; not a generated collision result | MIT OR Apache-2.0 |
| assets/render/selene/motion/mechanism-acceptance.json | SolidCraft contributors | original kinematic acceptance criteria, inspected source-container bindings and analytic planning checks from cited public SolidCraft documentation and the admitted native source; derivative runtime acceptance is recorded separately | MIT OR Apache-2.0 |
| assets/render/selene/motion/preflight-commands.json | SolidCraft contributors | original read-only inventory script using existing project commands; no source-model selectors or state mutations | MIT OR Apache-2.0 |
| assets/render/selene/motion/sample-sequence.json | SolidCraft contributors | original future lunar sampling and sealed-vial dynamics state specification; no simulation or physical calibration result | MIT OR Apache-2.0 |
| assets/render/selene/scenes/roles.json | SolidCraft contributors | original semantic role and candidate-material authoring specification for the prepared SELENE demo; no native geometry or external assets bundled | MIT OR Apache-2.0 |
| assets/render/selene/scenes/shots.json | SolidCraft contributors | original shot, framing and staged-sequence authoring specification for the prepared SELENE demo; no render or simulation result claimed | MIT OR Apache-2.0 |
| assets/render/selene/scenes/stages.json | SolidCraft contributors | original stage composition and light-ownership authoring specification derived from the project render catalog; scientific context linked in scenes/README.md | MIT OR Apache-2.0 |
| assets/render/selene/source-lock.json | SolidCraft contributors | original source-admission metadata and public-reference inventory pinning the supplied improved SELENE native file; original-model and font notices preserved in SOURCE.md | MIT OR Apache-2.0 |
| assets/render/selene/source/SELENE-showcase.solidcraft | 0xlillith (original rover); SychPL / Mateusz Sych (showcase assembly and helpers); SolidCraft contributors (emblem); Canonical Ltd. (lettering outlines) | unchanged author-published native file from https://github.com/SychPL/solidcraft/blob/32ec1720447e3fcc6bb6cc53a63fb5b39cfa21b9/SELENE-showcase.solidcraft; source hash and license evidence in assets/render/selene/SOURCE.md | Model and showcase contributions: MIT OR Apache-2.0 (explicit SychPL grant https://github.com/tangency-labs/solidcraft/pull/5494#issuecomment-6375788360); lettering: Ubuntu Font Licence 1.0, notice retained in assets/render/selene/licenses/UFL.txt |
| assets/render/selene/tools/bearings.py | SolidCraft contributors | original native-command authoring of grooved bearings, rolling-element kinematics, clamp hubs and arm/head clearances; analytic geometry, no copied CAD source | MIT OR Apache-2.0 |
| assets/render/selene/tools/build.py | SolidCraft contributors | original Python development tool for pinned-source native command replay, staged checkpoints, command-trace and geometry health checks; no application runtime dependency | MIT OR Apache-2.0 |
| assets/render/selene/tools/electronics.py | SolidCraft contributors | original native-command Raspberry Pi 5 visual reconstruction using public manufacturer mechanical dimensions; original package envelopes and connector internals, no manufacturer CAD or artwork copied | MIT OR Apache-2.0 |
| assets/render/selene/tools/export_views.py | SolidCraft contributors | original native MCP preview export tool for three reusable showcase views; changes only transient visibility and cameras and verifies the source master hash | MIT OR Apache-2.0 |
| assets/render/selene/tools/finishes.py | SolidCraft contributors | original native physical-material proxy and opaque PBR assignment generator preserving the supplied rover palette | MIT OR Apache-2.0 |
| assets/render/selene/tools/mechanisms.py | SolidCraft contributors | original native-command gripper sleeves, pads, wheel ownership, joint limits and inspection studies with independent analytic suspension relation | MIT OR Apache-2.0 |
| assets/render/selene/tools/prepare.py | SolidCraft contributors | original Python development tool for bounded native-container inspection and reviewed opaque body-assignment preparation; no application runtime dependency | MIT OR Apache-2.0 |
| assets/render/selene/tools/presentation.py | SolidCraft contributors | original native-command browser organization, camera/render setups and closed sample-vial glass preparation | MIT OR Apache-2.0 |
| assets/render/selene/tools/suspension.py | SolidCraft contributors | original native-command trailing-arm suspension, telescoping coilover loops, plain bushings, carrier gussets and local motion clearances derived from the admitted rover geometry; independent analytic pin-distance relation, no copied external CAD | MIT OR Apache-2.0 |
| assets/render/selene/tools/validate_native.py | SolidCraft contributors | original bounded native MCP verification tool for sampled SELENE kinematics, independent analytic follower and world-coordinate checks, optional exact-name interference pairs and unchanged input hashes; no application runtime dependency | MIT OR Apache-2.0 |
| assets/render/selene/tools/validate_prepared.py | SolidCraft contributors | original Python development validator for prepared SELENE resources, source status, references, hashes and command projections; no application runtime dependency | MIT OR Apache-2.0 |
| assets/render/selene/tools/yaw_supports.py | SolidCraft contributors | original native-command plain yaw journals, flanged brass thrust bushings, recessed axial retaining pins and mounting-head clearances derived from the admitted rover geometry; no copied external CAD | MIT OR Apache-2.0 |
| assets/render/selene/ui/demo-flow.json | SolidCraft contributors | original progressive-disclosure UI integration specification for the existing Render workspace; no copied UI artwork or runtime registration | MIT OR Apache-2.0 |
| docs/render-materials/baseline-setup-commands.json | SolidCraft contributors | original clean-room English catalog, preset or analytic planning data; external references documented in docs/render-materials/SOURCES.md; no external assets bundled | MIT OR Apache-2.0 |
| docs/render-materials/capabilities.json | SolidCraft contributors | original clean-room English catalog, preset or analytic planning data; external references documented in docs/render-materials/SOURCES.md; no external assets bundled | MIT OR Apache-2.0 |
| docs/render-materials/environment-presets.json | SolidCraft contributors | original clean-room English catalog, preset or analytic planning data; external references documented in docs/render-materials/SOURCES.md; no external assets bundled | MIT OR Apache-2.0 |
| docs/render-materials/environments.json | SolidCraft contributors | original clean-room English catalog, preset or analytic planning data; external references documented in docs/render-materials/SOURCES.md; no external assets bundled | MIT OR Apache-2.0 |
| docs/render-materials/lighting.json | SolidCraft contributors | original clean-room English catalog, preset or analytic planning data; external references documented in docs/render-materials/SOURCES.md; no external assets bundled | MIT OR Apache-2.0 |
| docs/render-materials/lunar-physics-fixtures.json | SolidCraft contributors | original clean-room English catalog, preset or analytic planning data; external references documented in docs/render-materials/SOURCES.md; no external assets bundled | MIT OR Apache-2.0 |
| docs/render-materials/materials.json | SolidCraft contributors | original clean-room English catalog, preset or analytic planning data; external references documented in docs/render-materials/SOURCES.md; no external assets bundled | MIT OR Apache-2.0 |
| docs/render-materials/provenance.json | SolidCraft contributors | original clean-room English catalog, preset or analytic planning data; external references documented in docs/render-materials/SOURCES.md; no external assets bundled | MIT OR Apache-2.0 |
| docs/render-materials/quality-presets.json | SolidCraft contributors | original clean-room English catalog, preset or analytic planning data; external references documented in docs/render-materials/SOURCES.md; no external assets bundled | MIT OR Apache-2.0 |
| docs/render-materials/roadmap.json | SolidCraft contributors | original clean-room English catalog, preset or analytic planning data; external references documented in docs/render-materials/SOURCES.md; no external assets bundled | MIT OR Apache-2.0 |
| docs/render-materials/scene-presets.json | SolidCraft contributors | original clean-room English catalog, preset or analytic planning data; external references documented in docs/render-materials/SOURCES.md; no external assets bundled | MIT OR Apache-2.0 |
| docs/render-materials/selene-bindings.json | SolidCraft contributors | original clean-room English catalog, preset or analytic planning data; external references documented in docs/render-materials/SOURCES.md; no external assets bundled | MIT OR Apache-2.0 |
| docs/render-materials/simulation-requirements.json | SolidCraft contributors | original clean-room English catalog, preset or analytic planning data; external references documented in docs/render-materials/SOURCES.md; no external assets bundled | MIT OR Apache-2.0 |
| docs/render-materials/studio-presets.json | SolidCraft contributors | original clean-room English catalog, preset or analytic planning data; external references documented in docs/render-materials/SOURCES.md; no external assets bundled | MIT OR Apache-2.0 |
| docs/render-materials/ui-integration.json | SolidCraft contributors | original English UI integration specification and exact source-revision mappings from SolidCraft's own implemented shell, UI kit and accepted icons; no copied artwork or runtime registration | MIT OR Apache-2.0 |
| docs/render-materials/ux.json | SolidCraft contributors | original clean-room English catalog, preset or analytic planning data; external references documented in docs/render-materials/SOURCES.md; no external assets bundled | MIT OR Apache-2.0 |
| docs/render-materials/validate_catalog.py | SolidCraft contributors | original Python development validator for catalog references, preset composition and analytic specification fixtures; no application runtime dependency | MIT OR Apache-2.0 |
### Built-in render resource adapters (personal integration batch, 2026-10-22)
The original adapters in `crates/engine/src/cmd/render/library.rs`, the generated lookup `library_data.rs` and its generator `tools/render-library/generate.py` project the already attributed starter and SELENE resources without replacing their bytes or authoring manifests. The added scalar texture channels, explicit color/V conventions and immutable-source/GPU-payload sharing are original contributor work under MIT OR Apache-2.0. No external code, commercial CAD data or new third-party imagery was imported. Source metadata and the original authoring attribution remain authoritative; pending runtime acceptance is not a photometric or engineering qualification.
## Physical reference resource additions
| File | Author | Source | Licence |
|---|---|---|---|
| assets/render/physical/bindings.json | SolidCraft contributors | original physical-reference curation, associations or derived schema-1 export; per-fact public sources retained without relicensing their publications | MIT OR Apache-2.0 |
| assets/render/physical/exports/coverage.json | SolidCraft contributors | original physical-reference curation, associations or derived schema-1 export; per-fact public sources retained without relicensing their publications | MIT OR Apache-2.0 |
| assets/render/physical/exports/native-cards.json | SolidCraft contributors | original physical-reference curation, associations or derived schema-1 export; per-fact public sources retained without relicensing their publications | MIT OR Apache-2.0 |
| assets/render/physical/profiles.json | SolidCraft contributors | original physical-reference curation, associations or derived schema-1 export; per-fact public sources retained without relicensing their publications | MIT OR Apache-2.0 |
| assets/render/physical/quantities.json | SolidCraft contributors | original physical-reference curation, associations or derived schema-1 export; per-fact public sources retained without relicensing their publications | MIT OR Apache-2.0 |
| assets/render/physical/sources.json | SolidCraft contributors | original physical-reference curation, associations or derived schema-1 export; per-fact public sources retained without relicensing their publications | MIT OR Apache-2.0 |
| assets/render/physical/tools/check_physical.py | SolidCraft contributors | original offline physical-reference validation and projection tool | MIT OR Apache-2.0 |
| assets/render/physical/tools/physical.py | SolidCraft contributors | original offline physical-reference validation and projection tool | MIT OR Apache-2.0 |
| assets/render/physical/tools/test_physical.py | SolidCraft contributors | original offline physical-reference validation and projection tool | MIT OR Apache-2.0 |
| assets/render/physical/tools/export_library.py | SolidCraft contributors | original explicit reference-library planner and native no-loss export verifier; no external code | MIT OR Apache-2.0 |
| assets/render/physical/tools/test_export_library.py | SolidCraft contributors | original regressions for native capability refusal, source identity and physical-card preservation | MIT OR Apache-2.0 |
| tools/render-library/selene_studio.py | SolidCraft contributors | original bounded native MCP studio orchestration with pinned editable master, explicit GPU policy and request/export receipts; no generated model or image substitutes | MIT OR Apache-2.0 |
| tools/render-library/selene_recipe.py | SolidCraft contributors | original full-detail SELENE material/light composition and analytic camera fit; unchanged pinned native master, no external imagery or measured optical dataset | MIT OR Apache-2.0 |
| tools/render-library/test_selene_recipe.py | SolidCraft contributors | original independent projection, scaling, nonmutation and input-refusal fixtures for the SELENE studio recipe | MIT OR Apache-2.0 |
| assets/render/selene/scenes/studio-hero.json | SolidCraft contributors | original executable render intent and verified provenance for the selected native SELENE studio showcase; authored RGB appearance and relative light units, not measured optics or simulation results | MIT OR Apache-2.0 |
| tools/render-library/publish_selene.py | SolidCraft contributors | original bounded native-frame publication checks for explicit visual review, source identity, resource hashes, PNG CRCs and scanline limits; copies completed native bytes without image synthesis or resampling | MIT OR Apache-2.0 |
| tools/render-library/test_publish_selene.py | SolidCraft contributors | original tiny analytic PNG fixtures for source-size, CRC, expansion, dimensions and explicit-publication-consent regressions | MIT OR Apache-2.0 |
| tools/render-library/package_selene.py | SolidCraft contributors | original byte-preserving packaging of the reviewed native SELENE PNG, public provenance and checksums; bounded resource/PNG verification, no fonts, executables or new render execution | MIT OR Apache-2.0 |
| .github/workflows/selene-showcase.yml | SolidCraft contributors | original read-only artifact-delivery workflow for the reviewed native SELENE export; existing pinned repository actions, no compilation, package installation, secret use or renderer claims | MIT OR Apache-2.0 |
| tools/render-library/verify_linear_master.py | SolidCraft contributors | original independent validation driver for native linear EXR output using the official OpenEXR Python reader as an external test oracle; no codec implementation copied or production dependency added | MIT OR Apache-2.0 |
## Aging, effects and backgrounds showcase, 2026-10-23
| File | Author | Source | Licence |
|---|---|---|---|
| tools/render-showcase/instrument.py | SolidCraft contributors | original native-scene authoring, checksum-verified editorial assembly or bounded delivery tool; no third-party source or font binary copied | MIT OR Apache-2.0 |
| tools/render-showcase/compose.py | SolidCraft contributors | original native-scene authoring, checksum-verified editorial assembly or bounded delivery tool; no third-party source or font binary copied | MIT OR Apache-2.0 |
| tools/render-showcase/package.py | SolidCraft contributors | original native-scene authoring, checksum-verified editorial assembly or bounded delivery tool; no third-party source or font binary copied | MIT OR Apache-2.0 |
| distribution/SolidCraft-Render-Showcase.zip | SolidCraft contributors and the explicitly credited original model/UI/resource authors | byte-verified selected collaborator package of reviewed native media, editable sample, recipes and public provenance; contained attribution and existing SELENE lettering notices retained; no fonts or executable binaries | MIT OR Apache-2.0 for new contributions; referenced original notices retained |
| distribution/SolidCraft-Render-Showcase.json | SolidCraft contributors | original verified delivery archive manifest with exact SHA-256, member count and bounded scope | MIT OR Apache-2.0 |
+2 -15
View File
@@ -186,14 +186,8 @@ no LTO, and debug assertions off (truck has debug-only assertions that reject va
builds several times faster than `--release`. If a test fails only in a plain debug build, say so
in the PR. `cargo xtask test` builds the tests with it and then runs every test binary at once
(`cargo test` runs them one after another); arguments after `--` go to each binary, e.g.
`cargo xtask test -- shell`. Set `SOLIDCRAFT_TEST_JOBS` to a positive integer to opt into a
shared bound on top-level test/side commands for `cargo xtask test` and `cargo xtask ci`.
For example, `SOLIDCRAFT_TEST_JOBS=2` allows two commands at once; the sequential wasm/doc
worker consumes one of those slots. Every binary and side check still runs after failures.
Without the variable the existing concurrency is unchanged. `RUST_TEST_THREADS`,
`ACTION_MATRIX_JOBS` and Cargo's build-job setting remain independent caller overrides;
this limit does not choose their values or change test filters, timeouts or baselines.
Tests run beside each other on a busy machine, so a test must not depend on wall-clock timing.
`cargo xtask test -- shell`. Tests run beside each other on a busy machine, so a test must not
depend on wall-clock timing.
**Ready pull requests test what they touch.** CI maps changed files to workspace packages and
runs Clippy, the test build and tests for those and their dependents. The
@@ -280,9 +274,6 @@ and a fresh CI run:
after each merge by the Generated files workflow (`cargo xtask licences`, `cargo xtask
features`). Don't regenerate or edit them in a pull request.
- **Design format**: one file per change, never an edit to a shared number (above).
- **New UI strings and attribution rows**: one new file per change, `assets/locales/pending/<slug>.json`
and `attribution/pending/<slug>.md`, instead of editing the catalogs or the ATTRIBUTION.md table;
`cargo xtask fold-pending` folds them on `main` (see the README in each folder).
## Sending a change
@@ -349,10 +340,6 @@ owning one area, with a human setting direction and reporting bugs. Their workin
[AGENTS.md](AGENTS.md). Human contributors are just as welcome. Use whatever tools you like, as
long as the change meets the rules above.
Two quick checks run on every pull request (`.github/workflows/pr-checks.yml`, not required): it
fails when a lower-numbered open PR by someone else already closes the same issue (the label
`not-a-duplicate` skips it), or when the description lacks the `Changes a default: no` / `Changes a default: yes — <what>, old behaviour kept behind preference <name>` line (docs-only and bot PRs are exempt).
## Labels and triage
The triage bot labels every new issue and PR (`.github/workflows/triage.yml`). It only adds labels;
Generated
+17 -22
View File
@@ -4208,7 +4208,7 @@ dependencies = [
[[package]]
name = "solidcraft"
version = "0.23.0"
version = "0.21.0"
dependencies = [
"block2 0.6.2",
"eframe",
@@ -4240,11 +4240,11 @@ dependencies = [
[[package]]
name = "solidcraft-3dx-client"
version = "0.23.0"
version = "0.21.0"
[[package]]
name = "solidcraft-cli"
version = "0.23.0"
version = "0.21.0"
dependencies = [
"image",
"log",
@@ -4256,7 +4256,7 @@ dependencies = [
[[package]]
name = "solidcraft-derive"
version = "0.23.0"
version = "0.21.0"
dependencies = [
"proc-macro2",
"quote",
@@ -4265,7 +4265,7 @@ dependencies = [
[[package]]
name = "solidcraft-doc"
version = "0.23.0"
version = "0.21.0"
dependencies = [
"log",
"serde",
@@ -4282,7 +4282,7 @@ dependencies = [
[[package]]
name = "solidcraft-engine"
version = "0.23.0"
version = "0.21.0"
dependencies = [
"log",
"schemars",
@@ -4296,13 +4296,12 @@ dependencies = [
"solidcraft-render",
"solidcraft-sketch",
"thiserror 2.0.21",
"toml_edit",
"web-time",
]
[[package]]
name = "solidcraft-geom"
version = "0.23.0"
version = "0.21.0"
dependencies = [
"serde",
"serde_json",
@@ -4311,7 +4310,7 @@ dependencies = [
[[package]]
name = "solidcraft-gpu"
version = "0.23.0"
version = "0.21.0"
dependencies = [
"image",
"log",
@@ -4322,7 +4321,6 @@ dependencies = [
"serde_json",
"solidcraft-engine",
"solidcraft-geom",
"solidcraft-io",
"solidcraft-kernel",
"solidcraft-render",
"thiserror 2.0.21",
@@ -4331,14 +4329,13 @@ dependencies = [
[[package]]
name = "solidcraft-io"
version = "0.23.0"
version = "0.21.0"
dependencies = [
"cadmpeg-core",
"cadmpeg-ir",
"cadmpeg-registry",
"chardetng",
"encoding_rs",
"gethostname",
"gltf",
"log",
"quick-xml",
@@ -4355,7 +4352,7 @@ dependencies = [
[[package]]
name = "solidcraft-kernel"
version = "0.23.0"
version = "0.21.0"
dependencies = [
"log",
"proptest",
@@ -4375,7 +4372,7 @@ dependencies = [
[[package]]
name = "solidcraft-mcp"
version = "0.23.0"
version = "0.21.0"
dependencies = [
"serde_json",
"solidcraft-engine",
@@ -4384,7 +4381,7 @@ dependencies = [
[[package]]
name = "solidcraft-render"
version = "0.23.0"
version = "0.21.0"
dependencies = [
"image",
"moxcms",
@@ -4398,7 +4395,7 @@ dependencies = [
[[package]]
name = "solidcraft-sketch"
version = "0.23.0"
version = "0.21.0"
dependencies = [
"serde",
"serde_json",
@@ -4409,14 +4406,14 @@ dependencies = [
[[package]]
name = "solidcraft-space-mouse"
version = "0.23.0"
version = "0.21.0"
dependencies = [
"serde",
]
[[package]]
name = "solidcraft-ui-egui"
version = "0.23.0"
version = "0.21.0"
dependencies = [
"arboard",
"egui",
@@ -4445,7 +4442,7 @@ dependencies = [
[[package]]
name = "solidcraft-web"
version = "0.23.0"
version = "0.21.0"
dependencies = [
"eframe",
"egui",
@@ -4454,7 +4451,6 @@ dependencies = [
"serde_json",
"solidcraft-engine",
"solidcraft-ui-egui",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
@@ -4701,7 +4697,6 @@ dependencies = [
"indexmap",
"toml_datetime",
"toml_parser",
"toml_writer",
"winnow",
]
@@ -5983,7 +5978,7 @@ checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7"
[[package]]
name = "xtask"
version = "0.23.0"
version = "0.21.0"
dependencies = [
"proc-macro2",
"serde_json",
+1 -5
View File
@@ -5,7 +5,7 @@ members = ["crates/*", "apps/*", "xtask"]
exclude = ["vendor"]
[workspace.package]
version = "0.23.0"
version = "0.21.0"
edition = "2024"
license = "MIT OR Apache-2.0"
rust-version = "1.95"
@@ -13,7 +13,6 @@ authors = ["SolidCraft contributors"]
repository = "https://github.com/bherbruck/solidcraft"
[workspace.dependencies]
robust = "1.2"
# internal
solidcraft-geom = { path = "crates/geom" }
solidcraft-sketch = { path = "crates/sketch" }
@@ -67,9 +66,6 @@ encoding_rs = "0.8"
# glTF 2.0 import (MIT OR Apache-2.0): the document model and GLB container only; buffers are
# read by crates/io itself, so no image decoding or file access comes with it.
gltf = { version = "1.4", default-features = false, features = ["names"] }
# The computer's name in a design's "open elsewhere" lock file (MIT OR Apache-2.0; already in the
# tree through x11rb).
gethostname = "1"
# FreeCAD and Rhino import (Apache-2.0, pure Rust; native and browser builds).
# Pinned: its IR and codec APIs change between minor versions. Open formats only: no default
+2 -2
View File
@@ -46,9 +46,9 @@
<a href="https://trendshift.io/repositories/296178?utm_source=trendshift-badge&amp;utm_medium=badge&amp;utm_campaign=badge-trendshift-296178" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/trendshift/repositories/296178/daily?language=Rust" alt="tangency-labs%2Fsolidcraft | Trendshift (Rust)" width="250" height="55"/></a>
</p>
![SolidCraft: model a Raspberry Pi 4 case from an empty design: a sketch dimensioned with parameters, extrude, fillets and shell, a rim rounded in one pick, standoffs patterned on the board's mounting holes, port cut-outs, vent slots, "Pi 4" engraved in a font, a PETG material with its mass in Properties, and a height change in Parameters](docs/screenshots/demo.gif)
![SolidCraft 0.8: model a Raspberry Pi 4 case from an empty design: a sketch dimensioned with parameters, extrude, fillets and shell, a rim rounded in one pick, standoffs patterned on the board's mounting holes, port cut-outs, vent slots, "Pi 4" engraved in a font, a PETG material with its mass in Properties, and a height change in Parameters](docs/screenshots/demo-0.8.gif)
*A Raspberry Pi 4 case from an empty design: a sketch dimensioned with the board's parameters, extrude, fillet, shell, the rim rounded in one pick, an M2.5 standoff patterned on the 58 × 49 mm hole rectangle, port cut-outs, vent slots, "Pi 4" engraved in DejaVu Sans Bold and a PETG material defined in the design, with its mass in Properties, then height 24 → 32 mm in Parameters and it all rebuilds. 6× playback with the idle pauses cut. [Full video](https://github.com/tangency-labs/solidcraft/raw/refs/heads/main/docs/screenshots/demo.mp4), recorded with the [demo script](examples/demo/record_ui.sh).*
*A Raspberry Pi 4 case from an empty design: a sketch dimensioned with the board's parameters, extrude, fillet, shell, the rim rounded in one pick, an M2.5 standoff patterned on the 58 × 49 mm hole rectangle, port cut-outs, vent slots, "Pi 4" engraved in DejaVu Sans Bold and a PETG material defined in the design, with its mass in Properties, then height 24 → 32 mm in Parameters and it all rebuilds. 9× playback. [Full video](https://github.com/tangency-labs/solidcraft/raw/refs/heads/main/docs/screenshots/demo-0.8.mp4), recorded with the [demo script](examples/demo/record_ui.sh).*
## Download
+3 -13
View File
@@ -52,18 +52,16 @@ PCB / Package, Render, Animation, Simulation, Manufacture/CAM, Drawings.
| M0 | Vertical slice | done | — |
| M1 | Sketch depth: command coverage implemented — projection/intersect/include as linked geometry (auto-project on faces, lost-reference warnings, break link), full SKETCH create/modify set (slots, polygons, tangent arcs/circles, ellipse, fit/control splines, conic, text, trim/extend/break, fillet/chamfer, offset, mirror, patterns, move/scale, blend curve, centerline), constraints incl. curvature and polygon with over-constraint refusal, Shift-picked line midpoints in Horizontal/Vertical using persisted points and ordinary constraints (#2087), driven/arc-length/linear-diameter dimensions, AutoConstrain, DXF/SVG insert, 3D sketch curves (Project To Surface, Intersection Curve, Isoparametric Curve, Spun Profile), exact free-form profiles (B-spline/NURBS edges, any spline degree, curves cut by crossings), sweeps and pipes along 3D sketch curves, canvases and decals, curvature combs. Exact free-form Extend and certified associative offsets now preserve geometry and IDs through edits, deletion and reopen. Occurrence-frame 3D wires now store explicit coordinate spaces and retain points and analytic fits through moves, refresh, undo and reopen. Constructive sketch solving (Owen 1991, Fudos & Hoffmann 1997) ahead of the numerical solver: points and rigid clusters are placed on the branch the sketch had, so dimension edits and drags keep their solution; edits that would turn a sketch over are refused; drags re-solve only the sketch per frame. Remaining workflow repairs: mixed offset junctions, legacy natural-spline seams, and damaged legacy wire links lacking frame provenance | in progress | 4 |
| M2 | Robust modelling: coplanar booleans in the kernel, holes (placed on faces, at distances from reference edges, and on sketch points), threads (cosmetic, then modelled), patterns, mirror, shell, draft, split, construction geometry | in progress | 16 |
| M1 | Sketch depth: command coverage implemented — projection/intersect/include as linked geometry (auto-project on faces, lost-reference warnings, break link), full SKETCH create/modify set (slots, polygons, tangent arcs/circles, ellipse, fit/control splines, conic, text, trim/extend/break, fillet/chamfer, offset, mirror, patterns, move/scale, blend curve, centerline), constraints incl. curvature and polygon with over-constraint refusal, Shift-picked line midpoints in Horizontal/Vertical using persisted points and ordinary constraints (#2087), drawing snaps to where two lines, circles or arcs cross (#4967), driven/arc-length/linear-diameter dimensions, AutoConstrain, DXF/SVG insert, 3D sketch curves (Project To Surface, Intersection Curve, Isoparametric Curve, Spun Profile), exact free-form profiles (B-spline/NURBS edges, any spline degree, curves cut by crossings), sweeps and pipes along 3D sketch curves, canvases and decals, curvature combs. Exact free-form Extend and certified associative offsets now preserve geometry and IDs through edits, deletion and reopen. Occurrence-frame 3D wires now store explicit coordinate spaces and retain points and analytic fits through moves, refresh, undo and reopen. Constructive sketch solving (Owen 1991, Fudos & Hoffmann 1997) ahead of the numerical solver: points and rigid clusters are placed on the branch the sketch had, so dimension edits and drags keep their solution; edits that would turn a sketch over are refused; drags re-solve only the sketch per frame. Remaining workflow repairs: mixed offset junctions, legacy natural-spline seams, and damaged legacy wire links lacking frame provenance | in progress | 4 |
| M2 | Robust modelling: coplanar booleans in the kernel, holes (placed on faces and on sketch points), threads (cosmetic, then modelled), patterns, mirror, shell, draft, split, construction geometry | in progress | 16 |
| M3 | General fillets and chamfers (curved edges, chains, vertex blends); saddle rolling-ball surfaces now use continuous seam sections and arc-length fitting with shared contact boundaries and bounded accuracy checks; tiny rim edges remain selected instead of nearby wall seams when filleting (#2498) | in progress | 30 |
| M4 | Files: STEP import (done — [docs/step-import.md](docs/step-import.md)), FreeCAD/Rhino solids through cadmpeg (done, desktop, browser and CLI), 3MF export and 3MF/STL import as mesh bodies (done), 3MF with B-rep face tags ([docs/formats/3mf-face-tags.md](docs/formats/3mf-face-tags.md), done), constant-section parametric mesh reconstruction, DXF sketches, OBJ import | in progress | 6 |
| M5 | Persistent naming of faces and edges (docs/book/src/dev/naming.md): faces named from their exact kernel-reported history (sketch curve sides, caps, revolve sections, primitive sides, blends, shell inner walls, pattern/mirror copies; split pieces told apart by the faces around them, lumps of a cut body named after their faces), edges by their faces; fillets/chamfers, sketches on faces, shell/draft/offset/replace faces, hole placements, joint origins and face appearances resolve by name first and warn when the entity split or vanished: done. sheet metal edges by base sketch line and flange side, shell inner faces, sweep and loft roles: done. kernel-reported face history: done (merged faces answer to every name; operations without history yet fall back to geometric names, flagged unverified; format 14 keeps older designs' geometric names). Left: history through sheet metal, plastic, surface and emboss features; FaceSource certificates through history | in progress | 20 |
| M6 | UI depth: selection feedback and origin (done), timeline editing (done: hover and selection highlight modifier faces, including fillets and chamfers; edit feature, history marker drag, dependency-checked single and multi-item reorder, suppress, rename, delete with dependents, re-resolved references; redefining selections refreshes persistent edge/face references while retaining feature identity and dimension names; responsive feature history with width-driven icons/names/title, snapping width resize, bottom/left/browser-stack docking, shared-column collapse, rollback bar, multi-selection and persistent folders), right-docked dialogs (done), live previews for every feature dialog (done), keyboard-first commands with on-canvas value boxes and sketch inline dimensions (done), drag manipulators with automatic join/cut (done: extrude incl. two sides, fillet, shell, hole depth, move, pattern spacing, revolve rotator, extrude taper), dark theme (done), right-click menu with Repeat (done), measure tool (done), section analysis (done), selection filter (done), drag-solve of sketch points (done); appearance controls now preserve body/face/component targeting, named transparent looks, reuse/search and override removal; Browser Expand All / Collapse All (`browser.expand_all`, `browser.collapse_all`) on the design and component menus; preferences | in progress | 6 |
| M6 | UI depth: selection feedback and origin (done), timeline editing (done: hover and selection highlight modifier faces, including fillets and chamfers; edit feature, history marker drag, dependency-checked single and multi-item reorder, suppress, rename, delete with dependents, re-resolved references; redefining selections refreshes persistent edge/face references while retaining feature identity and dimension names; responsive feature history with width-driven icons/names/title, snapping width resize, bottom/left/browser-stack docking, shared-column collapse, rollback bar, multi-selection and persistent folders), right-docked dialogs (done), live previews for every feature dialog (done), keyboard-first commands with on-canvas value boxes and sketch inline dimensions (done), drag manipulators with automatic join/cut (done: extrude incl. two sides, fillet, shell, hole depth, move, pattern spacing, revolve rotator, extrude taper), dark theme (done), right-click menu with Repeat (done), measure tool (done), section analysis (done), selection filter (done), drag-solve of sketch points (done); appearance controls now preserve body/face/component targeting, named transparent looks, reuse/search and override removal; preferences | in progress | 6 |
| M7 | Thin extrude (walls along open or closed curves: centre, inside, outside or two sides; mitred corners, crossing walls joined; #721), sweep, loft (incl. to a point), rib and web (to next / depth), emboss (planar faces and exact radial emboss/deboss of bounded orthogonal profiles on complete exterior cylinders/sleeves), thread (cosmetic, and modelled M3–M20 outside and inside: the ISO profile screwed in as a faceted cut), coil (smooth B-spline helical walls), pipe, pattern on path, patterns of bodies and components, replace face (parallel), align, remove, feature copy/paste: done. Unique eligible cylindrical targets are inferred for new Emboss features. Left: general curved-face emboss and exact (unfaceted) modelled threads | in progress | 12 |
| M8 | Surface workspace (#772). Done: Patch (sketch regions; edge loops filled G0, plus `crown`), Stitch, Thicken (planes and cylinders), Extend (straight edges of planar surfaces), Trim (by a plane, a face or sketch curves swept onto the surface, keeping a side or the picked regions; mutual trim of two surfaces, extended to meet and stitched, #767), Delete Face (healing), Boundary Fill, zebra/draft/curvature-map analysis, Surface Extrude/Revolve/Sweep/Loft from open and closed curves, Offset, Unstitch and Reverse Normal; the continuity-driven core (`kernel/src/bridge.rs`, G0–G3 Hermite rows) with **Blend** (`surface.blend`, #764: edge chains along one or several faces, closed rims paired seam to seam or from picked seams, a dialog), **Match** (`surface.match`), **Boundary** (`surface.boundary`, #763 — two sketch curves exact with G0–G3, three or four as the flat patch of their loop, and curve networks: Coons and Gordon surfaces through sketch, 3D-sketch or edge curves, a point for a three-sided side, G1/G2 against the faces along one direction's edges, joined into their body on request; G1/G2 on two sides that meet at a corner waits for the Gregory-type corner), **Fair** (`surface.fair`, CV fairing), **continuity analysis** (`inspect.continuity`, #770; a toolbar tool with edge markers and a legend, and curvature combs along face isocurves) and a multi-section **loft** with per-end continuity and crown, and guide rails on solid and surface lofts (one or two, scaling or stretching the sections between; #201). Also in the Class-A pass: G1…G4 blend curves in the sketch, chord and variable-radius fillets on curved edges (with honest refusals where the fit cannot hold), and a G2 corner patch where filleted edges meet (kernel; the command refuses until the patch's sections can be sewn to the blends' contacts). Left: bias/form-factor fillet sections (design recorded), a Gregory-type corner, surface Fillet between separate surface bodies (#769), Ruled (#762), Untrim (#761), curved-face Extend (#760), and every remaining #757–#772 item | deferred | — |
| M9 | Components and assemblies: New Component / Create Components from Bodies, active component, per-component origin/bodies/sketches/timeline entries, occurrences with transforms, Copy/Paste vs Paste New, nested Browser tree with visibility and isolate; Ground, Move/Copy of occurrences, joints (rigid, revolute, slider, cylindrical, pin-slot, planar, ball) by joint origins with limits and Drive Joints, closed kinematic loops (four-bar, slider-crank: the loop's other joints follow the driven one on its branch, and driving stops where a loop can no longer close), cams (`joint.cam`: a revolute cam drives a slider or revolute follower along a closed sketch profile or the cam's edges, knife, roller or flat followers, worked out again when the profile changes; drives, studies, closed loops, contact sets), As-built Joint, Rigid Group, Contact Sets (driving and motion studies stop where bodies in a set would pass through each other), Motion Study (joint values keyed along a timeline, playback, export of occurrence positions as JSON/CSV), motion analysis (joint values and a point's path charted over a study as value, velocity and acceleration; the parts that collide along a study, with the steps where they do), Exploded views (automatic or by hand, shown without changing placements, and exported as an animation coming apart and back together); interference, per-component physical properties; STEP with assembly structure; URDF export for robot simulators (links with meshes, mass and inertia; joint chains whose values match Drive Joints; docs/urdf.md). Physical properties now measure material-aware component/occurrence placements, descendants and repeated instances, including pending moves and body reassignment offsets; contact geometry and automatic exploded centres respect those offsets. Groundwork done: component tree, occurrences with transforms (instances, Paste New), world placement, active component as authoring frame, ground, Capture/Revert Position, moving bodies and sketches. UI: ASSEMBLE panel dialogs for New Component, Joint (snaps on faces, edges, circles and vertices; animated motion preview; offsets, flip, limits), As-built Joint, Joint Origin, Rigid Group, Drive Joints (live slider), Motion Link, Cam and Interference (overlaps shown red); joints in the timeline and the Browser | in progress | 32 |
| M9 | Components and assemblies: New Component / Create Components from Bodies, active component, per-component origin/bodies/sketches/timeline entries, occurrences with transforms, Copy/Paste vs Paste New, nested Browser tree with visibility and isolate; Ground, Move/Copy of occurrences, joints (rigid, revolute, slider, cylindrical, pin-slot, planar, ball) by joint origins with limits and Drive Joints, closed kinematic loops (four-bar, slider-crank: the loop's other joints follow the driven one on its branch, and driving stops where a loop can no longer close), cams (`joint.cam`: a revolute cam drives a slider or revolute follower along a closed sketch profile or the cam's edges, knife, roller or flat followers, worked out again when the profile changes; drives, studies, closed loops, contact sets), As-built Joint, Rigid Group, Contact Sets (driving and motion studies stop where bodies in a set would pass through each other), Motion Study (joint values keyed along a timeline, playback, export of occurrence positions as JSON/CSV), motion analysis (joint values and a point's path charted over a study as value, velocity and acceleration; the parts that collide along a study, with the steps where they do), Exploded views (automatic or by hand, shown without changing placements); interference, per-component physical properties; STEP with assembly structure; URDF export for robot simulators (links with meshes, mass and inertia; joint chains whose values match Drive Joints; docs/urdf.md). Physical properties now measure material-aware component/occurrence placements, descendants and repeated instances, including pending moves and body reassignment offsets; contact geometry and automatic exploded centres respect those offsets. Groundwork done: component tree, occurrences with transforms (instances, Paste New), world placement, active component as authoring frame, ground, Capture/Revert Position, moving bodies and sketches. UI: ASSEMBLE panel dialogs for New Component, Joint (snaps on faces, edges, circles and vertices; animated motion preview; offsets, flip, limits), As-built Joint, Joint Origin, Rigid Group, Drive Joints (live slider), Motion Link, Cam and Interference (overlaps shown red); joints in the timeline and the Browser | in progress | 32 |
| M10 | Sheet metal: flange (base, edge, contour), hem, unfold/refold, convert, flat pattern + DXF, rules: done. PLASTIC enclosure subset: Boss, Rib, Web, Lip/Groove, Snap Fit, Rest, Plastic Rules (library, edit, assign; rule draft and clearance as feature defaults): done, tested against hand-computed geometry. UI: SHEET METAL and PLASTIC tabs with dialogs (live preview, Edit Feature, pre-selection) for Flange, Hem, Unfold/Refold, Convert, Sheet Metal Rules, Create Flat Pattern (flat view with bend lines, bend table CSV/JSON and DXF export with exact circle/arc/line cutouts), Boss, Lip/Groove, Snap Fit, Rest, Manage and Assign Plastic Rules; Fold (bend along a sketch line); flanges meeting at a corner closed at any angle with the rule's gap; edge flanges with their own angle and height per edge in one feature; corner reliefs (notch, round, rectangular, tear) chosen by the rule; Convert to Sheet Metal from a solid or shelled body by its bend edges (flat faces, sharp bends) | in progress | 36 |
| M11 | 2D drawings | deferred | — |
| M12 | Release: cross-platform packages, verification and deliberate publication implemented; original current-UI demo refreshed. macOS Developer ID signing and notarization are in the release workflow (#5749; they switch on when the Apple secrets exist). Windows installer signing and broader performance work remain | in progress | 16 |
| M12 | Release: cross-platform packages, verification and deliberate publication implemented; original current-UI demo refreshed. Trusted distribution signing and broader performance work remain | in progress | 16 |
| M14 | Configurations: a table of rows (parameter expressions and feature suppressions per row) switched with config.activate (undoable, all or nothing), kept through parameter renames and feature deletes (config.configure, config.table, config.column/row/activate). Per-row body materials and appearances (library looks, custom colour/opacity and clearing overrides), undoable activation and save/reopen: done. Ordered typed configuration rules, atomic validation and structured rule editing: done. Component appearance and certified semantic face style columns, atomic preview/apply and independent-copy remapping: done. Exact constructed sketch-side and immutable imported-face lineage now preserve legacy names and reject retargeting. Left: ambiguous shifted/two-sided caps | in progress | 4 |
| M13 | Never lose work: atomic saves (temporary file, flush, rename; a crash at any moment leaves the old file or the new one) with the replaced version kept as `<file>.bak`; autosave of unsaved changes to the recovery folder every 5 minutes (preference) and after big operations, only when something changed; "Recover unsaved design?" on the next launch (`doc.recovery_list`, `doc.recover`, `doc.recovery_discard`); a kill-mid-save test (process killed while saving a 0.5 MB design 25 times). File format: `"format": "solidcraft/12"`, older formats upgraded step by step (`doc::format`), newer ones refused with a message, out-of-range numbers and broken placements refused; a corpus of designs saved by M0, components-era, joints-era and current builds (apps/solidcraft-cli/tests/corpus) that must open and evaluate as saved; damaged and hostile variants (truncation, bit flips, hostile values, removed keys, repeated features, deep nesting) never panic | done | 6 |
| M15 | Frames and weldments (#4003): structural members along 2D and 3D sketch lines, butt and mitre corners, trim to faces and bodies, end caps, gussets, cosmetic welds and a cut list. Done: the profile library (`frame.profile.list`, `frame.profile.properties`, `frame.profile.insert`): bar, round bar, rectangular and round tube, angle, channel and I-beam, each SolidCraft's own parametric definition with exact fillet arcs and exact section properties (area, centroid, second moments), tested against hand-computed values; members (`frame.member`): one body per straight line of a 2D or 3D frame sketch, following parameter edits, with a built-in profile (its dimensions parameter expressions) or a profile sketch (its drawn material by alternating nested loops), anchor, angle and mirror; each member's name kept with its line. Left: corners, trims, end caps, gussets, welds, cut list, FRAME tab and dialogs | in progress | 60 |
@@ -206,11 +204,6 @@ completed drawings publish atomically. The browser still exports synchronously.
## Dimensioning and error reporting
- Operation length fields share design-unit defaults and input evaluation, including secondary
distances, thin walls, blend controls, sheet/plastic rules, joints and motion keys. Canvas
handles and sketch previews use the same lengths as committed geometry. Existing stored
millimetre values retain their size when edited in a design using another unit (#3271).
- MCP `execute` accepts `verbose: false`, sharing the compact reply helper used by
`batch`: body changes and created sketch IDs stay, while repeated lists are omitted.
Both tools retain full replies by default.
@@ -254,9 +247,6 @@ completed drawings publish atomically. The browser still exports synchronously.
- Sketch Dimension takes a line and a circle or arc (#1803): it measures from the line to the
circle's edge (the gap, 0 when tangent, or how far the edge reaches past a line that cuts
it), drives it like any other dimension and keeps the circle on its side of the line.
- Sketch Dimension takes a point and a circle or arc, or two circles or arcs (#4064): it
measures to the circle's edge (from the point, or between the two edges: the gap, 0 when
they touch, how far they overlap, or the narrowest gap between nested circles).
- Sketch annotations, live drawing lengths and Measure use document units. Inches show
three decimals and retain a meaningful fourth digit. Bare length dimension entry captures
the current design unit; changing units leaves existing geometry and expressions intact.
-5
View File
@@ -19,11 +19,6 @@ path = "src/main.rs"
name = "solvecraft-cli"
path = "src/alias.rs"
[features]
default = ["raytrace-cpu"]
bundled-render-library = ["solidcraft-engine/bundled-render-library"]
raytrace-cpu = ["solidcraft-engine/raytrace-cpu"]
[dependencies]
solidcraft-engine = { workspace = true }
solidcraft-gpu = { workspace = true }
@@ -1,11 +1,9 @@
//! Reproducible evidence through the production command/job interfaces.
//! Usage: studio_evidence RECIPE.json RENDER-PARAMS.json OUTPUT.png
//! Writes the editable design, command timings and actual adapter/job metadata beside PNG.
//! Accepts runtime requests as well as inline or named saved render jobs.
use serde_json::{Value, json};
use solidcraft_engine::{
Session,
doc::render_settings::{RenderJob, RenderSetup},
render::job::{CpuHost, ErrorCode, JobId, Phase},
};
use std::{
@@ -23,35 +21,6 @@ fn read_json(path: &str) -> Result<Value, String> {
serde_json::from_slice(&bytes).map_err(|e| format!("{path}: {e}"))
}
// Only decide which optional host must be installed. The production command
// still validates and resolves the actual request without a second wire format.
fn needs_gpu(session: &Session, params: &Value) -> Result<bool, String> {
let inline = params.get("settings").filter(|value| !value.is_null());
let named = params.get("setup").filter(|value| !value.is_null());
if inline.is_some() && named.is_some() {
return Err("choose either setup or settings".into());
}
let inline_setup = inline
.filter(|value| value.get("job").is_some_and(|job| !job.is_null()))
.map(|value| serde_json::from_value::<RenderSetup>(value.clone()).map_err(|e| e.to_string()))
.transpose()?;
let setup = if let Some(name) = named.and_then(Value::as_str) {
Some(session.doc.render.setups.get(name).ok_or("no saved render setup with that name")?)
} else {
inline_setup.as_ref()
};
if let Some(setup) = setup
&& let Some(job) = &setup.job
{
if params.as_object().is_some_and(|object| object.keys().any(|key| !matches!(key.as_str(), "setup" | "settings" | "replace"))) {
return Err("saved render jobs cannot be mixed with runtime controls".into());
}
setup.validate().map_err(|e| e.to_string())?;
return Ok(matches!(job, RenderJob::GpuPath { .. }));
}
Ok(params.get("progressive").and_then(Value::as_bool) == Some(true))
}
fn run() -> Result<(), String> {
let args: Vec<String> = std::env::args().skip(1).collect();
let [recipe_path, params_path, output_path] = args.as_slice() else {
@@ -78,36 +47,18 @@ fn run() -> Result<(), String> {
.collect();
drop(scene);
let cpu_host_start = Instant::now();
let policy = solidcraft_gpu::select::Policy::from_env().map_err(|e| e.to_string())?;
let (adapter, adapter_report) = solidcraft_gpu::select::pick(policy).ok_or("no adapter satisfies the requested GPU policy")?;
let host_start = Instant::now();
session.set_render_job_host(Some(CpuHost::for_platform().map_err(|e| e.to_string())?)).map_err(|e| e.to_string())?;
let cpu_host_seconds = cpu_host_start.elapsed().as_secs_f64();
// Match runtime and saved-job dispatch. CPU evidence must not require a
// Vulkan device, parse GPU policy or initialize a GPU host.
let (adapter_report, gpu_host_seconds) = if needs_gpu(&session, &params)? {
let gpu_host_start = Instant::now();
let policy = solidcraft_gpu::select::Policy::from_env().map_err(|e| e.to_string())?;
let (adapter, report) =
solidcraft_gpu::select::pick_vulkan(policy, |_| true).ok_or("no Vulkan adapter satisfies the requested GPU policy")?;
session.register_render_job_host(solidcraft_gpu::path::host_on_adapter(&adapter).map_err(|e| e.to_string())?).map_err(|e| e.to_string())?;
(Some(report), Some(gpu_host_start.elapsed().as_secs_f64()))
} else {
(None, None)
};
session.register_render_job_host(solidcraft_gpu::path::host_on_adapter(&adapter).map_err(|e| e.to_string())?).map_err(|e| e.to_string())?;
let host_seconds = host_start.elapsed().as_secs_f64();
let preflight = session.execute("render.preflight", &params).map_err(|e| e.to_string())?;
let start = Instant::now();
let started = session.execute("render.start", &params).map_err(|e| e.to_string())?;
let executor = started
.get("resolved")
.and_then(|resolved| resolved.get("executor"))
.and_then(Value::as_str)
.ok_or("started job did not report the resolved image executor")?
.to_owned();
let id: JobId = serde_json::from_value(started.get("job").cloned().ok_or("missing job id")?).map_err(|e| e.to_string())?;
let handle = session.render_job_handle(id).map_err(|e| e.to_string())?;
eprintln!("Rendering through production {executor} executor");
if let Some(adapter) = &adapter_report {
eprintln!("GPU adapter: {adapter:?}");
}
eprintln!("Rendering through production gpu-path host: {adapter_report:?}");
let status = loop {
match handle.wait_timeout(Duration::from_secs(10)) {
Ok(status) => break status,
@@ -133,11 +84,11 @@ fn run() -> Result<(), String> {
}
let exported = session.execute("render.export", &json!({"job":id,"path":output_path,"overwrite":true})).map_err(|e| e.to_string())?;
let report = json!({
"recipe":recipe_path,"render_params":params,"executor":executor,"adapter":adapter_report,
"recipe":recipe_path,"render_params":params,"adapter":adapter_report,
"environment":{"SOLIDCRAFT_GPU":std::env::var("SOLIDCRAFT_GPU").ok(),"WGPU_BACKEND":std::env::var("WGPU_BACKEND").ok()},
"cad_generation_seconds":generation_seconds,"cpu_host_setup_seconds":cpu_host_seconds,"gpu_host_setup_seconds":gpu_host_seconds,
"cad_generation_seconds":generation_seconds,"gpu_host_setup_seconds":host_seconds,
"render_and_encode_seconds":render_and_encode_seconds,
"timing_scope":"wall time from render.start through completed PNG encoding; includes job preparation and readback, excludes CAD generation, preflight and initial CPU/GPU host setup; GPU setup timing includes adapter selection and pipelines when requested",
"timing_scope":"wall time from render.start through completed PNG encoding; includes job preparation and readback, excludes CAD generation and initial GPU pipeline setup",
"bodies":bodies,"commands":commands,"command_timings":command_timings,
"preflight":preflight,"export":exported,"editable_design":design_path
});
@@ -69,7 +69,6 @@ impl Control {
return Err("--connect requires a loopback address and a nonzero port (the app listens on 127.0.0.1)".into());
}
let token = solidcraft_engine::control_auth::client_token(addr.port(), token)
.map(|t| t.token)
.ok_or("no control token: pass --token, set SOLIDCRAFT_CONTROL_TOKEN, or run on the app's computer (its token file)")?;
if token.len() > 4096 {
return Err("the control token is longer than 4096 bytes".into());
+8 -37
View File
@@ -18,7 +18,6 @@
//! solidcraft-cli mcp [--headless] [--in design|script] [--connect HOST:PORT [--token T]] [--allow-path DIR]... [--allow-any-path]
//! [--param-check off|warn|strict]
//! MCP server on stdio (docs/mcp.md)
//! solidcraft-cli mcp setup [--dry-run] [--client NAME] [--live] (alias: mcp install) add it to OpenCode, Claude Desktop, Claude Code, Cursor
//! solidcraft-cli licences SolidCraft's licence and the third-party licences
//! solidcraft-cli icon --out F.png|F.ico [--size N] the app icon (packaging; drawn, not a file)
//!
@@ -83,7 +82,7 @@ const USAGE_ORACLE: &str = " solidcraft-cli oracle <case-dir>... [--json]\n";
const USAGE_FUZZ: &str =
" solidcraft-cli fuzz [--from N] [--count N] [--jobs J] [--timeout S] [--steps N] [--minimise] [--out DIR] [--report FILE]\n";
/// `mcp`'s lines in the usage.
const USAGE_MCP: &str = " solidcraft-cli mcp [--headless] [--in design.solidcraft|script.json] [--connect 127.0.0.1:PORT [--token T]] [--allow-path DIR]... [--allow-any-path]\n [--param-check off|warn|strict] [--render-gpu]\n solidcraft-cli mcp setup [--dry-run] [--client opencode|claude-desktop|claude-code|cursor] [--live]\n (adds a `solidcraft` server, with this program's path, to each installed AI client; --live adds `solidcraft-live` for the running app; alias: mcp install)\n";
const USAGE_MCP: &str = " solidcraft-cli mcp [--headless] [--in design.solidcraft|script.json] [--connect 127.0.0.1:PORT [--token T]] [--allow-path DIR]... [--allow-any-path]\n [--param-check off|warn|strict]\n";
/// `crashes`'s lines in the usage.
const USAGE_CRASHES: &str = " solidcraft-cli crashes [list [--json] | show <id> | replay <id> | dir | clear [<id>]]\n";
/// `licences`'s lines in the usage.
@@ -626,27 +625,10 @@ fn cmd_exec(args: &[String]) -> Result<(), String> {
Ok(())
}
/// `mcp setup`: add the server to each installed AI client, with this program's own path.
fn cmd_mcp_setup(args: &[String]) -> Result<(), String> {
use solidcraft_engine::agent_setup as setup;
let (lines, ok) = setup::setup(args, &setup::Env::from_process(), &setup::current_exe()?)?;
for l in lines {
println!("{l}");
}
if ok { Ok(()) } else { Err("mcp setup: some clients could not be configured".into()) }
}
/// MCP server on stdio: headless (optionally starting from a design or script) or bridged to
/// a running app's control port. stdout carries only protocol messages. Files the agent's tool
/// calls reach are confined to `solidcraft_mcp::agent_scope` unless `--allow-any-path` is given.
fn cmd_mcp(args: &[String]) -> Result<(), String> {
if matches!(args.first().map(String::as_str), Some("setup" | "install")) {
return cmd_mcp_setup(&args[1..]);
}
let render_gpu = args.iter().any(|arg| arg == "--render-gpu");
if render_gpu && flag(args, "--connect").is_some() {
return Err("mcp: --render-gpu installs a headless image host; a connected application owns its own device".into());
}
install_renderer();
let scope = (!args.iter().any(|a| a == "--allow-any-path"))
.then(|| solidcraft_mcp::agent_scope(flags(args, "--allow-path").into_iter().map(std::path::PathBuf::from)));
@@ -690,30 +672,19 @@ fn cmd_mcp(args: &[String]) -> Result<(), String> {
}
Box::new(remote)
}
(None, Some(path)) => {
let mut headless = solidcraft_mcp::Headless::new(load_checked(path, param_check)?);
if render_gpu {
headless.session.register_render_job_host(solidcraft_gpu::path::host().map_err(|e| e.to_string())?).map_err(|e| e.to_string())?;
}
Box::new(headless)
}
(None, Some(path)) => Box::new(solidcraft_mcp::Headless::new(load_checked(path, param_check)?)),
// No arguments: the running desktop app, found through the discovery file it wrote
// (docs/control-protocol.md); none running (or `--headless`): a session of its own.
(None, None) => {
let auto = !args.iter().any(|a| a == "--headless");
if auto && let Some(port) = discover_app() {
if !args.iter().any(|a| a == "--headless")
&& let Some(port) = discover_app()
{
eprintln!("solidcraft mcp: bridged to the running app at 127.0.0.1:{port} (--headless starts a session of its own)");
return serve_mcp(Box::new(solidcraft_mcp::Remote::new(&format!("127.0.0.1:{port}"), flag(args, "--token"))), scope);
}
let mut headless = solidcraft_mcp::Headless::default();
headless.session.param_check = param_check;
if render_gpu {
headless.session.register_render_job_host(solidcraft_gpu::path::host().map_err(|e| e.to_string())?).map_err(|e| e.to_string())?;
}
if auto {
// The app may start after the agent: look for it again until there is work here.
Box::new(solidcraft_mcp::Auto::new(headless, Box::new(discover_app), flag(args, "--token")))
} else {
Box::new(headless)
}
Box::new(headless)
}
};
serve_mcp(backend, scope)
+43 -97
View File
@@ -1,86 +1,32 @@
//! Complete headless image-job workflow through the same engine commands as the UI/MCP.
use serde_json::{Value, json};
use serde_json::json;
use solidcraft_engine::{
Session,
doc::render_settings::{RenderJob, RenderSetup},
render::job::{CpuHost, JobId, Phase, ToneMap},
doc::render_settings::RenderSetup,
render::job::{CpuHost, JobId, Phase},
};
/// Only explicit runtime CLI flags are stored here. A typed setup owns these
/// values even when a supplied flag happens to match the saved value.
#[derive(Default)]
struct RuntimeOptions {
progressive: bool,
transparent: bool,
samples: Option<u32>,
max_depth: Option<u32>,
seed: Option<u32>,
lighting: Option<String>,
tone_map: Option<ToneMap>,
environment: Option<String>,
}
impl RuntimeOptions {
fn explicit_flag(&self) -> Option<&'static str> {
[
(self.progressive, "--progressive"),
(self.transparent, "--transparent"),
(self.samples.is_some(), "--samples"),
(self.max_depth.is_some(), "--max-depth"),
(self.seed.is_some(), "--seed"),
(self.lighting.is_some(), "--lighting"),
(self.tone_map.is_some(), "--tone-map"),
(self.environment.is_some(), "--environment"),
]
.into_iter()
.find_map(|(present, flag)| present.then_some(flag))
}
fn request(&self, session: &Session, settings: RenderSetup) -> Result<(Value, bool), String> {
if let Some(job) = &settings.job {
if let Some(flag) = self.explicit_flag() {
return Err(format!("{flag} cannot accompany a typed saved render setup; change the saved job options instead"));
}
settings.validate().map_err(|e| e.to_string())?;
session.doc.render.validate_resources(&settings).map_err(|e| e.to_string())?;
let needs_gpu = matches!(job, RenderJob::GpuPath { .. });
return Ok((json!({"settings":settings}), needs_gpu));
}
// Legacy setups and unnamed requests retain their runtime wire contract.
settings.validate().map_err(|e| e.to_string())?;
let mut params = json!({"settings":settings,"transparent_background":self.transparent,
"progressive":self.progressive,"samples":self.samples,"max_depth":self.max_depth,"seed":self.seed});
if let Some(path) = &self.lighting {
let text = solidcraft_engine::io::vfs::read_to_string(path, 65536).map_err(|e| format!("could not read lighting: {e}"))?;
let lighting: solidcraft_engine::render::job::StudioLighting = serde_json::from_str(&text).map_err(|e| e.to_string())?;
params["lighting"] = json!(lighting);
}
if let Some(tone_map) = self.tone_map {
params["tone_map"] = json!(tone_map);
}
if let Some(path) = &self.environment {
let text = solidcraft_engine::io::vfs::read_to_string(path, 65536).map_err(|e| format!("could not read environment: {e}"))?;
params["environment"] = serde_json::from_str(&text).map_err(|e| e.to_string())?;
}
Ok((params, self.progressive))
}
}
pub fn run(args: &[String]) -> Result<(), String> {
let input = args.first().filter(|p| !p.starts_with('-')).ok_or("render requires an input design or script")?;
let mut out = None;
let mut setup = None;
let mut width = None;
let mut height = None;
let mut progressive = false;
let mut samples = None;
let mut max_depth = None;
let mut seed = None;
let mut exposure = None;
let mut runtime = RuntimeOptions::default();
let mut lighting = None;
let mut tone_map = None;
let mut environment = None;
let mut transparent = false;
let mut overwrite = false;
let mut timeout = 120u64;
let mut i = 1;
while let Some(arg) = args.get(i) {
match arg.as_str() {
"--progressive" => runtime.progressive = true,
"--transparent" => runtime.transparent = true,
"--progressive" => progressive = true,
"--transparent" => transparent = true,
"--overwrite" => overwrite = true,
"--out" | "--setup" | "--width" | "--height" | "--timeout" | "--samples" | "--max-depth" | "--seed" | "--exposure" | "--lighting"
| "--tone-map" | "--environment" => {
@@ -91,17 +37,23 @@ pub fn run(args: &[String]) -> Result<(), String> {
"--setup" => setup = Some(value.clone()),
"--width" => width = Some(value.parse::<u32>().map_err(|_| "invalid width")?),
"--height" => height = Some(value.parse::<u32>().map_err(|_| "invalid height")?),
"--samples" => runtime.samples = Some(value.parse::<u32>().map_err(|_| "invalid samples")?),
"--max-depth" => runtime.max_depth = Some(value.parse::<u32>().map_err(|_| "invalid depth")?),
"--seed" => runtime.seed = Some(value.parse::<u32>().map_err(|_| "invalid seed")?),
"--samples" => samples = Some(value.parse::<u32>().map_err(|_| "invalid samples")?),
"--max-depth" => max_depth = Some(value.parse::<u32>().map_err(|_| "invalid depth")?),
"--seed" => seed = Some(value.parse::<u32>().map_err(|_| "invalid seed")?),
"--exposure" => exposure = Some(value.parse::<f64>().map_err(|_| "invalid exposure")?),
// Defer file reads until a saved job has rejected conflicting controls.
"--environment" => runtime.environment = Some(value.clone()),
"--lighting" => runtime.lighting = Some(value.clone()),
"--environment" => {
let text =
solidcraft_engine::io::vfs::read_to_string(value, 65536).map_err(|e| format!("could not read environment: {e}"))?;
environment = Some(serde_json::from_str::<serde_json::Value>(&text).map_err(|e| e.to_string())?);
}
"--lighting" => {
let text = solidcraft_engine::io::vfs::read_to_string(value, 65536).map_err(|e| format!("could not read lighting: {e}"))?;
lighting = Some(serde_json::from_str::<solidcraft_engine::render::job::StudioLighting>(&text).map_err(|e| e.to_string())?);
}
"--tone-map" => {
runtime.tone_map = Some(match value.as_str() {
"reinhard" => ToneMap::Reinhard,
"aces_fitted" => ToneMap::AcesFitted,
tone_map = Some(match value.as_str() {
"reinhard" => solidcraft_engine::render::job::ToneMap::Reinhard,
"aces_fitted" => solidcraft_engine::render::job::ToneMap::AcesFitted,
_ => return Err("tone-map must be reinhard or aces_fitted".into()),
})
}
@@ -117,10 +69,21 @@ pub fn run(args: &[String]) -> Result<(), String> {
}
let out = out.ok_or("render requires --out FILE.png")?;
let mut session = crate::load(input)?;
let mut settings = if let Some(name) = setup.as_deref() {
session.doc.render.setups.get(name).cloned().ok_or("no saved render setup with that name")?
session.set_render_job_host(Some(CpuHost::for_platform().map_err(|e| e.to_string())?)).map_err(|e| e.to_string())?;
if progressive {
session.register_render_job_host(solidcraft_gpu::path::host().map_err(|e| e.to_string())?).map_err(|e| e.to_string())?;
}
session.execute("render.scene", &json!({})).map_err(|e| e.to_string())?;
let mut settings = if let Some(name) = setup {
session.doc.render.setups.get(&name).cloned().ok_or("no saved render setup with that name")?
} else {
RenderSetup::default()
let mut settings = RenderSetup::default();
if let Some(camera) = session.camera {
settings.camera = camera;
} else {
settings.camera.fit(&solidcraft_engine::product_scene::extract(&session).map_err(|e| e.to_string())?.bounds);
}
settings
};
if let Some(width) = width {
settings.width = width;
@@ -131,24 +94,7 @@ pub fn run(args: &[String]) -> Result<(), String> {
if let Some(exposure) = exposure {
settings.exposure_ev = exposure;
}
// Resolve saved intent before opening a GPU or explicitly settling geometry.
// Width/height/exposure only change this validated inline settings clone.
let (mut params, needs_gpu) = runtime.request(&session, settings)?;
session.set_render_job_host(Some(CpuHost::for_platform().map_err(|e| e.to_string())?)).map_err(|e| e.to_string())?;
if needs_gpu {
session.register_render_job_host(solidcraft_gpu::path::host().map_err(|e| e.to_string())?).map_err(|e| e.to_string())?;
}
session.execute("render.scene", &json!({})).map_err(|e| e.to_string())?;
if setup.is_none() {
let camera = if let Some(camera) = session.camera {
camera
} else {
let mut camera = RenderSetup::default().camera;
camera.fit(&solidcraft_engine::product_scene::extract(&session).map_err(|e| e.to_string())?.bounds);
camera
};
params["settings"]["camera"] = json!(camera);
}
let params = json!({"settings":settings,"transparent_background":transparent,"progressive":progressive,"samples":samples,"max_depth":max_depth,"seed":seed,"lighting":lighting,"tone_map":tone_map,"environment":environment});
let preflight = session.execute("render.preflight", &params).map_err(|e| e.to_string())?;
let started = session.execute("render.start", &params).map_err(|e| e.to_string())?;
let id: JobId = serde_json::from_value(started["job"].clone()).map_err(|e| e.to_string())?;
-112
View File
@@ -151,115 +151,3 @@ fn mcp_without_arguments_finds_the_running_app() {
assert!(!String::from_utf8_lossy(&out.stderr).contains("bridged to the running app"), "{}", String::from_utf8_lossy(&out.stderr));
std::fs::remove_dir_all(&control_dir).ok();
}
/// `mcp setup` against a temporary home: the client folders it finds are the only ones it writes.
mod setup {
use serde_json::{Value, json};
use std::path::{Path, PathBuf};
use std::process::Command;
fn home(name: &str) -> PathBuf {
let h = std::env::temp_dir().join(format!("solidcraft-mcp-setup-{name}-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&h);
std::fs::create_dir_all(&h).unwrap();
h
}
fn run(home: &Path, args: &[&str]) -> std::process::Output {
Command::new(env!("CARGO_BIN_EXE_solidcraft-cli"))
.args(["mcp", "setup"])
.args(args)
.env_clear()
.env("HOME", home)
.env("USERPROFILE", home)
.env("APPDATA", home.join("AppData/Roaming"))
.env("LOCALAPPDATA", home.join("AppData/Local"))
.env("XDG_CONFIG_HOME", home.join(".config"))
.env("PATH", "")
.output()
.unwrap()
}
fn json_of(p: &Path) -> Value {
serde_json::from_str(&std::fs::read_to_string(p).unwrap()).unwrap()
}
fn exe() -> String {
let p = Path::new(env!("CARGO_BIN_EXE_solidcraft-cli")).canonicalize().unwrap();
p.to_string_lossy().trim_start_matches(r"\\?\").to_owned()
}
#[test]
fn merges_into_opencode_and_cursor_keeping_other_servers() {
let h = home("merge");
let oc = h.join(".config/opencode");
std::fs::create_dir_all(&oc).unwrap();
let old = json!({"$schema": "https://opencode.ai/config.json", "theme": "x", "mcp": {"other": {"type": "local", "command": ["o"]}}});
std::fs::write(oc.join("opencode.json"), old.to_string()).unwrap();
std::fs::create_dir_all(h.join(".cursor")).unwrap();
std::fs::write(h.join(".cursor/mcp.json"), r#"{"mcpServers": {"other": {"command": "o"}}}"#).unwrap();
let out = run(&h, &["--live"]);
assert!(out.status.success(), "{}", String::from_utf8_lossy(&out.stderr));
let v = json_of(&oc.join("opencode.json"));
assert_eq!(v["theme"], "x");
assert_eq!(v["mcp"]["other"], old["mcp"]["other"]);
assert_eq!(v["mcp"]["solidcraft"], json!({"type": "local", "command": [exe(), "mcp"], "enabled": true}));
assert_eq!(v["mcp"]["solidcraft-live"]["command"], json!([exe(), "mcp", "--connect", "127.0.0.1:7878"]));
assert_eq!(json_of(&oc.join("opencode.json.bak")), old);
let c = json_of(&h.join(".cursor/mcp.json"));
assert_eq!(c["mcpServers"]["other"], json!({"command": "o"}));
assert_eq!(c["mcpServers"]["solidcraft"], json!({"command": exe(), "args": ["mcp"]}));
// Codex (TOML) and Claude Code (a big file the CLI owns) are edited in place too.
std::fs::create_dir_all(h.join(".codex")).unwrap();
std::fs::write(h.join(".codex/config.toml"), "# mine\nmodel = \"x\"\n").unwrap();
let cc = "{\n \"numStartups\": 3,\n \"projects\": {}\n}\n";
std::fs::write(h.join(".claude.json"), cc).unwrap();
assert!(run(&h, &[]).status.success());
let toml = std::fs::read_to_string(h.join(".codex/config.toml")).unwrap();
assert!(toml.starts_with("# mine\nmodel = \"x\"\n") && toml.contains("[mcp_servers.solidcraft]"), "{toml}");
let c = json_of(&h.join(".claude.json"));
assert_eq!(c["numStartups"], 3);
assert_eq!(c["mcpServers"]["solidcraft"], json!({"type": "stdio", "command": exe(), "args": ["mcp"]}));
assert_eq!(std::fs::read_to_string(h.join(".claude.json.bak")).unwrap(), cc);
// Running again updates the same entries and leaves one of each.
assert!(run(&h, &[]).status.success());
assert_eq!(json_of(&h.join(".cursor/mcp.json"))["mcpServers"].as_object().unwrap().len(), 3);
}
#[test]
fn dry_run_writes_nothing_and_missing_clients_are_skipped() {
let h = home("dry");
std::fs::create_dir_all(h.join(".cursor")).unwrap();
let out = run(&h, &["--dry-run"]);
assert!(out.status.success());
let text = String::from_utf8_lossy(&out.stdout);
assert!(text.contains("would write") && text.contains("solidcraft"), "{text}");
assert!(!h.join(".cursor/mcp.json").exists());
assert!(!h.join(".config/opencode").exists(), "OpenCode was not installed, so nothing is created for it");
let out = run(&h, &["--client", "claude-desktop"]);
assert!(String::from_utf8_lossy(&out.stdout).contains("not found"));
}
#[test]
fn a_file_that_is_not_json_is_left_alone() {
let h = home("bad");
std::fs::create_dir_all(h.join(".cursor")).unwrap();
std::fs::write(h.join(".cursor/mcp.json"), "{ // comment").unwrap();
let out = run(&h, &[]);
assert!(!out.status.success());
assert_eq!(std::fs::read_to_string(h.join(".cursor/mcp.json")).unwrap(), "{ // comment");
}
#[test]
fn claude_desktop_msix_folder_is_found() {
let h = home("msix");
let pkg = h.join("AppData/Local/Packages/Claude_abc/LocalCache/Roaming/Claude");
std::fs::create_dir_all(&pkg).unwrap();
// The MSIX folder is only read through LOCALAPPDATA, set for every platform here.
assert!(run(&h, &["--client", "claude-desktop"]).status.success());
assert!(json_of(&pkg.join("claude_desktop_config.json"))["mcpServers"]["solidcraft"].is_object());
}
}
@@ -1,9 +0,0 @@
//! Explicit native GPU registration must not change bridged MCP ownership.
use std::process::Command;
#[test]
fn connected_application_rejects_headless_gpu_installation_before_connecting() {
let result = Command::new(env!("CARGO_BIN_EXE_solidcraft-cli")).args(["mcp", "--render-gpu", "--connect", "127.0.0.1:1"]).output().unwrap();
assert!(!result.status.success());
let message = String::from_utf8_lossy(&result.stderr);
assert!(message.contains("connected application owns its own device"), "{message}");
}
-250
View File
@@ -59,84 +59,6 @@ fn cli_loads_both_design_encodings_by_contents() {
}
}
/// #1369: a pending concept that did not land in the numbered release must not be silently
/// dropped on open/resave, in either encoding or before replacing an existing destination.
#[test]
fn unknown_pending_concepts_preserve_the_open_design_and_output_files() {
use solidcraft_engine::{Session, doc, io};
let dir = Scratch::new();
for version in [33, doc::format::FORMAT] {
let mut experimental = doc::Document::new("Experimental");
experimental.format = doc::format::format_string(version);
experimental.format_pending.push("experimental-card".into());
for (extension, bytes) in [("json", io::write_design(&experimental)), ("solidcraft", io::write_design_container(&experimental).unwrap())] {
let source = dir.0.join(format!("experimental-{version}.{extension}"));
std::fs::write(&source, &bytes).unwrap();
let mut session = Session::default();
session.execute("solid.box", &json!({"length": 2, "width": 3, "height": 4})).unwrap();
let original = session.doc.clone();
let original_path = session.path.clone();
let undo = session.undo.len();
let dirty = session.is_dirty();
let err = session.execute("doc.open", &json!({"path": source})).unwrap_err().to_string();
assert!(err.contains("experimental-card") && err.contains("update SolidCraft"), "{err}");
assert_eq!(session.doc, original);
assert_eq!(session.path, original_path);
assert_eq!(session.undo.len(), undo);
assert_eq!(session.is_dirty(), dirty);
for existing in [false, true] {
let destination = dir.0.join(format!("output-{version}-{extension}-{existing}.{extension}"));
let backup = io::backup_path(&destination);
if existing {
std::fs::write(&destination, b"existing design").unwrap();
std::fs::write(&backup, b"existing backup").unwrap();
}
let output = cli().args(["run", "--in"]).arg(&source).arg("--save").arg(&destination).output().unwrap();
assert_eq!(output.status.code(), Some(1));
let error = String::from_utf8_lossy(&output.stderr);
assert!(error.contains("experimental-card") && error.contains("update SolidCraft"), "{error}");
if existing {
assert_eq!(std::fs::read(&destination).unwrap(), b"existing design");
assert_eq!(std::fs::read(&backup).unwrap(), b"existing backup");
} else {
assert!(!destination.exists() && !backup.exists());
}
assert_eq!(std::fs::read(&source).unwrap(), bytes);
}
}
}
}
#[test]
fn known_pending_concepts_and_released_files_round_trip_with_inert_metadata() {
use solidcraft_engine::{doc, io};
let dir = Scratch::new();
let current = doc::Document::new("Supported");
let mut released = current.clone();
released.format = doc::format::format_string(33);
released.format_pending.clear();
let mut experimental = released.clone();
experimental.format_pending.push("reserved-body-names".into());
for (index, mut document) in [current, released, experimental].into_iter().enumerate() {
document.metadata.0.insert("com.example.tool".into(), json!({"format_pending": ["experimental-card"], "physical_card": {"density": 700}}));
for (extension, bytes) in [("json", io::write_design(&document)), ("solidcraft", io::write_design_container(&document).unwrap())] {
let source = dir.0.join(format!("supported-{index}.{extension}"));
let destination = dir.0.join(format!("resaved-{index}.{extension}"));
std::fs::write(&source, &bytes).unwrap();
let output = cli().args(["run", "--in"]).arg(&source).arg("--save").arg(&destination).output().unwrap();
assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr));
let reopened = io::read_design(&std::fs::read(&destination).unwrap()).unwrap();
assert_eq!(reopened.metadata, document.metadata);
assert_eq!(reopened.name, document.name);
assert_eq!(reopened.features, document.features);
assert_eq!(std::fs::read(&source).unwrap(), bytes);
}
}
}
#[test]
fn results_still_save_and_export_the_script_once() {
let dir = Scratch::new();
@@ -287,175 +209,3 @@ fn timings_and_profile_report_each_command() {
assert!(!output.status.success());
assert!(String::from_utf8_lossy(&output.stderr).contains("--timings needs a script"));
}
fn render_setup_script(dir: &Scratch, job: Value) -> PathBuf {
let path = dir.0.join("render.json");
std::fs::write(
&path,
json!({"commands":[
{"command":"solid.box","params":{"length":10,"width":10,"height":10}},
{"command":"render.settings.set","params":{"name":"Portable setup","set":{"width":16,"height":16,"job":job}}}
]})
.to_string(),
)
.unwrap();
path
}
#[cfg(feature = "raytrace-cpu")]
#[test]
fn render_uses_typed_cpu_setups_from_scripts_and_saved_designs() {
for options in [json!({}), json!({"progressive":{"samples_per_pixel":3,"seed":81},"lights":[]})] {
let dir = Scratch::new();
let script = render_setup_script(&dir, json!({"method":"cpu_raytrace","options":options}));
let image = dir.0.join("script.png");
let result = json_output(
cli()
.arg("render")
.arg(&script)
.args(["--setup", "Portable setup", "--out"])
.arg(&image)
.env("SOLIDCRAFT_GPU", "must-not-be-read-for-cpu")
.output()
.unwrap(),
);
assert_eq!(result["preflight"]["resolved"]["executor"], "cpu-raytrace");
assert_eq!(result["export"]["frame"]["executor"], "cpu-raytrace");
assert_eq!(result["preflight"]["request"]["integrator"], "raytrace");
assert_eq!(result["preflight"]["request"]["raytrace"].get("progressive"), options.get("progressive"));
let png = image::load_from_memory(&std::fs::read(&image).unwrap()).unwrap();
assert_eq!((png.width(), png.height()), (16, 16));
let saved = dir.0.join("portable.solidcraft");
json_output(cli().arg("run").arg(&script).arg("--save").arg(&saved).output().unwrap());
let original = std::fs::read(&saved).unwrap();
let overridden = dir.0.join("overridden.png");
let result = json_output(
cli()
.arg("render")
.arg(&saved)
.args(["--setup", "Portable setup", "--out"])
.arg(&overridden)
.args(["--width", "12", "--height", "8", "--exposure", "1.25"])
.env("SOLIDCRAFT_GPU", "must-not-be-read-for-cpu")
.output()
.unwrap(),
);
assert_eq!(result["preflight"]["request"]["width"], 12);
assert_eq!(result["preflight"]["request"]["height"], 8);
assert_eq!(result["preflight"]["request"]["raytrace"]["exposure_ev"], 1.25);
assert_eq!(result["export"]["frame"]["executor"], "cpu-raytrace");
assert_eq!(result["preflight"]["request"]["raytrace"].get("progressive"), options.get("progressive"));
let png = image::load_from_memory(&std::fs::read(overridden).unwrap()).unwrap();
assert_eq!((png.width(), png.height()), (12, 8));
assert_eq!(std::fs::read(saved).unwrap(), original, "CLI overrides must not rewrite saved intent");
}
}
#[test]
fn render_preserves_typed_raster_and_legacy_runtime_controls() {
for job in [Value::Null, json!({"method":"raster","transparent_background":true})] {
let dir = Scratch::new();
let script = render_setup_script(&dir, job.clone());
let out = dir.0.join("raster.png");
let mut command = cli();
command
.arg("render")
.arg(&script)
.args(["--setup", "Portable setup", "--out"])
.arg(&out)
.env("SOLIDCRAFT_GPU", "must-not-be-read-for-raster");
if job.is_null() {
command.arg("--transparent");
}
let result = json_output(command.output().unwrap());
assert_eq!(result["preflight"]["resolved"]["executor"], "cpu-product");
assert_eq!(result["export"]["frame"]["executor"], "cpu-product");
assert_eq!(result["preflight"]["request"]["transparent_background"], true);
assert!(std::fs::read(out).unwrap().starts_with(b"\x89PNG\r\n\x1a\n"));
}
}
#[test]
fn typed_render_rejects_explicit_runtime_flags_before_gpu_or_option_file_reads() {
let dir = Scratch::new();
let script = render_setup_script(&dir, json!({"method":"gpu_path","options":{"samples":64,"seed":0}}));
let out = dir.0.join("existing.png");
std::fs::write(&out, b"previous completed image").unwrap();
for flags in [
&["--progressive"][..],
&["--transparent"][..],
&["--samples", "64"][..],
&["--max-depth", "8"][..],
&["--seed", "0"][..],
&["--lighting", "deliberately-missing-lighting.json"][..],
&["--tone-map", "reinhard"][..],
&["--environment", "deliberately-missing-environment.json"][..],
] {
let output = cli()
.arg("render")
.arg(&script)
.args(["--setup", "Portable setup", "--out"])
.arg(&out)
.arg("--overwrite")
.args(flags)
.env("SOLIDCRAFT_GPU", "must-not-be-read-for-conflicts")
.output()
.unwrap();
assert_eq!(output.status.code(), Some(1));
let error = String::from_utf8_lossy(&output.stderr);
assert!(error.contains(&format!("{} cannot accompany a typed saved render setup", flags[0])), "{error}");
assert!(output.stdout.is_empty());
assert_eq!(std::fs::read(&out).unwrap(), b"previous completed image");
}
}
#[test]
fn typed_gpu_render_selects_its_host_without_a_progressive_runtime_flag() {
// A deliberately invalid policy proves that dispatch reached the GPU host
// without depending on an adapter or accepting a CPU fallback. The legacy
// --progressive path must still reach that same host-selection boundary.
for job in [json!({"method":"gpu_path","options":{"samples":2}}), Value::Null] {
let dir = Scratch::new();
let script = render_setup_script(&dir, job.clone());
let out = dir.0.join("gpu.png");
let mut command = cli();
command.arg("render").arg(&script).args(["--setup", "Portable setup", "--out"]).arg(&out).env("SOLIDCRAFT_GPU", "not-a-gpu-policy");
if job.is_null() {
command.arg("--progressive");
}
let output = command.output().unwrap();
assert_eq!(output.status.code(), Some(1));
let error = String::from_utf8_lossy(&output.stderr);
assert!(error.contains("SOLIDCRAFT_GPU must be auto, hardware, software or cpu"), "{error}");
assert!(!out.exists());
}
}
#[test]
fn render_setup_overrides_obey_shared_validation_before_host_initialization() {
let dir = Scratch::new();
let script = render_setup_script(&dir, json!({"method":"cpu_raytrace","options":{"progressive":{"samples_per_pixel":64}}}));
let out = dir.0.join("invalid.png");
// 1024 * 1024 * 64 exceeds the unchanged default Compact camera-sample
// budget. Admission must name that envelope before initializing any host.
for (flags, message) in [
(&["--width", "1024", "--height", "1024"][..], "CPU Compact exceeds its 16777216 camera-sample admission"),
(&["--width", "0"][..], "render output must be at least"),
(&["--exposure", "NaN"][..], "render exposure must be finite"),
] {
let output = cli()
.arg("render")
.arg(&script)
.args(["--setup", "Portable setup", "--out"])
.arg(&out)
.args(flags)
.env("SOLIDCRAFT_GPU", "must-not-be-read-for-invalid-settings")
.output()
.unwrap();
assert_eq!(output.status.code(), Some(1));
let error = String::from_utf8_lossy(&output.stderr);
assert!(error.contains(message), "{error}");
assert!(!out.exists());
}
}
-6
View File
@@ -13,16 +13,11 @@ workspace = true
name = "solidcraft-web"
path = "src/main.rs"
[features]
default = []
bundled-render-library = ["solidcraft-engine/bundled-render-library"]
[dependencies]
solidcraft-engine = { workspace = true }
solidcraft-ui-egui = { workspace = true }
egui = { workspace = true }
log = { workspace = true }
url = "2"
[target.'cfg(target_arch = "wasm32")'.dependencies]
eframe = { workspace = true, features = ["default_fonts", "wgpu", "accesskit"] }
@@ -35,6 +30,5 @@ web-sys = { version = "0.3", features = [
"FileSystemHandle", "FileSystemDirectoryHandle", "FileSystemFileHandle", "FileSystemGetFileOptions", "FileSystemWritableFileStream",
"WritableStream", "File", "FileList", "Blob", "BlobPropertyBag", "Url", "HtmlAnchorElement", "HtmlInputElement", "Storage",
"IdbFactory", "IdbDatabase", "IdbObjectStore", "IdbOpenDbRequest", "IdbRequest", "IdbTransaction", "IdbTransactionMode",
"Headers", "ReadableStream", "ReadableStreamDefaultReader",
"BeforeUnloadEvent", "Event", "EventTarget", "KeyboardEvent", "Response", "MediaQueryList", "Gamepad", "Performance",
] }
-136
View File
@@ -1,136 +0,0 @@
//! Bounded HTTPS downloads, handed to the normal design-open command as unsaved copies.
use std::{cell::RefCell, rc::Rc};
use js_sys::{Reflect, Uint8Array};
use solidcraft_engine::io::vfs;
use solidcraft_ui_egui::{SolveApp, ui_kit};
use wasm_bindgen::{JsCast as _, JsValue};
use wasm_bindgen_futures::{JsFuture, spawn_local};
use crate::{check_download_size, design_url};
type Reply = Result<(String, Vec<u8>), String>;
#[derive(Default)]
pub struct Download {
reply: Rc<RefCell<Option<Reply>>>,
pending: Option<(String, Vec<u8>)>,
}
impl Download {
pub fn start(url: Option<&str>, ctx: &egui::Context) -> Self {
let download = Self::default();
if let Some(url) = url {
let url = url.to_owned();
let slot = download.reply.clone();
let ctx = ctx.clone();
spawn_local(async move {
*slot.borrow_mut() = Some(fetch(&url).await);
ctx.request_repaint();
});
}
download
}
pub fn tick(&mut self, app: &mut SolveApp, ctx: &egui::Context) {
if let Some(reply) = self.reply.borrow_mut().take() {
match reply {
Ok(file) => self.pending = Some(file),
Err(error) => app.set_status(format!("Could not open design link: {error}"), true),
}
}
if self.pending.is_none() || app.file_picker_busy() {
return;
}
if solidcraft_ui_egui::documents::any_dirty(app) {
let mut answer = None;
let closed = ui_kit::Window::new(egui::Id::new("open_link"), "status_warning", solidcraft_ui_egui::i18n::tr("web.open_link"))
.required()
.show_body(ctx, |ui| {
ui.label(solidcraft_ui_egui::i18n::tr("web.open_link_confirm"));
ui.horizontal(|ui| {
if ui_kit::button(ui, solidcraft_ui_egui::i18n::tr("ui.open"), ui_kit::Button::Normal).clicked() {
answer = Some(true);
}
if ui_kit::button(ui, solidcraft_ui_egui::i18n::tr("ui.cancel"), ui_kit::Button::Normal).clicked() {
answer = Some(false);
}
});
});
match answer {
Some(true) => {}
Some(false) => {
self.pending = None;
return;
}
None if closed => {
self.pending = None;
return;
}
None => return,
}
}
let Some((name, bytes)) = self.pending.take() else { return };
// The extension selects the design loader even when the URL has no extension.
let path = "/linked/design.solidcraft";
vfs::insert(path, bytes);
app.open_path(path);
if app.session.path.as_deref() == Some(path) {
app.session.path = None;
app.session.doc_mut().name = name;
app.session.mark_unsaved();
} else {
app.set_status("Could not open design link: the downloaded file is not a valid SolidCraft design", true);
}
let _ = vfs::remove(path);
}
}
async fn fetch(value: &str) -> Reply {
let url = design_url(value)?;
let name = url.path_segments().and_then(|mut segments| segments.next_back()).filter(|name| !name.is_empty()).unwrap_or("Design.solidcraft");
let decoded = js_sys::decode_uri_component(name).ok().and_then(|name| name.as_string()).unwrap_or_else(|| name.to_owned());
let name: String = decoded.strip_suffix(".solidcraft").unwrap_or(&decoded).chars().take(255).collect();
let window = web_sys::window().ok_or("No browser window")?;
let response = JsFuture::from(window.fetch_with_str(url.as_str())).await.map_err(|_| "Network request failed or the host refused CORS access")?;
let response: web_sys::Response = response.dyn_into().map_err(|_| "Invalid download response")?;
design_url(&response.url()).map_err(|_| "Design redirected to a URL that does not use HTTPS")?;
if !response.ok() {
return Err(format!("HTTP {} {}", response.status(), response.status_text()));
}
let length = response
.headers()
.get("Content-Length")
.map_err(|_| "Cannot read download size")?
.map(|value| value.parse::<u64>().map_err(|_| "Invalid Content-Length"))
.transpose()?;
if let Some(length) = length {
check_download_size(0, length)?;
}
let stream = response.body().ok_or("Download has no body")?;
let reader: web_sys::ReadableStreamDefaultReader = stream.get_reader().dyn_into().map_err(|_| "Cannot read download stream")?;
let result = read(&reader).await;
if result.is_err() {
let _ = JsFuture::from(reader.cancel()).await;
}
// Fetch rejects interrupted HTTP bodies and decodes Content-Encoding. Content-Length
// can describe compressed bytes, so the streamed limit also checks the decoded size.
let bytes = result?;
Ok((name, bytes))
}
async fn read(reader: &web_sys::ReadableStreamDefaultReader) -> Result<Vec<u8>, String> {
let mut bytes = Vec::new();
// Bound even a stream which keeps returning empty chunks.
for _ in 0..1_000_000 {
let chunk = JsFuture::from(reader.read()).await.map_err(|_| "Download interrupted")?;
if Reflect::get(&chunk, &JsValue::from_str("done")).map_err(|_| "Invalid download stream")?.as_bool() == Some(true) {
return Ok(bytes);
}
let value = Reflect::get(&chunk, &JsValue::from_str("value")).map_err(|_| "Invalid download chunk")?;
let chunk: Uint8Array = value.dyn_into().map_err(|_| "Invalid download bytes")?;
check_download_size(bytes.len(), u64::from(chunk.length()))?;
bytes.extend_from_slice(&chunk.to_vec());
}
Err("Download returned too many chunks".into())
}
+14 -172
View File
@@ -14,8 +14,6 @@ mod disk;
mod files;
#[cfg(target_arch = "wasm32")]
mod install;
#[cfg(target_arch = "wasm32")]
mod linked;
#[cfg(any(target_arch = "wasm32", test))]
mod space_mouse;
@@ -27,61 +25,6 @@ fn browser_switches_tabs(key: &str, ctrl: bool) -> bool {
ctrl && key == "Tab"
}
#[cfg(any(target_arch = "wasm32", test))]
fn query_flag(query: &str, name: &str) -> bool {
url::form_urlencoded::parse(query.trim_start_matches('?').as_bytes()).any(|(key, _)| key == name)
}
/// Launch options use exact query keys; URL values are decoded only once.
#[cfg(any(target_arch = "wasm32", test))]
#[derive(Default, Debug)]
struct Launch {
open: Option<String>,
embed: bool,
}
#[cfg(any(target_arch = "wasm32", test))]
impl Launch {
fn parse(query: &str) -> Self {
let mut launch = Self::default();
let mut embed = false;
for (key, value) in url::form_urlencoded::parse(query.trim_start_matches('?').as_bytes()) {
match key.as_ref() {
"open" if launch.open.is_none() => launch.open = Some(value.into_owned()),
"embed" => embed = true,
_ => {}
}
}
launch.embed = embed && launch.open.is_some();
launch
}
fn persists(&self) -> bool {
!self.embed
}
}
#[cfg(any(target_arch = "wasm32", test))]
fn design_url(value: &str) -> Result<url::Url, String> {
if value.len() > 16_384 {
return Err("Design URL is too long".into());
}
let url = url::Url::parse(value).map_err(|_| "Invalid design URL")?;
if url.scheme() != "https" || url.host_str().is_none() || !url.username().is_empty() || url.password().is_some() {
return Err("Design links must use HTTPS without credentials".into());
}
Ok(url)
}
#[cfg(any(target_arch = "wasm32", test))]
fn check_download_size(current: usize, incoming: u64) -> Result<(), String> {
let cap = solidcraft_engine::io::MAX_DESIGN_BYTES as u64;
if current as u64 > cap || incoming > cap.saturating_sub(current as u64) {
return Err(format!("Design exceeds the {} MB size limit", cap / (1024 * 1024)));
}
Ok(())
}
#[cfg(target_arch = "wasm32")]
mod web {
use std::rc::Rc;
@@ -169,9 +112,6 @@ mod web {
struct Shell {
app: SolveApp,
launch: crate::Launch,
linked: crate::linked::Download,
full_url: Option<String>,
api: Api,
files: Rc<Files>,
/// When the last autosave was (seconds), and the revision it holds (none stored: `None`).
@@ -197,9 +137,6 @@ mod web {
impl Shell {
/// Files from the computer, writes to store, messages and the autosave.
fn files_tick(&mut self, ctx: &egui::Context) {
if !self.launch.persists() {
return;
}
if self.files.ctx.borrow().is_none() {
*self.files.ctx.borrow_mut() = Some(ctx.clone());
}
@@ -293,15 +230,13 @@ mod web {
fn sync_recents(&mut self) {
let mut entries = self.files.entries.borrow().clone();
entries.sort_by(|a, b| b.modified.total_cmp(&a.modified));
let forgotten = self.app.home.forgotten.clone();
let recent = &mut self.app.home.recent;
let known = self.files.disk_known.borrow().clone();
// A design on the computer's disk stays listed while its file is remembered.
recent.retain(|p| vfs::exists(p) || disk::parse(p).is_some_and(|(id, _)| known.contains(id)));
for e in entries.iter().filter(|e| solidcraft_engine::io::is_design_path(&e.name)) {
let p = format!("{}{}", files::OPFS, e.name);
// A design the user removed from the list is not added back while it is stored.
if !recent.contains(&p) && !forgotten.contains(&p) {
if !recent.contains(&p) {
recent.push(p);
}
}
@@ -310,10 +245,6 @@ mod web {
let recovered = self.files.recovery.borrow().as_ref().and_then(|(_, p)| p.clone());
let keep = recent.iter().chain(recovered.as_ref()).filter_map(|p| disk::parse(p)).map(|(id, _)| id.to_string()).collect();
disk::prune(keep);
// Drop forgotten entries whose stored design is gone, so the set does not grow without bound.
let stored: std::collections::BTreeSet<String> =
entries.iter().filter(|e| solidcraft_engine::io::is_design_path(&e.name)).map(|e| format!("{}{}", files::OPFS, e.name)).collect();
self.app.home.forgotten.retain(|p| stored.contains(p));
}
/// The stored designs: open, insert, rename, delete, download; open from the computer.
@@ -452,8 +383,6 @@ mod web {
"delete" => {
files::delete(&self.files, &name);
self.app.home.recent.retain(|r| *r != path);
// The file is gone, so there is nothing left to keep forgotten.
self.app.home.forgotten.remove(&path);
}
"rename_cancel" => self.renaming = None,
"rename" => {
@@ -467,10 +396,6 @@ mod web {
*r = new.clone();
}
}
// A forgotten design follows its new name.
if self.app.home.forgotten.remove(&old) {
self.app.home.forgotten.insert(new);
}
}
Err(e) => self.app.set_status(e, true),
}
@@ -589,42 +514,19 @@ mod web {
if let Some(delay) = self.wheel_gestures.update(raw, now) {
ctx.request_repaint_after(delay);
}
if self.launch.persists() {
files::take_drops(&self.files, raw);
} else {
raw.dropped_files.clear();
}
files::take_drops(&self.files, raw);
// Before the app's own hook, which adds the control channel's events.
self.space_mouse.raw_input_hook(ctx, self.app.preferences.space_mouse_enabled, raw);
self.app.raw_input_hook(raw);
}
fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
self.linked.tick(&mut self.app, ui.ctx());
UNSAVED.with(|u| u.set(self.launch.persists() && solidcraft_ui_egui::documents::any_dirty(&self.app)));
if self.launch.embed {
ui_kit::Container::new(ui.ctx(), ui_kit::Surface::Chrome).edge("embed_header", ui_kit::Edge::Top).show(ui, |ui| {
if ui_kit::button(ui, solidcraft_ui_egui::i18n::tr("web.open_full"), ui_kit::Button::Normal).clicked()
&& let (Some(window), Some(url)) = (web_sys::window(), &self.full_url)
{
let _ = window.open_with_url_and_target_and_features(url, "_blank", "noopener");
}
});
}
self.app.ui(ui);
let ctx = ui.ctx().clone();
if self.launch.persists() {
self.file_panel(&ctx);
self.recovery_prompt(&ctx);
}
self.file_panel(&ctx);
self.recovery_prompt(&ctx);
self.watch_text_for_font(&ctx);
}
fn persist_egui_memory(&self) -> bool {
self.launch.persists()
}
fn save(&mut self, storage: &mut dyn eframe::Storage) {
if !self.launch.persists() {
return;
}
// Persist the user's own language, not a session-only English fallback.
let saved = self.font.persisted_language(&self.app.preferences.language).to_owned();
let live = std::mem::replace(&mut self.app.preferences.language, saved);
@@ -643,15 +545,15 @@ mod web {
/// Render with WebGL2: asked for now (`?webgl`, remembered) or before (until `?webgpu`).
fn webgl() -> bool {
let storage = crate::Launch::parse(&query()).persists().then(|| web_sys::window().and_then(|w| w.local_storage().ok().flatten())).flatten();
let storage = web_sys::window().and_then(|w| w.local_storage().ok().flatten());
let q = query();
if crate::query_flag(&q, "webgl") {
if q.contains("webgl") {
if let Some(s) = &storage {
let _ = s.set_item(WEBGL_KEY, "1");
}
return true;
}
if crate::query_flag(&q, "webgpu") {
if q.contains("webgpu") {
if let Some(s) = &storage {
let _ = s.remove_item(WEBGL_KEY);
}
@@ -677,10 +579,7 @@ mod web {
eframe::WebLogger::init(log::LevelFilter::Info).ok();
wasm_bindgen_futures::spawn_local(async {
let Some(window) = web_sys::window() else { return };
let launch = crate::Launch::parse(&query());
if launch.persists() {
ask_before_unload(&window);
}
ask_before_unload(&window);
leave_tab_switching_to_browser(&window);
let Some(document) = window.document() else { return };
let Some(canvas) = document.get_element_by_id(CANVAS_ID).and_then(|e| e.dyn_into::<web_sys::HtmlCanvasElement>().ok()) else {
@@ -700,12 +599,10 @@ mod web {
Box::new(move |cc| {
solidcraft_ui_egui::fonts::setup_fonts(&cc.egui_ctx);
let files = Rc::new(Files::default());
if launch.persists() {
files::start(&files);
}
files::start(&files);
let (panel, opener, saver, shown) = (files.clone(), files.clone(), files.clone(), files.clone());
let (restorable, restorer) = (files.clone(), files.clone());
let services = if launch.embed { Services::default() } else { Services {
let services = Services {
// Open shows the browser's open dialog for a design where it has one
// (the file keeps its handle, so Save writes back to it); otherwise,
// and for inserting STEP or meshes, the browser's files window.
@@ -739,27 +636,26 @@ mod web {
browser_files: Some(Box::new(move || shown.panel.set(true))),
install_app: Some(crate::install::service(&cc.egui_ctx)),
..Default::default()
}};
};
// Thumbnails of saves and start-page cards.
solidcraft_ui_egui::gpu::install_cpu_picture_renderer();
let (sender, receiver) = std::sync::mpsc::channel();
let mut app = SolveApp::new(Session::default(), services).with_control(receiver);
app.embedded = launch.embed;
if launch.persists() && let Some(p) = cc.storage.and_then(|s| solidcraft_ui_egui::prefs::stored(|k| s.get_string(k))) {
if let Some(p) = cc.storage.and_then(|s| solidcraft_ui_egui::prefs::stored(|k| s.get_string(k))) {
app.load_prefs(&p);
}
if let Some(rs) = &cc.wgpu_render_state {
// eframe's web painter has no stencil buffer.
app.set_wgpu(rs, DEPTH_BITS, 0, 1);
}
if launch.open.is_none() && crate::query_flag(&query(), "sample") {
if query().contains("sample") {
if let Err(e) = app.session.run_script(&solidcraft_engine::sample::script()) {
app.set_status(format!("sample: {e}"), true);
}
app.session.doc_mut().name = "Sample Plate".into();
app.session.mark_saved();
}
if launch.open.is_none() && !crate::query_flag(&query(), "sample") {
if !query().contains("sample") {
// The empty design takes Units of new designs (#1205). The sample
// part is built in millimetres and is left that way.
solidcraft_ui_egui::prefs::apply_new_design(&mut app);
@@ -768,20 +664,8 @@ mod web {
}
let api = Api::install(sender, cc.egui_ctx.clone()).map_err(|e| format!("SolidCraft browser API: {e:?}"))?;
let font_language = app.preferences.language.clone();
let linked = crate::linked::Download::start(launch.open.as_deref(), &cc.egui_ctx);
let full_url = window.location().href().ok().and_then(|value| url::Url::parse(&value).ok()).map(|mut url| {
url.set_query(None);
url.set_fragment(None);
if let Some(open) = &launch.open {
url.query_pairs_mut().append_pair("open", open);
}
url.to_string()
});
Ok(Box::new(Shell {
app,
launch,
linked,
full_url,
api,
files,
autosaved_at: f64::NEG_INFINITY,
@@ -825,48 +709,6 @@ fn main() {
mod tests {
use super::*;
#[test]
fn link_query_decodes_once_and_embed_disables_persistence() {
assert!(!query_flag("?open=https://example.org/webgl?sample", "webgl"));
assert!(!query_flag("?open=https://example.org/webgl?sample", "sample"));
assert!(query_flag("?embed&webgl", "webgl"));
let launch = Launch::parse("?embed&open=https%3A%2F%2Fexample.org%2Farm.solidcraft%3Fx%3Da%2520b%26y%3D1");
assert_eq!(launch.open.as_deref(), Some("https://example.org/arm.solidcraft?x=a%20b&y=1"));
assert!(launch.embed);
assert!(!launch.persists());
assert!(!Launch::parse("?embed").embed);
assert!(!Launch::parse("?other=embed&reopen=https://example.org").embed);
assert!(Launch::parse("?open=https://example.org/a").persists());
assert_eq!(Launch::parse("?open=first&open=second").open.as_deref(), Some("first"));
}
#[test]
fn design_links_refuse_hostile_schemes_credentials_and_sizes() {
assert!(design_url("https://example.org/arm.solidcraft?download=1").is_ok());
for value in [
"http://example.org/a",
"data:application/json,{}",
"blob:https://example.org/a",
"javascript:alert(1)",
"/arm.solidcraft",
"https://user:secret@example.org/a",
"",
] {
assert!(design_url(value).is_err(), "{value}");
}
assert!(design_url(&format!("https://example.org/{}", "x".repeat(16_384))).is_err());
assert!(design_url("http://redirect.example.org/a").is_err());
let cap = solidcraft_engine::io::MAX_DESIGN_BYTES;
assert!(check_download_size(0, cap as u64).is_ok());
assert!(check_download_size(0, cap as u64 + 1).is_err());
assert!(check_download_size(cap - 2, 2).is_ok());
assert!(check_download_size(cap - 2, 3).is_err());
assert!(check_download_size(0, u64::MAX).is_err());
assert!(check_download_size(cap, 1).is_err());
assert!(check_download_size(usize::MAX, 0).is_err());
}
#[test]
fn only_ctrl_tab_is_left_to_the_browser() {
// Ctrl+Tab and Ctrl+Shift+Tab (the event's key is "Tab" either way) switch browser tabs.
+4 -11
View File
@@ -7,14 +7,9 @@ license.workspace = true
publish = false
default-run = "solidcraft"
[features]
default = ["raytrace-cpu", "bundled-render-library"]
bundled-render-library = ["solidcraft-engine/bundled-render-library"]
raytrace-cpu = ["solidcraft-engine/raytrace-cpu"]
# The workspace lints, except unsafe_code: denied, not forbidden, so that src/open_documents.rs
# (macOS file-open events), src/native_crash.rs (Windows exception filter) and src/native_frame.rs
# (Windows resize border, taskbar icon) can allow it: none has a safe API. Keep the rest in sync with [workspace.lints].
# (Windows resize border) can allow it: none has a safe API. Keep the rest in sync with [workspace.lints].
[lints.rust]
unsafe_code = "deny"
@@ -54,8 +49,7 @@ hidapi = { version = "2.6.7", default-features = false, features = ["windows-nat
# The main window's handle for src/native_frame.rs (already in the tree through eframe and winit).
raw-window-handle = "0.6"
# Native crash capture (src/native_crash.rs): the exception filter, the faulting module, a minidump.
# The window's resize border, DPI size and class icon (src/native_frame.rs): the window procedure, hit
# test, DPI, the exe's icon.
# The window's resize border and DPI size (src/native_frame.rs): the window procedure, hit test, DPI.
# Finding 3D mice through Plug and Play without asking other devices anything (src/space_mouse/windows_hid.rs).
windows-sys = { version = "0.61", features = [
"Win32_Devices_DeviceAndDriverInstallation",
@@ -82,10 +76,9 @@ solidcraft-3dx-client = { workspace = true }
# The 3D mouse (src/space_mouse.rs), in the shared-device open mode so that a running 3DxWare
# driver does not shut the app out.
hidapi = { version = "2.6.7", default-features = false, features = ["macos-shared-device"] }
# Files opened from Finder and the Dock (src/open_documents.rs), and the fullscreen title-bar
# inset (src/main.rs); all already come with eframe and rfd.
# Files opened from Finder and the Dock (src/open_documents.rs); all already come with eframe and rfd.
objc2 = "0.6"
objc2-app-kit = { version = "0.3", default-features = false, features = ["std", "NSApplication", "NSResponder", "NSWindow"] }
objc2-app-kit = { version = "0.3", default-features = false, features = ["std", "NSApplication", "NSResponder"] }
objc2-foundation = { version = "0.3", default-features = false, features = ["std", "block2", "NSArray", "NSNotification", "NSOperation", "NSProcessInfo", "NSString", "NSURL"] }
block2 = "0.6"
+5 -41
View File
@@ -35,7 +35,7 @@ fn new_token() -> Option<String> {
pub fn remove_token_file() {
if let Ok(files) = TOKEN_FILES.lock() {
for p in files.iter() {
control_auth::remove_token_file(p);
let _ = std::fs::remove_file(p);
}
}
if let Some(port) = APP_PORT.lock().ok().and_then(|p| *p) {
@@ -127,7 +127,7 @@ fn listen_on_listener(listener: TcpListener, token: String, tx: Sender<ControlRe
if OPEN.fetch_add(1, Ordering::Relaxed) >= MAX_CONNECTIONS {
OPEN.fetch_sub(1, Ordering::Relaxed);
let mut refused = stream;
let _ = send(&mut refused, &json!({"ok": false, "error": "too many connections"}));
let _ = writeln!(refused, "{}", json!({"ok": false, "error": "too many connections"}));
continue;
}
let tx = tx.clone();
@@ -174,15 +174,6 @@ fn read_line(r: &mut impl BufRead, max: usize) -> std::io::Result<Option<Vec<u8>
}
}
/// A reply line, written to the socket at once. `writeln!` with a JSON value hands the socket each
/// of its pieces apart, a system call for every string, number and comma: half a second for a
/// design's component tree when it has thousands of components.
fn send(out: &mut impl Write, reply: &Value) -> std::io::Result<()> {
let mut line = serde_json::to_vec(reply).map_err(std::io::Error::other)?;
line.push(b'\n');
out.write_all(&line)
}
/// The reply to a connection that didn't show the token; the connection is then closed.
fn unauthorized() -> Value {
json!({"ok": false, "error": "unauthorized: send the control token as \"token\" (see docs/control-protocol.md)"})
@@ -193,7 +184,7 @@ fn unauthorized() -> Value {
/// the connection, and a reset can reach the client before it has read the refusal.
fn refuse(out: &mut TcpStream, reader: &mut BufReader<TcpStream>) {
use std::io::Read;
let _ = send(out, &unauthorized());
let _ = writeln!(out, "{}", unauthorized());
let _ = out.shutdown(std::net::Shutdown::Write);
let _ = reader.get_ref().set_read_timeout(Some(Duration::from_millis(500)));
let _ = std::io::copy(&mut reader.by_ref().take(64 * 1024), &mut std::io::sink());
@@ -390,7 +381,7 @@ fn serve_with(
Ok(Some(l)) => String::from_utf8_lossy(&l).into_owned(),
Ok(None) => break,
Err(e) => {
let _ = send(&mut out, &json!({"ok": false, "error": e.to_string()}));
let _ = writeln!(out, "{}", json!({"ok": false, "error": e.to_string()}));
break;
}
};
@@ -472,7 +463,7 @@ fn serve_with(
json!({"ok": false, "error": format!("bad JSON: {e}")})
}
};
if send(&mut out, &reply).is_err() {
if writeln!(out, "{reply}").is_err() {
break;
}
if bad >= MAX_BAD_LINES {
@@ -759,31 +750,4 @@ mod tests {
let mut r = BufReader::with_capacity(64, text.as_slice());
assert!(read_line(&mut r, 1000).is_err());
}
/// A large reply reaches the socket in one write, not one per JSON piece (a component tree of
/// thousands of components took half a second to send), and reads back as it was.
#[test]
fn a_large_reply_is_written_at_once() {
struct Counted {
writes: usize,
bytes: Vec<u8>,
}
impl Write for Counted {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.writes += 1;
self.bytes.extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
let components: Vec<Value> = (0..2000).map(|i| json!({"id": i, "name": format!("Part {i}"), "bodies": ["a", "b"], "parent": 0})).collect();
let reply = json!({"ok": true, "id": 7, "result": {"components": components}});
let mut out = Counted { writes: 0, bytes: Vec::new() };
send(&mut out, &reply).unwrap();
assert_eq!(out.writes, 1);
assert_eq!(out.bytes.last(), Some(&b'\n'));
assert_eq!(serde_json::from_slice::<Value>(&out.bytes).unwrap(), reply);
}
}
+22 -181
View File
@@ -8,7 +8,6 @@
//! A `.step`/`.stp` file opens as a new design holding the file's bodies (an Import feature); a
//! `.3mf`/`.stl`/`.obj`/`.glb`/`.gltf` file as a new design holding mesh bodies.
//!
//! `SOLIDCRAFT_SETTINGS_DIR` selects an explicit absolute settings directory without legacy adoption.
//! `--control <port>` (or `SOLIDCRAFT_CONTROL_PORT`) starts a localhost JSON-lines control
//! server: `{"id":1,"method":"engine.execute","params":{"command":"solid.box","params":{…}}}`.
//! Without them the channel still opens, on the default port, unless Preferences turns it off.
@@ -111,13 +110,6 @@ impl eframe::App for App {
ctx.send_viewport_cmd(egui::ViewportCommand::Focus);
}
solidcraft_engine::crash::phase(solidcraft_engine::crash::RUNNING);
// A DMG install has no installer step: put solidcraft-cli on PATH, quietly.
#[cfg(target_os = "macos")]
std::thread::spawn(solidcraft_ui_egui::cli_link::install_on_launch);
// Automation runs (a control port was asked for) leave the user's AI apps alone.
if !self.take_focus_unwanted {
solidcraft_ui_egui::ai_connect::begin(&mut self.solve);
}
if let Some(dir) = self.starting.take() {
startup::started(&dir);
}
@@ -154,8 +146,6 @@ impl eframe::App for App {
if let Some(menu) = &mut self.native_menu {
menu.raw_input_hook(&mut self.solve, ctx, raw);
}
#[cfg(target_os = "macos")]
macos_fullscreen_content(ctx, raw);
// Before the app's own hook, which adds the control channel's events.
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
self.space_mouse.raw_input_hook(self.solve.preferences.space_mouse_enabled, raw);
@@ -165,7 +155,7 @@ impl eframe::App for App {
self.solve.raw_input_hook(raw);
}
fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
content_ui(&mut self.solve, ui);
self.solve.ui(ui);
}
fn save(&mut self, storage: &mut dyn eframe::Storage) {
storage.set_string(solidcraft_ui_egui::prefs::STORAGE_KEY, self.solve.prefs());
@@ -175,13 +165,7 @@ impl eframe::App for App {
solidcraft_ui_egui::agent_chat::shutdown(&mut self.solve);
// A save still being written ends before the app does.
solidcraft_ui_egui::save_job::tick(&mut self.solve, true);
// Pictures still being drawn off the window thread (Home cards, file previews) end
// before the GPU is torn down: one drawn while the process exits can crash the driver.
if !solidcraft_engine::capture::close(std::time::Duration::from_secs(5)) {
log::warn!("a picture was still being drawn as the app closed");
}
solidcraft_ui_egui::recovery_ui::close(&mut self.solve);
solidcraft_ui_egui::open_elsewhere::close(&mut self.solve);
solidcraft_engine::crash::end_session();
if let Some(lost_at) = self.gpu.as_ref().and_then(|g| g.lost_at) {
device_lost::restart(self.gpu.as_ref().is_some_and(|g| g.restart_on_software), lost_at);
@@ -247,37 +231,6 @@ fn case_variants(exts: &[&str]) -> Vec<String> {
all
}
/// egui's root Ui spans the whole viewport, including native safe areas. Lay out the app
/// inside the unobscured rectangle so its application bar can receive native pointer input.
fn content_ui(app: &mut SolveApp, ui: &mut egui::Ui) {
let content = ui.ctx().content_rect();
let above = egui::Rect::from_min_max(ui.max_rect().min, egui::pos2(ui.max_rect().right(), content.top()));
ui.painter().rect_filled(above, 0.0, solidcraft_ui_egui::theme::Tokens::get().app_bar);
ui.scope_builder(egui::UiBuilder::new().max_rect(content), |ui| app.ui(ui));
}
/// A transparent macOS title bar still intercepts clicks at the top in fullscreen (#3643).
/// `contentLayoutRect` reports the whole content view once that bar auto-hides, so reserve
/// the native titled-window height even then. AppKit supplies the height in logical points;
/// egui's UI zoom is separate from the display's Retina scale.
#[cfg(target_os = "macos")]
fn macos_fullscreen_content(ctx: &egui::Context, raw: &mut egui::RawInput) {
let Some(mtm) = objc2::MainThreadMarker::new() else { return };
let frame = objc2_app_kit::NSWindow::frameRectForContentRect_styleMask(Default::default(), objc2_app_kit::NSWindowStyleMask::Titled, mtm);
fullscreen_content_insets(raw, frame.size.height as f32, ctx.zoom_factor());
}
#[cfg(any(target_os = "macos", test))]
fn fullscreen_content_insets(raw: &mut egui::RawInput, title_height: f32, zoom: f32) {
let fullscreen = raw.viewports.get(&raw.viewport_id).and_then(|v| v.fullscreen).unwrap_or(false);
let mut insets = raw.safe_area_insets.unwrap_or_default();
if fullscreen && title_height.is_finite() && title_height > 0.0 && zoom.is_finite() && zoom > 0.0 {
insets.0.top = insets.0.top.max(title_height / zoom);
}
// Explicitly reset on leaving fullscreen: None would retain the preceding frame's inset.
raw.safe_area_insets = Some(insets);
}
fn services(cc: &eframe::CreationContext<'_>) -> Services {
let parent = rfd::AsyncFileDialog::new().set_parent(cc);
let open_parent = parent.clone();
@@ -335,7 +288,6 @@ fn services(cc: &eframe::CreationContext<'_>) -> Services {
let future = dialog.save_file();
Box::pin(async move { future.await.map(|p| p.path().to_string_lossy().into_owned()) })
})),
graphics_adapters: Vec::new(),
graphics_path: graphics_path().map(|p| p.to_string_lossy().to_string()),
window_system: window_system_now(),
http_get: Some(std::sync::Arc::new(http_get)),
@@ -416,7 +368,7 @@ fn agent_http(r: &solidcraft_ui_egui::agent_chat::backend::HttpRequest) -> Resul
let agent: ureq::Agent = ureq::Agent::config_builder()
.tls_config(tls)
.http_status_as_error(false)
.timeout_global(Some(Duration::from_secs(r.timeout_s.clamp(1, 3600))))
.timeout_global(Some(Duration::from_secs(r.timeout_s.clamp(1, 900))))
.timeout_connect(Some(Duration::from_secs(10)))
.user_agent("SolidCraft")
.max_redirects(0)
@@ -507,15 +459,14 @@ fn http_call(url: &str, timeout: std::time::Duration) -> Result<ureq::http::Resp
}
/// The per-user settings folder: eframe's storage (`app.ron`) and the anti-aliasing preference.
fn settings_dir() -> Result<Option<std::path::PathBuf>, String> {
let selected = std::env::var_os("SOLIDCRAFT_SETTINGS_DIR");
startup::settings_directory(selected.as_deref(), eframe::storage_dir("SolidCraft"))
fn settings_dir() -> Option<std::path::PathBuf> {
eframe::storage_dir("SolidCraft")
}
/// The anti-aliasing preference, kept next to eframe's storage (it is needed before the
/// window opens).
fn graphics_path() -> Option<std::path::PathBuf> {
settings_dir().ok().flatten().map(|d| d.join("graphics.json"))
settings_dir().map(|d| d.join("graphics.json"))
}
/// Compatibility shim (docs/compatibility.md): on the first launch after the rename, copy the
@@ -525,7 +476,7 @@ fn graphics_path() -> Option<std::path::PathBuf> {
/// old app id's `~/.var/app` folder; that copy comes first, as it is the newest. The old folders
/// are never changed.
fn adopt_legacy_settings() {
let (Some(current), Some(legacy)) = (settings_dir().ok().flatten(), eframe::storage_dir("SolveCraft")) else { return };
let (Some(current), Some(legacy)) = (settings_dir(), eframe::storage_dir("SolveCraft")) else { return };
let legacy_eframe = eframe::storage_dir(solidcraft_engine::io::LEGACY_APP_ID);
let old_flatpak = solidcraft_engine::io::legacy_flatpak_path(&current);
if let Err(e) = adopt_settings(&current, old_flatpak.as_deref(), &legacy, legacy_eframe.as_deref()) {
@@ -598,8 +549,7 @@ const FLOAT_DEPTH_BITS: u8 = 32;
const STENCIL_BITS: u8 = 8;
const MSAA: u16 = 4;
/// `custom_titlebar`: no OS decorations, the application bar is the title bar.
fn app_viewport(custom_titlebar: bool) -> Result<egui::ViewportBuilder, eframe::Error> {
fn app_viewport() -> Result<egui::ViewportBuilder, eframe::Error> {
// Let macOS use the bundle's layered icon; eframe's default bitmap would replace it.
#[cfg(target_os = "macos")]
let icon = egui::IconData::default();
@@ -612,7 +562,7 @@ fn app_viewport(custom_titlebar: bool) -> Result<egui::ViewportBuilder, eframe::
.with_inner_size([1600.0, 1000.0])
.with_min_inner_size([960.0, 600.0])
.with_drag_and_drop(true)
.with_decorations(!custom_titlebar)
.with_decorations(!CUSTOM_TITLEBAR)
.with_fullsize_content_view(true)
.with_titlebar_shown(false)
.with_title_shown(false)
@@ -626,11 +576,7 @@ fn main() -> eframe::Result {
#[cfg(target_os = "macos")]
objc2_foundation::NSProcessInfo::processInfo().setProcessName(objc2_foundation::ns_string!("SolidCraft"));
mark("main");
// Validate before legacy adoption, crash markers or any user preference read.
let settings = settings_dir().map_err(|error| eframe::Error::AppCreation(error.into()))?;
if std::env::var_os("SOLIDCRAFT_SETTINGS_DIR").is_none() {
adopt_legacy_settings();
}
adopt_legacy_settings();
let mut safe_mode = startup::env_on(std::env::var(startup::SAFE_MODE_ENV).ok().as_deref());
let mut control_port: Option<u16> = solidcraft_engine::geom::env::var("CONTROL_PORT").and_then(|p| p.parse().ok());
let mut files = Vec::new();
@@ -667,6 +613,7 @@ fn main() -> eframe::Result {
}
// Settings that don't read are moved aside; launches that never got a window are counted,
// and after a few in a row this one starts in safe mode (docs/book/src/guide/troubleshooting.md).
let settings = settings_dir();
let mut notes: Vec<String> = Vec::new();
let mut failed_starts = 0;
if let Some(dir) = &settings {
@@ -704,16 +651,10 @@ fn main() -> eframe::Result {
// module on Windows, and on every system the startup step the app was in.
#[cfg(target_os = "windows")]
native_crash::install();
// Windows with only OpenGL to start: the system title bar instead of ours (#2063).
let started = match &wgpu_options.wgpu_setup {
eframe::egui_wgpu::WgpuSetup::CreateNew(setup) => setup.instance_descriptor.backends,
_ => eframe::wgpu::Backends::all(),
};
let custom_titlebar = CUSTOM_TITLEBAR && !startup::system_title_bar(cfg!(target_os = "windows"), started);
eframe::egui_wgpu::set_startup_hook(startup_step);
solidcraft_engine::crash::phase("window");
let options = eframe::NativeOptions {
viewport: app_viewport(custom_titlebar)?,
viewport: app_viewport()?,
// The window's pass (egui's) has no depth and one sample: the viewport draws its scene
// offscreen with its own depth and MSAA, then blits it, so the UI pass pays for neither.
depth_buffer: 0,
@@ -777,18 +718,13 @@ fn main() -> eframe::Result {
mark("fonts");
let mut app = SolveApp::new(Session::default(), services(cc));
app.apply_off_thread = Some(solidcraft_ui_egui::dialogs::APPLY_AT_ONCE);
app.custom_titlebar = custom_titlebar;
app.custom_titlebar = CUSTOM_TITLEBAR;
// Windows: the OS hit test offers the resize border, at the window's DPI, from the
// first message (#1381).
#[cfg(target_os = "windows")]
{
// Windows: the taskbar button shows the mark even when slow first frames keep the
// taskbar waiting for the window's icon.
if !native_frame::set_class_icon(cc) {
eprintln!("SolidCraft window: no class icon; the taskbar may show a generic one");
}
// The OS hit test offers the resize border, at the window's DPI, from the first
// message (#1381).
app.native_resize_border = custom_titlebar && native_frame::install(cc);
if custom_titlebar && !app.native_resize_border {
app.native_resize_border = CUSTOM_TITLEBAR && native_frame::install(cc);
if CUSTOM_TITLEBAR && !app.native_resize_border {
eprintln!("SolidCraft window: no native resize border; egui edge zones instead");
}
}
@@ -803,19 +739,7 @@ fn main() -> eframe::Result {
restart_on_software: device_lost::restart_on_software(after_loss, cpu_renderer),
lost_at: None,
});
if !safe_mode {
app.preferences.graphics_adapter =
graphics().and_then(|v| v.get("adapter").and_then(serde_json::Value::as_str).map(str::to_string)).unwrap_or_default();
}
if let Some(rs) = &cc.wgpu_render_state {
app.services.graphics_adapters = rs
.available_adapters
.iter()
.map(|adapter| {
let info = adapter.get_info();
(info.name.clone(), format!("{} ({:?}, {:?})", info.name, info.device_type, info.backend))
})
.collect();
let scene_samples = scene_samples(rs.adapter.get_info().backend, u32::from(samples), &rs.msaa_supported);
eprintln!("SolidCraft graphics: {:?}, MSAA {scene_samples}x (requested {}x)", rs.adapter.get_info().backend, samples);
solidcraft_engine::crash::phase("viewport pipelines (shader compile)");
@@ -852,9 +776,6 @@ fn main() -> eframe::Result {
app.session.mark_saved();
app.session.undo.clear();
}
// Lock files next to open designs: a design open on another computer says so (#5467),
// the files given at launch included.
solidcraft_ui_egui::open_elsewhere::start(&mut app);
// A launch from Finder: its files arrived before the window.
#[cfg(target_os = "macos")]
let files = {
@@ -945,12 +866,9 @@ fn wgpu_options(cpu: bool, preference: Option<&str>) -> eframe::egui_wgpu::WgpuC
eprintln!("SolidCraft graphics: starting {backends:?}");
setup.instance_descriptor.backends = backends;
}
let intel_mac = cfg!(all(target_os = "macos", target_arch = "x86_64"));
let indirect = std::env::var(startup::INDIRECT_VALIDATION_ENV).ok();
setup.instance_descriptor.flags = startup::window_instance_flags(setup.instance_descriptor.flags, intel_mac, indirect.as_deref());
let environment = std::env::var("WGPU_ADAPTER_NAME").ok();
let adapter = graphics().and_then(|v| v.get("adapter").and_then(serde_json::Value::as_str).map(str::to_string));
startup::configure_adapter(setup, cpu, environment, adapter);
if cpu {
setup.native_adapter_selector = Some(std::sync::Arc::new(startup::pick_cpu_adapter));
}
// A float depth buffer with a stencil, wherever the adapter has it (see `FLOAT_DEPTH_BITS`).
let base = setup.device_descriptor.clone();
setup.device_descriptor = std::sync::Arc::new(move |adapter| {
@@ -1068,11 +986,11 @@ mod backend_tests {
}
#[cfg(test)]
mod viewport_tests {
mod icon_tests {
#[cfg(target_os = "macos")]
#[test]
fn macos_preserves_the_bundle_icon_instead_of_setting_a_runtime_bitmap() {
let viewport = super::app_viewport(super::CUSTOM_TITLEBAR).expect("viewport");
let viewport = super::app_viewport().expect("viewport");
assert_eq!(viewport.icon.as_deref(), Some(&eframe::egui::IconData::default()));
assert_eq!(viewport.app_id.as_deref(), Some(super::APP_ID));
}
@@ -1080,89 +998,12 @@ mod viewport_tests {
#[cfg(not(target_os = "macos"))]
#[test]
fn desktop_viewport_supplies_the_original_mark_to_the_window() {
let viewport = super::app_viewport(super::CUSTOM_TITLEBAR).expect("viewport");
let viewport = super::app_viewport().expect("viewport");
let icon = viewport.icon.expect("running window must have an icon");
assert_eq!((icon.width, icon.height), (64, 64));
assert_eq!(icon.rgba, super::app_icon::rgba(64).expect("original mark"));
assert_eq!(viewport.app_id.as_deref(), Some(super::APP_ID));
}
#[test]
fn fullscreen_bar_clears_native_chrome_and_undo_redo_keep_working() {
use egui::{Event, PointerButton, Pos2, RawInput, Rect, ViewportId, vec2};
use serde_json::json;
use solidcraft_ui_egui::scenario::handle_at;
// AppKit points are independent of Retina scale and of the user's UI zoom.
for (zoom, retina) in [(1.0, 1.0), (1.0, 2.0), (1.5, 2.0), (2.0, 2.0)] {
let ctx = egui::Context::default();
solidcraft_ui_egui::fonts::setup_bundled_fonts(&ctx);
ctx.set_zoom_factor(zoom);
let mut app = super::SolveApp::new(super::Session::default(), Default::default());
app.integrated_titlebar = true;
for (command, params) in [
("sketch.create", json!({"plane":"XY"})),
("sketch.rectangle.two_point", json!({"p0":[0,0],"p1":[40,30]})),
("sketch.finish", json!({})),
("solid.extrude", json!({"distance":15})),
] {
app.run(command, params).unwrap();
}
let mut time = 0.0;
let mut frame = |app: &mut super::SolveApp, fullscreen, events| {
time += 0.05;
let mut raw = RawInput {
screen_rect: Some(Rect::from_min_size(Pos2::ZERO, vec2(1600.0, 1000.0))),
time: Some(time),
events,
..Default::default()
};
let viewport = raw.viewports.get_mut(&ViewportId::ROOT).unwrap();
viewport.fullscreen = Some(fullscreen);
viewport.native_pixels_per_point = Some(retina);
super::fullscreen_content_insets(&mut raw, 32.0, zoom);
let _ = ctx.run_ui(raw, |ui| super::content_ui(app, ui));
};
// Enter, leave, and enter again: no stale inset when returning to a normal window.
for fullscreen in [false, true, false, true] {
frame(&mut app, fullscreen, vec![]);
frame(&mut app, fullscreen, vec![]);
let top = if fullscreen { 32.0 / zoom } else { 0.0 };
for name in ["appbar:file", "appbar:undo", "appbar:redo", "appbar:help"] {
let at = handle_at(name).unwrap();
assert!((at.y - top - 17.0).abs() < 0.1, "{name}: {at:?}, fullscreen={fullscreen}, zoom={zoom}");
}
for (handle, bodies) in [("appbar:undo", 0), ("appbar:redo", 1)] {
let at = handle_at(handle).unwrap();
frame(&mut app, fullscreen, vec![Event::PointerMoved(at)]);
for pressed in [true, false] {
frame(
&mut app,
fullscreen,
vec![Event::PointerButton { pos: at, button: PointerButton::Primary, pressed, modifiers: Default::default() }],
);
}
assert_eq!(app.session.model.state().bodies.len(), bodies, "{handle}, fullscreen={fullscreen}, zoom={zoom}");
}
}
}
}
#[test]
fn fullscreen_inset_preserves_existing_safe_areas_and_resets_on_exit() {
use egui::epaint::MarginF32;
use egui::{RawInput, SafeAreaInsets, ViewportId};
let mut raw = RawInput::default();
raw.viewports.get_mut(&ViewportId::ROOT).unwrap().fullscreen = Some(true);
raw.safe_area_insets = Some(SafeAreaInsets(MarginF32 { top: 40.0, left: 5.0, right: 6.0, bottom: 7.0 }));
super::fullscreen_content_insets(&mut raw, 32.0, 2.0);
assert_eq!(raw.safe_area_insets.unwrap().0, MarginF32 { top: 40.0, left: 5.0, right: 6.0, bottom: 7.0 });
raw.viewports.get_mut(&ViewportId::ROOT).unwrap().fullscreen = Some(false);
raw.safe_area_insets = None;
super::fullscreen_content_insets(&mut raw, 32.0, 1.0);
assert_eq!(raw.safe_area_insets, Some(SafeAreaInsets::default()));
}
}
#[cfg(test)]
+5 -57
View File
@@ -15,14 +15,10 @@
//! window rescales once at the crossing and stays under the pointer, instead of the OS's move
//! loop and winit disagreeing about a frame the window doesn't have.
//!
//! Replacing a window procedure and setting a window class's icon have no safe API, hence the
//! `unsafe_code` allowance on this module (the app crate denies it; see `native_crash.rs` and
//! `open_documents.rs` for the others).
//! Replacing a window procedure has no safe API, hence the `unsafe_code` allowance on this
//! module (the app crate denies it; see `native_crash.rs` and `open_documents.rs` for the others).
//! The procedure is installed once, on the thread that owns the window, for the main window only,
//! and calls the previous one (winit's) for everything it doesn't answer.
//!
//! [`set_class_icon`] gives the window's class the exe's icon, so the taskbar button shows the
//! mark even when the window is too busy to answer the taskbar's request for its icon.
#![allow(unsafe_code)]
use std::sync::atomic::{AtomicIsize, Ordering};
@@ -31,18 +27,12 @@ use raw_window_handle::{HasWindowHandle, RawWindowHandle};
use solidcraft_ui_egui::titlebar;
use windows_sys::Win32::Foundation::{HWND, LPARAM, LRESULT, RECT, SIZE, WPARAM};
use windows_sys::Win32::Graphics::Gdi::{GetMonitorInfoW, MONITOR_DEFAULTTONEAREST, MONITORINFO, MonitorFromWindow};
use windows_sys::Win32::System::LibraryLoader::GetModuleHandleW;
use windows_sys::Win32::UI::HiDpi::{GetDpiForWindow, GetSystemMetricsForDpi};
use windows_sys::Win32::UI::WindowsAndMessaging::{
CallWindowProcW, GCLP_HICON, GCLP_HICONSM, GWL_STYLE, GWLP_WNDPROC, GetClientRect, GetWindowLongPtrW, GetWindowRect, HTBOTTOM, HTBOTTOMLEFT,
HTBOTTOMRIGHT, HTLEFT, HTRIGHT, HTTOP, HTTOPLEFT, HTTOPRIGHT, IMAGE_ICON, IsIconic, IsZoomed, LR_DEFAULTCOLOR, LoadImageW, SM_CXICON,
SM_CXPADDEDBORDER, SM_CXSIZEFRAME, SM_CXSMICON, SetWindowLongPtrW, WM_GETDPISCALEDSIZE, WM_NCDESTROY, WM_NCHITTEST, WNDPROC, WS_THICKFRAME,
CallWindowProcW, GWL_STYLE, GWLP_WNDPROC, GetClientRect, GetWindowLongPtrW, GetWindowRect, HTBOTTOM, HTBOTTOMLEFT, HTBOTTOMRIGHT, HTLEFT,
HTRIGHT, HTTOP, HTTOPLEFT, HTTOPRIGHT, IsIconic, IsZoomed, SM_CXPADDEDBORDER, SM_CXSIZEFRAME, SetWindowLongPtrW, WM_GETDPISCALEDSIZE,
WM_NCDESTROY, WM_NCHITTEST, WNDPROC, WS_THICKFRAME,
};
// windows-sys has SetClassLongPtrW only on 64-bit targets; on 32-bit Windows it is SetClassLongW.
#[cfg(target_pointer_width = "64")]
use windows_sys::Win32::UI::WindowsAndMessaging::SetClassLongPtrW;
#[cfg(target_pointer_width = "32")]
use windows_sys::Win32::UI::WindowsAndMessaging::SetClassLongW as SetClassLongPtrW;
/// The work area (the monitor without the taskbar) of the monitor nearest the window, in physical
/// pixels: left, top, width, height.
@@ -64,48 +54,6 @@ pub fn work_area(window: &impl HasWindowHandle) -> Option<(i32, i32, i32, i32)>
Some((rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top))
}
/// The exe's icon resource (`build.rs` embeds it as `1 ICON`).
const ICON_RESOURCE: usize = 1;
/// Give the main window's class the exe's icon, big and small. Call it before the window is
/// shown (eframe's app creator).
///
/// The taskbar asks a window for its icon when the window appears. eframe sets the window's
/// icon before that, while the window is still hidden, and then shows the window at the end of
/// its first frame; if that frame is slow, or startup kept the thread busy long enough for
/// Windows to mark the window as not responding, the request isn't answered in time. The taskbar then
/// takes the window class's icon and doesn't ask again. winit registers its class without one,
/// so the button showed Windows' generic icon for as long as the window was open.
/// `false` if it isn't a Win32 window or the exe's icon doesn't load; SetClassLongPtrW's own
/// failure can't be told from a class that had no icon (both return 0), so it isn't reported.
pub fn set_class_icon(window: &impl HasWindowHandle) -> bool {
let Ok(handle) = window.window_handle() else { return false };
let RawWindowHandle::Win32(w) = handle.as_raw() else { return false };
let hwnd = w.hwnd.get() as HWND;
// SAFETY: a plain query on the live main window.
let dpi = unsafe { GetDpiForWindow(hwnd) };
let (Some(big), Some(small)) = (exe_icon(SM_CXICON, dpi), exe_icon(SM_CXSMICON, dpi)) else { return false };
// SAFETY: `hwnd` is the live main window and both icons are valid handles. The class keeps
// them for the rest of the process, so they are never destroyed. `as _`: on 32-bit Windows
// this is SetClassLongW (see the imports), which takes an i32.
unsafe {
SetClassLongPtrW(hwnd, GCLP_HICON, big as _);
SetClassLongPtrW(hwnd, GCLP_HICONSM, small as _);
}
true
}
/// The exe's icon at the size of system metric `metric` (`SM_CXICON`, `SM_CXSMICON`) at `dpi`.
fn exe_icon(metric: i32, dpi: u32) -> Option<windows_sys::Win32::Foundation::HANDLE> {
// SAFETY: GetModuleHandleW(null) is this exe, loaded for the life of the process; the name
// is an integer resource id (MAKEINTRESOURCE), which LoadImageW accepts in place of a string.
let icon = unsafe {
let size = GetSystemMetricsForDpi(metric, dpi);
LoadImageW(GetModuleHandleW(std::ptr::null()), std::ptr::without_provenance(ICON_RESOURCE), IMAGE_ICON, size, size, LR_DEFAULTCOLOR)
};
(!icon.is_null()).then_some(icon)
}
/// The window procedure ours replaced (winit's), as an address; 0 before [`install`].
static PREVIOUS: AtomicIsize = AtomicIsize::new(0);
-1
View File
@@ -105,7 +105,6 @@ impl NativeMenu {
("messages", "Messages\u{2026}"),
("report", "Report an Issue\u{2026}"),
("crashes", "Crash Reports\u{2026}"),
("install_cli", "Install solidcraft-cli command"),
] {
let item = MenuItem::new(app.tr_label(label), true, None);
help.append(&item)?;
+30 -324
View File
@@ -241,7 +241,6 @@ struct Reading {
device: Option<Source>,
/// A source that is connected but has not yet shown a device behind it (spacenavd's socket answers
/// with no mouse plugged in; its protocol has no enumeration, so the first record is the evidence).
#[cfg(target_os = "linux")]
awaiting: Option<Source>,
axes: Axes,
/// When each of X, Y, Z, Rx, Ry and Rz last arrived. Each ages on its own: a device may send
@@ -288,7 +287,6 @@ impl Reading {
}
/// A record arrived from the source being waited on: there is a device.
#[cfg(target_os = "linux")]
fn heard(&mut self) {
if self.device.is_none() {
self.device = self.awaiting.take();
@@ -298,7 +296,6 @@ impl Reading {
fn new(now: Instant) -> Self {
Self {
device: None,
#[cfg(target_os = "linux")]
awaiting: None,
axes: Axes::default(),
at: [now; 6],
@@ -463,20 +460,12 @@ fn connected(ctx: &Context, shared: &Mutex<Reading>, source: Source) {
let now = Instant::now();
let stale = now.checked_sub(STALE_AFTER).unwrap_or(now);
// A daemon is not yet a device: it counts once a record comes from it.
let waiting = needs_evidence(source);
let (device, awaiting) = if needs_evidence(source) { (None, Some(source)) } else { (Some(source), None) };
// A HID device reports the state of its buttons, so the first state seen is a baseline; the daemon
// and the driver send presses and releases, which start from nothing down.
let buttons_known = !matches!(source, Source::Hid(_));
let generation = CONNECTIONS.fetch_add(1, Ordering::Relaxed).wrapping_add(1);
*reading = Reading {
device: (!waiting).then_some(source),
#[cfg(target_os = "linux")]
awaiting: waiting.then_some(source),
at: [stale; 6],
buttons_known,
generation,
..Reading::new(now)
};
*reading = Reading { device, awaiting, at: [stale; 6], buttons_known, generation, ..Reading::new(now) };
}
ctx.request_repaint();
}
@@ -521,119 +510,6 @@ fn hid_api() -> hidapi::HidResult<HidApi> {
HidApi::new()
}
/// An open 3D-mouse interface: the device, how its reports are laid out, and its vendor and
/// product ids.
#[cfg(any(target_os = "windows", target_os = "macos", test))]
struct Candidate<D> {
device: D,
layout: Layout,
ids: (u16, u16),
}
/// Input reports read with a timeout in milliseconds: hidapi's devices, and the tests' simulated
/// ones. `Ok(0)` is a timeout; an error means the device is gone.
#[cfg(any(target_os = "windows", target_os = "macos", test))]
trait ReportSource {
fn read_report(&self, report: &mut [u8], timeout_ms: i32) -> Result<usize, String>;
}
#[cfg(any(target_os = "windows", target_os = "macos"))]
impl ReportSource for hidapi::HidDevice {
fn read_report(&self, report: &mut [u8], timeout_ms: i32) -> Result<usize, String> {
self.read_timeout(report, timeout_ms).map_err(|error| error.to_string())
}
}
/// What following the candidates tells the reader.
#[cfg(any(target_os = "windows", target_os = "macos", test))]
enum Followed<'a> {
/// This device is the one read now, so its calibration applies. With the buttons it last
/// reported down, when it reported them while another was read: they are its baseline, so a
/// press that makes it the device in use counts as a press, not as a button already held.
Device((u16, u16), Option<u32>),
/// An input report from it.
Report(&'a Layout, &'a [u8]),
/// It is gone.
Gone,
}
/// Whether a report shows its device in use: an axis off zero or a button down. A cap at rest
/// sends zeros at most, and an idle 3Dconnexion Universal Receiver sends nothing at all.
#[cfg(any(target_os = "windows", target_os = "macos", test))]
fn shows_use(layout: Layout, report: &[u8]) -> bool {
layout.decode(report).is_some_and(|decoded| decoded.axes.iter().flatten().any(|axis| *axis != 0) || decoded.buttons.is_some_and(|down| down != 0))
}
/// Reads every candidate and follows the one in use (#3988). The first is followed from the start,
/// as when it was the only one read. Another takes over once it shows use while the followed one
/// has shown none for `STALE_AFTER`, so two devices moved together don't hand the reading back
/// and forth. An idle Universal Receiver opens fine and never fails; read on its own it kept a
/// SpaceMouse next to it from ever being read. A candidate that fails is dropped. If it was the
/// followed one, the reading is reset and the next takes over. Returns when none is left.
#[cfg(any(target_os = "windows", target_os = "macos", test))]
fn follow<D: ReportSource>(mut candidates: Vec<Candidate<D>>, now: impl Fn() -> Instant, mut emit: impl FnMut(Followed<'_>)) {
let Some(first) = candidates.first() else { return };
emit(Followed::Device(first.ids, None));
let mut followed = 0;
// When the followed device last showed use.
let mut used_at: Option<Instant> = None;
// The buttons each candidate last reported down, by index, as long as it has reported them.
let mut buttons: Vec<Option<u32>> = candidates.iter().map(|_| None).collect();
let mut report = [0_u8; REPORT_BUFFER_BYTES];
while !candidates.is_empty() {
// One device is waited on as before; several take turns, briefly, so none waits behind
// an idle one. Reports queue in the system meanwhile, so none is lost.
let timeout_ms = if candidates.len() == 1 { 100 } else { 10 };
let mut index = 0;
while let Some(candidate) = candidates.get(index) {
match candidate.device.read_report(&mut report, timeout_ms) {
Ok(0) => {}
Ok(size) => {
let bytes = report.get(..size).unwrap_or_default();
let before = buttons.get(index).copied().flatten();
if let (Some(seen), Some(down)) = (buttons.get_mut(index), candidate.layout.decode(bytes).and_then(|decoded| decoded.buttons)) {
*seen = Some(down);
}
if shows_use(candidate.layout, bytes) {
let at = now();
if index != followed && used_at.is_none_or(|used| at.duration_since(used) >= STALE_AFTER) {
let (vendor, product) = candidate.ids;
log::info!("3D mouse: reading {vendor:04x}:{product:04x}, the device in use");
followed = index;
emit(Followed::Device(candidate.ids, before));
}
if index == followed {
used_at = Some(at);
}
}
if index == followed {
emit(Followed::Report(&candidate.layout, bytes));
}
}
Err(error) => {
log::info!("3D mouse disconnected: {error}");
candidates.remove(index);
if index < buttons.len() {
buttons.remove(index);
}
if index == followed {
emit(Followed::Gone);
followed = 0;
used_at = None;
if let Some(next) = candidates.first() {
emit(Followed::Device(next.ids, buttons.first().copied().flatten()));
}
} else if index < followed {
followed -= 1;
}
continue;
}
}
index += 1;
}
}
}
#[cfg(any(target_os = "windows", target_os = "macos"))]
fn reader(ctx: Context, shared: Arc<Mutex<Reading>>) {
let Ok(mut api) = hid_api() else {
@@ -641,38 +517,36 @@ fn reader(ctx: Context, shared: Arc<Mutex<Reading>>) {
return;
};
loop {
let open: Vec<_> = candidates(&mut api)
.into_iter()
.filter_map(|path| {
let device = api
.open_path(&path)
.inspect_err(|error| {
// On macOS: missing Input Monitoring permission, or the 3DxWare helper holding the device.
// The driver having the device is the reason it cannot be opened, not a problem.
if !DRIVER_CONNECTED.load(Ordering::Relaxed) && !DRIVER_REGISTERED.load(Ordering::Relaxed) {
log::warn!("3D mouse: cannot open the device: {error}");
}
})
.ok()?;
// Read when the device was opened, from this device alone.
let info = device.get_device_info().ok()?;
let layout = device_layout(&device, info.vendor_id())?;
Some(Candidate { device, layout, ids: (info.vendor_id(), info.product_id()) })
})
.collect();
follow(open, Instant::now, |event| match event {
Followed::Device(ids, buttons) => {
connected(&ctx, &shared, Source::Hid(Some(ids)));
// Taken over from another device: what it reported meanwhile is its baseline.
if let Some(buttons) = buttons
&& let Ok(mut reading) = shared.lock()
{
reading.set_buttons(buttons);
if let Some((device, layout, ids)) = candidates(&mut api).into_iter().find_map(|path| {
let device = api
.open_path(&path)
.inspect_err(|error| {
// On macOS: missing Input Monitoring permission, or the 3DxWare helper holding the device.
// The driver having the device is the reason it cannot be opened, not a problem.
if !DRIVER_CONNECTED.load(Ordering::Relaxed) && !DRIVER_REGISTERED.load(Ordering::Relaxed) {
log::warn!("3D mouse: cannot open the device: {error}");
}
})
.ok()?;
// Read when the device was opened, from this device alone.
let info = device.get_device_info().ok()?;
let layout = device_layout(&device, info.vendor_id())?;
Some((device, layout, (info.vendor_id(), info.product_id())))
}) {
connected(&ctx, &shared, Source::Hid(Some(ids)));
let mut report = [0_u8; REPORT_BUFFER_BYTES];
loop {
match device.read_timeout(&mut report, 100) {
Ok(0) => {}
Ok(size) => publish(&ctx, &shared, layout, report.get(..size).unwrap_or_default()),
Err(error) => {
log::info!("3D mouse disconnected: {error}");
break;
}
}
}
Followed::Report(layout, report) => publish(&ctx, &shared, *layout, report),
Followed::Gone => reset(&ctx, &shared),
});
reset(&ctx, &shared);
}
std::thread::sleep(RETRY_AFTER);
}
}
@@ -1409,174 +1283,6 @@ mod tests {
assert!(shared.lock().unwrap().arrived, "but the report did arrive");
}
/// A simulated 3D-mouse interface. Each read takes the next scripted step, a report or a
/// timeout (empty), and sets the shared clock to the step's time. Once the script runs out,
/// the device is unplugged.
struct Simulated<'a> {
clock: &'a std::cell::Cell<Instant>,
start: Instant,
steps: std::cell::RefCell<std::collections::VecDeque<(u64, Vec<u8>)>>,
}
impl ReportSource for Simulated<'_> {
fn read_report(&self, report: &mut [u8], _timeout_ms: i32) -> Result<usize, String> {
let (ms, bytes) = self.steps.borrow_mut().pop_front().ok_or_else(|| "unplugged".to_string())?;
self.clock.set(self.start + Duration::from_millis(ms));
report.get_mut(..bytes.len()).ok_or_else(|| "report too long".to_string())?.copy_from_slice(&bytes);
Ok(bytes.len())
}
}
#[derive(Debug, PartialEq)]
enum Seen {
/// The product id now read.
Device(u16),
/// The buttons it had reported down before it was read, its baseline.
Baseline(u32),
/// A report published, the product id read when it was, and whether it showed use.
Report(u16, Vec<u8>, bool),
Gone,
}
/// Follows simulated devices, given as (product id, scripted steps in ms), and returns what
/// the reader was told.
fn follow_simulated(devices: &[(u16, &[(u64, &[u8])])]) -> Vec<Seen> {
follow_simulated_with(Layout::default(), devices)
}
/// As `follow_simulated`, with every device's reports laid out as `layout`.
fn follow_simulated_with(layout: Layout, devices: &[(u16, &[(u64, &[u8])])]) -> Vec<Seen> {
let start = Instant::now();
let clock = std::cell::Cell::new(start);
let candidates = devices
.iter()
.map(|(product, steps)| Candidate {
device: Simulated {
clock: &clock,
start,
steps: std::cell::RefCell::new(steps.iter().map(|(ms, bytes)| (*ms, bytes.to_vec())).collect()),
},
layout,
ids: (0x256f, *product),
})
.collect();
let (mut seen, mut read) = (Vec::new(), 0);
follow(
candidates,
|| clock.get(),
|event| match event {
Followed::Device((_, product), buttons) => {
read = product;
seen.push(Seen::Device(product));
seen.extend(buttons.map(Seen::Baseline));
}
Followed::Report(layout, bytes) => seen.push(Seen::Report(read, bytes.to_vec(), shows_use(*layout, bytes))),
Followed::Gone => seen.push(Seen::Gone),
},
);
seen
}
const RECEIVER: u16 = 0xc652;
const SPACEMOUSE: u16 = 0xc63a;
const PUSH_X: &[u8] = &[1, 100, 0, 0, 0, 0, 0];
const AT_REST: &[u8] = &[1, 0, 0, 0, 0, 0, 0];
const TILT: &[u8] = &[2, 0, 0, 50, 0, 0, 0];
/// #3988: a Universal Receiver with nothing paired is found first and never fails, and a wired
/// SpaceMouse next to it was never read. The receiver is followed only until the SpaceMouse
/// moves; the SpaceMouse's reports, its zeros included, are read from then on.
#[test]
fn an_idle_receiver_does_not_keep_a_spacemouse_from_being_read() {
let receiver: &[(u64, &[u8])] = &[(0, &[]), (10, &[]), (20, &[]), (30, &[])];
let spacemouse: &[(u64, &[u8])] = &[(5, &[]), (15, PUSH_X), (25, AT_REST), (35, TILT)];
assert_eq!(
follow_simulated(&[(RECEIVER, receiver), (SPACEMOUSE, spacemouse)]),
[
Seen::Device(RECEIVER),
Seen::Device(SPACEMOUSE),
Seen::Report(SPACEMOUSE, PUSH_X.to_vec(), true),
Seen::Report(SPACEMOUSE, AT_REST.to_vec(), false),
Seen::Report(SPACEMOUSE, TILT.to_vec(), true),
// The receiver is unplugged first, then the SpaceMouse, the one being read.
Seen::Gone,
]
);
}
/// Two devices in use at once don't trade the reading: the other takes over only after the
/// followed one has shown no use for `STALE_AFTER`. A report at rest or with an id the layout
/// doesn't know shows no use, and only the followed device's reports are published.
#[test]
fn another_device_takes_over_only_once_the_followed_one_is_still() {
let unknown_id: &[u8] = &[0x17, 80];
let first: &[(u64, &[u8])] = &[(0, PUSH_X), (100, AT_REST), (200, &[])];
let second: &[(u64, &[u8])] = &[(50, PUSH_X), (150, unknown_id), (250, TILT)];
assert_eq!(
follow_simulated(&[(0xc635, first), (SPACEMOUSE, second)]),
[
Seen::Device(0xc635),
Seen::Report(0xc635, PUSH_X.to_vec(), true),
// The second moves 50 ms after the first did: not yet. The first's own rest
// report is read, and the second's unknown report is not.
Seen::Report(0xc635, AT_REST.to_vec(), false),
// 250 ms after the first last moved, the second takes over.
Seen::Device(SPACEMOUSE),
Seen::Report(SPACEMOUSE, TILT.to_vec(), true),
Seen::Gone,
]
);
}
/// One device is read as before: every report, until it fails. When the followed one of
/// several fails, the reading is reset and the next one is read.
#[test]
fn a_device_that_fails_hands_the_reading_to_the_next() {
let only: &[(u64, &[u8])] = &[(0, &[]), (1, AT_REST), (2, &[0x17, 80])];
assert_eq!(
follow_simulated(&[(SPACEMOUSE, only)]),
[
Seen::Device(SPACEMOUSE),
Seen::Report(SPACEMOUSE, AT_REST.to_vec(), false),
Seen::Report(SPACEMOUSE, vec![0x17, 80], false),
Seen::Gone
]
);
let unplugged: &[(u64, &[u8])] = &[];
let next: &[(u64, &[u8])] = &[(1, PUSH_X)];
assert_eq!(
follow_simulated(&[(SPACEMOUSE, unplugged), (RECEIVER, next)]),
[Seen::Device(SPACEMOUSE), Seen::Gone, Seen::Device(RECEIVER), Seen::Report(RECEIVER, PUSH_X.to_vec(), true), Seen::Gone]
);
assert_eq!(follow_simulated(&[]), []);
}
/// A button press that makes a device the one in use is a press. The buttons it reported while
/// another was read are its baseline; the reader keeps them, so the press isn't taken for a
/// button already held, as a device's first button report is.
#[test]
fn a_press_that_takes_over_counts_as_a_press() {
let mut layout = Layout::default();
layout.buttons[0] =
Some(solidcraft_space_mouse::Field { report_id: 3, bit_offset: 0, bits: 1, logical_min: 0, logical_max: 1, relative: false });
let (none_down, first_down): (&[u8], &[u8]) = (&[3, 0], &[3, 1]);
let receiver: &[(u64, &[u8])] = &[(0, &[]), (10, &[]), (20, &[])];
let spacemouse: &[(u64, &[u8])] = &[(5, none_down), (15, first_down), (25, &[])];
assert_eq!(
follow_simulated_with(layout, &[(RECEIVER, receiver), (SPACEMOUSE, spacemouse)]),
[Seen::Device(RECEIVER), Seen::Device(SPACEMOUSE), Seen::Baseline(0), Seen::Report(SPACEMOUSE, first_down.to_vec(), true), Seen::Gone,]
);
// What the reader does with that: the press after the baseline is a tap.
let mut reading = Reading { buttons_known: false, ..Reading::new(Instant::now()) };
reading.set_buttons(0);
assert!(decode_report(first_down, layout, &mut reading, Instant::now()));
assert_eq!((reading.buttons, reading.taps), (1, 1));
// Without one, the same report is the baseline: held, not pressed.
let mut unseen = Reading { buttons_known: false, ..Reading::new(Instant::now()) };
assert!(decode_report(first_down, layout, &mut unseen, Instant::now()));
assert_eq!((unseen.buttons, unseen.taps), (1, 0));
}
#[test]
fn a_held_reading_stays_until_the_next_one_and_a_hid_reading_goes_stale() {
let now = Instant::now();
-164
View File
@@ -21,25 +21,6 @@ pub const RENDERER_ENV: &str = "SOLIDCRAFT_RENDERER";
pub const FAILED_STARTS_FOR_SAFE_MODE: u32 = 2;
const MARKER: &str = "starting";
/// Explicit application-settings isolation for side-by-side builds and native UI
/// acceptance. A supplied invalid path refuses startup rather than falling back
/// to the user's ordinary settings. No directory is read or created here.
pub fn settings_directory(override_path: Option<&std::ffi::OsStr>, default: Option<PathBuf>) -> Result<Option<PathBuf>, String> {
match override_path {
None => Ok(default),
Some(raw) => {
let path = PathBuf::from(raw);
if raw.is_empty() || !path.is_absolute() {
return Err("SOLIDCRAFT_SETTINGS_DIR must be a nonempty absolute directory path".into());
}
if path.exists() && !path.is_dir() {
return Err("SOLIDCRAFT_SETTINGS_DIR names a file, not a directory".into());
}
Ok(Some(path))
}
}
}
/// Whether an environment value switches something on.
pub fn env_on(v: Option<&str>) -> bool {
v.map(str::trim).is_some_and(|v| !v.is_empty() && v != "0" && !v.eq_ignore_ascii_case("false"))
@@ -129,48 +110,6 @@ pub fn pick_cpu_adapter(adapters: &[eframe::wgpu::Adapter], surface: Option<&efr
})
}
/// Adapter properties used to choose a card without opening a device.
#[derive(Clone, Debug)]
pub struct AdapterChoice {
pub name: String,
pub compatible: bool,
}
/// Choose only surface-compatible adapters. Names saved in preferences are exact; the
/// environment override follows wgpu's case-insensitive substring convention.
/// No match leaves selection to the normal wgpu request.
pub fn choose_adapter(adapters: &[AdapterChoice], environment: Option<&str>, preference: Option<&str>) -> Option<usize> {
// wgpu's environment helper panics on a missing name; an override must remain recoverable.
let compatible = || adapters.iter().enumerate().filter(|(_, a)| a.compatible);
if let Some(name) = environment.filter(|s| !s.is_empty()) {
let name = name.to_lowercase();
if let Some((index, _)) = compatible().find(|(_, a)| a.name.to_lowercase().contains(&name)) {
return Some(index);
}
}
if let Some(name) = preference.filter(|s| !s.is_empty())
&& let Some((index, _)) = compatible().find(|(_, a)| a.name == name)
{
return Some(index);
}
None
}
/// Leave Automatic on wgpu's normal request path; named choices may defer to it too.
pub fn configure_adapter(setup: &mut eframe::egui_wgpu::WgpuSetupCreateNew, cpu: bool, environment: Option<String>, preference: Option<String>) {
if cpu {
setup.native_adapter_selector = Some(std::sync::Arc::new(|adapters, surface| pick_cpu_adapter(adapters, surface).map(Some)));
} else if environment.as_deref().is_some_and(|s| !s.is_empty()) || preference.as_deref().is_some_and(|s| !s.is_empty()) {
setup.native_adapter_selector = Some(std::sync::Arc::new(move |adapters, surface| {
let choices: Vec<_> = adapters
.iter()
.map(|a| AdapterChoice { name: a.get_info().name, compatible: surface.is_none_or(|s| a.is_surface_supported(s)) })
.collect();
Ok(choose_adapter(&choices, environment.as_deref(), preference.as_deref()).and_then(|i| adapters.get(i)).cloned())
}));
}
}
/// `WGPU_BACKEND`: when set, wgpu picks the backends from it and the order below is not used.
pub const WGPU_BACKEND_ENV: &str = "WGPU_BACKEND";
@@ -250,29 +189,6 @@ pub fn has_hardware_adapter(backends: eframe::wgpu::Backends) -> bool {
adapters.iter().any(|a| a.get_info().device_type != eframe::wgpu::DeviceType::Cpu)
}
/// The environment variable that switches wgpu's indirect-call validation on or off.
pub const INDIRECT_VALIDATION_ENV: &str = "WGPU_VALIDATION_INDIRECT_CALL";
/// The instance flags for the window. wgpu's indirect-call validation compiles a compute shader
/// when the device is created, and on Intel Macs (macOS 12 with Intel Iris graphics, #3024,
/// #3080, #4605) the Metal compiler refuses it: the device is lost and the window never opens.
/// So on an Intel Mac the validation is left off, unless the user turned it on or off with
/// `WGPU_VALIDATION_INDIRECT_CALL` themselves.
pub fn window_instance_flags(flags: eframe::wgpu::InstanceFlags, intel_mac: bool, env: Option<&str>) -> eframe::wgpu::InstanceFlags {
if intel_mac && env.map(str::trim).is_none_or(str::is_empty) { flags - eframe::wgpu::InstanceFlags::VALIDATION_INDIRECT_CALL } else { flags }
}
/// Windows with OpenGL: the window keeps the system title bar (#2063). The window without OS
/// decorations keeps its caption style while having no frame, and some OpenGL drivers (older
/// Intel HD/UHD) then show the picture a caption's height lower than the window, so it no longer
/// lines up with the pointer. Direct3D 12 and Vulkan draw it in place and keep the application bar
/// as the title bar. `backends` are the ones wgpu starts: OpenGL alone is decided before the
/// window opens.
pub fn system_title_bar(windows: bool, backends: eframe::wgpu::Backends) -> bool {
use eframe::wgpu::Backends;
windows && backends.contains(Backends::GL) && !backends.intersects(Backends::DX12 | Backends::VULKAN)
}
/// The message shown when no window could be opened at all.
pub fn no_window_message(error: &str, settings: Option<&Path>) -> String {
let folder = settings.map(|d| d.display().to_string()).unwrap_or_else(|| "the SolidCraft settings folder".into());
@@ -291,48 +207,6 @@ pub fn no_window_message(error: &str, settings: Option<&Path>) -> String {
mod tests {
use super::*;
fn cards() -> Vec<AdapterChoice> {
["Integrated", "Discrete small", "Discrete large"].into_iter().map(|name| AdapterChoice { name: name.into(), compatible: true }).collect()
}
#[test]
fn adapter_names_environment_override_and_missing_card_fallback() {
let cards = cards();
assert_eq!(choose_adapter(&cards, None, Some("Discrete small")), Some(1));
assert_eq!(choose_adapter(&cards, Some("LARGE"), Some("Discrete small")), Some(2));
assert_eq!(choose_adapter(&cards, None, Some("removed")), None);
assert_eq!(choose_adapter(&cards, Some("missing"), Some("Discrete small")), Some(1));
assert_eq!(choose_adapter(&cards, Some(""), Some("Discrete small")), Some(1));
assert_eq!(choose_adapter(&cards, None, Some("Discrete")), None, "saved names are exact");
}
#[test]
fn automatic_adapter_leaves_selection_to_wgpu() {
let mut cards = cards();
assert_eq!(choose_adapter(&cards, None, None), None);
assert_eq!(choose_adapter(&cards, Some(""), Some("")), None);
cards[2].compatible = false;
assert_eq!(choose_adapter(&cards, Some("large"), Some("Discrete large")), None);
assert_eq!(choose_adapter(&cards, Some("small"), None), Some(1));
assert_eq!(choose_adapter(&[], None, None), None);
}
#[test]
fn automatic_adapter_preserves_the_previous_request_configuration() {
use eframe::egui_wgpu::WgpuSetupCreateNew;
let previous = WgpuSetupCreateNew::without_display_handle();
for preference in [None, Some(String::new())] {
let mut setup = WgpuSetupCreateNew::without_display_handle();
configure_adapter(&mut setup, false, None, preference);
assert!(setup.native_adapter_selector.is_none(), "Automatic must use the normal wgpu request");
assert_eq!(setup.power_preference, previous.power_preference);
assert_eq!(setup.instance_descriptor.backends, previous.instance_descriptor.backends);
}
let mut setup = WgpuSetupCreateNew::without_display_handle();
configure_adapter(&mut setup, false, Some("missing".into()), Some("removed".into()));
assert!(setup.native_adapter_selector.unwrap()(&[], None).unwrap().is_none(), "missing names must use the normal request too");
}
fn scratch(name: &str) -> PathBuf {
let d = std::env::temp_dir().join(format!("solidcraft-startup-{name}-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&d);
@@ -340,32 +214,6 @@ mod tests {
d
}
#[test]
fn isolated_settings_are_explicit_absolute_and_never_fall_back() {
let default = std::env::temp_dir().join("ordinary-settings");
assert_eq!(settings_directory(None, Some(default.clone())).unwrap(), Some(default.clone()));
let selected = std::env::temp_dir().join("solidcraft-isolated-settings-fixture");
assert_eq!(settings_directory(Some(selected.as_os_str()), Some(default.clone())).unwrap(), Some(selected));
for raw in ["", "relative/settings", "../ordinary"] {
assert!(settings_directory(Some(std::ffi::OsStr::new(raw)), Some(default.clone())).is_err());
}
let file = std::env::current_exe().unwrap();
assert!(settings_directory(Some(file.as_os_str()), Some(default)).is_err());
}
#[test]
fn intel_macs_start_without_indirect_call_validation() {
use eframe::wgpu::InstanceFlags as F;
let on = F::VALIDATION | F::VALIDATION_INDIRECT_CALL;
assert_eq!(window_instance_flags(on, true, None), F::VALIDATION);
assert_eq!(window_instance_flags(on, true, Some(" ")), F::VALIDATION);
// The user's own choice stands, on or off.
assert_eq!(window_instance_flags(on, true, Some("1")), on);
assert_eq!(window_instance_flags(F::VALIDATION, true, Some("0")), F::VALIDATION);
// Every other machine keeps what it had.
assert_eq!(window_instance_flags(on, false, None), on);
}
#[test]
fn switches_read_from_the_environment() {
assert!(env_on(Some("1")) && env_on(Some("yes")) && !env_on(Some("0")) && !env_on(Some(" ")) && !env_on(None) && !env_on(Some("false")));
@@ -442,18 +290,6 @@ mod tests {
assert_eq!(choose_backends(true, false, false, Some(""), probe(&[Backends::GL])), Some(Backends::GL));
}
#[test]
fn only_opengl_on_windows_gets_the_system_title_bar() {
use eframe::wgpu::Backends;
assert!(system_title_bar(true, Backends::GL));
for b in [Backends::DX12, Backends::VULKAN, Backends::DX12 | Backends::GL, Backends::VULKAN | Backends::GL, Backends::all()] {
assert!(!system_title_bar(true, b), "{b:?}");
}
for b in [Backends::GL, Backends::VULKAN, Backends::METAL] {
assert!(!system_title_bar(false, b), "{b:?}");
}
}
#[test]
fn wgpu_backend_the_preference_and_the_software_renderer_override_the_order() {
use eframe::wgpu::Backends;
-11
View File
@@ -1,11 +0,0 @@
//! Original illustrative opaque finishes; not measured optical or engineering data.
//! (name, sRGB color, metal fraction, perceptual roughness). IOR is an illustrative 1.5.
//! Separate names preserve the scalar library's appearance and saved assignments.
pub const OPAQUE_FINISHES: [(&str, [u8; 3], f64, f64); 6] = [
("Steel - Satin PBR", [158, 164, 172], 1.0, 0.4),
("Aluminum - Satin PBR", [204, 208, 214], 1.0, 0.3),
("Copper - Clean PBR", [204, 122, 80], 1.0, 0.25),
("ABS - Molded PBR", [222, 222, 216], 0.0, 0.4),
("PLA - Smooth PBR", [110, 160, 222], 0.0, 0.35),
("Wood - Oak Uniform PBR Preview", [190, 146, 92], 0.0, 0.65),
];
@@ -148,7 +148,6 @@ origin false
pan false
params false
paste false
path_flange false
pattern_circ false
pattern_path false
pattern_rect false
@@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<title>Path Flange</title>
<path d="M9.5 44L23.5 44A6 6 0 0 0 29.5 38L29.5 26A6 6 0 0 1 35.5 20L43.5 20A6 6 0 0 1 47.74 21.76L54.81 28.83" fill="none" stroke="#34465b" stroke-width="11.4" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M9.5 44L23.5 44" fill="none" stroke="#b8c3cf" stroke-width="6.3" stroke-linejoin="round" stroke-linecap="round"/>
<path d="M23.5 44A6 6 0 0 0 29.5 38L29.5 26A6 6 0 0 1 35.5 20L43.5 20A6 6 0 0 1 47.74 21.76L54.81 28.83" fill="none" stroke="#328bd3" stroke-width="6.3" stroke-linejoin="round" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 638 B

+2 -2
View File
@@ -108,8 +108,8 @@ pub(crate) fn glyph_icon(name: &str) -> Option<&'static str> {
}
pub(crate) fn glyph_letter(name: &str) -> &'static str {
match name {
"Horizontal" => "-",
"Vertical" => "|",
"Horizontal" => "H",
"Vertical" => "V",
_ => "?",
}
}
+3 -3
View File
@@ -152,11 +152,11 @@ pub(crate) fn curved_arrow(painter: &egui::Painter, at: Pos2, clockwise: bool, h
pub(crate) const AXIS_RADIUS: f32 = 15.0;
pub(crate) fn axis_tip(painter: &egui::Painter, c: Pos2, name: &str, d: &Vec3, at: Pos2, depth: f64, hovered: bool, t: Tokens) {
let col = if d.x != 0.0 {
t.axis_x
t.triad[0]
} else if d.y != 0.0 {
t.axis_y
t.triad[1]
} else {
t.axis_z
t.triad[2]
};
let positive = !name.starts_with('-');
let light = t.panel.r() > 128;
+2 -179
View File
@@ -72,7 +72,6 @@
"Choose…": "Vybrat…",
"Circular": "Kruhově",
"Circumference": "Obvod",
"Clockwise": "Ve směru hodinových ručiček",
"Close draft": "Zavřít koncept",
"Collisions": "Kolize",
"Colour": "Barva",
@@ -323,7 +322,6 @@
"Search commands": "Hledat příkazy",
"Search this library": "Prohledat tuto knihovnu",
"Section": "Řez",
"Section position": "Poloha průřezu",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Vybrat jiné: podržte levé tlačítko na modelu (nebo pravé tlačítko › Vybrat jiné…) a zobrazí se vše pod kurzorem; dalším kliknutím na stejné místo vyberete další prvek. Dvojklikem prostředním tlačítkem přejdete na výchozí pohled (Předvolby › Navigace).",
"Select bodies (a face picks its body)": "Vyberte tělesa (plocha vybere své těleso)",
"Select bodies or faces instead": "Vyberte místo toho tělesa nebo plochy",
@@ -335,7 +333,6 @@
"Show Agent Cursor": "Zobrazit kurzor agenta",
"Show in folder": "Zobrazit ve složce",
"Show the pointer an AI agent moves (#35)": "Zobrazit ukazatel, kterým pohybuje AI agent (#35)",
"Show tool at cursor": "Zobrazit nástroj u kurzoru",
"Signed axes": "Osy se znaménkem",
"Size": "Velikost",
"Sketch constraints": "Vazby náčrtu",
@@ -348,11 +345,9 @@
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft se zavřel dříve, než se tyto návrhy uložily. Lze otevřít jejich poslední automatické uložení.",
"Specialise": "Specializovat",
"Speed": "Rychlost",
"Square": "Čtverec",
"Stand it up": "Postavit",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Postavit: otočení o čtvrt otáčky kolem osy X jako jeden prvek Posunutí, který lze vrátit.",
"Stand this part up?": "Postavit tento díl?",
"Start angle": "Počáteční úhel",
"Start bulge": "Vypouklost začátku",
"Start continuity": "Návaznost začátku",
"Step": "Krok",
@@ -417,7 +412,6 @@
"angle, height": "úhel, výška",
"appearance.dielectric_ior": "Index lomu dielektrika",
"appearance.metallic": "Kovovost",
"appearance.only_available": "Pouze dostupné vzhledy",
"appearance.pbr_help": "Tento povrch zobrazíte v renderu nebo v produktovém PNG. Bez lomu a průchodu světla.",
"appearance.pbr_opaque": "Neprůhledné PBR",
"appearance.roughness": "Drsnost",
@@ -579,7 +573,6 @@
"cmd.material_create": "Nový fyzický materiál",
"cmd.material_delete": "Smazat fyzický materiál",
"cmd.material_edit": "Upravit fyzický materiál",
"cmd.material_inspect": "Zkontrolovat fyzický materiál",
"cmd.material_list": "Seznam materiálů",
"cmd.material_properties_directional": "Směrový modul pružnosti",
"cmd.material_properties_validate": "Ověřit data fyzického materiálu",
@@ -626,8 +619,6 @@
"cmd.plastic_rules": "Seznam pravidel pro plasty",
"cmd.plastic_snap_fit": "Zacvakávací spoj",
"cmd.render_backends": "Renderovací jádra",
"cmd.render_camera_fit": "Zarámovat objekt renderu",
"cmd.render_camera_get": "Prohlédnout kameru renderu",
"cmd.render_cancel": "Zrušit render",
"cmd.render_export": "Exportovat obrázek renderu",
"cmd.render_preflight": "Zkontrolovat požadavek na render",
@@ -635,7 +626,6 @@
"cmd.render_settings_apply": "Použít kameru renderu",
"cmd.render_settings_get": "Nastavení renderu",
"cmd.render_settings_set": "Uložit nastavení renderu",
"cmd.render_stage_backdrop": "Uložit fyzické pozadí renderu",
"cmd.render_start": "Renderovat obrázek",
"cmd.render_status": "Průběh renderu",
"cmd.select_clear": "Zrušit výběr",
@@ -999,16 +989,6 @@
"pattern.direction_1": "Směr 1",
"pattern.direction_2": "Směr 2",
"pattern.flip_direction": "Převrátit směr",
"perf.click_for_details": "Klikněte pro podrobnosti: graf časů snímků, histogram a co vykreslila GPU",
"perf.draw_calls": "Volání vykreslení",
"perf.line_segments": "Úsečky",
"perf.low_1": "1% minimum",
"perf.renderer": "Vykreslování",
"perf.scene_build": "Sestavení scény",
"perf.triangles": "Trojúhelníky",
"perf.ui_time": "Čas UI",
"perf.viewport": "Pohled",
"perf.worst": "nejhorší",
"pick a hole to seat it, or OK places it at the origin": "vyberte otvor pro usazení, nebo OK umístí do počátku",
"pick one or two vertices, edges or faces": "vyberte jeden nebo dva vrcholy, hrany či plochy",
"prefs.cpu": "CPU (softwarově)",
@@ -1021,43 +1001,15 @@
"recovery.min_ago": "před {count} min",
"recovery.never_saved": "nikdy neuloženo",
"render.background": "Pozadí scény",
"render.bloom_enable": "Záře ve výstupu",
"render.bloom_help": "Kreativní záře z lineárních jasů se třemi normalizovanými průchody. Poloměr v pixelech výstupu, přičtení před sRGB. Nejde o simulaci čočky, objemu ani zlepšení konvergence. Surové EXR se nemění.",
"render.bloom_opaque": "Záře vyžaduje neprůhledný výstup. Průhledné PNG se odmítne, neprovede se skryté sloučení.",
"render.bloom_radius": "Poloměr záře",
"render.bloom_strength": "Síla záře",
"render.bloom_threshold": "Lineární práh jasů",
"render.cpu_aa_help": "Prostorové vzorky: více vzorků zlepší hrany a prodlouží vykreslení.",
"render.cpu_key_rim": "Hlavní a obrysové",
"render.cpu_lighting": "Předvolba osvětlení",
"render.cpu_neutral": "Neutrální",
"render.cpu_opaque_required": "CPU trasování vyžaduje neprůhledné pozadí. Vypněte průhledné pozadí.",
"render.cpu_options_pending": "Panel podporuje vzorky hran, předvolby světel, stíny a expozici. Další volby jsou v API.",
"render.cpu_primary_samples": "Vzorky hran na pixel",
"render.cpu_shadows": "Vrhat stíny",
"render.cpu_summary": "Neprůhledný CPU PNG · světla · stíny · bez skla a textur",
"render.cpu_warm_cool": "Teplé a studené",
"render.exr_pending": "GPU může uchovat surové lineární EXR. Komprese, vícevrstvé obrazy a pomocné průchody zatím nejsou dostupné.",
"render.fit": "Přizpůsobit produktu",
"render.gpu_depth": "Maximální hloubka cesty",
"render.gpu_options_help": "Tyto volby platí pro tuto úlohu. Expozice vychází z uloženého nastavení.",
"render.gpu_path_failed": "Trasování cest GPU není dostupné",
"render.gpu_path_first_setup": "První příprava na této GPU může trvat několik minut; SolidCraft lze mezitím používat. Při dalších spuštěních se použijí zkompilované shadery, pokud je ovladač uchová.",
"render.gpu_path_preparing": "Trasování cest GPU: kompilace shaderů…",
"render.gpu_seed": "Náhodné semeno",
"render.gpu_unavailable": "Trasování cest GPU není na tomto zařízení připraveno.",
"render.image_advanced": "Pokročilé nastavení GPU",
"render.image_cancelled": "Render zrušen",
"render.image_cancelling": "Rušení",
"render.image_completed": "Obrázek připraven k exportu",
"render.image_encoding": "Kódování PNG",
"render.image_export": "Exportovat hotový obrázek",
"render.image_exposure": "Expozice",
"render.image_export": "Exportovat PNG",
"render.image_failed": "Render selhal",
"render.image_height": "Výška",
"render.image_help": "Náhledový obrázek z CPU · PNG · přibližná průhlednost",
"render.image_host_missing": "Není dostupný vykreslovací hostitel.",
"render.image_method": "Metoda obrazu",
"render.image_overwrite": "Nahradit existující soubor",
"render.image_preparing": "Příprava obrázku",
"render.image_progressive": "Progresivní render",
@@ -1065,7 +1017,6 @@
"render.image_reduce": "Zkuste menší obrázek nebo jednodušší scénu.",
"render.image_running": "Renderování obrázku",
"render.image_samples": "Vzorků na pixel",
"render.image_scene_stale": "Před kontrolou nebo spuštěním dokončete modelování nebo obnovte scénu.",
"render.image_stale": "Tento obrázek používá scénu zachycenou před pozdějšími změnami.",
"render.image_start": "Spustit render",
"render.image_supported": "Požadavek na render je podporován; fronta a paměť se znovu ověří při spuštění.",
@@ -1075,135 +1026,18 @@
"render.image_unknown": "Stav renderu není k dispozici",
"render.image_use_largest": "Použít největší velikost v tomto poměru stran:",
"render.image_width": "Šířka",
"render.material_absorption": "Absorpce R · G · B (na mm)",
"render.material_add": "Přidat nebo upravit těleso",
"render.material_assignments": "Přiřazení těles",
"render.material_binding_enable": "Zahrnout úpravy tohoto tělesa",
"render.material_box": "Osově zarovnaný kvádr",
"render.material_browse": "Vybrat obrázek…",
"render.material_certification": "Uzavřené sklo používá geometrickou vzdálenost. GPU příprava ověřuje topologii, orientaci, překryvy a optická rozhraní; kontrola jen přijímá požadavek.",
"render.material_choose_body": "Vybrat těleso scény",
"render.material_closed": "Uzavřená objemová síť",
"render.material_coat": "Čirý povlak na základním materiálu",
"render.material_coat_enable": "Zapnout čirý povlak",
"render.material_coat_help": "Ilustrativní dielektrický povlak na stávajícím neprůhledném vzhledu. Nulová váha ponechá základ beze změny.",
"render.material_color_map": "Obraz základní barvy (sRGB)",
"render.material_custom": "Vlastní ilustrativní optika",
"render.material_enable": "Použít tyto úpravy materiálů GPU",
"render.material_filter": "Filtrovat tělesa scény…",
"render.material_filter_help": "Zobrazuje se nejvýše 128 shod. U velkých scén zpřesněte filtr.",
"render.material_glass": "Sklo a optická absorpce",
"render.material_glass_enable": "Použít fyzikální sklo na těleso",
"render.material_glass_exclusive": "Tento renderer nepodporuje sklo s povlakem nebo mapami na témže tělese.",
"render.material_glass_help": "Původní ilustrativní recepty. Vzorky ukazují propustnost, ne render. Absorpce je −ln(lineární propustnosti) / referenční vzdálenost; alfa se nemění.",
"render.material_gpu_required": "Aktivní úpravy vyžadují GPU tracing. Před kontrolou nebo spuštěním vyberte GPU nebo úpravy vypněte.",
"render.material_ior": "Index lomu (IOR)",
"render.material_limits": "Nejvýše 128 nastavení těles, 32 oddělených skel, 64 povlaků a 128 vazeb textur. Shodné mapy sdílejí paměť; limity zařízení a celkové paměti zůstávají aktivní.",
"render.material_map_invalid": "Cesty map musí být omezené místní cesty; síla normál musí být konečná mezi 0 a 1.",
"render.material_map_required": "Před použitím map vyberte obraz barvy nebo normál.",
"render.material_maps": "Rovinné mapy barvy a normál",
"render.material_maps_enable": "Zapnout mapy neprůhledného tělesa",
"render.material_maps_help": "PNG/JPEG do 1024×1024; celkem 16 MiB kódovaných dat. U a V jsou kolmé vektory opakování ve světových mm (nejvýše 128 opakování). Barva je sRGB, normály OpenGL +Y. Soubory se zachytí při kontrole/spuštění.",
"render.material_normal_map": "Obraz normál (OpenGL, +Y)",
"render.material_normal_strength": "Síla normál (0–1)",
"render.material_origin": "Počátek mapování X · Y · Z (mm)",
"render.material_refresh": "Obnovit tělesa scény",
"render.material_refresh_needed": "Scéna se změnila. Před přidáním cíle obnovte seznam.",
"render.material_remove": "Odstranit přiřazení",
"render.material_repeat_u": "Jedno opakování U X · Y · Z (mm)",
"render.material_repeat_v": "Jedno opakování V X · Y · Z (mm)",
"render.material_revalidate": "Znovu ověřit těleso",
"render.material_revalidate_needed": "Přiřazení patří ke starší revizi scény. Před renderem znovu ověřte přesnou identitu tělesa.",
"render.material_roughness": "Drsnost povlaku (0,045–1)",
"render.material_shadow": "Propouštět přímé stínové paprsky",
"render.material_target_missing": "Pořadí a název tělesa již nesouhlasí. Odstraňte přiřazení a znovu výslovně vyberte těleso.",
"render.material_thickness": "Tloušťka vrstvy (mm)",
"render.material_thin": "Tenká vrstva",
"render.material_title": "Materiály GPU obrazu",
"render.material_transient": "Dočasné optické úpravy pro tuto kartu. Neukládají se do návrhu, nastavení renderu ani knihovny materiálů.",
"render.material_weight": "Váha povlaku (0–1)",
"render.method_cpu": "Trasování paprsků CPU",
"render.method_gpu": "Trasování cest GPU",
"render.method_raster": "CPU rastr",
"render.navigation_help": "Kamera se ovládá stejně jako v návrhu (Předvolby › Navigace). Uložit nastavení zaznamená aktuální kameru.",
"render.opaque_image_summary": "Náhledové PNG · neprůhledné PBR · smíšená průhlednost",
"render.opaque_preview_help": "Neprůhledné PBR a jednoduché barvy; smíšená průhlednost. Lom světla, HDRI a finální render zatím nejsou k dispozici.",
"render.output_format_status": "PNG je přesný dokončený obraz. Volitelné GPU EXR uchovává lineární FLOAT32 radianci před filtry. Export nikdy nenahrazuje výsledek náhledem nebo jinou úlohou.",
"render.perspective": "Perspektivní kamera",
"render.preview": "Integrovaný náhled",
"render.preview_help": "Rastrové stínování a smíšená průhlednost. Lom světla, HDRI a finální render zatím nejsou k dispozici.",
"render.progressive_image_summary": "Náhledové PNG · neprůhledné PBR · postupné vzorkování",
"render.reload_settings": "Znovu načíst uložené nastavení",
"render.restore_camera": "Obnovit uloženou kameru",
"render.result_actual_help": "Jeden pixel obrázku na jeden pixel obrazovky.",
"render.result_capture": "Zachycená revize",
"render.result_current": "Zachycená revize modelu je aktuální.",
"render.result_empty": "Zatím není hotový obrázek. Zkontrolujte nastavení a spusťte vykreslování.",
"render.result_fit": "Přizpůsobit obrázek",
"render.result_job": "Úloha",
"render.result_navigation": "Posun a přiblížení používají nastavení navigace. Mezerník + tažení posouvá.",
"render.result_open": "Otevřít hotový obrázek na hlavním plátně.",
"render.result_preview": "Živý náhled",
"render.result_retained": "Zobrazuje se poslední hotový obrázek; novější vykreslení jej zatím nenahradilo.",
"render.result_title": "Výsledek vykreslení",
"render.return_design": "Zpět k návrhu",
"render.ribbon_backgrounds": "Pozadí",
"render.ribbon_check": "Kontrola",
"render.ribbon_check_help": "Ověřit přesný aktuální návrh renderu. Bez skrytého přepnutí vykreslovače, zmenšení nebo nahrazení zdrojů.",
"render.ribbon_edit_lights": "Upravit světla",
"render.ribbon_environment_help": "HDR osvětlení, pozadí kamery a fyzické plochy jsou samostatné ovládací prvky s potvrzením nahrazení.",
"render.ribbon_finish_dialog": "Nejprve dokončete dialog souboru nebo potvrzení.",
"render.ribbon_frame": "Zarámovat",
"render.ribbon_hdr_library": "Knihovna HDR",
"render.ribbon_job_active": "Render již běží; před spuštěním dalšího jej výslovně zrušte.",
"render.ribbon_light_rigs": "Sestavy světel",
"render.ribbon_lights_help": "Zkontrolovat světelné sestavy nebo upravit podporovaná světla; aktuální nastavení nahradí až výslovné použití.",
"render.ribbon_materials": "Materiály",
"render.ribbon_materials_help": "Otevřít inspektor vzhledu a původní povrchy. Optické úpravy nemění skrytě technické vlastnosti.",
"render.ribbon_no_active_job": "Není aktivní render ke zrušení.",
"render.ribbon_no_result": "Před exportem dokončete render.",
"render.ribbon_no_saved_camera": "Před obnovením kamery uložte záběr.",
"render.ribbon_saved_camera": "Uložená kamera",
"render.ribbon_shots": "Záběry",
"render.ribbon_studios": "Studia",
"render.ribbon_studios_help": "Procházejte dostupná studia a před použitím zkontrolujte nahrazované prvky. Koncepty zůstávají označené.",
"render.ribbon_surface_maps": "Mapy povrchu",
"render.save_setup": "Uložit nastavení",
"render.setup_name": "Název nastavení",
"render.studio_add_area": "Přidat plošné světlo",
"render.studio_ambient": "Konstantní zář prostředí",
"render.studio_aperture": "Poloměr clony",
"render.studio_area": "Plošné světlo",
"render.studio_area_help": "Nejvýše čtyři jednostranné obdélníky. Kolmé poloviční hrany U a V určují velikost; U × V míří na osvětlenou stranu. Souřadnice jsou světové milimetry.",
"render.studio_center": "Střed X, Y, Z (mm)",
"render.studio_denoise": "Potlačit šum v hotovém obrazu",
"render.studio_denoise_help": "Závěrečný filtr respektuje hrany; může změkčit detaily. Sklo a ostré zrcadlové povrchy se nefiltrují.",
"render.studio_direction": "Směr ke slunci (X, Y, Z)",
"render.studio_environment": "Prostředí",
"render.studio_environment_bounds": "Intenzita prostředí musí být 0–100 a otočení −360° až 360°.",
"render.studio_environment_none": "Bez mapy prostředí",
"render.studio_focus": "Osová zaostřovací vzdálenost",
"render.studio_half_u": "Poloviční hrana U (mm)",
"render.studio_half_v": "Poloviční hrana V (mm)",
"render.studio_hdr": "Soubor Radiance HDR",
"render.studio_hdr_browse": "Vybrat HDR…",
"render.studio_hdr_help": "Radiance .hdr, panorama 2:1, nejvýše 2048 × 1024. EXR není dostupné. Předpoklad barev platí jen při neurčených primárních barvách.",
"render.studio_hdr_required": "Vyberte místní soubor .hdr.",
"render.studio_hdr_srgb": "Neurčené primární barvy chápat jako lineární sRGB",
"render.studio_intensity": "Intenzita prostředí",
"render.studio_lens": "Hloubka ostrosti",
"render.studio_lens_perspective": "Hloubka ostrosti vyžaduje perspektivní kameru.",
"render.studio_lighting": "Studiové osvětlení",
"render.studio_lighting_enable": "Použít vlastní studiové osvětlení",
"render.studio_radiance": "Lineární RGB zář",
"render.studio_remove_area": "Odebrat plošné světlo",
"render.studio_rotation": "Otočení prostředí",
"render.studio_runtime_help": "Optické úpravy těles jsou v Materiálech GPU obrazu. Ovládání studia a materiálů je dočasné a nemění uložené vzhledy.",
"render.studio_sky": "Umělecká obloha",
"render.studio_sky_help": "Původní přechod oblohy pro osvětlení; nejde o kalibrovaný model atmosféry.",
"render.studio_sun": "Slunce",
"render.studio_sun_radius": "Úhlový poloměr",
"render.studio_tone_map": "Mapování tónů",
"render.unavailable_preview": "Toto nastavení vyžaduje nedostupný profil náhledu.",
"render.unsaved": "Neuložené změny náhledu",
"render.use_preview": "Použít integrovaný náhled",
@@ -1233,7 +1067,6 @@
"search.try": "Zadejte název příkazu, prvek v tomto návrhu nebo parametr. Chcete-li spustit příkaz s hodnotami, zadejte jeho id a objekt JSON: {example}",
"search.valid": "Platné",
"settings.display_quality_note": "Jemnější úrovně kreslí zakřivené plochy a hrany hladčeji, ale síťování trvá déle. Mění se jen zobrazení, nikdy návrh ani exporty.",
"settings.frame_rate_details_note": "Přidá graf časů posledních snímků s vykreslenými trojúhelníky, histogram časů snímků a to, co vykreslila GPU. Kliknutím na snímkovou frekvenci v pohledu je zobrazíte nebo skryjete.",
"settings.frame_rate_note": "Zobrazuje snímky za sekundu v pravém dolním rohu pohledu.",
"settings.measure_axis_distances": "Zobrazit vzdálenosti X/Y/Z",
"settings.measure_axis_distances_help": "Při otevřeném měření zobrazit kromě celkové vzdálenosti i složky ve směru os.",
@@ -1290,10 +1123,6 @@
"toolbar.customize_note": "Karta {tab}. Zaškrtnutím příkazu přidáte jeho tlačítko na panel nástrojů. Můžete také přetáhnout řádek z rozbalovací nabídky panelu na panel nástrojů, odstranit tlačítko přetažením mimo panel nástrojů nebo použít připínáček na řádku rozbalovací nabídky.",
"toolbar.image": "OBRÁZEK",
"toolbar.render": "RENDER",
"toolbar.render_appearance": "VZHLED",
"toolbar.render_camera": "KAMERA",
"toolbar.render_environment": "PROSTŘEDÍ",
"toolbar.render_lighting": "OSVĚTLENÍ",
"toolbar.render_scene": "SCÉNA",
"trace.points": "Bodů podél cesty: {count}",
"trace.stopped_by_contact": "Končí ve vzorku {sample}: {a} by prošlo skrz {b}",
@@ -1583,6 +1412,7 @@
"ui.ai_permissions": "Oprávnění",
"ui.ai_preset": "Předvolba",
"ui.ai_remove": "Odebrat",
"ui.ai_request_limit": "zastaveno po 24 požadavcích na jednu zprávu; pro pokračování pošlete další zprávu",
"ui.ai_reset": "Obnovit",
"ui.ai_settings": "Nastavení AI…",
"ui.ai_show_features": "Zobrazit funkce AI",
@@ -1759,8 +1589,6 @@
"ui.filter_commands_or_keys": "Filtrovat příkazy nebo klávesy",
"ui.findInTimeline": "Najít na časové ose",
"ui.findInWindow": "Najít v okně",
"ui.fine": "Jemná",
"ui.finest": "Nejjemnější",
"ui.finish_sketch": "Dokončit náčrt",
"ui.fit": "Přizpůsobit",
"ui.fix_unfix": "Zafixovat/uvolnit",
@@ -1768,7 +1596,6 @@
"ui.flip": "Převrátit",
"ui.format": "Formát",
"ui.frame_rate": "Snímková frekvence",
"ui.frame_rate_details": "Podrobnosti snímkové frekvence",
"ui.front": "Zepředu",
"ui.fully_constrained": "Plně zavazbeno",
"ui.function": "Funkce",
@@ -1857,8 +1684,6 @@
"ui.hideOrigin": "Skrýt počátek",
"ui.hidePlanes": "Skrýt roviny",
"ui.hideProfile": "Skrýt profil",
"ui.hide_origin_by_default": "Ve výchozím stavu skrýt počátek",
"ui.hide_origin_note": "Nové návrhy začínají se skrytou složkou Počátek. Návrh si ponechá vlastní nastavení počátku, jakmile ho zobrazíte nebo skryjete.",
"ui.history_back": "O krok zpět",
"ui.history_below_browser": "Pod prohlížečem",
"ui.history_first": "Přejít na začátek",
@@ -1988,7 +1813,6 @@
"ui.project_face_edges_sketch": "Promítat hrany plochy do nových náčrtů",
"ui.properties": "Vlastnosti",
"ui.pull_direction": "Směr vyjímání",
"ui.quality": "Kvalita",
"ui.quantity": "Počet",
"ui.quantity_2": "Počet 2",
"ui.quit": "Ukončit",
@@ -2083,7 +1907,6 @@
"ui.spline": "Splajn",
"ui.split_body": "Rozdělit těleso",
"ui.stand_up_tip": "Když se soubor STEP nebo IGES otevře s osou Z nahoru, nabídnout otočení o čtvrt otáčky kolem X",
"ui.standard": "Standardní",
"ui.start": "Start",
"ui.start_over": "Začít znovu",
"ui.start_page": "Úvodní stránka",
+7 -396
View File
@@ -5,9 +5,7 @@
"2. Attach your screenshot: copy and paste it, or save and upload the PNG.": "2. Vedhæft dit skærmbillede: Kopiér og indsæt det, eller gem og upload PNG-filen.",
"2D": "2D",
"3. Click Submit new issue on GitHub to send your report.": "3. Klik på Submit new issue på GitHub for at sende rapporten.",
"A Single hole snaps to the middle of its face and to the centres of its round edges": "Et enkelt hul snapper til fladens midtpunkt og centrene af dens cirkulære kanter",
"A new configuration, a copy of the active one": "Opret en ny konfiguration som en kopi af den aktive",
"A placed Single hole has a handle to drag it, and picking its face again moves the hole there instead of taking it out": "Et placeret enkelt hul har et håndtag til at trække i. Hvis fladen vælges igen, flyttes hullet dertil i stedet for at blive fjernet",
"ARRANGE": "ARRANGER",
"About SolidCraft": "Om SolidCraft",
"Acceleration": "Acceleration",
@@ -16,31 +14,23 @@
"Active": "Aktiv",
"Active rule": "Aktiv regel",
"Add": "Tilføj",
"Add Column": "Tilføj kolonne",
"Add Key": "Tilføj animationspunkt",
"Add Mid Radius": "Tilføj mellemradius",
"Add Row": "Tilføj række",
"Add a tool": "Tilføj et værktøj",
"Add condition": "Tilføj betingelse",
"Add or edit rule": "Tilføj eller redigér regel",
"Add to New Folder": "Føj til ny mappe",
"Advanced": "Avanceret",
"Against": "Mod",
"Alignment": "Justering",
"All Lines": "Alle linjer",
"All Sketch Points": "Alle skitsepunkter",
"All components": "Alle komponenter",
"All open designs": "Alle åbne designs",
"Allowed expressions, separated by semicolons": "Tilladte udtryk adskilt af semikolon",
"Also deletes": "Sletter også",
"Anchor": "Forankring",
"Angle 2": "Vinkel 2",
"Animate": "Animér",
"App/system information and command settings are included. Review private details before sharing.": "Oplysninger om programmet, systemet og kommandoindstillingerne er inkluderet. Gennemgå private oplysninger, før du deler rapporten.",
"Appearance name": "Navn på udseende",
"Applied Radius": "Anvendt radius",
"Apply To": "Anvend på",
"Apply library appearance": "Anvend udseende fra biblioteket",
"Apply to selection": "Anvend på markering",
"Apply your appearance first. Exports assigned appearances and materials created in this design.": "Anvend først dit udseende. Eksporterer tildelte udseender og materialer oprettet i dette design.",
"Arc length": "Buelængde",
@@ -51,22 +41,13 @@
"BLEND SURFACE": "OVERGANGSFLADE",
"BOUNDARY SURFACE": "AFGRÆNSET OVERFLADE",
"BROWSER": "MODELTRÆ",
"Bar": "Stang",
"Bend Edges": "Bøjningskanter",
"Bend Line Position": "Bukkelinjens placering",
"Bend Position": "Bukningens placering",
"Bend Radius": "Bukkeradius",
"Bend Table": "Bukketabel",
"Bends": "Bukninger",
"Bodies": "Emner",
"Bodies (optional)": "Legemer (valgfrit)",
"Body": "Emne",
"Body (last if none)": "Legeme (det sidste, hvis intet er valgt)",
"Body to split": "Legeme, der skal opdeles",
"Body, or faces to take out": "Legeme eller flader, der skal fjernes",
"Bottom Left": "Nederst til venstre",
"Bottom Middle": "Nederst i midten",
"Bottom Right": "Nederst til højre",
"Boundary": "Afgrænsning",
"Bounding box": "Omsluttende boks",
"CHECK": "KONTROLLÉR",
@@ -78,45 +59,31 @@
"CSV or JSON: updates existing parameters, adds new ones": "CSV eller JSON: Opdaterer eksisterende parametre og tilføjer nye",
"Capture All Positions": "Registrér alle positioner",
"Capture Pos.": "Registrér pos.",
"Catch Depth": "Låsedybde",
"Catch Direction": "Indgrebsretning",
"Catch Length": "Låselængde",
"Cells": "Celler",
"Center X": "Centrum X",
"Center Y": "Centrum Y",
"Center of mass": "Massecentrum",
"Centre to centre": "Centrum til centrum",
"Centroid": "Tyngdepunkt",
"Chamfer Type": "Affasningstype",
"Channel": "U-profil",
"Check collisions": "Kontrollér kollisioner",
"Checked": "Kontrolleret",
"Choose Variable radius or Spline in Type.": "Vælg variabel radius eller spline under Type.",
"Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.": "Vælg et nyt plan. Skitsegeometri og mål bevarer deres todimensionelle koordinater.",
"Choose…": "Vælg…",
"Circle center": "Cirklens centrum",
"Circular": "Cirkulær",
"Circumference": "Omkreds",
"Clockwise": "Med uret",
"Close draft": "Luk kladde",
"Collapse": "Fold sammen",
"Collisions": "Kollisioner",
"Colour": "Farve",
"Command": "Kommando",
"Comment": "Kommentar",
"Component 1": "Komponent 1",
"Component 2": "Komponent 2",
"Component1": "Komponent1",
"Components": "Komponenter",
"Config Table": "Konfig.tabel",
"Configuration": "Konfiguration",
"Configuration rules": "Konfigurationsregler",
"Contact": "Kontakt",
"Contact Sets": "Kontaktsæt",
"Contact sets": "Kontaktsæt",
"Continue on GitHub": "Fortsæt på GitHub",
"Continuity": "Kontinuitet",
"Control Radius": "Styreradius",
"Copy Error Message": "Kopiér fejlmeddelelse",
"Copy Warning Message": "Kopiér advarselsmeddelelse",
"Copy all licences": "Kopiér alle licenser",
@@ -125,14 +92,9 @@
"Copy screenshot": "Kopiér skærmbillede",
"Corner Relief": "Hjørneaflastning",
"Corner Relief Shape": "Hjørneaflastningens form",
"Corner radius": "Hjørneradius",
"Counterbore depth": "Dybde af cylindrisk forsænkning",
"Counterbore Ø": "Cylindrisk forsænkning Ø",
"Countersink angle": "Vinkel på konisk forsænkning",
"Countersink Ø": "Konisk forsænkning Ø",
"Create Hole": "Opret hul",
"Create Issue — review draft": "Opret issue — gennemgå kladde",
"Create Issue…": "Opret fejlrapport…",
"Cross curves": "Tværkurver",
"Cube": "Terning",
"Current orthographic view (1:1 mm)": "Aktuel visning uden perspektiv (1:1 mm)",
@@ -140,33 +102,22 @@
"Curv. Comb": "Krumningskam",
"Curvature (G2)": "Krumningskontinuitet (G2)",
"Curvature change (G3)": "Ændring i krumning (G3)",
"Custom": "Brugerdefineret",
"Customize Toolbar": "Tilpas værktøjslinje",
"Customize…": "Tilpas…",
"Cylindrical face": "Cylindrisk flade",
"Delete": "Slet",
"Delete Selected Features…": "Slet valgte features…",
"Delete Selected Folders": "Slet valgte mapper",
"Delete all": "Slet alle",
"Delete rule": "Slet regel",
"Delete the row": "Slet rækken",
"Delete the set": "Slet sættet",
"Density": "Densitet",
"Dependents": "Afhængige",
"Describe the problem you saw": "Beskriv problemet, du oplevede",
"Describe what happened before continuing.": "Beskriv, hvad der skete, før du fortsætter.",
"Designs you open or save appear here.": "Designs, du åbner eller gemmer, vises her.",
"Determined by To Object. Choose Angle to edit the angular extent.": "Bestemmes af Til objekt. Vælg Vinkel for at ændre vinkeludstrækningen.",
"Diameter": "Diameter",
"Direction": "Retning",
"Discard": "Kassér",
"Distance": "Afstand",
"Distance 1": "Afstand 1",
"Do not export hidden edges": "Eksportér ikke skjulte kanter",
"Don't ask again": "Spørg ikke igen",
"Double-click to rename": "Dobbeltklik for at omdøbe",
"Double-clicking the middle button in the view: Home (the home view, framing what is shown), Fit (frame what is shown, keeping the orientation) or nothing": "Dobbeltklik på den midterste museknap i visningen: Hjem (hjemmevisningen, der indrammer det viste), Tilpas (indrammer det viste og bevarer orienteringen) eller ingen handling",
"Drag to move the hole; click to remove it": "Træk for at flytte hullet; klik for at fjerne det",
"Drive Joint": "Bevæg led",
"Duration": "Varighed",
"Edges": "Kanter",
@@ -174,113 +125,80 @@
"Edit Profile Sketch": "Redigér profilskitse",
"Enable rule": "Aktivér regel",
"Enabled": "Aktiveret",
"End Radius": "Slutradius",
"End bulge": "Udbuling ved slutningen",
"End continuity": "Overgang ved slutningen",
"Expand": "Fold ud",
"Expected result (optional)": "Forventet resultat (valgfrit)",
"Explode": "Adskil",
"Explode Text": "Opløs tekst",
"Export Animation…": "Eksportér animation…",
"Export Bend Table…": "Eksportér bøjningstabel…",
"Export CSV…": "Eksportér CSV…",
"Export Current View as DXF": "Eksportér aktuel visning som DXF",
"Export Current View as DXF…": "Eksportér aktuel visning som DXF…",
"Export DXF": "Eksportér DXF",
"Export DXF…": "Eksportér DXF…",
"Export JSON…": "Eksportér JSON…",
"Export design library...": "Eksportér designbibliotek...",
"Export hidden edges as dashed lines": "Eksportér skjulte kanter som stiplede linjer",
"Exported by SolidWorks, which usually saves Y up": "Eksporteret af SolidWorks, som normalt gemmer med Y opad",
"Expression": "Udtryk",
"Extend to meet": "Forlæng, til de mødes",
"Extents": "Udstrækning",
"FILES": "FILER",
"FLAT": "FLAD",
"Face": "Flade",
"Face Appearance": "Fladens udseende",
"Face attachment lost": "Forbindelsen til fladen er mistet",
"Face center": "Fladens midtpunkt",
"Faces": "Flader",
"Faces / edges / vertices": "Flader / kanter / hjørnepunkter",
"Faces to split": "Flader, der skal opdeles",
"Fastener": "Befæstigelse",
"Favorites": "Favoritter",
"Favourite": "Favorit",
"Feature": "Feature",
"Files from many CAD programs have Y up, so they open lying on their back here (Z up).": "Filer fra mange CAD-programmer har Y opad og åbnes derfor liggende på ryggen her (Z opad).",
"Filter": "Filter",
"Filter…": "Filtrér…",
"Find in Browser": "Find i modeltræ",
"Finish": "Afslut",
"First plane": "Første plan",
"Fit and G2 corners require Constant radius.": "Tilpasning og G2-hjørner kræver konstant radius.",
"Fixed Face": "Fast flade",
"Flange thickness": "Flangetykkelse",
"Flat Pattern": "Flad afvikling",
"Flat size": "Bredde mellem flader",
"Follow camera (Look At sketches)": "Følg kamera (Se på skitser)",
"Follow the camera (Look At sketches)": "Følg kameraet (Se på skitser)",
"Font": "Skrifttype",
"From": "Fra",
"From Bodies": "Fra emner",
"From edges": "Fra kanter",
"Full Length": "Fuld længde",
"G2 corners require a constant radius and Exact fit. G2 corner construction is not supported yet.": "G2-hjørner kræver konstant radius og præcis tilpasning. Konstruktion af G2-hjørner understøttes endnu ikke.",
"Gap": "Spalte",
"Grid": "Gitter",
"HISTORY": "HISTORIK",
"Height": "Højde",
"Hidden edges": "Skjulte kanter",
"History layout": "Historiklayout",
"History names": "Navne i historikken",
"Hole (optional)": "Hul (valgfrit)",
"Holes": "Huller",
"Holes (edges on them)": "Huller (kanterne omkring dem)",
"Home view": "Hjemmevisning",
"Horiz./Vert.": "Vandr./Lodr.",
"I-Beam": "I-profil",
"IMAGES": "BILLEDER",
"Import…": "Importér…",
"In This Design": "I dette design",
"In place (surfaces)": "På stedet (overflader)",
"In plane": "I planet",
"Inside": "Indvendigt",
"Instant": "Øjeblikkelig",
"Interface font": "Skrifttype i brugergrænsefladen",
"Interferes": "Overlapper",
"Invert": "Invertér",
"Isocurve": "Isokurve",
"Issue title": "Titel på issue",
"Items": "Elementer",
"JOINTS": "LED",
"Joint Origin1": "Lednulpunkt1",
"Keep (points on it)": "Behold (punkter på fladen)",
"Keep Tools": "Behold værktøjsemner",
"Keep of the other surface": "Behold fra den anden overflade",
"Key a joint's value at this step": "Indsæt en nøgle for leddets værdi på dette trin",
"Keys": "Nøgler",
"L-Angle": "L-profil",
"LIBRARY": "BIBLIOTEK",
"LINKS": "LINKS",
"Later": "Senere",
"Leave it": "Lad det være",
"Left Middle": "Til venstre i midten",
"Legacy face attachment. Rebinding to a certified face creates an exact attachment.": "Ældre fladetilknytning. Ny tilknytning til en certificeret flade giver en præcis tilknytning.",
"Length": "Længde",
"Length units": "Længdeenheder",
"Letter Spacing": "Tegnafstand",
"Library": "Bibliotek",
"Library file": "Biblioteksfil",
"Limits": "Grænser",
"Line Spacing": "Linjeafstand",
"Line Type": "Linjetype",
"Lines": "Linjer",
"Listing the installed fonts…": "Henter listen over installerede skrifttyper…",
"Load library": "Indlæs bibliotek",
"Local library": "Lokalt bibliotek",
"Loop": "Løkke",
"M5 or M5x0.5": "M5 eller M5x0.5",
"MANAGE": "ADMINISTRÉR",
"MATERIALS": "MATERIALER",
"MODEL": "MODEL",
@@ -291,25 +209,18 @@
"Matched restrictions check the final values.": "Matchede begrænsninger kontrollerer de endelige værdier.",
"Material": "Materiale",
"Max": "Maks.",
"Maximum": "Maksimum",
"Mesh body": "Meshlegeme",
"Mid Radius": "Mellemradius",
"Middle double-click": "Dobbeltklik med midterste museknap",
"Min": "Min.",
"Minimum": "Minimum",
"Mirror Plane": "Spejlingsplan",
"Mitre": "Gering",
"Model Parameters": "Modelparametre",
"Modeled": "Modelleret",
"More toolbar panels": "Flere værktøjspaneler",
"Motion": "Bevægelse",
"Mouse gestures": "Musebevægelser",
"Mouse gestures only work with the SolidWorks mouse mode (the wheel orbits, Ctrl + wheel pans).": "Musebevægelser virker kun med SolidWorks-musetilstanden (musehjulet roterer visningen, Ctrl + musehjul panorerer).",
"Move / Copy": "Flyt / Kopiér",
"Move Type": "Flytningstype",
"Move down": "Flyt ned",
"Move earlier": "Flyt tidligere",
"Move holes by clicking or dragging": "Flyt huller ved at klikke eller trække",
"Move later": "Flyt senere",
"Move up": "Flyt op",
"Name": "Navn",
@@ -323,25 +234,17 @@
"No matching appearance presets.": "Ingen matchende udseendeforudindstillinger.",
"No matching appearances": "Ingen matchende udseender",
"No visible bodies or sketches to export.": "Der er ingen synlige emner eller skitser at eksportere.",
"Nominal diameter": "Nominel diameter",
"None": "Ingen",
"Not available yet": "Endnu ikke tilgængelig",
"Notch": "Udskæring",
"OUTPUT": "OUTPUT",
"Of": "Af",
"On your computer": "På din computer",
"Opacity": "Opacitet",
"Open GitHub draft": "Åbn GitHub-kladde",
"Open library...": "Åbn bibliotek...",
"Open local libraries in the desktop app. You can export this design's library here.": "Åbn lokale biblioteker i desktopappen. Du kan eksportere dette designs bibliotek her.",
"Open-source parametric 3D CAD in Rust. Licensed MIT OR Apache-2.0.": "Parametrisk 3D-CAD i Rust med åben kildekode. Licenseret under MIT OR Apache-2.0.",
"Opens your browser and copies the report text for you to paste into GitHub.": "Åbner din browser og kopierer rapportteksten, så du kan indsætte den på GitHub.",
"Opens your browser. Review and submit your report on GitHub.": "Åbner din browser. Gennemgå og indsend rapporten på GitHub.",
"Operator": "Operator",
"Orbit (right drag)": "Rotér visningen (træk med højre museknap)",
"Other side": "Anden side",
"Other surface (mutual trim)": "Anden overflade (trim begge)",
"Outer radius": "Udvendig radius",
"Outside": "Udvendigt",
"Overall length": "Samlet længde",
"Param. Graph": "Param.graf",
@@ -354,11 +257,8 @@
"Pick the tool bodies": "Vælg værktøjsemnerne",
"Picked": "Valgt",
"Picked point": "Valgt punkt",
"Pitch": "Stigning",
"Pivot": "Drejepunkt",
"Place at the origin instead": "Placér ved nulpunktet i stedet",
"Plastic Rules": "Plastregler",
"Play the joint's motion in the view": "Afspil leddets bevægelse i visningen",
"Playback": "Afspilning",
"Plot": "Diagram",
"Position": "Position",
@@ -366,29 +266,22 @@
"Position (G0)": "Positionskontinuitet (G0)",
"Position along the edge, 0 at the start end, 1 at the other": "Position langs kanten: 0 ved den ene ende, 1 ved den anden",
"Preferences › Design turns the offer back on": "Indstillinger › Design slår tilbuddet til igen",
"Preview": "Forhåndsvisning",
"Preview Row": "Forhåndsvis række",
"Preview leaves the current design unchanged.": "Forhåndsvisning ændrer ikke det aktuelle design.",
"Profile / Edges": "Profil / kanter",
"Profiles or curves": "Profiler eller kurver",
"Profiles/Curves": "Profiler/kurver",
"Quantity": "Antal",
"RECENT": "SENESTE",
"REFERENCE": "REFERENCE",
"RULED SURFACE": "REGELFLADE",
"RULES": "REGLER",
"Radius": "Radius",
"Radius Fit": "Tilpasning af radius",
"Ratio": "Forhold",
"Reattach selected face": "Tilknyt den valgte flade igen",
"Recover": "Gendan",
"Recover unsaved design?": "Gendan design, der ikke er gemt?",
"Rectangular": "Rektangulær",
"Rectangular Tube": "Rektangulært hulprofil",
"Reference face": "Referenceflade",
"Remove Tools": "Fjern værktøjsemner",
"Remove condition": "Fjern betingelse",
"Remove from Folder": "Fjern fra mappe",
"Remove from list": "Fjern fra liste",
"Remove override": "Fjern tilsidesættelse",
"Remove private design details or paths before sharing.": "Fjern private designoplysninger eller filstier, før du deler.",
@@ -396,18 +289,12 @@
"Remove the column": "Fjern kolonnen",
"Remove the track": "Fjern sporet",
"Remove this point": "Fjern dette punkt",
"Rename Feature": "Omdøb feature",
"Rename Folder": "Omdøb mappe",
"Replace existing library file": "Erstat eksisterende biblioteksfil",
"Report a bug — review a screenshot and open a GitHub draft": "Rapportér en fejl — gennemgå et skærmbillede, og åbn en GitHub-kladde",
"Report text copied. Paste it into GitHub and submit your report there.": "Rapportteksten er kopieret. Indsæt den på GitHub, og indsend rapporten der.",
"Reports are public. A GitHub account is required.": "Rapporter er offentlige. Du skal have en GitHub-konto.",
"Reset Toolbar": "Nulstil værktøjslinje",
"Resize Part": "Ændr størrelse på del",
"Restore View": "Gendan visning",
"Restore outer boundary": "Gendan ydre afgrænsning",
"Restore the inherited component or physical material appearance": "Gendan udseendet fra komponenten eller det fysiske materiale",
"Restore this studio's tools": "Gendan værktøjerne i dette arbejdsområde",
"Result": "Resultat",
"Retake screenshot": "Tag skærmbillede igen",
"Reverse": "Vend om",
@@ -417,20 +304,9 @@
"Revert Pos.": "Fortryd pos.",
"Revert Position": "Tilbagefør position",
"Review the report and screenshot before opening GitHub.": "Gennemgå rapport og skærmbillede, før du åbner GitHub.",
"Review your report on GitHub and click Submit new issue to publish it.": "Gennemgå rapporten på GitHub, og klik på Submit new issue for at offentliggøre den.",
"Rib Length": "Ribbelængde",
"Rib Offset": "Ribbeforskydning",
"Rib Thickness": "Ribbetykkelse",
"Ribs": "Ribber",
"Right Middle": "Til højre i midten",
"Rim Edge": "Randkant",
"Ring of tools on the right button": "Værktøjsring på højre museknap",
"Roll Back Here": "Rul tilbage hertil",
"Roll History Marker Here": "Flyt tilbagerulningsmarkør hertil",
"Root radius": "Rodradius",
"Round": "Rund",
"Round Bar": "Rundstang",
"Round Tube": "Rundt hulprofil",
"Rule": "Regel",
"Rule name": "Regelnavn",
"Rules run from top to bottom. Matched drives change effective values.": "Regler køres oppefra og ned. Matchede drivere ændrer de effektive værdier.",
@@ -438,35 +314,25 @@
"SURFACE QUALITY": "OVERFLADEKVALITET",
"Save Rule": "Gem regel",
"Save View": "Gem visning",
"Save screenshot…": "Gem skærmbillede…",
"Save the study as an animated GIF, seen as the viewport shows it": "Gem undersøgelsen som en animeret GIF med samme visning som i modelvinduet",
"Saved preview. Geometry is being checked before editing.": "Gemt forhåndsvisning. Geometrien kontrolleres før redigering.",
"Scale factor": "Skaleringsfaktor",
"Scene": "Scene",
"Screenshot (optional)": "Skærmbillede (valgfrit)",
"Search": "Søg",
"Search commands": "Søg efter kommandoer",
"Search this library": "Søg i dette bibliotek",
"Second plane": "Andet plan",
"Section": "Snit",
"Section (optional)": "Tværsnit (valgfrit)",
"Section position": "Profilets placering",
"Sections (one sketch each)": "Tværsnit (én skitse til hvert)",
"Select Other": "Vælg andet",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Vælg andet: Hold venstre museknap nede uden at flytte markøren på modellen (eller højreklik › Vælg andet…) for at vise alt under markøren. Klik igen samme sted for at vælge næste objekt. Dobbeltklik på den midterste museknap for at gå til Hjem (Indstillinger › Navigation).",
"Select bodies (a face picks its body)": "Vælg emner (en flade vælger sit emne)",
"Select bodies or faces instead": "Vælg emner eller flader i stedet",
"Select edges": "Vælg kanter",
"Selection filter": "Markeringsfilter",
"Semantic faces share their appearance across instances.": "Semantiske flader deler udseende på tværs af instanser.",
"Send your report on GitHub": "Send din rapport på GitHub",
"Shaded": "Skyggelagt",
"Shape": "Form",
"Sheet Rules": "Pladeregler",
"Show Agent Cursor": "Vis agentmarkør",
"Show in folder": "Vis i mappe",
"Show the pointer an AI agent moves (#35)": "Vis markøren, som en AI-agent bevæger (#35)",
"Show tool at cursor": "Vis værktøj ved markøren",
"Signed axes": "Akser med fortegn",
"Size": "Størrelse",
"Sketch constraints": "Skitsebindinger",
@@ -476,41 +342,27 @@
"Sketch plane has no body-face attachment.": "Skitseplanet er ikke tilknyttet en flade på et emne.",
"Sketch points": "Skitsepunkter",
"Slot width": "Slidsbredde",
"Snap to face and edge centres": "Snap til flade- og kantcentre",
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft lukkede, før disse designs blev gemt. Den seneste automatiske lagring kan åbnes.",
"Specialise": "Specialisér",
"Speed": "Hastighed",
"Splitting tool": "Opdelingsværktøj",
"Square": "Kvadrat",
"Stand it up": "Stil op",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Stil op: En kvart omdrejning om X som én flyttefeature, der kan fortrydes.",
"Stand this part up?": "Stil dette emne op?",
"Start angle": "Startvinkel",
"Start bulge": "Udbuling ved starten",
"Start continuity": "Overgang ved starten",
"Step": "Trin",
"Steps": "Trin",
"Steps to reproduce (optional)": "Trin til at genskabe problemet (valgfrit)",
"Stitch the kept pieces": "Sammenføj de bevarede dele",
"Stitch to both bodies": "Sammenføj med begge emner",
"Studio": "Arbejdsområde",
"Study": "Undersøgelse",
"Style": "Stil",
"Summary": "Resumé",
"Suppress": "Undertryk",
"Suppressed": "Undertrykt",
"Surface": "Overflade",
"Surface, or faces to turn over": "Overflade eller flader, der skal vendes",
"Swap Ends": "Ombyt ender",
"TRIM": "TRIM",
"Tangent (G1)": "Tangentkontinuitet (G1)",
"Tangent Chain": "Tangentkæde",
"Tangent chain": "Tangentkæde",
"Tap drill diameter": "Diameter på gevindbor",
"Target": "Mål",
"Target Body": "Mållegeme",
"Tear": "Rivning",
"Technical details": "Tekniske detaljer",
"The dashed sketch is a saved plane guide. Select a new face to repair the attachment.": "Den stiplede skitse er en gemt planreference. Vælg en ny flade for at reparere tilknytningen.",
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "Den gemte oprindelige komponentforekomst mangler. Vælg en gyldig oprindelig placering før ny tilknytning.",
"Then": "Så",
@@ -522,36 +374,24 @@
"This child is shared. Deleting it removes it from every copy of its parent.": "Dette underobjekt er delt. Sletning fjerner det fra alle kopier af dets overordnede objekt.",
"This child is shared. Position changes apply to every copy of its parent.": "Dette underobjekt er delt. Positionsændringer gælder for alle kopier af dets overordnede objekt.",
"This face cannot provide a durable attachment. Choose another face or explicitly use a fixed plane.": "Denne flade kan ikke give en varig tilknytning. Vælg en anden flade, eller vælg udtrykkeligt et fast plan.",
"This image is not attached to GitHub yet. Copy or save it, then attach it on GitHub.": "Dette billede er endnu ikke vedhæftet på GitHub. Kopiér eller gem det, og vedhæft det derefter på GitHub.",
"To": "Til",
"To Offset": "Til forskydning",
"To edges": "Til kanter",
"Toe radius": "Tåradius",
"Tolerance": "Tolerance",
"Too close": "For tæt på",
"Tool Bodies": "Værktøjslegemer",
"Tools": "Værktøjer",
"Tools, clockwise from the top": "Værktøjer, med uret fra toppen",
"Top Left": "Øverst til venstre",
"Top Middle": "Øverst i midten",
"Top Right": "Øverst til højre",
"Trimming tool (plane, face or curves)": "Trim med plan, flade eller kurver",
"Turn off section analysis before exporting the view.": "Slå snitanalyse fra, før visningen eksporteres.",
"Type": "Type",
"UNTRIM": "FJERN BESKÆRING",
"Unit": "Enhed",
"Unsuppress": "Ophæv undertrykkelse",
"Updating the preview": "Opdaterer forhåndsvisningen",
"Use active author placement": "Brug den aktive komponents placering",
"Use fixed plane": "Brug fast plan",
"Use saved fixed plane": "Brug gemt fast plan",
"Use selected face as fixed plane": "Brug valgt flade som fast plan",
"Use the SolidWorks mouse mode": "Brug SolidWorks-musetilstanden",
"User Parameters": "Brugerparametre",
"VIEW": "VISNING",
"VIEWS": "VISNINGER",
"Value": "Værdi",
"Variable Radius": "Variabel radius",
"Velocity": "Hastighed",
"Vertices": "Hjørnepunkter",
"View navigator": "Visningsnavigator",
@@ -559,57 +399,19 @@
"Wall": "Væg",
"Wall Location": "Vægplacering",
"Wall Thickness": "Vægtykkelse",
"Web thickness": "Kroptykkelse",
"What did you do just before the problem? A short list is enough": "Hvad gjorde du lige før problemet opstod? En kort liste er nok",
"What did you expect to see?": "Hvad forventede du at se?",
"What happened?": "Hvad skete der?",
"When": "Når",
"Width": "Bredde",
"Will fail": "Vil fejle",
"With gestures on, the right button no longer orbits the view: hold it and move to open the ring, then touch a tool's zone. A plain right-click still opens the menu.": "Når musebevægelser er slået til, roterer højre museknap ikke længere visningen: Hold den nede, og bevæg musen for at åbne ringen. Før derefter markøren ind i et værktøjs område. Et almindeligt højreklik åbner stadig menuen.",
"X": "X",
"X Angle": "X-vinkel",
"X angle": "X-vinkel",
"X distance": "X-afstand",
"Y": "Y",
"Y Angle": "Y-vinkel",
"Y angle": "Y-vinkel",
"Y distance": "Y-afstand",
"Z": "Z",
"Z Angle": "Z-vinkel",
"Z Distance": "Z-afstand",
"Z angle": "Z-vinkel",
"Z distance": "Z-afstand",
"Zebra": "Zebra",
"ai.prefs.allow_control": "Tillad AI-agenter at oprette forbindelse (kontrolkanal / MCP)",
"ai.prefs.allow_control_note": "Åbner kun en kontrolkanal på denne computer (127.0.0.1, port 27878 eller den næste ledige). Et program, der opretter forbindelse, skal også fremvise et hemmeligt token, som kun din brugerkonto kan læse, og som oprettes på ny ved hver start. Træder i kraft, næste gang SolidCraft starter. Fra: Ingen port lytter, medmindre du starter SolidCraft med --control.",
"ai.prefs.availability": "Assistentstyring",
"ai.prefs.background_commands": "Kør agentkommandoer i baggrunden",
"ai.prefs.follow_camera": "Vend kameraet mod skitser",
"ai.prefs.follow_camera_off": "Automatisk kamerafølgning er slået fra.",
"ai.prefs.follow_camera_on": "Kameraet vender sig mod skitser, som agenten opretter.",
"ai.prefs.intro": "Agenter, tilladelser og visning af deres arbejde.",
"ai.prefs.make_default": "Brug som standard",
"ai.prefs.no_agents": "Ingen agenter endnu. Tilføj en agent, eller opdatér for at finde installerede agenter.",
"ai.prefs.no_saved_approvals": "Ingen gemte godkendelser.",
"ai.prefs.override_all_warning": "Denne agent kan gemme, overskrive, eksportere eller erstatte designet uden at spørge.",
"ai.prefs.permission_all": "Tillad alt",
"ai.prefs.permission_all_summary": "Tilgængelige SolidCraft-værktøjer kører uden at spørge.",
"ai.prefs.permission_ask": "Spørg før ændringer",
"ai.prefs.permission_ask_summary": "Godkend hver modelændring og filhandling.",
"ai.prefs.permission_edits": "Tillad modelændringer",
"ai.prefs.permission_edits_summary": "Modelændringer udføres; filhandlinger og destruktive handlinger kræver stadig godkendelse.",
"ai.prefs.permission_scope": "Gælder kun SolidCraft-værktøjer. Agentens shell og filændringer bruger dens egne tilladelser.",
"ai.prefs.pointer_preview_hidden": "Markør skjult",
"ai.prefs.pointer_preview_speed": "Agentmarkør · {speed}",
"ai.prefs.pointer_speed": "Bevægelseshastighed",
"ai.prefs.privacy_note": "Kun din valgte agent eller dit valgte slutpunkt modtager samtalen. Slutpunktsnøgler gemmes i systemets nøglering.",
"ai.prefs.show_pointer": "Vis agentmarkør",
"ai.prefs.turn_limit_hint": "Det maksimale antal værktøjskald, der ændrer designet, som assistenten må foretage for én besked.",
"angle, height": "vinkel, højde",
"appearance.dielectric_ior": "Dielektrisk brydningsindeks",
"appearance.metallic": "Metallisk",
"appearance.only_available": "Kun tilgængelige udseender",
"appearance.pbr_help": "Se denne overflade i Rendering eller i en billedfil. Lyset brydes ikke og passerer ikke gennem overfladen.",
"appearance.pbr_opaque": "Uigennemsigtig PBR",
"appearance.roughness": "Ruhed",
@@ -652,10 +454,8 @@
"cmd.component_activate": "Aktivér komponent",
"cmd.component_break_link": "Bryd forbindelse",
"cmd.component_capture_position": "Registrér position",
"cmd.component_copies": "Find identiske kopier",
"cmd.component_create": "Ny komponent",
"cmd.component_delete": "Slet komponent",
"cmd.component_extract": "Udtræk til fil",
"cmd.component_from_bodies": "Opret komponenter fra emner",
"cmd.component_insert_design": "Indsæt design",
"cmd.component_links": "Vis forbindelser",
@@ -668,7 +468,6 @@
"cmd.component_properties": "Komponentens fysiske egenskaber",
"cmd.component_relink": "Skift kilde",
"cmd.component_rename": "Omdøb komponent",
"cmd.component_replace_with_link": "Erstat med link",
"cmd.component_revert_position": "Tilbagefør position",
"cmd.component_unlock": "Lås indhold op",
"cmd.component_update_links": "Opdatér forbindelser",
@@ -774,7 +573,6 @@
"cmd.material_create": "Nyt fysisk materiale",
"cmd.material_delete": "Slet fysisk materiale",
"cmd.material_edit": "Redigér fysisk materiale",
"cmd.material_inspect": "Inspicér fysisk materiale",
"cmd.material_list": "Vis materialer",
"cmd.material_properties_directional": "Retningsafhængigt elasticitetsmodul",
"cmd.material_properties_validate": "Validér fysiske materialedata",
@@ -791,10 +589,8 @@
"cmd.motion_plot": "Vis diagram for bevægelsesanalyse",
"cmd.motion_study": "Bevægelsesanalyse",
"cmd.motion_trace": "Bevægelsesspor",
"cmd.motion_trace_edit": "Redigér bevægelsesspor",
"cmd.motion_trace_keep": "Behold bevægelsesspor",
"cmd.occurrence_copy": "Indsæt (komponentforekomst)",
"cmd.occurrence_drag": "Træk forekomst",
"cmd.occurrence_ground": "Fastgør",
"cmd.occurrence_move": "Flyt komponentforekomst",
"cmd.occurrence_properties": "Komponentforekomstens fysiske egenskaber",
@@ -852,7 +648,6 @@
"cmd.sketch_auto_constrain_from_datum": "Automatisk binding ud fra reference",
"cmd.sketch_auto_project": "Projektér referencekanter automatisk",
"cmd.sketch_blend_curve": "Overgangskurve",
"cmd.sketch_cancel": "Annullér skitse",
"cmd.sketch_chain": "Kæde",
"cmd.sketch_chamfer_distance_angle": "Affasning med afstand og vinkel",
"cmd.sketch_chamfer_equal_distance": "Affasning med ens afstande",
@@ -968,7 +763,6 @@
"cmd.view_graphics_preset": "Grafikprofil",
"cmd.view_list": "Vis navngivne visninger",
"cmd.view_navigate": "Navigér i visning",
"cmd.view_normal_to": "Vinkelret på markering",
"cmd.view_rename": "Omdøb navngiven visning",
"cmd.view_save": "Ny navngiven visning",
"color.copy": "Klik for at kopiere farveværdier",
@@ -1057,7 +851,6 @@
"file_load.stopping": "Venter på, at den forrige handling stopper...",
"file_load.waiting": "Venter på filhandling",
"fits the face, e.g. M8": "passer til fladen, f.eks. M8",
"from the profile": "fra profilen",
"from the rule": "fra reglen",
"help.apache": "Apache-licens 2.0",
"help.attributions": "Tilskrivninger (ATTRIBUTION.md)",
@@ -1067,12 +860,6 @@
"help.licences": "Licenser",
"help.mit": "MIT-licens",
"help.notice": "NOTICE",
"help.solid.chamfer": "Affasning skærer de valgte kanter skråt med den angivne afstand. Brug den til at fjerne skarpe kanter eller lette indføringen af en tap eller skrue.",
"help.solid.extrude": "Ekstrudering giver en skitseprofil dybde ved at tilføje eller fjerne materiale langs en retning. Angiv afstanden for at danne en klods, en forhøjning eller en lomme. Brug Udhulning, hvis en massiv model i stedet skal gøres hul med en bestemt vægtykkelse.",
"help.solid.fillet": "Afrunding erstatter de valgte kanter med en glat, cirkulær overgang med den angivne radius. Den kan blødgøre skarpe kanter af hensyn til styrke, komfort eller udseende.",
"help.solid.hole": "Hul opretter et rundt hul ved et valgt punkt på en flade. Angiv diameteren, og vælg eventuelt konisk forsænkning, cylindrisk forsænkning eller gevind. Ved gevind vælges en gevindbetegnelse, f.eks. M6.",
"help.solid.revolve": "Omdrejning drejer en skitseprofil omkring en valgt akse og danner et omdrejningslegeme. Brug den f.eks. til aksler, knopper og skåle, der er runde omkring en centerlinje.",
"help.solid.shell": "Udhulning gør et massivt legeme hult med den angivne vægtykkelse. Vælg de flader, der skal åbnes. Indvendig udhulning bevarer de udvendige mål, mens udvendig udhulning bevarer de indvendige mål. Brug den f.eks. til kasser, dæksler og andre tyndvæggede dele.",
"help.third_party": "Tredjepartslicenser",
"home.back": "Tilbage til {name}",
"home.crashes": "Nedbrudsrapporter",
@@ -1182,15 +969,6 @@
"motion.to_end": "Til slutningen",
"motion.to_start": "Til starten",
"motion.track_name": "Spor {number}",
"nav.alias.help": "Shift+Alt+træk med venstre museknap roterer visningen, Shift+Alt+midterste panorerer, Shift+Alt+højre zoomer; træk med midterste museknap panorerer også, og træk med højre roterer visningen",
"nav.blender.help": "Træk med midterste museknap roterer visningen, Shift+midterste panorerer, Ctrl+midterste zoomer; træk med højre museknap påvirker ikke kameraet",
"nav.free_orbit.help": "Musen og 3D-musen roterer gennem begge poler og bevarer rulning. Snap til akser med Shift er slået fra.",
"nav.free_orbit.label": "Fri rotation af visningen",
"nav.fusion.help": "Træk med midterste museknap panorerer, Shift+midterste roterer visningen, træk med højre roterer visningen",
"nav.inventor.help": "Træk med midterste museknap panorerer, Shift+midterste roterer visningen, Ctrl+midterste zoomer, træk med højre roterer visningen",
"nav.openscad.help": "Træk med venstre museknap i et tomt område roterer visningen, Shift+venstre markerer med en ramme, træk i skitsegeometri redigerer den; træk med højre eller midterste museknap panorerer, Shift+højre eller Shift+midterste zoomer",
"nav.solidworks.help": "Træk med midterste museknap roterer visningen, Ctrl+midterste panorerer, Shift+midterste zoomer, træk med højre roterer visningen",
"nav.tinkercad.help": "Træk med højre museknap roterer visningen, træk med midterste eller Shift+højre panorerer",
"nav.touchpad.help": "Hold Shift nede, og bevæg markøren for at panorere; hold Alt/Option nede, og bevæg den for at rotere visningen. Ingen museknap behøver at være trykket ned. Lodret rulning zoomer.",
"nav.touchpad.label": "Pegefelt",
"nav.touchpad.orbit": "Rotér visning (hold Alt/Option nede og bevæg markøren; ingen museknap)",
@@ -1213,130 +991,8 @@
"pattern.flip_direction": "Vend retning",
"pick a hole to seat it, or OK places it at the origin": "vælg et hul til placeringen, eller tryk OK for at placere ved nulpunktet",
"pick one or two vertices, edges or faces": "vælg et eller to hjørnepunkter, kanter eller flader",
"prefs.add_item": "Tilføj elementet",
"prefs.alt_frees_manipulators": "Alt / Option frigør også manipulatorpilene",
"prefs.anti_aliasing_note": "Udjævner takkede kanter.",
"prefs.applies_on_restart": "Træder i kraft efter genstart",
"prefs.assistant": "Assistent",
"prefs.axes": "Akser",
"prefs.axis": "Akse",
"prefs.bottom": "Bund",
"prefs.browser": "Modelbrowser",
"prefs.built_in": "Indbygget",
"prefs.button_left": "Træk med venstre museknap",
"prefs.button_middle": "Træk med midterste museknap",
"prefs.button_right": "Træk med højre museknap",
"prefs.choose_font": "Vælg skrifttypefil…",
"prefs.click": "Klik",
"prefs.contact_shading": "Blød kontaktskygning",
"prefs.contact_shading_note": "Blød skygning, hvor dele er tæt på hinanden.",
"prefs.context_menu": "Genvejsmenu",
"prefs.context_menu_ring": "Genvejsmenu · hold nede for at åbne den radiale menu",
"prefs.cpu": "CPU (software)",
"prefs.crash_reports": "Åbn nedbrudsrapporter…",
"prefs.crash_reports_note": "Rapporter gemmes på denne computer med designet og de kommandoer, der genskaber problemet.",
"prefs.ctrl_cmd": "Ctrl / Cmd",
"prefs.curved_surfaces": "Krumme overflader",
"prefs.curves": "Krumme overflader",
"prefs.dead_zone_note": "Det skraverede bånd er dødzonen: Bevægelser inden for det ignoreres.",
"prefs.default": "standard",
"prefs.design": "Standardindstillinger for design",
"prefs.design_note": "Udgangspunktet for nye design. Åbne design beholder deres egne indstillinger.",
"prefs.devices": "3D-mus",
"prefs.display": "Visning",
"prefs.edges": "Kanter og skygning",
"prefs.extend": "Udvid",
"prefs.extra_navigation": "Ctrl+træk med venstre museknap i et tomt område roterer visningen; hold mellemrumstasten nede for at panorere.",
"prefs.follows_os": "Følger operativsystemet",
"prefs.font": "Skrifttype",
"prefs.font_fallback": "Tegn, som den valgte skrifttype mangler, vises med den indbyggede skrifttype, så ingen tekst viser en tom firkant.",
"prefs.font_sample": "Høj bly gom vandt fræk sexquiz · Ø12,50 mm · 45,0° · 0123456789",
"prefs.font_unreadable": "Den valgte skrifttype kunne ikke læses.",
"prefs.general": "Generelt",
"prefs.general_note": "Sprog, lagring og opdateringer.",
"prefs.gesture_orbit": "Rotér visningen",
"prefs.gesture_pan": "Panorér",
"prefs.gesture_zoom": "Zoom",
"prefs.graphics": "Grafik",
"prefs.graphics_note": "Afvej hastighed mod udjævning. Hvert valg viser sin virkning på et eksempel.",
"prefs.graphics_presets_note": "Forudindstillinger angiver de nedenstående valg samlet.",
"prefs.history": "Historik",
"prefs.hold_stop_snapping": "Hold nede for at slå snapping fra",
"prefs.icons_only": "Kun ikoner",
"prefs.imported_file": "Importeret fil",
"prefs.input": "Input",
"prefs.interface": "Brugergrænseflade",
"prefs.interface_language": "Sprog i brugergrænsefladen",
"prefs.interface_note": "SolidCrafts udseende og placeringen af dets dele.",
"prefs.interval_minutes": "{n} min",
"prefs.labels": "Etiketter",
"prefs.layout": "Layout",
"prefs.layout_note": "Kompakt fastgør panelerne til vinduets kanter. Gennemsigtighed i modelbrowseren er tilgængelig i Flydende. Skift assistentens side på siden Assistent.",
"prefs.left": "Venstre",
"prefs.left_click": "Venstreklik",
"prefs.live_input": "Løbende input",
"prefs.live_preview": "Løbende forhåndsvisning",
"prefs.minutes": "minutter",
"prefs.minutes_suffix": " min",
"prefs.mouse": "Mus og pegefelt",
"prefs.mouse_heading": "Mus",
"prefs.mouse_mapping": "Musetildeling",
"prefs.navigation": "Navigation",
"prefs.navigation_note": "Hvordan musen, pegefeltet og 3D-musen styrer visningen.",
"prefs.not_on_gpu": "ikke på denne GPU",
"prefs.on": "Til",
"prefs.opaque": "Uigennemsigtig",
"prefs.orbit_snap": "Hold Shift nede under rotation af visningen for at snappe til en akse.",
"prefs.orientation": "Orientering",
"prefs.overall_speed": "Samlet hastighed",
"prefs.preview_choices": "Forhåndsvisning af dine valg",
"prefs.quality_default": "standard",
"prefs.quality_fastest": "hurtigst",
"prefs.quality_slowest": "langsomst",
"prefs.quality_smoother": "mere udjævnet",
"prefs.reports_saved": "{n} gemt",
"prefs.right_click": "Højreklik",
"prefs.saving": "Automatisk lagring og gendannelse",
"prefs.scale_note": "Også Ctrl + / Ctrl − i vinduet. Knapper er 28 px ved 100%.",
"prefs.scale_text": "Skalering og tekst",
"prefs.select": "Vælg",
"prefs.select_one_item": "Vælg ét element",
"prefs.selecting": "Markering",
"prefs.shift": "Shift",
"prefs.shift_add_ctrl_toggle": "Shift tilføjer kun; Ctrl / Cmd skifter markeringen",
"prefs.shift_add_note": "Til: Shift tilføjer, Ctrl / Cmd skifter markeringen. Fra (standard): Begge skifter markeringen.",
"prefs.shortcuts": "Genveje",
"prefs.sketch_selection": "Skitse og markering",
"prefs.sketch_selection_heading": "Skitse og markering",
"prefs.sketch_selection_note": "Hvordan skitser snapper, og hvordan klik markerer.",
"prefs.sketching": "Skitsering",
"prefs.snap_drawing_note": "Mens du tegner eller trækker et skitsepunkt.",
"prefs.source": "Kilde",
"prefs.speed": "Hastighed",
"prefs.stand_up_note": "Når en STEP- eller IGES-fil åbnes med Z opad, tilbydes en kvart omdrejning om X.",
"prefs.status_done": "Færdig",
"prefs.status_error": "Fejl",
"prefs.status_selected": "Valgt",
"prefs.status_warning": "Advarsel",
"prefs.supersampling_brief": "Tegner visningen i større størrelse og nedskalerer den med filtrering: meget skarpt og mere krævende.",
"prefs.tab_accent_note": "Accentfarven følger den aktive fane",
"prefs.theme_file": "temafil",
"prefs.timeline_half_hour": "−30 min",
"prefs.timeline_now": "nu",
"prefs.timeline_quarter_hour": "−15",
"prefs.toggle_item": "Tilføj elementet, eller fjern det igen",
"prefs.toolbar": "Værktøjslinje",
"prefs.translucent": "Halvgennemsigtig",
"prefs.under_browser": "Under modelbrowseren",
"prefs.under_cursor": "Under markøren",
"prefs.units_precision": "Enheder og præcision",
"prefs.updates": "Opdateringer",
"prefs.view": "Visningens adfærd",
"prefs.viewport": "Visningsområde",
"prefs.wheel": "Musehjul",
"prefs.wheel_trackpad": "Musehjul og pegefelt",
"prefs.workspace": "Arbejdsområde",
"prefs.zoom_toward_cursor": "Zoom mod markøren",
"properties.title": "Egenskaber: {name}",
"recovery.days_ago": "for {count} dage siden",
"recovery.detail": "{file} · {features} features · autogemt {when}",
@@ -1407,20 +1063,16 @@
"search.suggest": "Mente du {label}?",
"search.try": "Skriv et kommandonavn, en feature i dette design eller en parameter. Du kører en kommando med værdier ved at skrive dens id og et JSON-objekt: {example}",
"search.valid": "Gyldig",
"section.off_hint": "Intet plan valgt: OK slår snittet fra.",
"settings.display_quality_note": "Gælder kun visningen. Design og eksport påvirkes ikke.",
"settings.display_quality_note": "Finere niveauer viser krumme flader og kanter mere jævnt og tager længere tid at opbygge mesh til. Kun modelvisningen ændres, aldrig designet eller eksporten.",
"settings.frame_rate_note": "Viser billeder pr. sekund nederst til højre i modelvisningen.",
"settings.measure_axis_distances": "Vis X/Y/Z-afstande",
"settings.measure_axis_distances_help": "Vis afstanden langs X-, Y- og Z-aksen sammen med den samlede afstand i Mål.",
"settings.msaa_applies_on_restart": "Kantudjævning træder i kraft, næste gang SolidCraft starter",
"settings.msaa_reset": "{setting}: {requested}× understøttes ikke af denne grafikenhed; værdien er nu {reset}× (understøttet: {supported}).",
"settings.msaa_unsupported": "Understøttes ikke af denne grafikenhed",
"settings.reference_geometry_through_bodies": "Vis referencegeometri gennem legemer",
"settings.reference_geometry_through_bodies_help": "Tegn inaktive skitsekonturer og referenceplaner oven på skyggelagte legemer",
"settings.reset_intro": "Disse indstillinger virker ikke på denne computer og ville forhindre SolidCraft i at starte. De er derfor ændret:",
"settings.reset_title": "Indstillinger blev ændret",
"settings.supersampling_note": "Tegner visningen med højere opløsning og nedskalerer den: Jævnere kanter, linjer og gitter på skærme under 4K. Det kræver GPU-tid: ca. 2,25× så mange pixels ved 1,5× og 4× ved 2×.",
"sheet.no_body": "der er intet pladelegeme: Start et med Flange > Basis, eller omdan en plade til et pladelegeme med Konvertér til plade (vælg dens store plane flade)",
"shortcut.json_error": "parametre skal være JSON: {error}",
"shortcut.no_command": "ingen kommando `{id}`; teksten før JSON skal være et kommando-id",
"shortcut.pin": "Fastgør øverst i S-boksen",
@@ -1457,9 +1109,6 @@
"thread.designation": "Betegnelse",
"thread.iso_metric": "ISO-metrisk",
"thread.select_size": "Vælg størrelse",
"timeline.delete_after_marker_one": "Slet 1 feature efter historikmarkøren",
"timeline.delete_after_marker_other": "Slet {count} features efter historikmarkøren",
"timeline.delete_many": "Slet {count} valgte features",
"timeline.group": "Ny featuremappe",
"timeline.move_many": "Flyt elementer i tidslinjen",
"timeline.move_to_group": "Flyt features til mappe",
@@ -1705,15 +1354,11 @@
"ui.agent_chat_waiting": "Venter på dig",
"ui.agent_cursor": "Agentmarkør",
"ui.agent_cursor_speed": "Agentmarkørens hastighed",
"ui.agent_off_thread": "Hold programvinduet responsivt, mens agenter udfører kommandoer",
"ui.agent_off_thread_note": "Langsomme kommandoer fra en agent (via AI-panelet eller en MCP-klient) køres i en separat tråd, så du stadig kan rotere og undersøge modellen. Du kan stoppe en kommando via statusfeltet i visningen. Når funktionen er slået fra, køres kommandoerne på programvinduets tråd, og vinduet venter, indtil de er færdige.",
"ui.ai_add": "Tilføj",
"ui.ai_add_agent": "Tilføj agent…",
"ui.ai_add_agent_title": "Tilføj agent",
"ui.ai_add_anthropic": "Anthropic-kompatibel API",
"ui.ai_add_api": "OpenAI-kompatibel API",
"ui.ai_add_custom": "Brugerdefineret kommando",
"ui.ai_add_endpoint": "API-slutpunkt",
"ui.ai_add_installed": "Installeret agent",
"ui.ai_add_installed_note": "Din egen opsætning af en installeret agent, f.eks. med andre argumenter. Installerede agenter vises automatisk.",
"ui.ai_added": "tilføjet",
@@ -1742,7 +1387,6 @@
"ui.ai_key_needs_https": "En nøgle sendes kun via HTTPS eller til denne computer.",
"ui.ai_key_note": "Gemmes i systemets nøglering, aldrig i SolidCrafts indstillinger. Lad feltet være tomt ved en lokal server.",
"ui.ai_keychain_error": "Systemets nøglering afviste anmodningen: {error}",
"ui.ai_kind": "Type",
"ui.ai_mode_all": "Tillad alt",
"ui.ai_mode_all_note": "Intet kræver godkendelse først. Nedbrudsrapporter er stadig utilgængelige, og mappebegrænsningerne gælder fortsat.",
"ui.ai_mode_all_warning": "Agenten kan gemme, overskrive, eksportere og erstatte dit design uden at spørge. Brug kun dette med en agent, du har tillid til, og på designs, du har gemt.",
@@ -1764,8 +1408,8 @@
"ui.ai_panel": "Panel",
"ui.ai_permissions": "Tilladelser",
"ui.ai_preset": "Forudindstilling",
"ui.ai_protocol": "Protokol",
"ui.ai_remove": "Fjern",
"ui.ai_request_limit": "stoppet efter 24 anmodninger for én besked; send en ny besked for at fortsætte",
"ui.ai_reset": "Nulstil",
"ui.ai_settings": "AI-indstillinger…",
"ui.ai_show_features": "Vis AI-funktioner",
@@ -1774,8 +1418,6 @@
"ui.ai_test": "Test",
"ui.ai_test_acp_ok": "Svarer: {agent}",
"ui.ai_test_acp_tip": "Starter agenten kortvarigt og anmoder om en session; afslutter den derefter igen",
"ui.ai_test_answered": "Forbindelse oprettet: {model} svarede",
"ui.ai_test_anthropic_tip": "Sender en kort besked til slutpunktet",
"ui.ai_test_api_tip": "Henter endepunktets modeller",
"ui.ai_test_no_model": "Forbindelse oprettet: {count} modeller, men ikke {model}",
"ui.ai_test_ok": "Forbindelse oprettet: {count} modeller",
@@ -1785,8 +1427,8 @@
"ui.ai_tool_running": "kører",
"ui.ai_tool_unfold": "Klik for at vise svaret",
"ui.ai_tool_waiting": "venter",
"ui.ai_toolbar_button": "Vis Assistent-knappen på programlinjen",
"ui.ai_toolbar_button_note": "Assistent-knappen på programlinjen. Du kan stadig åbne panelet med Ctrl+Shift+A eller via menuen Vis.",
"ui.ai_toolbar_button": "Vis knap på værktøjslinjen",
"ui.ai_toolbar_button_note": "AI-knappen i højre ende af værktøjslinjen. Ctrl+Shift+A og menuen Vis åbner stadig panelet.",
"ui.ai_undo_tip": "Fortryd alt, agenten ændrede for denne besked, som ét trin",
"ui.ai_url_hint": "https://example.com/v1",
"ui.all": "Alle",
@@ -1934,12 +1576,7 @@
"ui.export_keymap_tip": "Gem tastaturgenveje, favoritter i S-menuen og værktøjslinjen i en fil",
"ui.extend": "Forlæng",
"ui.extent_type": "Udstrækningstype",
"ui.extract_component": "Udtræk til fil…",
"ui.extract_design_changed": "Designet blev ændret, mens du valgte en fil. Start Udtræk til fil igen.",
"ui.extract_done": "Komponenten er nu et selvstændigt design, der er linket hertil.",
"ui.extrude": "Ekstrudering",
"ui.extrude_auto_to_object": "Ekstrudér til et valgt mål",
"ui.extrude_auto_to_object_tip": "Når en profil er valgt i Ekstrudering, skifter udstrækningen til Til objekt, hvis du vælger en flade, et hjørnepunkt eller et parallelt plan uden for profilens plan",
"ui.factor": "Faktor",
"ui.feature_history": "Featuretidslinje",
"ui.feature_options": "Featureindstillinger",
@@ -1949,8 +1586,6 @@
"ui.filter_commands_or_keys": "Filtrér kommandoer eller taster",
"ui.findInTimeline": "Find i tidslinje",
"ui.findInWindow": "Find i vindue",
"ui.fine": "Fin",
"ui.finest": "Fineste",
"ui.finish_sketch": "Afslut skitse",
"ui.fit": "Tilpas",
"ui.fix_unfix": "Lås fast/frigør",
@@ -2048,17 +1683,10 @@
"ui.hideProfile": "Skjul profil",
"ui.history_back": "Gå ét trin tilbage",
"ui.history_below_browser": "Under browseren",
"ui.history_edge_hint": "dobbeltklik på kanten",
"ui.history_first": "Gå til begyndelsen",
"ui.history_forward": "Gå ét trin frem",
"ui.history_hide_names": "Skjul navne",
"ui.history_last": "Gå til slutningen",
"ui.history_marker_tip": "Historikmarkør: Træk for at rulle modellen tilbage; nye features indsættes her",
"ui.history_names": "Navne",
"ui.history_play": "Afspil til slutningen",
"ui.history_show_names": "Vis navne",
"ui.history_title": "Historik",
"ui.history_toggle_names": "Vis eller skjul featurenavne",
"ui.hole": "Hul",
"ui.hole_tap_type": "Hullets gevindtype",
"ui.hole_type": "Hultype",
@@ -2080,10 +1708,6 @@
"ui.length_decimals": "Længdedecimaler",
"ui.light": "Lys",
"ui.line": "Linje",
"ui.link_copies": "Link identiske kopier",
"ui.link_copies_keep": "Behold",
"ui.link_copies_link": "Link",
"ui.link_copies_text": "Disse komponenter danner de samme legemer som {name}. Skal de gøres til placeringer af dens link?",
"ui.lock": "Lås",
"ui.loft": "Loft",
"ui.look_at": "Se på",
@@ -2186,7 +1810,6 @@
"ui.project_face_edges_sketch": "Projektér fladekanter til nye skitser",
"ui.properties": "Egenskaber",
"ui.pull_direction": "Udtræksretning",
"ui.quality": "Kvalitet",
"ui.quantity": "Antal",
"ui.quantity_2": "Antal 2",
"ui.quit": "Afslut",
@@ -2198,9 +1821,6 @@
"ui.rectangle": "Rektangel",
"ui.rectangular_pattern": "Rektangulært mønster",
"ui.redo": "Gentag",
"ui.reference_1": "Reference 1",
"ui.reference_2": "Reference 2",
"ui.reference_distance": "Referenceafstand",
"ui.relink_design_changed": "Samlingen blev ændret, mens du valgte en kilde. Start Skift kilde igen.",
"ui.relink_done": "Kilden er ændret for alle forekomster.",
"ui.relink_not_linked": "Denne komponent er ikke knyttet til et kildedesign.",
@@ -2211,7 +1831,6 @@
"ui.render": "Rendering",
"ui.renderer": "Renderingsmotor",
"ui.repeat": "Gentag",
"ui.replace_with_link_done": "Kopierne er nu placeringer af det linkede design.",
"ui.report_a_bug": "Rapportér en fejl",
"ui.reset": "Nulstil",
"ui.reset_all": "Nulstil alle",
@@ -2250,12 +1869,6 @@
"ui.sent_to_slicer": "Sendt til {slicer}",
"ui.sheet_gap": "Mellemrum",
"ui.shell": "Udhulning",
"ui.shortcut_capture_add_key": "Tilføj én almindelig tast. Escape annullerer.",
"ui.shortcut_capture_hint": "Klik på en tastetildeling, og tryk derefter på modifikationstaster og én tast. Tilføj modifikationstaster inden for ét sekund; slip alle taster for at bekræfte. Escape annullerer.",
"ui.shortcut_capture_press": "Tryk på modifikationstaster og én tast. Escape annullerer.",
"ui.shortcut_capture_release": "Slip alle taster for at bekræfte. Escape annullerer.",
"ui.shortcut_capture_settling": "Bekræfter genvej… Escape annullerer.",
"ui.shortcut_multiple_keys": "Brug modifikationstaster og kun én anden tast. Klik på tastetildelingen for at prøve igen.",
"ui.show": "Vis",
"ui.showAll": "Vis alle",
"ui.showAllBodies": "Vis alle emner",
@@ -2291,7 +1904,6 @@
"ui.spline": "Spline",
"ui.split_body": "Opdel emne",
"ui.stand_up_tip": "Når en STEP- eller IGES-fil åbnes med Z opad, tilbydes en kvart omdrejning om X",
"ui.standard": "Standard",
"ui.start": "Start",
"ui.start_over": "Start forfra",
"ui.start_page": "Startside",
@@ -2319,7 +1931,7 @@
"ui.tie_ends": "Sammenbind",
"ui.tie_ends_tip": "Bind alle ubundne ender",
"ui.timeline_bottom": "Tidslinje nederst",
"ui.timeline_left_note": "Den lodrette historik vises til venstre for modelbrowseren. Featurenavne kan vises eller skjules på strimlen.",
"ui.timeline_left_note": "Layoutet til venstre viser tilbagerulningslinjen over modeltræet med emner og scene.",
"ui.timeline_left_sidebar": "Venstre sidepanel",
"ui.tip_angle": "Spidsvinkel",
"ui.to_object": "Til objekt",
@@ -2327,7 +1939,7 @@
"ui.top": "Ovenfra",
"ui.torus": "Torus",
"ui.total_angle": "Samlet vinkel",
"ui.trackpad_pan_tip": "Rulning med to fingre panorerer; i Fusion roterer Shift + to fingre visningen. Fra: Rulning zoomer. Musehjul og knibebevægelser zoomer; tommelfingerhjulet panorerer sidelæns.",
"ui.trackpad_pan_tip": "Bevægelser på pegefeltet panorerer (slået fra: zoomer som musehjulet); musehjulet zoomer, tommelfingerhjulet panorerer sidelæns, og knibebevægelse zoomer altid",
"ui.trim": "Trim",
"ui.twist": "Vridning",
"ui.two_finger_scroll_pans": "Rulning med to fingre panorerer",
@@ -2404,6 +2016,5 @@
"update.up_to_date": "SolidCraft er opdateret.",
"update.update_now": "Opdatér nu",
"update.you_have": "Du har {version}.",
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ vælg · Enter / Tab udfyld · Esc luk",
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ vælg · Enter anvender det indtastede · Esc lukker"
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ vælg · Enter / Tab udfyld · Esc luk"
}
+2 -142
View File
@@ -85,7 +85,6 @@
"Choose…": "Auswählen…",
"Circular": "Kreisförmig",
"Circumference": "Umfang",
"Clockwise": "Im Uhrzeigersinn",
"Close draft": "Entwurf schließen",
"Collapse": "Einklappen",
"Collisions": "Kollisionen",
@@ -405,7 +404,6 @@
"Search this library": "Diese Bibliothek durchsuchen",
"Second plane": "Zweite Ebene",
"Section": "Schnitt",
"Section position": "Lage des Querschnitts",
"Sections (one sketch each)": "Querschnitte (je eine Skizze)",
"Select Other": "Andere auswählen",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Anderes auswählen: Halten Sie die linke Taste ruhig auf dem Modell gedrückt (oder Rechtsklick › Anderes auswählen…), um alles unter dem Cursor aufzulisten; klicken Sie erneut an dieselbe Stelle, um das nächste Element dort auszuwählen. Doppelklick mit der mittleren Taste wechselt zur Startansicht (Einstellungen › Navigation).",
@@ -421,7 +419,6 @@
"Show Agent Cursor": "Agenten-Cursor anzeigen",
"Show in folder": "Im Ordner anzeigen",
"Show the pointer an AI agent moves (#35)": "Den Zeiger anzeigen, den ein KI-Agent bewegt (#35)",
"Show tool at cursor": "Werkzeug am Cursor anzeigen",
"Signed axes": "Vorzeichenbehaftete Achsen",
"Size": "Größe",
"Sketch constraints": "Skizzenbedingungen",
@@ -435,11 +432,9 @@
"Specialise": "Spezialisieren",
"Speed": "Geschwindigkeit",
"Splitting tool": "Trennwerkzeug",
"Square": "Quadrat",
"Stand it up": "Aufrichten",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Aufrichten: eine Vierteldrehung um X, als ein Verschieben-Merkmal, das sich rückgängig machen lässt.",
"Stand this part up?": "Dieses Teil aufrichten?",
"Start angle": "Startwinkel",
"Start bulge": "Startausbauchung",
"Start continuity": "Startstetigkeit",
"Step": "Schritt",
@@ -525,7 +520,6 @@
"angle, height": "Winkel, Höhe",
"appearance.dielectric_ior": "Brechungsindex des Dielektrikums",
"appearance.metallic": "Metallanteil",
"appearance.only_available": "Nur verfügbare Erscheinungsbilder",
"appearance.pbr_help": "Diese Oberfläche in Render oder einem Produkt-PNG ansehen. Keine Brechung oder Transmission.",
"appearance.pbr_opaque": "Opakes PBR",
"appearance.roughness": "Rauheit",
@@ -690,7 +684,6 @@
"cmd.material_create": "Neues physikalisches Material",
"cmd.material_delete": "Physisches Material löschen",
"cmd.material_edit": "Physisches Material bearbeiten",
"cmd.material_inspect": "Physikalisches Material prüfen",
"cmd.material_list": "Materialien auflisten",
"cmd.material_properties_directional": "Richtungsabhängiger Elastizitätsmodul",
"cmd.material_properties_validate": "Physikalische Materialdaten prüfen",
@@ -737,32 +730,13 @@
"cmd.plastic_rules": "Kunststoffregeln auflisten",
"cmd.plastic_snap_fit": "Rastverbindung",
"cmd.render_backends": "Render-Backends",
"cmd.render_camera_fit": "Renderobjekt einpassen",
"cmd.render_camera_get": "Renderkamera prüfen",
"cmd.render_cancel": "Rendering abbrechen",
"cmd.render_export": "PNG exportieren",
"cmd.render_hdr_inspect": "HDR-Quelle untersuchen",
"cmd.render_library_appearance_apply": "Opakes Bibliotheksaussehen anwenden",
"cmd.render_library_describe": "Render-Ressource untersuchen",
"cmd.render_library_environment_apply": "Integrierte HDR-Umgebung anwenden",
"cmd.render_library_list": "Render-Bibliothek durchsuchen",
"cmd.render_library_physical_prepare": "Prepare Physical Reference",
"cmd.render_library_studio_apply": "Erstellte Studiolichter anwenden",
"cmd.render_library_studio_prepare": "Erstellte Studiolichter vorbereiten",
"cmd.render_library_surface_bind": "Save Surface Binding",
"cmd.render_library_surface_prepare": "Erstellte Oberflächenmaps vorbereiten",
"cmd.render_library_surface_unbind": "Remove Surface Binding",
"cmd.render_library_targets": "Ziele für Render-Ressourcen suchen",
"cmd.render_preflight": "Renderanfrage prüfen",
"cmd.render_result_pin": "Angezeigtes Render-Ergebnis behalten",
"cmd.render_scene": "Renderszene prüfen",
"cmd.render_settings_apply": "Renderkamera anwenden",
"cmd.render_settings_capture_hdr": "Render-Einstellungen mit HDR speichern",
"cmd.render_settings_get": "Rendereinstellungen",
"cmd.render_settings_set": "Rendereinstellungen speichern",
"cmd.render_stage_backdrop": "Physischen Renderhintergrund speichern",
"cmd.render_stage_clear": "Remove Render Stage",
"cmd.render_stage_ground": "Save Render Ground",
"cmd.render_start": "Bild rendern",
"cmd.render_status": "Renderfortschritt",
"cmd.select_clear": "Auswahl aufheben",
@@ -1084,8 +1058,6 @@
"manage.material_hint": "Wählen Sie Körper und deren physikalisches Material aus.",
"manage.parameters_hint": "Bearbeiten Sie Ausdrücke und Einheiten. Änderungen werden mit der Eingabetaste oder beim Verlassen eines Feldes übernommen.",
"manage.rules_hint": "Wählen Sie eine Regel zum Bearbeiten oder erstellen Sie eine neue. Bestätigen Sie, um die Regel zu speichern.",
"material.editor.axes_numeric": "1 / 2 / 3",
"material.editor.axes_wood": "L / R / T",
"measure.at": "Bei",
"measure.item": "Element {number}",
"messages.ago_h": "vor {n} Std.",
@@ -1149,35 +1121,13 @@
"recovery.just_now": "gerade eben",
"recovery.min_ago": "vor {count} Min.",
"recovery.never_saved": "nie gespeichert",
"render.add_ground": "Add fitted ground to setup",
"render.background": "Szenenhintergrund",
"render.bloom_enable": "Darstellungs-Bloom",
"render.bloom_help": "Kreativer Ausgabeglanz aus linearen Glanzlichtern mit drei normierten Durchläufen. Radius in Ausgabepixeln; Addition vor sRGB. Keine Linsen-, Volumen- oder Konvergenzsimulation. Das rohe EXR bleibt unverändert.",
"render.bloom_opaque": "Bloom benötigt eine deckende Ausgabe. Transparentes PNG wird abgelehnt, nicht still abgeflacht.",
"render.bloom_radius": "Bloom-Radius",
"render.bloom_strength": "Bloom-Stärke",
"render.bloom_threshold": "Linearer Glanzlichtschwellwert",
"render.camera_pitch": "Kameraneigung",
"render.camera_set": "Render-Kamera einstellen",
"render.clear_stage": "Remove saved stage",
"render.cpu_budget": "CPU-Ressourcenprofil",
"render.cpu_budget_bodies": "Körperobergrenze",
"render.cpu_budget_compact": "Kompakt (bisherige Grenzen)",
"render.cpu_budget_details": "Ressourcengrenzen",
"render.cpu_budget_help": "Größere Grenzen ausdrücklich wählen. Auflösung und Samples werden nie automatisch reduziert.",
"render.cpu_budget_memory": "Speicherobergrenze pro Auftrag",
"render.cpu_budget_not_guarantee": "Dies sind Zulassungsgrenzen, keine RAM-Reservierung oder Geschwindigkeitsgarantie. Geometrie, Film und Vorschauen teilen das Speicherbudget. Das Rendern bleibt abbrechbar.",
"render.cpu_budget_resize": "Der Entwurf überschreitet das CPU-Profil oder sein Kamera-Sample-Budget. Kleinere Abmessungen ausdrücklich bestätigen oder ein größeres Profil wählen.",
"render.cpu_budget_studio": "Studio (große Bilder)",
"render.cpu_budget_triangles": "Dreiecksobergrenze",
"render.cpu_budget_workstation": "Workstation (komplexe Szenen)",
"render.exr_pending": "Die GPU kann rohe lineare EXR-Dateien behalten. Kompression, mehrschichtige Bilder und zusätzliche Renderpässe bleiben nicht verfügbar.",
"render.fit": "Produkt einpassen",
"render.image_cancelled": "Rendering abgebrochen",
"render.image_cancelling": "Wird abgebrochen",
"render.image_completed": "Bild bereit zum Export",
"render.image_encoding": "PNG wird kodiert",
"render.image_export": "Fertiges Bild exportieren",
"render.image_export": "PNG exportieren",
"render.image_failed": "Rendering fehlgeschlagen",
"render.image_height": "Höhe",
"render.image_help": "CPU-Vorschaubild · PNG · angenäherte Transparenz",
@@ -1195,98 +1145,17 @@
"render.image_unknown": "Renderstatus nicht verfügbar",
"render.image_use_largest": "Größte Größe mit diesem Seitenverhältnis verwenden:",
"render.image_width": "Breite",
"render.include_rig_cards": "Include passive cards and flags",
"render.library_applied": "Ressourcenvariante angewendet. Vollständigen Renderauftrag vor dem Start prüfen.",
"render.library_apply_hdr": "Mit dieser HDR-Umgebung speichern",
"render.library_apply_maps": "Erstellte GPU-Oberflächenmaps verwenden",
"render.library_apply_studio": "Erstellte Studiovariante speichern",
"render.library_apply_uniform": "Einheitliches opakes Aussehen anwenden",
"render.library_authored_help": "Original erstellte Ressourcen dienen der Darstellung, nicht als gemessene technische Daten. Jede Aktion wendet nur ihre angegebene Variante an.",
"render.library_blocked": "Diese Ressource benötigt noch Kalibrierung oder eine externe Sonne und kann noch nicht angewendet werden.",
"render.library_center": "Mittelpunkt des Lichtaufbaus",
"render.library_emitters_only_ack": "Nur Strahler verwenden; dokumentierte passive Karten auslassen",
"render.library_environment_concepts": "Umgebungskonzepte",
"render.library_extent": "Objektausdehnung",
"render.library_hdr": "Integrierte HDR-Umgebungen",
"render.library_hdr_help": "Bettet das originale HDR in die gespeicherte Einstellung ein. Der Längengradursprung wird ausdrücklich umgerechnet; nach dem Speichern ist keine externe Datei nötig.",
"render.library_light_concepts": "Beleuchtungskonzepte",
"render.library_maps_help": "Built-in maps can be saved with pinned sources and local occurrence mapping. Temporary optical overrides remain a separate mode.",
"render.library_material_concepts": "Materialkonzepte",
"render.library_next": "Nächste Seite",
"render.library_physical_profiles": "Physical reference profiles",
"render.library_previous": "Vorherige Seite",
"render.library_quality_concepts": "Qualitätsspezifikationen",
"render.library_replace_environment": "Vorhandene Umgebung ausdrücklich ersetzen",
"render.library_replace_studio": "Replace saved lighting, environment and stage",
"render.library_results": "Passende Ressourcen",
"render.library_rig_help": "Fit once at an explicit center and physical extent. Complete rigs include passive cards; calibration blockers still refuse. Conservative emitter/card clearance is checked before saving.",
"render.library_rigs": "Erstellte Lichtaufbauten",
"render.library_save_maps": "Save maps to this setup",
"render.library_scene_bounds": "Erfasste Szenengrenzen verwenden",
"render.library_scenes": "Szenenspezifikationen",
"render.library_search": "Bibliothek durchsuchen",
"render.library_selene": "SELENE: opake Oberflächen",
"render.library_specification_only": "Nur Spezifikation: keine vollständig ausführbare Voreinstellung. Erforderliche Effekte, Ressourcen und Objektrollen müssen noch implementiert und gebunden werden.",
"render.library_surfaces": "Erstellte PBR-Oberflächen",
"render.library_target": "Zielkörper",
"render.library_title": "Render-Ressourcenbibliothek",
"render.library_uniform_ack": "Einheitliche Näherung ohne räumliche oder erweiterte Effekte verwenden",
"render.material_color_replace": "Basismap ersetzt Körperfarbe",
"render.material_limits": "Höchstens 128 Körperüberschreibungen, 32 getrennte Glaskörper, 64 Beschichtungen und 128 Texturbindungen. Identische Maps teilen Speicher; Geräte- und Gesamtspeichergrenzen bleiben aktiv.",
"render.material_metallic_map": "Metallanteil-Map (linear)",
"render.material_roughness_map": "Rauheitsmap (linear)",
"render.material_v_up": "Textur-V zeigt im Bild nach oben",
"render.navigation_help": "Die Kamera bewegt sich wie im Konstruieren (Einstellungen › Navigation). Setup speichern übernimmt die aktuelle Kamera.",
"render.opaque_image_summary": "Vorschau-PNG · opakes PBR · Transparenz durch Blending",
"render.opaque_preview_help": "Opakes PBR und einfache Farben; Transparenz durch Blending. Brechung, HDRI und finales Rendering sind noch nicht verfügbar.",
"render.output_format_status": "PNG enthält das exakte fertige Anzeigebild. Optionales GPU-EXR bewahrt rohe lineare FLOAT32-Strahldichte vor Filtern. Export ersetzt nie durch Vorschaucache oder anderen Auftrag.",
"render.passive_stage": "Render-only stage",
"render.passive_stage_help": "Finite passive panels belong only to rendered images. They do not add CAD bodies or mass. Placement is fixed after applying; intersecting product bounds refuse. CPU does not provide diffuse bounce lighting.",
"render.perspective": "Perspektivische Kamera",
"render.physical_acknowledge": "I understand this partial reference needs grade, condition and source review",
"render.physical_associations": "Physical candidates and roles",
"render.physical_copy_plan": "Copy plan with sources and omissions",
"render.physical_material_name": "Proposed reference material name",
"render.physical_omissions": "Properties not representable in the native card",
"render.physical_plan_only": "Read-only plan prepared. No material was created or assigned. Keep its extended profile and omissions when reviewing the native card.",
"render.physical_prepare_plan": "Prepare creation plan (does not apply)",
"render.physical_separate": "Appearance is not material identification. These are sourced references, not certified effective properties. Existing mass, engineering materials and optics are unchanged.",
"render.physical_unknown": "No numerical candidate is established. Grade, formulation or construction must be specified; unknown is not zero.",
"render.preview": "Integrierte Vorschau",
"render.preview_help": "Rasterdarstellung mit gemischter Transparenz. Brechung, HDRI und finales Rendering sind noch nicht verfügbar.",
"render.reload_settings": "Gespeicherte Einstellungen laden",
"render.replace_saved_surface": "Replace an existing saved binding for this target",
"render.restore_camera": "Gespeicherte Kamera laden",
"render.result_reduced_display": "Anzeigevorschau auf die Geräte-/Cachegrenze reduziert. PNG und Export behalten die volle Auflösung; native 100%-Ansicht ist hier nicht verfügbar.",
"render.return_design": "Zurück zu Design",
"render.ribbon_backgrounds": "Hintergründe",
"render.ribbon_check": "Prüfen",
"render.ribbon_check_help": "Genau den aktuellen Renderentwurf prüfen. Kein stiller Rendererwechsel, keine Verkleinerung oder Ersetzung.",
"render.ribbon_edit_lights": "Licht ändern",
"render.ribbon_environment_help": "HDR-Beleuchtung, Kamerahintergründe und physische Bühnen sind getrennte Einstellungen mit ausdrücklicher Ersetzungsbestätigung.",
"render.ribbon_finish_dialog": "Zuerst den Datei- oder Bestätigungsdialog abschließen.",
"render.ribbon_frame": "Einpassen",
"render.ribbon_hdr_library": "HDR-Bibliothek",
"render.ribbon_job_active": "Ein Render läuft bereits; vor einem neuen Auftrag ausdrücklich abbrechen.",
"render.ribbon_light_rigs": "Lichtaufbauten",
"render.ribbon_lights_help": "Lichtaufbauten prüfen oder unterstützte Lichter bearbeiten; erst ausdrücklich Anwenden ersetzt die Einrichtung.",
"render.ribbon_materials": "Materialien",
"render.ribbon_materials_help": "Gemeinsamen Erscheinungsinspektor und Originaloberflächen öffnen. Optik ändert keine technischen Eigenschaften stillschweigend.",
"render.ribbon_no_active_job": "Kein aktiver Render zum Abbrechen.",
"render.ribbon_no_result": "Vor dem Export muss ein Render abgeschlossen sein.",
"render.ribbon_no_saved_camera": "Vor dem Wiederherstellen eine Aufnahme speichern.",
"render.ribbon_saved_camera": "Gespeicherte Kamera",
"render.ribbon_shots": "Aufnahmen",
"render.ribbon_studios": "Studios",
"render.ribbon_studios_help": "Implementierte Studios durchsuchen und Ersetzungen vor dem Anwenden prüfen. Konzepte bleiben gekennzeichnet.",
"render.ribbon_surface_maps": "Oberflächen",
"render.save_setup": "Setup speichern",
"render.saved_maps_runtime_conflict": "Saved surface bindings cannot be mixed with temporary optical overrides or an unsaved HDR. Save the HDR or remove the temporary inputs explicitly.",
"render.saved_surface_remove": "Remove saved binding",
"render.saved_surfaces": "Saved surface bindings",
"render.saved_surfaces_help": "Pinned built-in maps follow the selected occurrence in local millimeters. A replaced or missing target is an error, never an automatic reassignment.",
"render.setup_name": "Name des Setups",
"render.stage_panel_count": "Saved panels",
"render.unavailable_preview": "Dieses Setup verlangt ein nicht verfügbares Vorschauprofil.",
"render.unsaved": "Ungespeicherte Vorschauänderungen",
"render.use_preview": "Integrierte Vorschau verwenden",
@@ -1363,7 +1232,6 @@
"thread.designation": "Bezeichnung",
"thread.iso_metric": "ISO metrisch",
"thread.select_size": "Größe wählen",
"timeline.feature_hint": "Doppelklicken zum Bearbeiten · Ziehen zum Neuordnen · Rechtsklicken für weitere Optionen",
"timeline.group": "Neuen Merkmalsordner anlegen",
"timeline.move_many": "Zeitleistenelemente verschieben",
"timeline.move_to_group": "Merkmale in Ordner verschieben",
@@ -1375,10 +1243,6 @@
"toolbar.customize_note": "Registerkarte {tab}. Markieren Sie einen Befehl, um ihm eine Schaltfläche in der Werkzeugleiste zu geben. Sie können auch eine Zeile aus dem Aufklappmenü eines Panels auf die Werkzeugleiste ziehen, eine Schaltfläche zum Entfernen aus der Werkzeugleiste ziehen oder die Stecknadel einer Menüzeile verwenden.",
"toolbar.image": "BILD",
"toolbar.render": "RENDERN",
"toolbar.render_appearance": "ERSCHEINUNG",
"toolbar.render_camera": "KAMERA",
"toolbar.render_environment": "UMGEBUNG",
"toolbar.render_lighting": "BELEUCHTUNG",
"toolbar.render_scene": "SZENE",
"trace.points": "{count} Punkte entlang der Bahn",
"trace.stopped_by_contact": "Endet bei Probe {sample}: {a} würde {b} durchdringen",
@@ -1668,6 +1532,7 @@
"ui.ai_permissions": "Berechtigungen",
"ui.ai_preset": "Vorlage",
"ui.ai_remove": "Entfernen",
"ui.ai_request_limit": "nach 24 Anfragen für eine Nachricht angehalten; senden Sie eine weitere Nachricht, um fortzufahren",
"ui.ai_reset": "Zurücksetzen",
"ui.ai_settings": "KI-Einstellungen…",
"ui.ai_show_features": "KI-Funktionen anzeigen",
@@ -1847,8 +1712,6 @@
"ui.filter_commands_or_keys": "Befehle oder Tasten filtern",
"ui.findInTimeline": "In der Zeitleiste suchen",
"ui.findInWindow": "Im Fenster suchen",
"ui.fine": "Fein",
"ui.finest": "Sehr fein",
"ui.finish_sketch": "Skizze beenden",
"ui.fit": "Einpassen",
"ui.fix_unfix": "Fixieren/Lösen",
@@ -2077,7 +1940,6 @@
"ui.project_face_edges_sketch": "Flächenkanten in neue Skizzen projizieren",
"ui.properties": "Eigenschaften",
"ui.pull_direction": "Entformungsrichtung",
"ui.quality": "Qualität",
"ui.quantity": "Anzahl",
"ui.quantity_2": "Anzahl 2",
"ui.quit": "Beenden",
@@ -2179,7 +2041,6 @@
"ui.spline": "Spline",
"ui.split_body": "Körper trennen",
"ui.stand_up_tip": "Nach dem Öffnen einer STEP- oder IGES-Datei mit Z nach oben eine Vierteldrehung um X anbieten",
"ui.standard": "Standard",
"ui.start": "Start",
"ui.start_over": "Von vorn beginnen",
"ui.start_page": "Startseite",
@@ -2282,7 +2143,6 @@
"update.prefs_flatpak": "Flatpak aktualisiert diese Installation ({version}): {command} ausführen oder die Softwareverwaltung nutzen. SolidCraft sucht nicht selbst.",
"update.prefs_label": "Updates",
"update.prefs_note": "Dies ist SolidCraft {version} ({kind}). Die Suche fragt GitHub nach dem neuesten Release und sendet sonst nichts.",
"update.prefs_web": "Laden Sie die Seite neu, um die neueste Web-App zu erhalten. Ist das Netzwerk nicht verfügbar oder kann das Update nicht rechtzeitig vollständig heruntergeladen werden, wird die gespeicherte Version geöffnet. Ein laufender Download wird für das nächste Neuladen fortgesetzt.",
"update.release_page": "Release-Seite öffnen",
"update.restart": "Jetzt neu starten",
"update.restart_later": "Später",
-76
View File
@@ -7,8 +7,6 @@
"Modeled": "Modelled",
"cmd.construct_point_center": "Point At Centre Of Circle/Sphere/Torus",
"cmd.inspect_center_of_mass": "Centre of Mass",
"cmd.render_aging_describe": "Layered Surface Aging",
"cmd.render_effects_describe": "Lens and Creative Effects",
"cmd.sketch_arc_center_point": "Centre Point Arc",
"cmd.sketch_circle_center": "Centre Diameter Circle",
"cmd.sketch_rectangle_center": "Centre Rectangle",
@@ -16,81 +14,7 @@
"cmd.sketch_slot_center_point": "Centre Point Slot",
"cmd.sketch_slot_center_to_center": "Centre to Centre Slot",
"pattern.alignment_hint": "Counts include the original. Centre balances copies; Flip chooses the extra side for even totals.",
"render.aging_add": "Add aging layer",
"render.aging_airborne": "Airborne dust / participating medium",
"render.aging_airborne_help": "Airborne dust is a separate finite homogeneous scattering volume. It changes transport and raw EXR, not the surface-aging stack. Existing volume/glass/denoising restrictions remain explicit; no atmosphere is added automatically.",
"render.aging_all_bodies": "All eligible opaque bodies",
"render.aging_chips": "Chipped paint",
"render.aging_class_any": "All opaque material classes",
"render.aging_class_dielectric": "Nonmetallic surfaces only",
"render.aging_class_metal": "Metallic surfaces only",
"render.aging_color": "Layer colour · sRGB",
"render.aging_coverage": "Coverage",
"render.aging_dust": "Deposited dust",
"render.aging_empty": "No aging layers: the original surfaces remain unchanged.",
"render.aging_fading": "Faded coating",
"render.aging_grime": "Grime & staining",
"render.aging_help": "Combine up to eight optical layers, in order. Rust and patina are authored appearances, not age/corrosion simulations. Scale is in world millimetres. No geometry, mass or engineering property is changed; glass and passive stages are excluded.",
"render.aging_metallic": "Layer metallic fraction",
"render.aging_origin": "Field origin · world mm",
"render.aging_patina": "Copper-green patina",
"render.aging_refresh_targets": "Inspect current bodies",
"render.aging_roughness": "Layer roughness",
"render.aging_rust": "Oxidized iron / rust",
"render.aging_scale": "Pattern scale",
"render.aging_scratches": "Fine scratches",
"render.aging_target": "Target & coordinates",
"render.aging_target_help": "A named target must still match its captured index and name. Global masks are world-anchored; they do not follow moving parts. This is neither cavity detection nor true edge wear. Filter the list to reach additional bodies.",
"render.aging_target_search": "Filter body names",
"render.aging_upward": "Upward-facing deposition",
"render.backdrop_x": "Centre X",
"render.backdrop_y": "Centre Y",
"render.background_camera_section": "Camera background · PNG presentation",
"render.background_completed_frozen": "The displayed result is a frozen completed image. Editing a background does not repaint that result automatically. Use Regrade for camera-only changes, or Render for a new physical stage.",
"render.background_hdr_separate": "The current HDR resources illuminate and reflect on surfaces; they are not automatically displayed as a panorama behind the subject. Changing a gradient does not rotate or recolour the lighting.",
"render.background_lighting_section": "HDR illumination · separate from camera background",
"render.background_no_alpha_override": "A camera background is not added to transparent output silently. Raw-master regrading uses captured coverage and does not reapply denoising; the source EXR remains unchanged.",
"render.background_regrade": "Apply to retained raw master",
"render.background_stage_occlusion": "This shot includes physical stage geometry. A large floor or wall can fill the frame and hide a camera-only background. Clear or resize the stage deliberately, or change its physical reflectance. Regrading cannot remove captured geometry.",
"render.background_workflow": "Choose the background you intend: a camera-only colour/gradient, a physical stage that receives light, or HDR illumination. They are independent. Live Preview is a modelling view; render a new image or regrade a retained master to see camera finishing.",
"render.chromatic_offset": "Chromatic edge offset",
"render.effects_gpu_only": "These effects require GPU path tracing. Changing the page never switches the renderer automatically.",
"render.effects_help": "Combine ordered colour looks and image-space lens effects. Rendering and raw-master regrading use the same saved settings. Raw EXR stays unchanged. These are original artistic controls, not calibrated lens or branded-film emulations.",
"render.effects_reset_lens": "Reset lens effects",
"render.effects_seed": "Pattern seed",
"render.film_grain": "Photographic grain",
"render.flare_ghosts": "Ghost rings per source",
"render.flare_halo": "Flare halo",
"render.flare_streak": "Anamorphic-style streak",
"render.flare_strength": "Highlight flare strength",
"render.flare_threshold": "Scene-linear flare threshold",
"render.layer_down": "Move down",
"render.layer_enabled": "Layer enabled",
"render.layer_remove": "Remove layer",
"render.layer_strength": "Layer strength",
"render.layer_up": "Move up",
"render.lens_effects_help": "Flare uses up to four separated bright sources in the actual linear image. Ghosts, halo, streaks, chromatic offsets and radial distortion are artistic image-space approximations. Spatial effects need opaque output; outside samples extend the image edge. Grain is deterministic. No physics, EXR or geometry is altered.",
"render.lens_effects_title": "Lens-inspired & photographic",
"render.look_add": "Add colour look",
"render.look_bleach": "Bleach-bypass-inspired",
"render.look_cool": "Cool",
"render.look_cyanotype": "Cyanotype-inspired",
"render.look_faded": "Faded print",
"render.look_mono": "Monochrome",
"render.look_sepia": "Sepia",
"render.look_strength": "Look strength",
"render.look_teal_orange": "Teal / orange",
"render.look_title": "Creative look stack",
"render.look_warm": "Warm",
"render.opaque_preview_help": "Opaque PBR and simple colours; blended transparency. Refraction, HDRI and final rendering are not available yet.",
"render.page_aging": "Aging & contamination",
"render.page_background": "Backgrounds",
"render.page_effects": "Lens & finishing effects",
"render.radial_distortion": "Radial distortion",
"render.ribbon_aging": "Aging",
"render.ribbon_effects": "Effects",
"render.sharpening": "Detail sharpening",
"render.soft_focus": "Soft focus",
"sketch_text.align_center": "Centre",
"ui.3d_mouse_step_pull": "Pull the cap towards you, hold it, then let go.",
"ui.center": "Centre",
+2 -769
View File
File diff suppressed because it is too large Load Diff
+2 -650
View File
@@ -72,7 +72,6 @@
"Choose…": "Elegir…",
"Circular": "Circular",
"Circumference": "Circunferencia",
"Clockwise": "Horario",
"Close draft": "Cerrar borrador",
"Collisions": "Colisiones",
"Colour": "Color",
@@ -85,7 +84,6 @@
"Contact Sets": "Conjuntos de contacto",
"Contact sets": "Conjuntos de contacto",
"Continuity": "Continuidad",
"Copies the definition only. Assign it from Physical Material when ready.": "Copia solo la definición. Asígnala desde Material físico cuando esté lista.",
"Copy Error Message": "Copiar mensaje de error",
"Copy Warning Message": "Copiar mensaje de advertencia",
"Copy all licences": "Copiar todas las licencias",
@@ -175,7 +173,6 @@
"Home view": "Vista de inicio",
"Horiz./Vert.": "Horiz./Vert.",
"IMAGES": "IMÁGENES",
"Import material into design": "Importar material al diseño",
"Import…": "Importar…",
"In This Design": "En este diseño",
"In place (surfaces)": "En su posición (superficies)",
@@ -211,7 +208,6 @@
"Mass Center": "Centro de masas",
"Matched restrictions check the final values.": "Las restricciones que se cumplen comprueban los valores finales.",
"Material": "Material",
"Material copied. Existing body assignments are unchanged.": "Material copiado. Se conservan las asignaciones actuales de los cuerpos.",
"Max": "Máx.",
"Mid Radius": "Radio intermedio",
"Middle double-click": "Doble clic central",
@@ -228,7 +224,6 @@
"Move later": "Mover después",
"Move up": "Mover arriba",
"Name": "Nombre",
"Name on import": "Nombre al importar",
"Nearest": "Más cercano",
"New": "Nuevo",
"New Comp.": "Nuevo comp.",
@@ -238,7 +233,6 @@
"No crash reports.": "No hay informes de fallos.",
"No matching appearance presets.": "No hay preajustes de apariencia coincidentes.",
"No matching appearances": "No hay apariencias coincidentes",
"No matching physical materials.": "No hay materiales físicos coincidentes.",
"No visible bodies or sketches to export.": "No hay cuerpos ni bocetos visibles para exportar.",
"None": "Ninguno",
"Not available yet": "Todavía no disponible",
@@ -260,7 +254,6 @@
"Path": "Trayectoria",
"Pattern": "Patrón",
"Per Edge": "Por arista",
"Physical material definitions": "Definiciones de materiales físicos",
"Pick the tool bodies": "Selecciona los cuerpos herramienta",
"Picked": "Seleccionado",
"Picked point": "Punto seleccionado",
@@ -329,7 +322,6 @@
"Search commands": "Buscar comandos",
"Search this library": "Buscar en esta biblioteca",
"Section": "Sección",
"Section position": "Posición de la sección",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Seleccionar otro: mantén pulsado el botón izquierdo sobre el modelo (o haz clic derecho › Seleccionar otro…) para ver todo lo que hay bajo el cursor; vuelve a pulsar en el mismo lugar para seleccionar el siguiente elemento. Haz doble clic con el botón central para volver a Inicio (Preferencias › Navegación).",
"Select bodies (a face picks its body)": "Selecciona cuerpos (al seleccionar una cara se selecciona su cuerpo)",
"Select bodies or faces instead": "Selecciona cuerpos o caras en su lugar",
@@ -341,7 +333,6 @@
"Show Agent Cursor": "Mostrar cursor del agente",
"Show in folder": "Mostrar en carpeta",
"Show the pointer an AI agent moves (#35)": "Mostrar el puntero que mueve un agente de IA (#35)",
"Show tool at cursor": "Mostrar la herramienta junto al cursor",
"Signed axes": "Ejes con signo",
"Size": "Tamaño",
"Sketch constraints": "Restricciones del boceto",
@@ -354,11 +345,9 @@
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft se cerró antes de guardar estos diseños. Puedes abrir su último guardado automático.",
"Specialise": "Especializar",
"Speed": "Velocidad",
"Square": "Cuadrado",
"Stand it up": "Poner en vertical",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Poner en vertical: un cuarto de vuelta alrededor del eje X, como una única operación Mover que puedes deshacer.",
"Stand this part up?": "¿Poner esta pieza en vertical?",
"Start angle": "Ángulo inicial",
"Start bulge": "Abombamiento inicial",
"Start continuity": "Continuidad inicial",
"Step": "Paso",
@@ -423,7 +412,6 @@
"angle, height": "ángulo, altura",
"appearance.dielectric_ior": "Índice de refracción dieléctrico",
"appearance.metallic": "Metalicidad",
"appearance.only_available": "Solo apariencias disponibles",
"appearance.pbr_help": "Visualiza esta superficie en Renderizado o en un PNG del producto. Sin refracción ni transmisión.",
"appearance.pbr_opaque": "PBR opaco",
"appearance.roughness": "Rugosidad",
@@ -585,7 +573,6 @@
"cmd.material_create": "Nuevo material físico",
"cmd.material_delete": "Eliminar material físico",
"cmd.material_edit": "Editar material físico",
"cmd.material_inspect": "Inspeccionar material físico",
"cmd.material_list": "Listar materiales",
"cmd.material_properties_directional": "Módulo elástico direccional",
"cmd.material_properties_validate": "Validar datos del material físico",
@@ -631,45 +618,14 @@
"cmd.plastic_rest": "Apoyo",
"cmd.plastic_rules": "Listar reglas de plástico",
"cmd.plastic_snap_fit": "Encaje a presión",
"cmd.render_aging_describe": "Envejecimiento por capas",
"cmd.render_backends": "Motores de renderizado",
"cmd.render_camera_fit": "Encuadrar sujeto de render",
"cmd.render_camera_get": "Inspeccionar cámara de render",
"cmd.render_cancel": "Cancelar renderizado",
"cmd.render_effects_describe": "Efectos de lente y creativos",
"cmd.render_export": "Exportar imagen renderizada",
"cmd.render_hdr_inspect": "Inspeccionar fuente HDR",
"cmd.render_library_appearance_apply": "Aplicar aspecto opaco de la biblioteca",
"cmd.render_library_describe": "Inspeccionar recurso de renderizado",
"cmd.render_library_environment_apply": "Aplicar entorno HDR incluido",
"cmd.render_library_list": "Explorar la biblioteca de renderizado",
"cmd.render_library_physical_prepare": "Prepare Physical Reference",
"cmd.render_library_preview": "Inspect Original Surface Map",
"cmd.render_library_studio_apply": "Aplicar iluminación de estudio",
"cmd.render_library_studio_prepare": "Preparar iluminación de estudio",
"cmd.render_library_surface_bind": "Save Surface Binding",
"cmd.render_library_surface_prepare": "Preparar mapas de superficie",
"cmd.render_library_surface_unbind": "Remove Surface Binding",
"cmd.render_library_targets": "Buscar destinos de recursos de renderizado",
"cmd.render_optics_bind": "Guardar asignación óptica",
"cmd.render_optics_unbind": "Quitar asignación óptica",
"cmd.render_preflight": "Comprobar solicitud de renderizado",
"cmd.render_preset_apply": "Apply Native Studio",
"cmd.render_preset_resolve": "Review Native Studio",
"cmd.render_quality_prepare": "Review Fixed Sampling Budget",
"cmd.render_result_pin": "Conservar el resultado mostrado",
"cmd.render_result_regrade": "Reeditar máster lineal",
"cmd.render_scene": "Inspeccionar escena de renderizado",
"cmd.render_settings_apply": "Aplicar cámara de renderizado",
"cmd.render_settings_capture_hdr": "Guardar configuración de renderizado con HDR",
"cmd.render_settings_get": "Ajustes de renderizado",
"cmd.render_settings_set": "Guardar ajustes de renderizado",
"cmd.render_shot_duplicate": "Duplicate Render Shot",
"cmd.render_shot_remove": "Remove Render Shot",
"cmd.render_shot_rename": "Rename Render Shot",
"cmd.render_stage_backdrop": "Guardar fondo físico de render",
"cmd.render_stage_clear": "Remove Render Stage",
"cmd.render_stage_ground": "Save Render Ground",
"cmd.render_start": "Renderizar imagen",
"cmd.render_status": "Progreso del renderizado",
"cmd.select_clear": "Borrar selección",
@@ -996,49 +952,6 @@
"manage.material_hint": "Seleccione cuerpos y elija su material físico.",
"manage.parameters_hint": "Edite expresiones y unidades. Los cambios se aplican al pulsar Intro o salir de un campo.",
"manage.rules_hint": "Elija una regla para editarla o cree una nueva. Confirme para guardar la regla.",
"material.editor.add_property": "Añadir propiedad",
"material.editor.add_source": "Añadir fuente",
"material.editor.assigned_density": "Densidad asignada",
"material.editor.axes": "Ejes del material",
"material.editor.axes_numeric": "1 / 2 / 3",
"material.editor.axes_wood": "L / R / T",
"material.editor.copy_name": "Copia de {name}",
"material.editor.density_range": "La densidad asignada debe ser mayor que 0 y no superar 1000 g/cm³.",
"material.editor.designation": "Designación",
"material.editor.duplicate": "Duplicar",
"material.editor.engineering_card": "Ficha de propiedades físicas",
"material.editor.family": "Familia del material",
"material.editor.glass": "Vidrio",
"material.editor.illustrative": "Ilustrativa",
"material.editor.invalid_property": "Tipo de propiedad no admitido.",
"material.editor.isotropic": "Isótropo",
"material.editor.measurement": "Medición",
"material.editor.metal": "Metal",
"material.editor.moisture": "Humedad en base seca",
"material.editor.name_required": "Introduce un nombre de 1 a 128 caracteres.",
"material.editor.no_draft": "No hay ningún borrador de material abierto.",
"material.editor.number_required": "Introduce un valor numérico finito.",
"material.editor.orthotropic": "Ortótropo",
"material.editor.other": "Otra",
"material.editor.polymer": "Polímero",
"material.editor.preserved_appearance": "Apariencia conservada",
"material.editor.public_reference": "Referencia pública",
"material.editor.reference": "Referencia",
"material.editor.save_or_discard": "Guarda o descarta el borrador antes de asignar un material.",
"material.editor.saved": "Material guardado. Las asignaciones a cuerpos no han cambiado.",
"material.editor.scope": "Guardar almacena este material sin asignarlo a cuerpos. Las propiedades y condiciones vacías siguen siendo desconocidas. La densidad de la ficha no cambia la densidad asignada ni la masa.",
"material.editor.source_id": "ID de la fuente",
"material.editor.source_kind": "Tipo de fuente",
"material.editor.source_title": "Título de la fuente",
"material.editor.sources": "Fuentes",
"material.editor.stale": "Este material cambió mientras el borrador estaba abierto. Descártalo y vuelve a abrirlo antes de guardar.",
"material.editor.state": "Estado del material",
"material.editor.strain_rate": "Velocidad de deformación",
"material.editor.symmetry": "Simetría elástica",
"material.editor.temperature": "Temperatura",
"material.editor.unknown_card": "Las propiedades físicas son desconocidas. Puedes guardar solo la densidad asignada y la apariencia existente.",
"material.editor.user_supplied": "Aportada por la usuaria",
"material.editor.wood": "Madera",
"measure.at": "En",
"measure.item": "Elemento {number}",
"messages.ago_h": "hace {n} h",
@@ -1093,248 +1006,21 @@
"recovery.just_now": "justo ahora",
"recovery.min_ago": "hace {count} min",
"recovery.never_saved": "nunca guardado",
"render.adaptive_absolute": "Umbral de error RGB absoluto",
"render.adaptive_coverage": "Objetivo de error de cobertura",
"render.adaptive_enable": "Asignar muestras con diagnóstico independiente",
"render.adaptive_help": "Solo GPU. Se descartan las muestras de diagnóstico independientes y se asignan al menos 16 muestras finales por píxel, hasta el límite Muestras por píxel. La varianza RGB y de cobertura orienta la asignación; pueden omitirse trayectorias raras y quedar ruido al alcanzar el límite. El informe cuenta el trabajo real, sin garantía de ruido ni tiempo.",
"render.adaptive_minimum": "Mínimo de muestras finales",
"render.adaptive_pending": "Disponibles en GPU la asignación adaptativa con diagnóstico independiente y los estratos de Fresnel. Siguen pendientes la convergencia certificada, las garantías de eventos raros y el transporte adaptativo CPU.",
"render.adaptive_pilots": "Muestras de diagnóstico por píxel",
"render.adaptive_relative": "Objetivo de error RGB relativo",
"render.add_ground": "Add fitted ground to setup",
"render.advanced_material_scope": "Disponible un medio homogéneo GPU finito con límites explícitos. Siguen pendientes las rejillas de densidad, vidrio y volumen combinados, superficies subsuperficiales, desplazamiento, transporte espectral y conceptos avanzados sin implementar.",
"render.aging_add": "Añadir capa de envejecimiento",
"render.aging_airborne": "Polvo en suspensión / medio participante",
"render.aging_airborne_help": "El polvo suspendido es un volumen homogéneo finito e independiente. Cambia el transporte y el EXR, no la pila de superficies. Se mantienen las restricciones de volumen, vidrio y reducción de ruido; nunca se añade una atmósfera automáticamente.",
"render.aging_all_bodies": "Todos los cuerpos opacos aptos",
"render.aging_chips": "Pintura desconchada",
"render.aging_class_any": "Todas las clases opacas",
"render.aging_class_dielectric": "Solo superficies no metálicas",
"render.aging_class_metal": "Solo superficies metálicas",
"render.aging_color": "Color de capa · sRGB",
"render.aging_coverage": "Cobertura",
"render.aging_dust": "Polvo depositado",
"render.aging_empty": "Sin capas: se conservan las superficies originales.",
"render.aging_fading": "Recubrimiento descolorido",
"render.aging_grime": "Suciedad y manchas",
"render.aging_help": "Combina hasta ocho capas ópticas, en orden. Óxido y pátina son apariencias, no simulaciones de edad o corrosión. La escala usa milímetros globales. No cambian geometría, masa ni propiedades de ingeniería; vidrio y escenario quedan excluidos.",
"render.aging_metallic": "Fracción metálica de capa",
"render.aging_origin": "Origen del campo · mm globales",
"render.aging_patina": "Pátina verde de cobre",
"render.aging_refresh_targets": "Inspeccionar cuerpos actuales",
"render.aging_roughness": "Rugosidad de capa",
"render.aging_rust": "Óxido de hierro",
"render.aging_scale": "Escala del patrón",
"render.aging_scratches": "Arañazos finos",
"render.aging_target": "Destino y coordenadas",
"render.aging_target_help": "El destino debe conservar el índice y nombre capturados. Las máscaras se anclan al mundo, no siguen piezas móviles. No detectan cavidades ni desgaste geométrico de aristas. Filtra la lista para encontrar otros cuerpos.",
"render.aging_target_search": "Filtrar nombres de cuerpos",
"render.aging_upward": "Depósito en caras superiores",
"render.all_controls_help": "Inspección de todos los controles. Las secciones permiten editar por contexto sin cambiar la petición subyacente.",
"render.appearance_physics_separate": "El aspecto visual no asigna densidad, propiedades térmicas ni otros datos de ingeniería.",
"render.availability_native": "Composición nativa; revisar antes de aplicar",
"render.availability_projection": "Proyección compatible; consulta alcance y omisiones",
"render.availability_reference": "Datos de referencia; no es una asignación automática",
"render.backdrop_alpha_conflict": "Elige el fondo o la salida PNG transparente; el renderizador no ignorará ninguna opción.",
"render.backdrop_enable": "Usar fondo degradado solo para la cámara",
"render.backdrop_first": "Color inicial / central",
"render.backdrop_help": "Este fondo de pantalla no ilumina, proyecta sombras, refleja ni representa terreno. Se excluye del EXR sin procesar. Geometría, luces y entorno HDR son independientes.",
"render.backdrop_horizontal": "Degradado horizontal",
"render.backdrop_radial": "Degradado radial",
"render.backdrop_radius": "Radio",
"render.backdrop_second": "Color final / del borde",
"render.backdrop_vertical": "Degradado vertical",
"render.backdrop_x": "Centro X",
"render.backdrop_y": "Centro Y",
"render.background": "Fondo de la escena",
"render.background_camera_section": "Fondo de cámara · presentación PNG",
"render.background_completed_frozen": "El resultado mostrado es una imagen terminada e inmutable. Editar el fondo no repinta automáticamente ese resultado. Usa Reeditar para cambios de cámara o Renderizar para un escenario físico nuevo.",
"render.background_hdr_separate": "Los HDR actuales iluminan y se reflejan en las superficies; no se muestran automáticamente como una panorámica detrás del objeto. Cambiar el degradado no gira ni recolorea la iluminación.",
"render.background_lighting_section": "Iluminación HDR · independiente del fondo de cámara",
"render.background_no_alpha_override": "El fondo de cámara no se añade silenciosamente a una salida transparente. La reedición usa la cobertura capturada y no vuelve a reducir ruido; el EXR original se conserva.",
"render.background_not_light": "El color de fondo es presentación, no una fuente de luz adicional.",
"render.background_regrade": "Aplicar al máster bruto retenido",
"render.background_stage_occlusion": "Esta toma incluye geometría de escenario. Un suelo o pared grandes pueden llenar el encuadre y ocultar el fondo de cámara. Elimina o ajusta el escenario de forma explícita, o cambia su reflectancia. Reeditar no elimina geometría capturada.",
"render.background_workflow": "Elige el tipo de fondo: color/degradado solo para la cámara, escenario físico que recibe luz o iluminación HDR. Son independientes. Vista previa es una vista de modelado; renderiza o reedita un máster retenido para ver el acabado de cámara.",
"render.base_appearance": "Aspecto editable del cuerpo",
"render.base_apply": "Aplicar aspecto al cuerpo",
"render.base_apply_help": "Asignación visual deshacible al cuerpo exacto revisado. Los aspectos por cara y materiales físicos permanecen separados.",
"render.base_color": "Color base · sRGB",
"render.base_metallic": "Fracción metálica",
"render.base_opaque_only": "Este editor conserva la semántica opaca. Utiliza los controles de vidrio para transmisión óptica.",
"render.bloom_enable": "Resplandor de presentación",
"render.bloom_help": "Resplandor creativo del resultado a partir de altas luces lineales, extendido con tres pasadas normalizadas. Radio en píxeles de salida. Se añade en luz lineal de pantalla antes de sRGB; no simula lentes ni volúmenes ni mejora la convergencia. El EXR original no cambia.",
"render.bloom_opaque": "El resplandor requiere salida opaca. Se rechaza el PNG transparente, sin aplanarlo silenciosamente.",
"render.bloom_radius": "Radio del resplandor",
"render.bloom_strength": "Intensidad del resplandor",
"render.bloom_threshold": "Umbral lineal de altas luces",
"render.browse_capabilities": "Consultar catálogo completo de capacidades",
"render.browse_environments": "Explorar entornos HDR originales",
"render.browse_native_scenes": "Explorar estudios nativos",
"render.browse_physical": "Consultar referencias físicas",
"render.browse_quality": "Consultar especificaciones de calidad",
"render.browse_rigs": "Explorar conjuntos de luces",
"render.browse_scene_specs": "Explorar especificaciones de escenas avanzadas",
"render.browse_surfaces": "Explorar superficies originales",
"render.browser_library": "Biblioteca",
"render.browser_model": "Modelo",
"render.camera_distance": "Distancia de la cámara al objetivo",
"render.camera_exact_help": "Milímetros del mundo; ángulos mostrados en grados y guardados en radianes. La navegación utiliza la misma cámara que el visor.",
"render.camera_fov": "Campo de visión vertical",
"render.camera_pitch": "Inclinación de cámara",
"render.camera_roll": "Giro",
"render.camera_set": "Establecer cámara de render",
"render.camera_target": "Posición del objetivo · mm",
"render.camera_yaw": "Azimut",
"render.chromatic_offset": "Separación cromática",
"render.clear_stage": "Remove saved stage",
"render.copy_request": "Copiar JSON",
"render.cpu_aa_help": "Más muestras suavizan los bordes y aumentan el tiempo de renderizado.",
"render.cpu_area_add": "Añadir luz rectangular CPU",
"render.cpu_area_editor": "Luces rectangulares CPU",
"render.cpu_area_editor_help": "Vectores de lado completos en milímetros; U por V señala la cara emisora. No son los semilados de las luces GPU.",
"render.cpu_area_full_u": "Lado U completo · mm",
"render.cpu_area_full_v": "Lado V completo · mm",
"render.cpu_area_samples": "Muestras por luz de área",
"render.cpu_binding_confirm": "Confirmar este cuerpo",
"render.cpu_binding_stale": "La escena ha cambiado. Confirma de nuevo este cuerpo de los reflejos antes de renderizar.",
"render.cpu_body_choose": "Elegir un cuerpo",
"render.cpu_body_filter": "Filtrar cuerpos por nombre",
"render.cpu_body_filter_more": "Afina el filtro para ver más cuerpos.",
"render.cpu_body_missing": "Un cuerpo de los reflejos falta, está oculto, es ambiguo o su nombre supera 128 bytes. Vuelve a elegirlo.",
"render.cpu_budget": "Perfil de recursos de CPU",
"render.cpu_budget_bodies": "Máximo de cuerpos",
"render.cpu_budget_compact": "Compacto (límites anteriores)",
"render.cpu_budget_details": "Límites de recursos",
"render.cpu_budget_help": "Selecciona límites mayores de forma explícita. La resolución y las muestras nunca se reducen automáticamente.",
"render.cpu_budget_memory": "Memoria máxima del trabajo",
"render.cpu_budget_not_guarantee": "Son límites de admisión, no una reserva de RAM ni una garantía de velocidad. Geometría, acumulación y vistas previas comparten el presupuesto de memoria; el renderizado se puede cancelar.",
"render.cpu_budget_resize": "La configuración supera este perfil de CPU o su presupuesto de muestras. Acepta unas dimensiones menores de forma explícita o selecciona un perfil superior.",
"render.cpu_budget_studio": "Estudio (imágenes grandes)",
"render.cpu_budget_triangles": "Máximo de triángulos",
"render.cpu_budget_workstation": "Estación de trabajo (escenas pesadas)",
"render.cpu_captured_hdr": "HDR capturado para esta imagen",
"render.cpu_check_capture_help": "Comprobar e Iniciar verifican la misma huella del archivo o usan el HDR del diseño. Cada imagen conserva su fuente capturada.",
"render.cpu_check_changed": "Los controles o el modelo han cambiado desde esta comprobación. Comprueba de nuevo antes de renderizar.",
"render.cpu_constant_help": "La radiancia constante llega a los rayos reflejados o transmitidos. No aporta iluminación difusa directa.",
"render.cpu_custom_directions": "Sustituir el preajuste por luces direccionales",
"render.cpu_direction": "Dirección hacia la luz (X, Y, Z)",
"render.cpu_directional": "Luz direccional",
"render.cpu_directional_add": "Añadir luz direccional",
"render.cpu_environment": "Entorno CPU",
"render.cpu_environment_background": "El fondo de la imagen se mantiene separado de la iluminación del entorno.",
"render.cpu_environment_bounds": "La intensidad CPU debe ser 0–64, el giro HDR finito dentro de ±1 000 000 radianes y las muestras HDR 1, 4, 16 o 64.",
"render.cpu_environment_constant": "Entorno constante para rayos secundarios",
"render.cpu_environment_none": "Sin entorno CPU",
"render.cpu_environment_samples": "Muestras de iluminación HDR",
"render.cpu_exposure_help": "La exposición CPU debe estar entre −16 y 16 EV. Una exposición GPU fuera de ese rango se conserva hasta que la cambies.",
"render.cpu_fixed": "Imagen fija",
"render.cpu_hdr_help": "Radiance .hdr, panorama 2:1, hasta 2048 × 1024 y 16 MiB. La CPU exige primarias sRGB/D65 explícitas en el archivo; rechaza primarias sin definir y EXR.",
"render.cpu_hdr_reflection_help": "Los reflejos rugosos con HDR requieren superficies totalmente metálicas en el cuerpo y sus caras. Comprobar indica las combinaciones no admitidas.",
"render.cpu_key_rim": "Principal y contorno",
"render.cpu_last_check": "Última comprobación CPU",
"render.cpu_light_help": "Hasta ocho luces direccionales. Una lista personalizada vacía las apaga; elegir un preajuste restaura sus luces. El color es RGB lineal y la intensidad es relativa, no vatios.",
"render.cpu_light_intensity": "Intensidad relativa (0–64)",
"render.cpu_light_minimum": "Distancia mínima de atenuación",
"render.cpu_light_position": "Posición en el mundo (X, Y, Z)",
"render.cpu_light_reference": "Distancia de referencia de la intensidad",
"render.cpu_light_remove": "Quitar luz",
"render.cpu_lighting": "Preajuste de iluminación",
"render.cpu_lights": "Luces CPU",
"render.cpu_linear_color": "Color lineal (R, G, B)",
"render.cpu_neutral": "Neutro",
"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_point": "Luz puntual",
"render.cpu_point_add": "Añadir luz puntual",
"render.cpu_point_help": "Hasta ocho luces puntuales, añadidas a la iluminación direccional elegida. Las distancias están en mm; la mínima no puede superar la de referencia.",
"render.cpu_primary_samples": "Muestras de borde por píxel",
"render.cpu_progressive": "CPU progresivo",
"render.cpu_progressive_help": "Acumula el integrador CPU actual en pasadas de una muestra primaria. Los reflejos seleccionados requieren una muestra; luces de área, dieléctricos y anisotropía requieren Imagen fija.",
"render.cpu_progressive_limits": "Máximo de 1.048.576 píxeles y 16.777.216 evaluaciones de píxel × muestra. Cambiar las muestras nunca redimensiona la imagen automáticamente.",
"render.cpu_progressive_seed": "Semilla de muestreo CPU",
"render.cpu_progressive_spp": "muestras por píxel",
"render.cpu_reflection_add": "Añadir reflejo rugoso",
"render.cpu_reflection_depth": "Profundidad de reflejos (1–8)",
"render.cpu_reflection_empty": "Sin cuerpos seleccionados: los reflejos de la escena están desactivados.",
"render.cpu_reflection_help": "Elige cuerpos y selecciona Desactivado, Espejo o Rugoso. Rugoso usa la rugosidad asignada al material. Más muestras y rebotes aumentan el tiempo; el transporte especular es acotado y no calcula iluminación global.",
"render.cpu_reflection_limit": "Se admiten hasta 64 cuerpos con reflejos.",
"render.cpu_reflection_mirror": "Espejo",
"render.cpu_reflection_off": "Desactivado",
"render.cpu_reflection_rough": "Rugoso",
"render.cpu_reflection_samples": "Muestras de reflejos (1–64)",
"render.cpu_reflections": "Reflejos CPU",
"render.cpu_runtime_help": "Los ajustes numéricos de luces y entorno se guardan con la configuración. Los reflejos por cuerpo y los archivos HDR siguen siendo temporales. Se conservan los valores existentes de luces de área y conos de foco.",
"render.cpu_sampling_mode": "Modo de muestreo CPU",
"render.cpu_shadows": "Proyectar sombras",
"render.cpu_summary": "Trazado CPU · reflejos · HDR · luces direccionales y puntuales",
"render.cpu_warm_cool": "Cálido y frío",
"render.diag_bodies": "Cuerpos visibles",
"render.diag_job_details": "Detalles del trabajo capturado",
"render.diag_last_check": "Última comprobación",
"render.diag_memory": "Estimación lógica de memoria",
"render.diag_not_implemented": "Límites explícitos de capacidades",
"render.diag_preflight_details": "Detalles de la comprobación",
"render.diag_request": "Petición actual exacta",
"render.diag_triangles": "Triángulos",
"render.discard_continue": "Descartar borrador y continuar",
"render.draft_confirm_help": "El borrador contiene cambios pendientes. Guárdalos antes de cambiar, descártalos explícitamente o sigue editando.",
"render.draft_confirm_title": "Conservar los ajustes editados",
"render.effects_gpu_only": "Estos efectos requieren trazado GPU. Cambiar de pestaña no cambia automáticamente el motor.",
"render.effects_help": "Combina filtros de color ordenados y efectos de lente en imagen. Render y reedición del máster usan los mismos ajustes guardados. El EXR bruto no cambia. Son controles artísticos originales, no emulaciones calibradas de lentes o películas comerciales.",
"render.effects_reset_lens": "Restablecer efectos de lente",
"render.effects_seed": "Semilla del patrón",
"render.export_display": "Exportar: PNG de visualización",
"render.export_linear": "Exportar: EXR lineal",
"render.exr_pending": "La GPU puede conservar EXR lineal original. La compresión, las imágenes multicapa y los pases auxiliares siguen sin estar disponibles.",
"render.film_grain": "Grano fotográfico",
"render.fit": "Ajustar producto",
"render.flare_ghosts": "Anillos por fuente",
"render.flare_halo": "Halo de flare",
"render.flare_streak": "Estela estilo anamórfico",
"render.flare_strength": "Intensidad del flare",
"render.flare_threshold": "Umbral lineal del flare",
"render.gpu_depth": "Profundidad máxima del trazado",
"render.gpu_options_help": "Las muestras, la profundidad, la semilla y la exposición se guardan con esta configuración. Solo se guarda el método de imagen activo.",
"render.gpu_path_failed": "Trazado GPU no disponible",
"render.gpu_path_first_setup": "La primera preparación en esta GPU puede tardar varios minutos; puedes seguir usando SolidCraft. Los siguientes inicios reutilizan los sombreadores compilados cuando el controlador los conserva.",
"render.gpu_path_preparing": "Trazado GPU: compilando sombreadores…",
"render.gpu_seed": "Semilla aleatoria",
"render.gpu_unavailable": "El trazado GPU aún no está disponible en este dispositivo.",
"render.ground_apply": "Ajustar y guardar suelo",
"render.ground_gap": "Separación geométrica explícita",
"render.ground_linear_rgb": "Reflectancia difusa · RGB lineal",
"render.ground_margin": "Margen de extensión del suelo",
"render.hdr_details": "Detalles de la fuente HDR",
"render.hdr_embedded": "HDR guardado dentro del diseño",
"render.hdr_embedded_help": "El renderizado usa los bytes incluidos en el diseño; el archivo original ya no es necesario.",
"render.hdr_file_captured": "Archivo HDR comprobado. Guardar incluirá estos bytes; si el archivo cambia, compruébalo de nuevo explícitamente.",
"render.hdr_inspect": "Comprobar archivo HDR",
"render.hdr_inspect_required": "Selecciona y comprueba un archivo HDR antes de guardar o renderizar.",
"render.hdr_primaries_declared": "La fuente declara sus primarias de color.",
"render.hdr_primaries_srgb": "Primarias de color: sRGB/D65",
"render.hdr_primaries_undeclared": "La fuente no declara primarias; revisa los requisitos de color del motor.",
"render.hdr_resource_missing": "El recurso HDR guardado no existe en este diseño.",
"render.image_advanced": "Opciones avanzadas de GPU",
"render.image_cancelled": "Renderizado cancelado",
"render.image_cancelling": "Cancelando",
"render.image_completed": "Imagen lista para exportar",
"render.image_encoding": "Codificando PNG",
"render.image_export": "Exportar imagen terminada",
"render.image_exposure": "Exposición",
"render.image_export": "Exportar PNG",
"render.image_failed": "Error de renderizado",
"render.image_height": "Altura",
"render.image_help": "Imagen de vista previa por CPU · PNG · transparencia aproximada",
"render.image_host_missing": "No hay un motor de imagen instalado.",
"render.image_method": "Método de imagen",
"render.image_overwrite": "Sustituir archivo existente",
"render.image_preparing": "Preparando imagen",
"render.image_progressive": "Renderizado progresivo",
"render.image_queued": "Esperando para renderizar",
"render.image_reduce": "Prueba con una imagen más pequeña o una escena más sencilla.",
"render.image_running": "Renderizando imagen",
"render.image_samples": "Muestras por píxel",
"render.image_scene_stale": "Termina la operación de modelado o actualiza la escena antes de comprobar o iniciar una imagen.",
"render.image_stale": "Esta imagen utiliza la escena capturada antes de los cambios posteriores.",
"render.image_start": "Iniciar renderizado",
"render.image_supported": "La solicitud de renderizado es compatible; la cola y la memoria disponible se comprobarán de nuevo al iniciar.",
@@ -1344,347 +1030,20 @@
"render.image_unknown": "Estado del renderizado no disponible",
"render.image_use_largest": "Usar el tamaño máximo para esta proporción:",
"render.image_width": "Anchura",
"render.include_rig_cards": "Include passive cards and flags",
"render.inspect_scene": "Actualizar resumen de la escena",
"render.keep_editing": "Seguir editando",
"render.layer_down": "Bajar",
"render.layer_enabled": "Capa activada",
"render.layer_remove": "Eliminar capa",
"render.layer_strength": "Intensidad de capa",
"render.layer_up": "Subir",
"render.lens_effects_help": "El flare usa hasta cuatro altas luces separadas de la imagen lineal real. Anillos, halo, estelas, separación cromática y distorsión son aproximaciones artísticas en imagen. Los efectos espaciales requieren salida opaca; las muestras exteriores extienden el borde. El grano es determinista. No cambian física, EXR ni geometría.",
"render.lens_effects_title": "Lente y fotografía",
"render.library_all": "Catálogo",
"render.library_all_states": "Todos los estados",
"render.library_applied": "Variante del recurso aplicada. Comprueba la solicitud completa antes de renderizar.",
"render.library_apply_hdr": "Guardar con este entorno HDR",
"render.library_apply_maps": "Usar mapas de superficie GPU",
"render.library_apply_studio": "Guardar la variante de iluminación",
"render.library_apply_uniform": "Aplicar aspecto opaco uniforme",
"render.library_authored_help": "Los recursos originales son ilustrativos, no datos de ingeniería medidos. Cada acción aplica únicamente la variante indicada.",
"render.library_blocked": "Bloqueado / requiere calibración",
"render.library_capabilities": "Especificaciones de capacidades",
"render.library_center": "Centro de la iluminación",
"render.library_clear_search": "Limpiar",
"render.library_details": "Especificación y procedencia",
"render.library_emitters_only_ack": "Usar solo emisores; omitir las tarjetas pasivas indicadas",
"render.library_empty": "No hay recursos coincidentes. Prueba otra búsqueda o filtro.",
"render.library_environment_concepts": "Conceptos de entornos",
"render.library_extent": "Extensión del objeto",
"render.library_favorite": "Añadir favorito",
"render.library_favorites": "Favoritos",
"render.library_hdr": "Entornos HDR incluidos",
"render.library_hdr_help": "Incorpora el HDR original a la configuración guardada. Convierte explícitamente el origen azimutal; tras guardar no se necesita ningún archivo externo.",
"render.library_inspect_only": "Selecciona para inspeccionar; no se aplica nada automáticamente. Favoritos e historial pertenecen a la sesión de este documento.",
"render.library_light_concepts": "Conceptos de iluminación",
"render.library_maps_help": "Built-in maps can be saved with pinned sources and local occurrence mapping. Temporary optical overrides remain a separate mode.",
"render.library_material_concepts": "Conceptos de materiales",
"render.library_native_scenes": "Estudios nativos",
"render.library_next": "Página siguiente",
"render.library_omitted": "No incluido",
"render.library_physical_profiles": "Physical reference profiles",
"render.library_previous": "Página anterior",
"render.library_quality_concepts": "Especificaciones de calidad",
"render.library_recent": "Recientes",
"render.library_replace_environment": "Sustituir explícitamente el entorno existente",
"render.library_replace_studio": "Replace saved lighting, environment and stage",
"render.library_results": "Recursos encontrados",
"render.library_rig_help": "Fit once at an explicit center and physical extent. Complete rigs include passive cards; calibration blockers still refuse. Conservative emitter/card clearance is checked before saving.",
"render.library_rigs": "Configuraciones de iluminación creadas",
"render.library_save_maps": "Save maps to this setup",
"render.library_scene_bounds": "Usar los límites de la escena capturada",
"render.library_scenes": "Especificaciones de escenas",
"render.library_search": "Buscar en la biblioteca",
"render.library_select_hint": "Elige un recurso en la Biblioteca para ver sus acciones disponibles y sus límites.",
"render.library_selene": "Aspectos opacos de SELENE",
"render.library_selene_shots": "Especificaciones de tomas de SELENE",
"render.library_short_blocked": "No disponible",
"render.library_short_native": "Composición nativa",
"render.library_short_projection": "Proyección compatible",
"render.library_short_reference": "Solo referencia",
"render.library_short_specification": "Solo especificación",
"render.library_source_preview": "Cargar vista del mapa original",
"render.library_source_preview_help": "Mapa de color base original. No es una vista renderizada ni una medición del material.",
"render.library_specification_only": "Solo especificación: no es un preajuste ejecutable completo. Los efectos, recursos y roles del objeto requieren implementación y asociación explícitas.",
"render.library_specs": "Solo especificaciones",
"render.library_status": "Disponibilidad",
"render.library_surfaces": "Superficies PBR creadas",
"render.library_target": "Cuerpo de destino",
"render.library_title": "Biblioteca de recursos de renderizado",
"render.library_unfavorite": "Quitar favorito",
"render.library_uniform_ack": "Usar la aproximación uniforme sin efectos espaciales ni avanzados",
"render.library_usable": "Proyecciones ejecutables",
"render.linear_help": "Conserva un máster RGBA de 32 bits en coma flotante antes de la reducción de ruido, exposición, mapeo tonal, filtros y fondo. sRGB lineal/D65, premultiplicado por cobertura. Requiere memoria adicional. Sin compresión ni pases extra.",
"render.linear_retain": "Conservar EXR lineal sin procesar",
"render.linear_unavailable": "La imagen terminada seleccionada no conserva EXR. Activa la opción antes de renderizar; no se puede recuperar la radiancia original de un PNG.",
"render.look_add": "Añadir filtro de color",
"render.look_bleach": "Inspirado en bleach bypass",
"render.look_cool": "Frío",
"render.look_cyanotype": "Inspirado en cianotipia",
"render.look_faded": "Copia desvanecida",
"render.look_mono": "Monocromo",
"render.look_sepia": "Sepia",
"render.look_strength": "Intensidad del filtro",
"render.look_teal_orange": "Verde azulado / naranja",
"render.look_title": "Pila de filtros creativos",
"render.look_warm": "Cálido",
"render.mapping_scale": "Multiplicador de repetición física",
"render.material_absorption": "Absorción R · G · B (por mm)",
"render.material_add": "Añadir o editar este cuerpo",
"render.material_assignments": "Asignaciones por cuerpo",
"render.material_binding_enable": "Incluir los ajustes de este cuerpo",
"render.material_box": "Caja alineada con los ejes",
"render.material_browse": "Elegir imagen…",
"render.material_certification": "El vidrio cerrado usa la distancia geométrica. La preparación GPU valida topología, orientación, solapamientos y límites ópticos; Comprobar solo admite la petición.",
"render.material_choose_body": "Elegir un cuerpo de la escena",
"render.material_closed": "Malla sólida cerrada",
"render.material_coat": "Recubrimiento sobre el material base",
"render.material_coat_enable": "Activar recubrimiento",
"render.material_coat_help": "Recubrimiento dieléctrico ilustrativo sobre la apariencia opaca existente. Un peso cero conserva la base.",
"render.material_color_map": "Imagen de color base (sRGB)",
"render.material_color_replace": "El mapa base sustituye el color del cuerpo",
"render.material_custom": "Óptica ilustrativa personalizada",
"render.material_enable": "Usar estos ajustes de materiales GPU",
"render.material_filter": "Filtrar cuerpos de la escena…",
"render.material_filter_help": "Se muestran hasta 128 coincidencias. Ajusta el filtro para escenas grandes.",
"render.material_glass": "Vidrio y absorción óptica",
"render.material_glass_enable": "Usar vidrio físico en este cuerpo",
"render.material_glass_exclusive": "Este motor no admite vidrio con recubrimiento ni mapas en el mismo cuerpo.",
"render.material_glass_help": "Recetas originales e ilustrativas. Las muestras indican transmitancia, no un render. La absorción es −ln(transmitancia lineal) / distancia de referencia; no se cambia el alfa.",
"render.material_gpu_required": "Estos materiales activos requieren trazado GPU. Selecciona GPU o desactiva los ajustes antes de Comprobar o Iniciar.",
"render.material_ior": "Índice de refracción (IOR)",
"render.material_limits": "Máximo de 128 modificaciones de cuerpos, 32 vidrios separados, 64 recubrimientos y 128 asociaciones de texturas. Los mapas idénticos comparten memoria; se mantienen los límites reales del dispositivo y de memoria total.",
"render.material_map_invalid": "Las rutas de mapas deben ser locales y acotadas; la intensidad de normales debe ser finita y estar entre 0 y 1.",
"render.material_map_required": "Elige una imagen de color o de normales antes de usar mapas.",
"render.material_maps": "Mapas planos de color y normales",
"render.material_maps_enable": "Activar mapas para este cuerpo opaco",
"render.material_maps_help": "PNG/JPEG hasta 1024×1024; máximo total de 16 MiB codificados. U y V son vectores ortogonales de repetición en mm globales (máximo 128 repeticiones). Color sRGB y normales OpenGL +Y. Los archivos se capturan al Comprobar/Iniciar.",
"render.material_metallic_map": "Mapa de metalicidad (lineal)",
"render.material_normal_map": "Imagen de normales (OpenGL, +Y)",
"render.material_normal_strength": "Intensidad de normales (0–1)",
"render.material_origin": "Origen del mapa X · Y · Z (mm)",
"render.material_refresh": "Actualizar cuerpos de la escena",
"render.material_refresh_needed": "La escena ha cambiado. Actualiza la lista antes de añadir un cuerpo.",
"render.material_remove": "Eliminar esta asignación",
"render.material_repeat_u": "Una repetición U X · Y · Z (mm)",
"render.material_repeat_v": "Una repetición V X · Y · Z (mm)",
"render.material_revalidate": "Revalidar este cuerpo",
"render.material_revalidate_needed": "Esta asignación pertenece a una revisión anterior. Revalida la identidad exacta del cuerpo antes de renderizar.",
"render.material_roughness": "Rugosidad del recubrimiento (0,045–1)",
"render.material_roughness_map": "Mapa de rugosidad (lineal)",
"render.material_shadow": "Transmitir rayos de sombra rectos",
"render.material_target_missing": "El ordinal y el nombre del cuerpo ya no coinciden. Elimina esta asignación y elige de nuevo el cuerpo deseado.",
"render.material_thickness": "Espesor de la lámina (mm)",
"render.material_thin": "Lámina delgada",
"render.material_title": "Materiales de la imagen GPU",
"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.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.",
"render.method_raster": "Raster CPU",
"render.navigation_help": "La cámara se mueve como en Diseño (Preferencias › Navegación). Guardar configuración registra la cámara actual.",
"render.nested_enable": "Dieléctricos anidados certificados",
"render.nested_help": "Hasta 16 capas contenidas: cada padre debe ser convexo y cada hijo estar estrictamente dentro. Se rechazan intersecciones, contactos, láminas finas y sombras transmitidas en línea recta. La cámara interior usa la jerarquía certificada. No crea la superficie del líquido ni fusiona fronteras CAD en contacto.",
"render.opaque_image_summary": "PNG de vista previa · PBR opaco · transparencia combinada",
"render.opaque_preview_help": "PBR opaco y colores sencillos; transparencia combinada. La refracción, HDRI y el renderizado final aún no están disponibles.",
"render.optics_remove": "Quitar óptica guardada",
"render.optics_replace": "Sustituir explícitamente la óptica guardada del objetivo",
"render.optics_save": "Guardar esta óptica en la toma",
"render.optics_saved": "Objetivos ópticos guardados",
"render.optics_saved_help": "La identidad exacta de definición, operación e instancia se conserva al reabrir sin conexión. Los objetivos ausentes, sustituidos u ocultos se rechazan; los materiales de ingeniería no cambian. Guarda o descarta los cambios numéricos antes de quitar un objetivo.",
"render.output_format_status": "PNG es la imagen final exacta. El EXR opcional de GPU conserva radiancia lineal FLOAT32 antes de los filtros. La exportación no sustituye el resultado por la previsualización ni por otro trabajo.",
"render.output_lock_ratio": "Bloquear proporción",
"render.output_over_budget": "La imagen supera el presupuesto seleccionado. Elige explícitamente otro tamaño o perfil; no se reduce automáticamente.",
"render.output_ratio_refused": "La dimensión vinculada supera el límite del método. Se conserva el tamaño anterior; desbloquea la proporción o elige un valor menor.",
"render.output_swap_orientation": "Intercambiar ancho y alto",
"render.page_aging": "Envejecimiento y depósitos",
"render.page_all": "Todos los controles",
"render.page_appearance": "Aspecto",
"render.page_background": "Fondos",
"render.page_camera": "Cámara",
"render.page_diagnostics": "Diagnóstico",
"render.page_effects": "Lente y acabados",
"render.page_environment": "Entorno",
"render.page_lighting": "Iluminación",
"render.page_output": "Salida",
"render.page_scene": "Escena",
"render.passive_stage": "Suelo y escenario pasivo",
"render.passive_stage_help": "Finite passive panels belong only to rendered images. They do not add CAD bodies or mass. Placement is fixed after applying; intersecting product bounds refuse. CPU does not provide diffuse bounce lighting.",
"render.perspective": "Cámara en perspectiva",
"render.physical_acknowledge": "I understand this partial reference needs grade, condition and source review",
"render.physical_associations": "Physical candidates and roles",
"render.physical_background_help": "Suelo y pared físicos pasivos, distintos del fondo de cámara. El ciclorama usa 24 franjas poligonales; no es infinito ni una superficie analíticamente suave. Solo sustituye el escenario con tu confirmación.",
"render.physical_copy_plan": "Copy plan with sources and omissions",
"render.physical_cyclorama": "Ajustar ciclorama poligonal",
"render.physical_floor_wall": "Ajustar suelo y pared físicos",
"render.physical_material_name": "Proposed reference material name",
"render.physical_omissions": "Properties not representable in the native card",
"render.physical_plan_only": "Read-only plan prepared. No material was created or assigned. Keep its extended profile and omissions when reviewing the native card.",
"render.physical_prepare_plan": "Prepare creation plan (does not apply)",
"render.physical_separate": "Appearance is not material identification. These are sourced references, not certified effective properties. Existing mass, engineering materials and optics are unchanged.",
"render.physical_unknown": "No numerical candidate is established. Grade, formulation or construction must be specified; unknown is not zero.",
"render.post_contrast": "Contraste",
"render.post_gain_b": "Ganancia azul",
"render.post_gain_g": "Ganancia verde",
"render.post_gain_r": "Ganancia roja",
"render.post_help": "Solo presentación creativa. Las ganancias RGB preceden al mapeo tonal; contraste, saturación y viñeta se aplican después en luz lineal de pantalla. Renderizar crea muestras nuevas. Reeditar utiliza el máster bruto retenido sin reducir ruido ni lanzar rayos nuevos.",
"render.post_reset": "Restablecer filtros",
"render.post_saturation": "Saturación",
"render.post_title": "Filtros de presentación",
"render.post_vignette": "Viñeta",
"render.preview": "Vista previa integrada",
"render.preview_help": "Sombreado por rasterización y transparencia combinada. La refracción, HDRI y el renderizado final aún no están disponibles.",
"render.preview_image_settings_help": "La vista previa mantiene una iluminación de estudio fija. La exposición y la iluminación de imagen se aplican al trabajo al Comprobar o Iniciar.",
"render.preview_retry": "Reintentar vista previa",
"render.preview_unavailable_help": "La vista previa no está disponible. Los ajustes guardados y los controles de imagen siguen accesibles. Cambia la escena o reintenta expresamente; una escena rechazada no se reconstruye en cada fotograma.",
"render.preview_upload": "Datos de vértices de la vista previa",
"render.preview_upload_help": "Bytes exactos de los vértices de vista previa, no memoria total de GPU o proceso. No se simplifica la geometría. Los trabajos de imagen tienen admisión e iluminación propias.",
"render.progressive_image_summary": "Vista previa PNG · PBR opaco · muestreo progresivo",
"render.quality_ack": "Aceptar la proyección de presupuesto fijo y sus omisiones",
"render.quality_apply": "Usar este presupuesto fijo",
"render.quality_projection_help": "Los perfiles Q incluyen requisitos futuros. Esta acción proyecta solo un presupuesto PNG de muestras fijas y omite parada adaptativa, salida lineal y límites de transmisión independientes.",
"render.quality_review": "Revisar presupuesto de muestras",
"render.queue_pending": "Esta interfaz no contiene cola automática de producción ni renderizador de animaciones. Las imágenes terminadas se exportan por separado.",
"render.radial_distortion": "Distorsión radial",
"render.regrade_action": "Reeditar máster bruto (sin reducción de ruido)",
"render.regrade_help": "Crea un PNG independiente a partir del máster bruto del resultado seleccionado y estos controles de presentación. No traza rayos, cambia tamaño, cámara o luces capturados, ni sobrescribe el EXR. No vuelve a reducir el ruido: el máster no conserva las guías del filtro. El resultado se exporta mediante los controles habituales.",
"render.reload_settings": "Recargar ajustes guardados",
"render.replace_ground": "Confirmar sustitución del escenario existente",
"render.replace_saved_surface": "Replace an existing saved binding for this target",
"render.restore_camera": "Restaurar cámara guardada",
"render.result_actual_help": "Un píxel de la imagen por cada píxel de la pantalla.",
"render.result_capture": "Revisión capturada",
"render.result_current": "La revisión capturada del modelo está actualizada.",
"render.result_empty": "Todavía no hay ninguna imagen terminada. Comprueba los ajustes e inicia un renderizado.",
"render.result_fit": "Ajustar imagen",
"render.result_job": "Trabajo",
"render.result_navigation": "El desplazamiento y el zoom siguen las preferencias de Navegación. Espacio + arrastrar desplaza la imagen.",
"render.result_open": "Abrir la imagen terminada en el lienzo principal.",
"render.result_preview": "Vista previa en vivo",
"render.result_provisional": "Imagen CPU provisional",
"render.result_provisional_help": "Este avance no se puede exportar. Exportar conserva el último resultado terminado; al cancelar se vuelve a ese resultado.",
"render.result_reduced_display": "Vista reducida al límite del dispositivo o de la caché. El PNG conservado y la exportación mantienen la resolución completa; esta vista reducida no permite inspección nativa al 100%.",
"render.result_retained": "Se muestra la última imagen terminada; el nuevo renderizado todavía no la ha sustituido.",
"render.result_sequence": "Avance",
"render.result_title": "Resultado del renderizado",
"render.return_design": "Volver a Diseño",
"render.review_stale": "Esta revisión ha caducado. Resuelve o comprueba de nuevo antes de aplicar.",
"render.ribbon_aging": "Envejecimiento",
"render.ribbon_backgrounds": "Fondos",
"render.ribbon_check": "Comprobar",
"render.ribbon_check_help": "Valida exactamente el borrador que enviará Render. Sin cambiar de motor, reducir resolución ni sustituir recursos silenciosamente.",
"render.ribbon_edit_lights": "Editar luces",
"render.ribbon_effects": "Efectos",
"render.ribbon_environment_help": "La iluminación HDR, el fondo exclusivo de cámara y las superficies físicas son controles independientes, con confirmación para sustituirlos.",
"render.ribbon_finish_dialog": "Finaliza antes el diálogo de archivo o confirmación.",
"render.ribbon_frame": "Encuadrar",
"render.ribbon_hdr_library": "Biblioteca HDR",
"render.ribbon_job_active": "Ya hay un render en curso; cancélalo explícitamente antes de iniciar otro.",
"render.ribbon_light_rigs": "Iluminación",
"render.ribbon_lights_help": "Revisa conjuntos originales o edita luces compatibles; la configuración actual no se sustituye hasta aplicar el cambio.",
"render.ribbon_materials": "Materiales",
"render.ribbon_materials_help": "Abre el inspector compartido de apariencia y la biblioteca de superficies originales. Los cambios ópticos no alteran propiedades de ingeniería sin avisar.",
"render.ribbon_no_active_job": "No hay ningún render activo que cancelar.",
"render.ribbon_no_result": "Completa un render antes de exportar su imagen.",
"render.ribbon_no_saved_camera": "Guarda una toma antes de recuperar su cámara.",
"render.ribbon_saved_camera": "Cámara guardada",
"render.ribbon_shots": "Tomas",
"render.ribbon_studios": "Estudios",
"render.ribbon_studios_help": "Explora composiciones de estudio implementadas y revisa lo que sustituyen antes de aplicarlas. Las escenas conceptuales siguen identificadas.",
"render.ribbon_surface_maps": "Mapas",
"render.sampling_title": "Muestreo adaptativo y transmisión",
"render.save_continue": "Guardar y continuar",
"render.save_setup": "Guardar configuración",
"render.saved_maps_runtime_conflict": "Saved surface bindings cannot be mixed with temporary optical overrides or an unsaved HDR. Save the HDR or remove the temporary inputs explicitly.",
"render.saved_surface_remove": "Remove saved binding",
"render.saved_surfaces": "Saved surface bindings",
"render.saved_surfaces_help": "Pinned built-in maps follow the selected occurrence in local millimeters. A replaced or missing target is an error, never an automatic reassignment.",
"render.scene_apply": "Aplicar estudio revisado",
"render.scene_consent": "Confirmar sustitución de iluminación, entorno, exposición y escenario",
"render.scene_preserved_fields": "Se conservan cámara, muestreo, dimensiones, asignaciones ópticas, geometría CAD y materiales físicos.",
"render.scene_preserves_design": "Compón iluminación y escenario pasivo para el objeto visible. Geometría, materiales físicos y cámara se mantienen separados.",
"render.scene_replaces": "Sustituye",
"render.scene_review": "Resolver y revisar",
"render.scene_review_help": "Revisa la composición nativa completa antes de aplicar. No se sustituyen las especificaciones SCENE avanzadas sin resolver.",
"render.scene_scope_details": "Alcance de la composición",
"render.setup_legacy_environment": "Esta configuración antigua usa rotación o intensidad de entorno heredadas. Se conservan esos valores; conviértelos explícitamente antes de guardar un trabajo de imagen completo.",
"render.setup_name": "Nombre de configuración",
"render.setup_numeric_help": "Guardar conserva el método de imagen activo y todos sus ajustes numéricos. Recargar o elegir otra configuración sustituye este borrador; la cámara se restaura por separado.",
"render.setup_offline_help": "Puedes editar y guardar este método sin tener su motor disponible. Comprobar e Iniciar requieren un motor disponible.",
"render.setup_runtime_unsupported": "Todavía no se puede guardar este trabajo completo: contiene mapas o asignaciones a cuerpos de la escena. Elimina esas entradas temporales antes de guardar una configuración portable.",
"render.setup_save_missing": "No se ha encontrado la configuración guardada después de completar el comando.",
"render.sharpening": "Realce de detalle",
"render.shot_duplicate": "Duplicar toma guardada",
"render.shot_manager": "Tomas guardadas",
"render.shot_new_name": "Nombre de la nueva toma",
"render.shot_remove": "Eliminar toma guardada",
"render.shot_remove_help": "Elimina solo estos ajustes guardados. La geometría y las imágenes terminadas se conservan; se puede deshacer.",
"render.shot_remove_title": "¿Eliminar toma guardada?",
"render.shot_rename": "Renombrar toma guardada",
"render.shot_saved_only": "Guarda primero el borrador. Duplicar y renombrar conservan los ajustes y las referencias de recursos.",
"render.sky_ground": "Suelo · RGB lineal",
"render.sky_horizon": "Horizonte · RGB lineal",
"render.sky_zenith": "Cénit · RGB lineal",
"render.soft_focus": "Enfoque suave",
"render.stage_panel_count": "Saved panels",
"render.studio_add_area": "Añadir luz de área",
"render.studio_ambient": "Radiancia constante del entorno",
"render.studio_ambient_rgb": "Radiancia constante del entorno (RGB lineal)",
"render.studio_aperture": "Radio de apertura",
"render.studio_area": "Luz de área",
"render.studio_area_help": "Hasta cuatro rectángulos de una cara. Los semiejes perpendiculares U y V definen el tamaño; U × V apunta hacia la cara emisora. Coordenadas en milímetros del mundo.",
"render.studio_center": "Centro X, Y, Z (mm)",
"render.studio_denoise": "Reducir ruido en la imagen terminada",
"render.studio_denoise_help": "Filtro final que respeta los bordes; puede suavizar detalles. El vidrio y los reflejos especulares nítidos no se filtran.",
"render.studio_direction": "Dirección hacia el sol (X, Y, Z)",
"render.studio_environment": "Entorno",
"render.studio_environment_bounds": "La rotación del entorno debe ser finita dentro de ±1 000 000 radianes y la intensidad debe estar entre 0 y 100.",
"render.studio_environment_none": "Sin mapa de entorno",
"render.studio_focus": "Distancia de enfoque en el eje",
"render.studio_half_u": "Semieje U (mm)",
"render.studio_half_v": "Semieje V (mm)",
"render.studio_hdr": "Archivo Radiance HDR",
"render.studio_hdr_browse": "Elegir HDR…",
"render.studio_hdr_help": "Radiance .hdr, panorama 2:1, hasta 2048 × 1024. EXR no está disponible. La suposición de color sólo se aplica cuando no se especifican primarias.",
"render.studio_hdr_required": "Elige un archivo .hdr local.",
"render.studio_hdr_srgb": "Interpretar primarias sin especificar como sRGB lineal",
"render.studio_intensity": "Intensidad del entorno",
"render.studio_lens": "Profundidad de campo",
"render.studio_lens_perspective": "La profundidad de campo requiere una cámara en perspectiva.",
"render.studio_lighting": "Iluminación de estudio",
"render.studio_lighting_enable": "Usar iluminación de estudio personalizada",
"render.studio_needs_review": "Revisar método y ajustes",
"render.studio_radiance": "Radiancia RGB lineal",
"render.studio_ready": "Preparado para comprobar",
"render.studio_remove_area": "Eliminar luz de área",
"render.studio_rotation": "Rotación del entorno",
"render.studio_runtime_help": "Los ajustes numéricos del estudio y los HDR comprobados se pueden guardar con la configuración. Las asignaciones ópticas o de materiales por cuerpo siguen siendo temporales y no cambian las apariencias guardadas.",
"render.studio_saved": "Ajustes guardados / cámara actual",
"render.studio_sky": "Cielo artístico",
"render.studio_sky_help": "Gradiente de cielo para iluminar la escena; no es un modelo atmosférico calibrado.",
"render.studio_sun": "Sol",
"render.studio_sun_radius": "Radio angular",
"render.studio_title": "Estudio de renderizado",
"render.studio_tone_map": "Mapeo de tonos",
"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.transmission_scope": "El vidrio GPU admite medios separados certificados. No se admiten medios anidados sin restricciones ni un estimador de transmisión sin ruido.",
"render.transmission_strata": "Estratos de Fresnel condicionales",
"render.transmission_strata_help": "1 conserva la secuencia original. 2 o 4 subtrayectorias estratifican la primera decisión de reflexión o transmisión del vidrio, sin cambiar Snell ni la absorción. Aumentan el trabajo; no son un filtro, soporte de medios anidados ni una garantía de mayor velocidad.",
"render.unavailable_preview": "Esta configuración solicita un perfil de vista previa no disponible.",
"render.unsaved": "Cambios de vista previa sin guardar",
"render.use_preview": "Usar vista previa integrada",
"render.volume_albedo": "Albedo de dispersión · RGB lineal",
"render.volume_anisotropy": "Coseno medio de fase",
"render.volume_enable": "Activar medio participante",
"render.volume_extinction": "Coeficiente de extinción",
"render.volume_help": "Transporte GPU dentro de una caja finita, con atenuación de Beer y dispersión HG en trayectorias de cámara e indirectas. Utiliza luces explícitas de estudio, HDR o cielo, con atenuación de sombras. El coseno positivo dispersa hacia delante. Deben desactivarse explícitamente el vidrio y la reducción de ruido de superficies. Extinción cero conserva el transporte. No es atmósfera lunar, rejilla de densidad, emisión volumétrica, transporte espectral ni propiedad certificada.",
"render.volume_max": "Límites máximos · mm globales",
"render.volume_min": "Límites mínimos · mm globales",
"render.volume_title": "Volumen homogéneo acotado",
"sample.bracket.about": "Brida base con pestañas laterales y reborde, según una regla de 2 mm",
"sample.bracket.name": "Soporte de chapa",
"sample.enclosure.about": "Caja vaciada con labio, salientes para tornillos, apoyo y encaje a presión",
@@ -1769,10 +1128,6 @@
"toolbar.customize_note": "Pestaña {tab}. Marque un comando para añadir su botón a la barra de herramientas. También puede arrastrar una fila del desplegable de un panel a la barra, arrastrar un botón fuera de la barra para quitarlo o usar la chincheta de una fila del desplegable.",
"toolbar.image": "IMAGEN",
"toolbar.render": "RENDERIZAR",
"toolbar.render_appearance": "APARIENCIA",
"toolbar.render_camera": "CÁMARA",
"toolbar.render_environment": "ENTORNO",
"toolbar.render_lighting": "ILUMINACIÓN",
"toolbar.render_scene": "ESCENA",
"trace.points": "{count} puntos a lo largo de la trayectoria",
"trace.stopped_by_contact": "Termina en la muestra {sample}: {a} atravesaría {b}",
@@ -2064,6 +1419,7 @@
"ui.ai_permissions": "Permisos",
"ui.ai_preset": "Preajuste",
"ui.ai_remove": "Eliminar",
"ui.ai_request_limit": "se ha detenido tras 24 solicitudes en un mensaje; envía otro para continuar",
"ui.ai_reset": "Restablecer",
"ui.ai_settings": "Ajustes de IA…",
"ui.ai_show_features": "Mostrar funciones de IA",
@@ -2240,8 +1596,6 @@
"ui.filter_commands_or_keys": "Filtrar comandos o teclas",
"ui.findInTimeline": "Buscar en la línea de tiempo",
"ui.findInWindow": "Buscar en la ventana",
"ui.fine": "Fina",
"ui.finest": "Muy fina",
"ui.finish_sketch": "Finalizar boceto",
"ui.fit": "Ajustar",
"ui.fix_unfix": "Fijar/liberar",
@@ -2466,7 +1820,6 @@
"ui.project_face_edges_sketch": "Proyectar aristas de caras en bocetos nuevos",
"ui.properties": "Propiedades",
"ui.pull_direction": "Dirección de tracción",
"ui.quality": "Calidad",
"ui.quantity": "Cantidad",
"ui.quantity_2": "Cantidad 2",
"ui.quit": "Salir",
@@ -2561,7 +1914,6 @@
"ui.spline": "Spline",
"ui.split_body": "Dividir cuerpo",
"ui.stand_up_tip": "Tras abrir un archivo STEP o IGES con Z hacia arriba, ofrecer un cuarto de vuelta alrededor de X",
"ui.standard": "Estándar",
"ui.start": "Iniciar",
"ui.start_over": "Volver a empezar",
"ui.start_page": "Página de inicio",
+1 -17
View File
@@ -72,7 +72,6 @@
"Choose…": "Choisir…",
"Circular": "Circulaire",
"Circumference": "Circonférence",
"Clockwise": "Horaire",
"Close draft": "Fermer le brouillon",
"Collisions": "Collisions",
"Colour": "Couleur",
@@ -323,7 +322,6 @@
"Search commands": "Rechercher des commandes",
"Search this library": "Rechercher dans cette bibliothèque",
"Section": "Coupe",
"Section position": "Position de la section",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Sélectionner un autre élément : maintenir le bouton gauche immobile sur le modèle (ou clic droit › Sélectionner un autre…) pour lister tout ce qui se trouve sous le curseur ; cliquer de nouveau au même endroit pour sélectionner l'élément suivant. Double-cliquer avec le bouton du milieu pour revenir à l'accueil (Préférences › Navigation).",
"Select bodies (a face picks its body)": "Sélectionner des corps (une face sélectionne son corps)",
"Select bodies or faces instead": "Sélectionner plutôt des corps ou des faces",
@@ -335,7 +333,6 @@
"Show Agent Cursor": "Afficher le curseur de l'agent",
"Show in folder": "Afficher dans le dossier",
"Show the pointer an AI agent moves (#35)": "Afficher le pointeur déplacé par un agent IA (#35)",
"Show tool at cursor": "Afficher l'outil près du curseur",
"Signed axes": "Axes signés",
"Size": "Taille",
"Sketch constraints": "Contraintes d'esquisse",
@@ -348,11 +345,9 @@
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft s'est fermé avant l'enregistrement de ces conceptions. Leur dernier enregistrement automatique peut être ouvert.",
"Specialise": "Spécialiser",
"Speed": "Vitesse",
"Square": "Carré",
"Stand it up": "Redresser",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Redresser : un quart de tour autour de X, sous forme d'une fonction Déplacer que vous pouvez annuler.",
"Stand this part up?": "Redresser cette pièce ?",
"Start angle": "Angle de départ",
"Start bulge": "Renflement de début",
"Start continuity": "Continuité de début",
"Step": "Étape",
@@ -417,7 +412,6 @@
"angle, height": "angle, hauteur",
"appearance.dielectric_ior": "Indice de réfraction (diélectrique)",
"appearance.metallic": "Métallique",
"appearance.only_available": "Apparences disponibles uniquement",
"appearance.pbr_help": "Voir cette surface dans l'espace Rendu ou dans un PNG du produit. Sans réfraction ni transmission.",
"appearance.pbr_opaque": "PBR opaque",
"appearance.roughness": "Rugosité",
@@ -579,7 +573,6 @@
"cmd.material_create": "Nouveau matériau physique",
"cmd.material_delete": "Supprimer le matériau physique",
"cmd.material_edit": "Modifier le matériau physique",
"cmd.material_inspect": "Inspecter le matériau physique",
"cmd.material_list": "Lister les matériaux",
"cmd.material_properties_directional": "Module d'élasticité directionnel",
"cmd.material_properties_validate": "Valider les données du matériau physique",
@@ -868,17 +861,11 @@
"help.mit": "Licence MIT",
"help.notice": "NOTICE",
"help.solid.chamfer": "Coupe les arêtes sélectionnées par un biseau plat de la distance indiquée. À utiliser pour casser des arêtes vives ou faciliter l'entrée d'une goupille ou d'une vis.",
"help.solid.combine": "Joint des corps, les retire d'un corps cible ou ne garde que le volume qu'ils ont en commun, et peut conserver les corps outils. À utiliser pour fusionner des pièces, tailler une forme avec une autre ou repérer où deux corps se recouvrent.",
"help.solid.extrude": "Pousse le profil d'une esquisse en 3D sur une distance, en ajoutant ou en retirant de la matière. À utiliser pour transformer un contour plat en bloc, en bossage ou en poche.",
"help.solid.fillet": "Arrondit les arêtes sélectionnées par un raccord circulaire lisse du rayon indiqué. À utiliser pour adoucir des arêtes vives par souci de solidité, de confort ou d'aspect.",
"help.solid.hole": "Perce un trou rond du diamètre indiqué en un point choisi d'une face. À utiliser pour les passages de vis, les goupilles et autres ouvertures rondes simples.",
"help.solid.loft": "Raccorde en douceur deux profils d'esquisse ou plus, ou un profil et un point, par un solide lissé. À utiliser pour les transitions, entonnoirs, poignées et toute forme dont la section change sur sa longueur.",
"help.solid.mirror": "Crée une copie symétrique des corps ou fonctions sélectionnés par rapport à un plan, et peut la fusionner avec l'original. À utiliser pour les pièces symétriques : on modélise une moitié et on obtient les deux.",
"help.solid.pattern.circular": "Répète les objets sélectionnés autour d'un axe, selon le nombre et l'angle total indiqués. À utiliser pour les cercles de perçages, rayons, ailettes et dents.",
"help.solid.pattern.rectangular": "Répète les objets sélectionnés dans une ou deux directions, selon le nombre et l'espacement indiqués. À utiliser pour les rangées et grilles de trous, nervures, goupilles ou pieds.",
"help.solid.revolve": "Fait tourner le profil d'une esquisse autour d'un axe pour obtenir un solide de révolution. À utiliser pour les arbres, boutons, bols et toute pièce ronde autour d'une ligne centrale.",
"help.solid.shell": "Évide un solide en laissant des parois de l'épaisseur indiquée, et peut ouvrir les faces choisies. À utiliser pour les boîtes, couvercles et autres pièces à parois minces.",
"help.solid.sweep": "Entraîne le profil d'une esquisse le long d'une trajectoire pour obtenir un solide. À utiliser pour les tuyaux, rails, poignées et toute pièce de section constante qui suit une courbe.",
"help.third_party": "Licences tierces",
"home.back": "Revenir à {name}",
"home.crashes": "Rapports de plantage",
@@ -1430,6 +1417,7 @@
"ui.ai_permissions": "Autorisations",
"ui.ai_preset": "Préréglage",
"ui.ai_remove": "Retirer",
"ui.ai_request_limit": "arrêté après 24 requêtes pour un message ; envoyez un autre message pour poursuivre",
"ui.ai_reset": "Réinitialiser",
"ui.ai_settings": "Réglages IA…",
"ui.ai_show_features": "Afficher les fonctionnalités IA",
@@ -1606,8 +1594,6 @@
"ui.filter_commands_or_keys": "Filtrer les commandes ou les touches",
"ui.findInTimeline": "Trouver dans la chronologie",
"ui.findInWindow": "Trouver dans la fenêtre",
"ui.fine": "Fine",
"ui.finest": "Très fine",
"ui.finish_sketch": "Terminer l'esquisse",
"ui.fit": "Cadrer",
"ui.fix_unfix": "Fixer/Libérer",
@@ -1832,7 +1818,6 @@
"ui.project_face_edges_sketch": "Projeter les arêtes de la face dans les nouvelles esquisses",
"ui.properties": "Propriétés",
"ui.pull_direction": "Direction de démoulage",
"ui.quality": "Qualité",
"ui.quantity": "Quantité",
"ui.quantity_2": "Quantité 2",
"ui.quit": "Quitter",
@@ -1927,7 +1912,6 @@
"ui.spline": "Spline",
"ui.split_body": "Scinder le corps",
"ui.stand_up_tip": "Après l'ouverture d'un fichier STEP ou IGES avec Z vers le haut, proposer un quart de tour autour de X",
"ui.standard": "Standard",
"ui.start": "Début",
"ui.start_over": "Recommencer",
"ui.start_page": "Page d’accueil",
+1 -11
View File
@@ -72,7 +72,6 @@
"Choose…": "Escoller…",
"Circular": "Circular",
"Circumference": "Circunferencia",
"Clockwise": "Horario",
"Close draft": "Pechar o borrador",
"Collisions": "Colisións",
"Colour": "Cor",
@@ -323,7 +322,6 @@
"Search commands": "Buscar ordes",
"Search this library": "Buscar nesta biblioteca",
"Section": "Sección",
"Section position": "Posición da sección",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Seleccionar outro: mantén o botón esquerdo quieto sobre o modelo (ou clic dereito › Seleccionar outro…) para listar todo o que hai baixo o cursor; fai clic de novo no mesmo punto para seleccionar o seguinte. Fai dobre clic co botón central para ir ao Inicio (Preferencias › Navegación).",
"Select bodies (a face picks its body)": "Selecciona corpos (unha cara escolle o seu corpo)",
"Select bodies or faces instead": "Seleccionar corpos ou caras no seu lugar",
@@ -335,7 +333,6 @@
"Show Agent Cursor": "Mostrar o cursor do axente",
"Show in folder": "Mostrar no cartafol",
"Show the pointer an AI agent moves (#35)": "Mostrar o punteiro que move un axente de IA (#35)",
"Show tool at cursor": "Mostrar a ferramenta xunto ao cursor",
"Signed axes": "Eixes con signo",
"Size": "Tamaño",
"Sketch constraints": "Restricións do bosquexo",
@@ -348,11 +345,9 @@
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft pechouse antes de gardar estes deseños. Pódese abrir a súa última gravación automática.",
"Specialise": "Especializar",
"Speed": "Velocidade",
"Square": "Cadrado",
"Stand it up": "Poñela de pé",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Poñela de pé: un cuarto de volta arredor de X, como unha operación Mover que podes desfacer.",
"Stand this part up?": "Poñer esta peza de pé?",
"Start angle": "Ángulo inicial",
"Start bulge": "Abaulamento inicial",
"Start continuity": "Continuidade inicial",
"Step": "Paso",
@@ -417,7 +412,6 @@
"angle, height": "ángulo, altura",
"appearance.dielectric_ior": "IOR dieléctrico",
"appearance.metallic": "Metálico",
"appearance.only_available": "Só aparencias dispoñibles",
"appearance.pbr_help": "Ve esta superficie en Render ou nun PNG de produto. Sen refracción nin transmisión.",
"appearance.pbr_opaque": "PBR opaco",
"appearance.roughness": "Rugosidade",
@@ -579,7 +573,6 @@
"cmd.material_create": "Material físico novo",
"cmd.material_delete": "Eliminar o material físico",
"cmd.material_edit": "Editar o material físico",
"cmd.material_inspect": "Inspeccionar o material físico",
"cmd.material_list": "Listar os materiais",
"cmd.material_properties_directional": "Módulo elástico direccional",
"cmd.material_properties_validate": "Validar os datos do material físico",
@@ -1416,6 +1409,7 @@
"ui.ai_permissions": "Permisos",
"ui.ai_preset": "Axuste predefinido",
"ui.ai_remove": "Quitar",
"ui.ai_request_limit": "detívose tras 24 solicitudes para unha mensaxe; envía outra mensaxe para continuar",
"ui.ai_reset": "Restablecer",
"ui.ai_settings": "Axustes de IA…",
"ui.ai_show_features": "Mostrar as funcións de IA",
@@ -1592,8 +1586,6 @@
"ui.filter_commands_or_keys": "Filtrar ordes ou teclas",
"ui.findInTimeline": "Buscar na liña de tempo",
"ui.findInWindow": "Buscar na xanela",
"ui.fine": "Fina",
"ui.finest": "Moi fina",
"ui.finish_sketch": "Rematar o bosquexo",
"ui.fit": "Axustar",
"ui.fix_unfix": "Fixar/Liberar",
@@ -1818,7 +1810,6 @@
"ui.project_face_edges_sketch": "Proxectar as arestas da cara en bosquexos novos",
"ui.properties": "Propiedades",
"ui.pull_direction": "Dirección de extracción",
"ui.quality": "Calidade",
"ui.quantity": "Cantidade",
"ui.quantity_2": "Cantidade 2",
"ui.quit": "Saír",
@@ -1913,7 +1904,6 @@
"ui.spline": "Spline",
"ui.split_body": "Dividir o corpo",
"ui.stand_up_tip": "Despois de abrir un ficheiro STEP ou IGES con Z cara arriba, ofrecer un cuarto de volta arredor de X",
"ui.standard": "Estándar",
"ui.start": "Inicio",
"ui.start_over": "Volver comezar",
"ui.start_page": "Páxina de inicio",
+1 -11
View File
@@ -72,7 +72,6 @@
"Choose…": "Válasszon…",
"Circular": "Kör alakú",
"Circumference": "Kerület",
"Clockwise": "Óramutató járásával megegyező",
"Close draft": "Vázlat bezárása",
"Collisions": "Ütközések",
"Colour": "Szín",
@@ -322,7 +321,6 @@
"Search commands": "Parancsok keresése",
"Search this library": "Keresés ebben a könyvtárban",
"Section": "Metszet",
"Section position": "Szelvény helyzete",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Más kijelölése: tartsa nyomva a bal gombot egy helyen a modellen (vagy jobb kattintás › Más kijelölése…) a kurzor alatti elemek listázásához; kattintson újra ugyanarra a helyre a következő elem kijelöléséhez. A középső gomb dupla kattintása a kezdőnézetre vált (Beállítások › Navigáció).",
"Select bodies (a face picks its body)": "Testek kijelölése (egy lap a testét jelöli ki)",
"Select bodies or faces instead": "Inkább testek vagy lapok kijelölése",
@@ -334,7 +332,6 @@
"Show Agent Cursor": "Ügynök kurzorának megjelenítése",
"Show in folder": "Megjelenítés a mappában",
"Show the pointer an AI agent moves (#35)": "Az AI-ügynök által mozgatott mutató megjelenítése (#35)",
"Show tool at cursor": "Eszköz megjelenítése a kurzornál",
"Signed axes": "Előjeles tengelyek",
"Size": "Méret",
"Sketch constraints": "Vázlatkényszerek",
@@ -347,11 +344,9 @@
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "A SolidCraft bezárult, mielőtt ezek a tervek mentve lettek volna. Az utolsó automatikus mentésük megnyitható.",
"Specialise": "Specializálás",
"Speed": "Sebesség",
"Square": "Négyzet",
"Stand it up": "Felállítás",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Felállítás: negyedfordulat az X körül, egyetlen visszavonható Mozgatás elemként.",
"Stand this part up?": "Felállítja ezt az alkatrészt?",
"Start angle": "Kezdőszög",
"Start bulge": "Kezdő ívelés",
"Start continuity": "Kezdő folytonosság",
"Step": "Lépés",
@@ -416,7 +411,6 @@
"angle, height": "szög, magasság",
"appearance.dielectric_ior": "Dielektromos törésmutató",
"appearance.metallic": "Fémes",
"appearance.only_available": "Csak elérhető megjelenések",
"appearance.pbr_help": "Tekintse meg ezt a felületet Renderelésben vagy termék-PNG-ben. Nincs fénytörés vagy áteresztés.",
"appearance.pbr_opaque": "Átlátszatlan PBR",
"appearance.roughness": "Érdesség",
@@ -578,7 +572,6 @@
"cmd.material_create": "Új fizikai anyag",
"cmd.material_delete": "Fizikai anyag törlése",
"cmd.material_edit": "Fizikai anyag szerkesztése",
"cmd.material_inspect": "Fizikai anyag vizsgálata",
"cmd.material_list": "Anyagok listázása",
"cmd.material_properties_directional": "Irányfüggő rugalmassági modulus",
"cmd.material_properties_validate": "Fizikai anyagadatok ellenőrzése",
@@ -1359,6 +1352,7 @@
"ui.ai_permissions": "Jogosultságok",
"ui.ai_preset": "Előbeállítás",
"ui.ai_remove": "Eltávolítás",
"ui.ai_request_limit": "egy üzenetre eső 24 kérés után leállt; küldjön újabb üzenetet a folytatáshoz",
"ui.ai_reset": "Visszaállítás",
"ui.ai_settings": "AI-beállítások…",
"ui.ai_show_features": "AI-funkciók megjelenítése",
@@ -1534,8 +1528,6 @@
"ui.filter_commands_or_keys": "Parancsok vagy billentyűk szűrése",
"ui.findInTimeline": "Keresés az idővonalon",
"ui.findInWindow": "Keresés az ablakban",
"ui.fine": "Finom",
"ui.finest": "Legfinomabb",
"ui.finish_sketch": "Vázlat befejezése",
"ui.fit": "Illesztés",
"ui.fix_unfix": "Rögzítés/feloldás",
@@ -1760,7 +1752,6 @@
"ui.project_face_edges_sketch": "Lapélek vetítése új vázlatokba",
"ui.properties": "Tulajdonságok",
"ui.pull_direction": "Húzási irány",
"ui.quality": "Minőség",
"ui.quantity": "Mennyiség",
"ui.quantity_2": "2. mennyiség",
"ui.quit": "Kilépés",
@@ -1855,7 +1846,6 @@
"ui.spline": "Spline",
"ui.split_body": "Test felosztása",
"ui.stand_up_tip": "Miután egy STEP vagy IGES fájl Z felfelé tájolással megnyílik, negyedfordulat felajánlása az X körül",
"ui.standard": "Normál",
"ui.start": "Kezdés",
"ui.start_over": "Újrakezdés",
"ui.start_page": "Kezdőoldal",
+1 -11
View File
@@ -72,7 +72,6 @@
"Choose…": "Pilih…",
"Circular": "Melingkar",
"Circumference": "Keliling",
"Clockwise": "Searah jarum jam",
"Close draft": "Tutup draf",
"Collisions": "Tabrakan",
"Colour": "Warna",
@@ -322,7 +321,6 @@
"Search commands": "Cari perintah",
"Search this library": "Cari di pustaka ini",
"Section": "Penampang",
"Section position": "Posisi penampang",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Select Other: tahan tombol kiri diam di model (atau klik kanan › Select Other…) untuk mendaftar semua yang ada di bawah kursor; klik lagi di titik yang sama untuk memilih benda berikutnya. Klik dua kali tombol tengah untuk ke Home (Preferensi › Navigasi).",
"Select bodies (a face picks its body)": "Pilih bodi (memilih muka berarti memilih bodinya)",
"Select bodies or faces instead": "Pilih bodi atau muka saja",
@@ -334,7 +332,6 @@
"Show Agent Cursor": "Tampilkan Kursor Agen",
"Show in folder": "Tampilkan di folder",
"Show the pointer an AI agent moves (#35)": "Tampilkan penunjuk yang digerakkan agen AI (#35)",
"Show tool at cursor": "Tampilkan alat di dekat kursor",
"Signed axes": "Sumbu bertanda",
"Size": "Ukuran",
"Sketch constraints": "Batasan sketsa",
@@ -347,11 +344,9 @@
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft tertutup sebelum desain ini disimpan. Simpan otomatis terakhirnya dapat dibuka.",
"Specialise": "Spesialisasikan",
"Speed": "Kecepatan",
"Square": "Persegi",
"Stand it up": "Tegakkan",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Tegakkan: putaran seperempat terhadap X, sebagai satu fitur Move yang dapat dibatalkan.",
"Stand this part up?": "Tegakkan part ini?",
"Start angle": "Sudut awal",
"Start bulge": "Bulge awal",
"Start continuity": "Kontinuitas awal",
"Step": "Langkah",
@@ -416,7 +411,6 @@
"angle, height": "sudut, tinggi",
"appearance.dielectric_ior": "IOR Dielektrik",
"appearance.metallic": "Metalik",
"appearance.only_available": "Hanya tampilan yang tersedia",
"appearance.pbr_help": "Lihat permukaan ini di Render atau PNG produk. Tanpa refraksi atau transmisi.",
"appearance.pbr_opaque": "PBR Opak",
"appearance.roughness": "Kekasaran",
@@ -578,7 +572,6 @@
"cmd.material_create": "Material Fisik Baru",
"cmd.material_delete": "Hapus Material Fisik",
"cmd.material_edit": "Edit Material Fisik",
"cmd.material_inspect": "Periksa Material Fisik",
"cmd.material_list": "Daftar Material",
"cmd.material_properties_directional": "Modulus Elastis Arah",
"cmd.material_properties_validate": "Validasi Data Material Fisik",
@@ -1359,6 +1352,7 @@
"ui.ai_permissions": "Izin",
"ui.ai_preset": "Preset",
"ui.ai_remove": "Hapus",
"ui.ai_request_limit": "dihentikan setelah 24 permintaan untuk satu pesan; kirim pesan lain untuk melanjutkan",
"ui.ai_reset": "Reset",
"ui.ai_settings": "Pengaturan AI…",
"ui.ai_show_features": "Tampilkan fitur AI",
@@ -1534,8 +1528,6 @@
"ui.filter_commands_or_keys": "Filter perintah atau tombol",
"ui.findInTimeline": "Cari di Linimasa",
"ui.findInWindow": "Cari di Jendela",
"ui.fine": "Halus",
"ui.finest": "Sangat halus",
"ui.finish_sketch": "Selesai Sketsa",
"ui.fit": "Pas",
"ui.fix_unfix": "Kunci/Lepas Kunci",
@@ -1760,7 +1752,6 @@
"ui.project_face_edges_sketch": "Proyeksikan tepi muka ke sketsa baru",
"ui.properties": "Properti",
"ui.pull_direction": "Arah tarik",
"ui.quality": "Kualitas",
"ui.quantity": "Jumlah",
"ui.quantity_2": "Jumlah 2",
"ui.quit": "Keluar",
@@ -1855,7 +1846,6 @@
"ui.spline": "Spline",
"ui.split_body": "Belah Bodi",
"ui.stand_up_tip": "Setelah file STEP atau IGES terbuka dengan Z ke atas, tawarkan putaran seperempat terhadap X",
"ui.standard": "Standar",
"ui.start": "Mulai",
"ui.start_over": "Mulai ulang",
"ui.start_page": "Halaman awal",
+1 -25
View File
@@ -72,7 +72,6 @@
"Choose…": "Scegli…",
"Circular": "Circolare",
"Circumference": "Circonferenza",
"Clockwise": "Orario",
"Close draft": "Chiudi bozza",
"Collisions": "Collisioni",
"Colour": "Colore",
@@ -164,7 +163,6 @@
"From Bodies": "Dai corpi",
"From edges": "Spigoli di partenza",
"Full Length": "Lunghezza totale",
"Full thread depth": "Profondità filetto completa",
"Gap": "Gioco",
"Grid": "Griglia",
"HISTORY": "CRONOLOGIA",
@@ -327,7 +325,6 @@
"Search commands": "Cerca comandi",
"Search this library": "Cerca in questa libreria",
"Section": "Sezione",
"Section position": "Posizione della sezione",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Seleziona altro: tieni premuto il pulsante sinistro senza muovere il puntatore sul modello (o fai clic destro › Seleziona altro…) per elencare gli oggetti sotto il cursore; fai di nuovo clic nello stesso punto per selezionare l'oggetto successivo. Fai doppio clic con il pulsante centrale per tornare alla vista iniziale (Preferenze › Navigazione).",
"Select bodies (a face picks its body)": "Seleziona corpi (una faccia seleziona il corpo a cui appartiene)",
"Select bodies or faces instead": "Seleziona invece corpi o facce",
@@ -339,7 +336,6 @@
"Show Agent Cursor": "Mostra cursore dell'agente",
"Show in folder": "Mostra nella cartella",
"Show the pointer an AI agent moves (#35)": "Mostra il puntatore mosso da un agente IA (#35)",
"Show tool at cursor": "Mostra lo strumento accanto al cursore",
"Signed axes": "Assi con segno",
"Size": "Dimensioni",
"Sketch constraints": "Vincoli dello schizzo",
@@ -352,11 +348,9 @@
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft si è chiuso prima del salvataggio di questi progetti. È possibile aprire il loro ultimo salvataggio automatico.",
"Specialise": "Specializza",
"Speed": "Velocità",
"Square": "Quadrato",
"Stand it up": "Raddrizza",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Raddrizza: un quarto di giro attorno a X, come singola lavorazione Sposta annullabile.",
"Stand this part up?": "Raddrizzare questa parte?",
"Start angle": "Angolo iniziale",
"Start bulge": "Rigonfiamento iniziale",
"Start continuity": "Continuità iniziale",
"Step": "Passo",
@@ -383,7 +377,6 @@
"This child is shared. Deleting it removes it from every copy of its parent.": "Questo elemento figlio è condiviso. Eliminandolo verrà rimosso da ogni copia del padre.",
"This child is shared. Position changes apply to every copy of its parent.": "Questo elemento figlio è condiviso. Le modifiche alla posizione si applicano a ogni copia del padre.",
"This face cannot provide a durable attachment. Choose another face or explicitly use a fixed plane.": "Questa faccia non consente un collegamento persistente. Scegli un'altra faccia o usa esplicitamente un piano fisso.",
"Thread depth": "Profondità filetto",
"To Offset": "Fino a una distanza",
"To edges": "Spigoli di arrivo",
"Tolerance": "Tolleranza",
@@ -422,7 +415,6 @@
"angle, height": "angolo, altezza",
"appearance.dielectric_ior": "Indice di rifrazione dielettrico",
"appearance.metallic": "Metallicità",
"appearance.only_available": "Solo aspetti disponibili",
"appearance.pbr_help": "Visualizza questa superficie in Rendering o in un PNG del prodotto. Senza rifrazione né trasmissione.",
"appearance.pbr_opaque": "PBR opaco",
"appearance.roughness": "Rugosità",
@@ -584,7 +576,6 @@
"cmd.material_create": "Nuovo materiale fisico",
"cmd.material_delete": "Elimina materiale fisico",
"cmd.material_edit": "Modifica materiale fisico",
"cmd.material_inspect": "Ispeziona materiale fisico",
"cmd.material_list": "Elenca materiali",
"cmd.material_properties_directional": "Modulo elastico direzionale",
"cmd.material_properties_validate": "Convalida dati del materiale fisico",
@@ -1001,16 +992,6 @@
"pattern.direction_1": "Direzione 1",
"pattern.direction_2": "Direzione 2",
"pattern.flip_direction": "Inverti direzione",
"perf.click_for_details": "Fai clic per i dettagli: grafico dei tempi, istogramma e ciò che la GPU ha disegnato",
"perf.draw_calls": "Chiamate di disegno",
"perf.line_segments": "Segmenti",
"perf.low_1": "1% più lento",
"perf.renderer": "Renderer",
"perf.scene_build": "Costruzione scena",
"perf.triangles": "Triangoli",
"perf.ui_time": "Tempo UI",
"perf.viewport": "Vista",
"perf.worst": "peggiore",
"pick a hole to seat it, or OK places it at the origin": "seleziona un foro in cui inserirlo, oppure premi OK per posizionarlo nell'origine",
"pick one or two vertices, edges or faces": "seleziona uno o due vertici, spigoli o facce",
"prefs.cpu": "CPU (software)",
@@ -1086,7 +1067,6 @@
"search.try": "Digita il nome di un comando, una feature di questo progetto o un parametro. Per eseguire un comando con dei valori, digita il suo id e un oggetto JSON: {example}",
"search.valid": "Valido",
"settings.display_quality_note": "I livelli più fini rendono più uniformi le facce curve e gli spigoli e richiedono più tempo per la mesh. Cambia solo la vista, mai il progetto o le esportazioni.",
"settings.frame_rate_details_note": "Aggiunge un grafico dei tempi degli ultimi fotogrammi con i triangoli disegnati sopra, un istogramma dei tempi dei fotogrammi e ciò che la GPU ha disegnato. Fai clic sulla frequenza nella vista per mostrarli o nasconderli.",
"settings.frame_rate_note": "Mostra i fotogrammi al secondo nell'angolo inferiore destro della vista.",
"settings.measure_axis_distances": "Mostra distanze X/Y/Z",
"settings.measure_axis_distances_help": "Mostra le componenti lungo gli assi globali accanto alla distanza totale quando Misura è aperto.",
@@ -1432,6 +1412,7 @@
"ui.ai_permissions": "Autorizzazioni",
"ui.ai_preset": "Preimpostazione",
"ui.ai_remove": "Rimuovi",
"ui.ai_request_limit": "arrestato dopo 24 richieste per un messaggio; invia un altro messaggio per proseguire",
"ui.ai_reset": "Reimposta",
"ui.ai_settings": "Impostazioni IA…",
"ui.ai_show_features": "Mostra funzioni IA",
@@ -1608,8 +1589,6 @@
"ui.filter_commands_or_keys": "Filtra comandi o tasti",
"ui.findInTimeline": "Trova nella sequenza temporale",
"ui.findInWindow": "Trova nella finestra",
"ui.fine": "Fine",
"ui.finest": "Finissima",
"ui.finish_sketch": "Termina schizzo",
"ui.fit": "Adatta",
"ui.fix_unfix": "Fissa/Libera",
@@ -1617,7 +1596,6 @@
"ui.flip": "Inverti",
"ui.format": "Formato",
"ui.frame_rate": "Frequenza dei fotogrammi",
"ui.frame_rate_details": "Dettagli della frequenza dei fotogrammi",
"ui.front": "Frontale",
"ui.fully_constrained": "Completamente vincolato",
"ui.function": "Funzione",
@@ -1835,7 +1813,6 @@
"ui.project_face_edges_sketch": "Proietta gli spigoli delle facce nei nuovi schizzi",
"ui.properties": "Proprietà",
"ui.pull_direction": "Direzione di estrazione",
"ui.quality": "Qualità",
"ui.quantity": "Quantità",
"ui.quantity_2": "Quantità 2",
"ui.quit": "Esci",
@@ -1930,7 +1907,6 @@
"ui.spline": "Spline",
"ui.split_body": "Dividi corpo",
"ui.stand_up_tip": "Dopo l'apertura di un file STEP o IGES con asse Z verticale, proponi un quarto di giro attorno a X",
"ui.standard": "Standard",
"ui.start": "Inizio",
"ui.start_over": "Ricomincia",
"ui.start_page": "Pagina iniziale",
+2 -769
View File
File diff suppressed because it is too large Load Diff
+2 -435
View File
@@ -14,15 +14,11 @@
"Active": "활성",
"Active rule": "활성 규칙",
"Add": "추가",
"Add Column": "열 추가",
"Add Key": "키 추가",
"Add Mid Radius": "중간 반지름 추가",
"Add Row": "행 추가",
"Add a tool": "도구 추가",
"Add condition": "조건 추가",
"Add or edit rule": "규칙 추가 또는 편집",
"Add to New Folder": "새 폴더에 추가",
"Advanced": "고급",
"Against": "상대",
"Alignment": "정렬",
"All Sketch Points": "모든 스케치 점",
@@ -32,11 +28,9 @@
"Also deletes": "함께 삭제됨",
"Angle 2": "각도 2",
"Animate": "애니메이션",
"App/system information and command settings are included. Review private details before sharing.": "앱/시스템 정보와 명령 설정이 포함됩니다. 공유하기 전에 개인 정보를 확인하세요.",
"Appearance name": "외관 이름",
"Applied Radius": "적용 반지름",
"Apply To": "적용 대상",
"Apply library appearance": "라이브러리 외관 적용",
"Apply to selection": "선택 항목에 적용",
"Apply your appearance first. Exports assigned appearances and materials created in this design.": "먼저 외관을 적용하세요. 지정된 외관과 이 디자인에서 만든 재질을 내보냅니다.",
"Arc length": "호 길이",
@@ -47,18 +41,13 @@
"BLEND SURFACE": "블렌드 곡면",
"BOUNDARY SURFACE": "경계 곡면",
"BROWSER": "브라우저",
"Bend Edges": "벤딩 모서리",
"Bend Line Position": "벤딩 선 위치",
"Bend Position": "벤딩 위치",
"Bend Radius": "벤딩 반지름",
"Bend Table": "벤딩 표",
"Bends": "벤딩",
"Bodies": "바디",
"Bodies (optional)": "바디 (선택 사항)",
"Body": "바디",
"Body (last if none)": "바디 (없으면 마지막)",
"Body to split": "분할할 바디",
"Body, or faces to take out": "바디 또는 제거할 면",
"Boundary": "경계",
"Bounding box": "경계 상자",
"CHECK": "검사",
@@ -70,9 +59,7 @@
"CSV or JSON: updates existing parameters, adds new ones": "CSV or JSON: updates existing parameters, adds new ones",
"Capture All Positions": "모든 위치 캡처",
"Capture Pos.": "위치 캡처",
"Catch Depth": "걸림 깊이",
"Catch Direction": "받기 방향",
"Catch Length": "걸림 길이",
"Cells": "셀",
"Center X": "중심 X",
"Center Y": "중심 Y",
@@ -81,31 +68,22 @@
"Chamfer Type": "모따기 유형",
"Check collisions": "충돌 확인",
"Checked": "검사됨",
"Choose Variable radius or Spline in Type.": "유형에서 가변 반지름 또는 스플라인을 선택하세요.",
"Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.": "새 평면을 선택하세요. 스케치 기하와 치수는 2차원 좌표 그대로 유지됩니다.",
"Choose…": "선택…",
"Circular": "원형",
"Circumference": "원주",
"Clockwise": "시계 방향",
"Close draft": "초안 닫기",
"Collapse": "접기",
"Collisions": "충돌",
"Colour": "색상",
"Command": "명령",
"Comment": "설명",
"Component 1": "구성요소 1",
"Component 2": "구성요소 2",
"Component1": "구성요소1",
"Components": "구성요소",
"Config Table": "구성 표",
"Configuration": "구성",
"Configuration rules": "Configuration rules",
"Contact": "접촉",
"Contact Sets": "접촉 집합",
"Contact sets": "접촉 집합",
"Continue on GitHub": "GitHub에서 계속",
"Continuity": "연속성",
"Control Radius": "제어 반지름",
"Copy Error Message": "오류 메시지 복사",
"Copy Warning Message": "경고 메시지 복사",
"Copy all licences": "모든 라이선스 복사",
@@ -115,11 +93,8 @@
"Corner Relief": "모서리 릴리프",
"Corner Relief Shape": "모서리 릴리프 모양",
"Counterbore depth": "카운터보어 깊이",
"Counterbore Ø": "카운터보어 Ø",
"Countersink angle": "카운터싱크 각도",
"Countersink Ø": "카운터싱크 Ø",
"Create Issue — review draft": "이슈 만들기 — 초안 검토",
"Create Issue…": "이슈 만들기…",
"Cross curves": "교차 곡선",
"Cube": "큐브",
"Current orthographic view (1:1 mm)": "현재 직교 투영 뷰 (1:1 mm)",
@@ -127,28 +102,18 @@
"Curv. Comb": "곡률 빗",
"Curvature (G2)": "곡률 (G2)",
"Curvature change (G3)": "곡률 변화 (G3)",
"Custom": "사용자 지정",
"Customize Toolbar": "도구 모음 사용자 지정",
"Customize…": "사용자 지정…",
"Cylindrical face": "원통면",
"Delete": "삭제",
"Delete Selected Features…": "선택한 피처 삭제…",
"Delete Selected Folders": "선택한 폴더 삭제",
"Delete all": "모두 삭제",
"Delete rule": "규칙 삭제",
"Delete the row": "행 삭제",
"Delete the set": "집합 삭제",
"Density": "밀도",
"Dependents": "종속 항목",
"Describe the problem you saw": "발견한 문제를 설명하세요",
"Describe what happened before continuing.": "계속하기 전에 어떤 일이 있었는지 설명하세요.",
"Designs you open or save appear here.": "열거나 저장한 디자인이 여기에 표시됩니다.",
"Determined by To Object. Choose Angle to edit the angular extent.": "대상 객체까지로 결정됩니다. 각도 범위를 편집하려면 각도를 선택하세요.",
"Diameter": "지름",
"Direction": "방향",
"Discard": "버리기",
"Distance": "거리",
"Distance 1": "거리 1",
"Do not export hidden edges": "숨겨진 모서리 내보내지 않기",
"Don't ask again": "다시 묻지 않기",
"Double-click to rename": "Double-click to rename",
@@ -160,22 +125,17 @@
"Edit Profile Sketch": "윤곽 스케치 편집",
"Enable rule": "규칙 사용",
"Enabled": "사용",
"End Radius": "끝 반지름",
"End bulge": "끝 벌지",
"End continuity": "끝 연속성",
"Expand": "펼치기",
"Expected result (optional)": "예상 결과 (선택 사항)",
"Explode": "분해",
"Explode Text": "텍스트 분해",
"Export Animation…": "애니메이션 내보내기…",
"Export Bend Table…": "벤딩 표 내보내기…",
"Export CSV…": "CSV 내보내기…",
"Export Current View as DXF": "현재 뷰를 DXF로 내보내기",
"Export Current View as DXF…": "현재 뷰를 DXF로 내보내기…",
"Export DXF": "DXF 내보내기",
"Export DXF…": "DXF 내보내기…",
"Export JSON…": "JSON 내보내기…",
"Export design library...": "설계 라이브러리 내보내기...",
"Export hidden edges as dashed lines": "숨겨진 모서리를 파선으로 내보내기",
"Expression": "표현식",
"Extend to meet": "만날 때까지 연장",
@@ -183,41 +143,31 @@
"FILES": "파일",
"FLAT": "전개",
"Face": "면",
"Face Appearance": "면 외관",
"Face attachment lost": "면 부착 끊김",
"Faces": "면",
"Faces / edges / vertices": "면 / 모서리 / 꼭짓점",
"Faces to split": "분할할 면",
"Fastener": "체결 부품",
"Favorites": "즐겨찾기",
"Favourite": "Favourite",
"Feature": "피처",
"Files from many CAD programs have Y up, so they open lying on their back here (Z up).": "많은 CAD 프로그램의 파일은 Y가 위쪽이라 여기(Z 위쪽)에서는 누워서 열립니다.",
"Filter": "Filter",
"Filter…": "필터…",
"Find in Browser": "브라우저에서 찾기",
"Finish": "완료",
"First plane": "첫 번째 평면",
"Fit and G2 corners require Constant radius.": "맞춤 및 G2 모서리에는 일정 반지름이 필요합니다.",
"Fixed Face": "고정 면",
"Flat Pattern": "플랫 패턴",
"Flat size": "전개 크기",
"Follow camera (Look At sketches)": "카메라 따라가기 (스케치 보기)",
"Follow the camera (Look At sketches)": "카메라 따라가기 (스케치 보기)",
"Font": "글꼴",
"From": "시작",
"From Bodies": "바디에서",
"From edges": "시작 모서리",
"Full Length": "전체 길이",
"G2 corners require a constant radius and Exact fit. G2 corner construction is not supported yet.": "G2 모서리에는 일정 반지름과 정확한 맞춤이 필요합니다. G2 모서리 생성은 아직 지원되지 않습니다.",
"Gap": "간격",
"Grid": "격자",
"HISTORY": "이력",
"Height": "높이",
"Hidden edges": "숨겨진 모서리",
"History layout": "기록 레이아웃",
"History names": "이력 이름",
"Hole (optional)": "구멍 (선택 사항)",
"Holes": "구멍",
"Holes (edges on them)": "구멍 (구멍의 모서리)",
"Home view": "홈 뷰",
@@ -228,20 +178,14 @@
"In place (surfaces)": "제자리에서 (서피스)",
"In plane": "평면 내",
"Inside": "안쪽",
"Instant": "즉시",
"Interface font": "인터페이스 글꼴",
"Interferes": "간섭",
"Invert": "반전",
"Isocurve": "등매개 곡선",
"Issue title": "이슈 제목",
"Items": "항목",
"JOINTS": "조인트",
"Joint Origin1": "조인트 원점1",
"Keep (points on it)": "유지할 부분(그 위의 점)",
"Keep Tools": "공구 유지",
"Keep of the other surface": "다른 곡면에서 유지할 부분",
"Key a joint's value at this step": "이 단계에서 조인트 값을 키프레임으로 설정",
"Keys": "키프레임",
"LIBRARY": "라이브러리",
"LINKS": "링크",
"Later": "나중에",
@@ -252,14 +196,9 @@
"Letter Spacing": "글자 간격",
"Library": "라이브러리",
"Library file": "라이브러리 파일",
"Limits": "제한",
"Line Spacing": "줄 간격",
"Line Type": "선 유형",
"Listing the installed fonts…": "설치된 글꼴 목록을 불러오는 중…",
"Load library": "라이브러리 불러오기",
"Local library": "로컬 라이브러리",
"Loop": "루프",
"M5 or M5x0.5": "M5 또는 M5x0.5",
"MANAGE": "관리",
"MATERIALS": "재질",
"MODEL": "모델",
@@ -270,20 +209,14 @@
"Matched restrictions check the final values.": "조건이 충족된 제한 규칙은 최종 값을 검사합니다.",
"Material": "재질",
"Max": "최대",
"Maximum": "최대",
"Mesh body": "메쉬 바디",
"Mid Radius": "중간 반지름",
"Middle double-click": "가운데 버튼 더블 클릭",
"Min": "최소",
"Minimum": "최소",
"Mirror Plane": "대칭 평면",
"Mitre": "연귀",
"Model Parameters": "모델 매개변수",
"Modeled": "모델링됨",
"More toolbar panels": "도구 모음 패널 더 보기",
"Motion": "모션",
"Mouse gestures": "마우스 제스처",
"Mouse gestures only work with the SolidWorks mouse mode (the wheel orbits, Ctrl + wheel pans).": "마우스 제스처는 SolidWorks 마우스 모드(휠로 회전, Ctrl + 휠로 이동)에서만 작동합니다.",
"Move / Copy": "이동 / 복사",
"Move Type": "이동 유형",
"Move down": "아래로 이동",
@@ -301,19 +234,14 @@
"No matching appearance presets.": "일치하는 외관 프리셋이 없습니다.",
"No matching appearances": "일치하는 외관 없음",
"No visible bodies or sketches to export.": "표시된 바디나 스케치가 없어 내보낼 수 없습니다.",
"Nominal diameter": "호칭 지름",
"None": "없음",
"Not available yet": "아직 사용할 수 없음",
"Notch": "노치",
"OUTPUT": "출력",
"On your computer": "내 컴퓨터",
"Opacity": "불투명도",
"Open GitHub draft": "GitHub 초안 열기",
"Open library...": "라이브러리 열기...",
"Open local libraries in the desktop app. You can export this design's library here.": "로컬 라이브러리는 데스크톱 앱에서 여세요. 이 디자인의 라이브러리는 여기서 내보낼 수 있습니다.",
"Open-source parametric 3D CAD in Rust. Licensed MIT OR Apache-2.0.": "Rust로 만든 오픈 소스 매개변수 3D CAD. 라이선스: MIT OR Apache-2.0.",
"Opens your browser and copies the report text for you to paste into GitHub.": "브라우저를 열고, GitHub에 붙여넣을 수 있도록 보고서 텍스트를 복사합니다.",
"Opens your browser. Review and submit your report on GitHub.": "브라우저를 엽니다. GitHub에서 보고서를 검토하고 제출하세요.",
"Operator": "연산자",
"Orbit (right drag)": "회전 (오른쪽 드래그)",
"Other side": "반대쪽",
@@ -330,11 +258,8 @@
"Pick the tool bodies": "공구 바디를 선택하세요",
"Picked": "선택한 항목",
"Picked point": "선택한 점",
"Pitch": "피치",
"Pivot": "피벗",
"Place at the origin instead": "대신 원점에 배치",
"Plastic Rules": "플라스틱 규칙",
"Play the joint's motion in the view": "뷰에서 조인트의 모션 재생",
"Playback": "재생",
"Plot": "그래프",
"Position": "위치",
@@ -342,18 +267,14 @@
"Position (G0)": "위치 (G0)",
"Position along the edge, 0 at the start end, 1 at the other": "모서리를 따른 위치, 0은 시작점, 1은 반대쪽 끝",
"Preferences › Design turns the offer back on": "환경 설정 › 디자인에서 다시 켤 수 있습니다",
"Preview": "미리보기",
"Preview Row": "행 미리보기",
"Preview leaves the current design unchanged.": "미리보기는 현재 디자인을 변경하지 않습니다.",
"Profile / Edges": "프로파일 / 모서리",
"Profiles or curves": "프로파일 또는 곡선",
"Profiles/Curves": "프로파일/곡선",
"Quantity": "양",
"RECENT": "최근",
"REFERENCE": "참조",
"RULED SURFACE": "룰드 서피스",
"RULES": "규칙",
"Radius": "반지름",
"Radius Fit": "반지름 맞춤",
"Ratio": "비율",
"Reattach selected face": "선택한 면 다시 부착",
@@ -362,8 +283,6 @@
"Rectangular": "직사각형",
"Reference face": "참조 면",
"Remove Tools": "공구 제거",
"Remove condition": "조건 제거",
"Remove from Folder": "폴더에서 제거",
"Remove from list": "목록에서 제거",
"Remove override": "재정의 제거",
"Remove private design details or paths before sharing.": "공유하기 전에 비공개 디자인 정보나 경로를 제거하세요.",
@@ -371,18 +290,12 @@
"Remove the column": "열 제거",
"Remove the track": "트랙 제거",
"Remove this point": "이 점 제거",
"Rename Feature": "피처 이름 바꾸기",
"Rename Folder": "폴더 이름 바꾸기",
"Replace existing library file": "기존 라이브러리 파일 바꾸기",
"Report a bug — review a screenshot and open a GitHub draft": "버그 신고 — 스크린샷을 검토하고 GitHub 초안 열기",
"Report text copied. Paste it into GitHub and submit your report there.": "보고서 텍스트를 복사했습니다. GitHub에 붙여넣고 그곳에서 보고서를 제출하세요.",
"Reports are public. A GitHub account is required.": "보고서는 공개됩니다. GitHub 계정이 필요합니다.",
"Reset Toolbar": "도구 모음 재설정",
"Resize Part": "부품 크기 조정",
"Restore View": "뷰 복원",
"Restore outer boundary": "외부 경계 복원",
"Restore the inherited component or physical material appearance": "상속된 구성요소 또는 물리 재질 외관 복원",
"Restore this studio's tools": "이 스튜디오의 도구 복원",
"Result": "결과",
"Retake screenshot": "스크린샷 다시 찍기",
"Reverse": "반전",
@@ -392,14 +305,7 @@
"Revert Pos.": "위치 되돌리기",
"Revert Position": "위치 복원",
"Review the report and screenshot before opening GitHub.": "GitHub를 열기 전에 보고서와 스크린샷을 검토하세요.",
"Review your report on GitHub and click Submit new issue to publish it.": "GitHub에서 보고서를 검토한 뒤 Submit new issue를 클릭해 게시하세요.",
"Rib Length": "리브 길이",
"Rib Offset": "리브 오프셋",
"Rib Thickness": "리브 두께",
"Ribs": "리브",
"Rim Edge": "테두리 모서리",
"Ring of tools on the right button": "오른쪽 버튼의 도구 링",
"Roll Back Here": "여기로 롤백",
"Roll History Marker Here": "여기로 히스토리 마커 이동",
"Round": "둥글게",
"Rule": "Rule",
@@ -409,34 +315,25 @@
"SURFACE QUALITY": "곡면 품질",
"Save Rule": "규칙 저장",
"Save View": "뷰 저장",
"Save screenshot…": "스크린샷 저장…",
"Save the study as an animated GIF, seen as the viewport shows it": "뷰포트에 보이는 대로 스터디를 애니메이션 GIF로 저장",
"Saved preview. Geometry is being checked before editing.": "저장된 미리보기입니다. 편집하기 전에 기하를 검사하는 중입니다.",
"Scale factor": "배율",
"Scene": "장면",
"Screenshot (optional)": "스크린샷 (선택 사항)",
"Search": "검색",
"Search commands": "명령 검색",
"Search this library": "이 라이브러리 검색",
"Second plane": "두 번째 평면",
"Section": "단면",
"Section position": "단면 위치",
"Sections (one sketch each)": "단면 (각각 스케치 하나)",
"Select Other": "다른 항목 선택",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "다른 항목 선택: 모델 위에서 왼쪽 버튼을 가만히 누르고 있거나(또는 오른쪽 클릭 › 다른 항목 선택…) 커서 아래의 모든 항목을 나열합니다. 같은 위치를 다시 클릭하면 다음 항목이 선택됩니다. 가운데 버튼을 더블 클릭하면 홈으로 이동합니다(환경설정 › 탐색).",
"Select bodies (a face picks its body)": "바디 선택(면을 클릭하면 해당 바디 선택)",
"Select bodies or faces instead": "대신 바디 또는 면을 선택하세요",
"Select edges": "모서리 선택",
"Selection filter": "선택 필터",
"Semantic faces share their appearance across instances.": "시맨틱 면은 인스턴스 간에 외관을 공유합니다.",
"Send your report on GitHub": "GitHub에서 보고서 보내기",
"Shaded": "음영 처리",
"Shape": "모양",
"Sheet Rules": "시트 규칙",
"Show Agent Cursor": "에이전트 커서 표시",
"Show in folder": "폴더에서 보기",
"Show the pointer an AI agent moves (#35)": "AI 에이전트가 움직이는 포인터 표시 (#35)",
"Show tool at cursor": "커서 옆에 도구 표시",
"Signed axes": "부호 있는 축",
"Size": "크기",
"Sketch constraints": "스케치 구속 조건",
@@ -449,37 +346,24 @@
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "이 디자인들이 저장되기 전에 SolidCraft가 종료되었습니다. 마지막 자동 저장본을 열 수 있습니다.",
"Specialise": "특수화",
"Speed": "속도",
"Splitting tool": "분할 도구",
"Square": "정사각형",
"Stand it up": "세우기",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "세우기: X축 기준 90도 회전으로, 실행 취소할 수 있는 하나의 이동 피처로 적용됩니다.",
"Stand this part up?": "이 부품을 세울까요?",
"Start angle": "시작 각도",
"Start bulge": "시작 벌지",
"Start continuity": "시작 연속성",
"Step": "단계",
"Steps": "단계",
"Steps to reproduce (optional)": "재현 단계 (선택 사항)",
"Stitch the kept pieces": "유지한 부분 재봉",
"Stitch to both bodies": "두 바디에 재봉",
"Studio": "스튜디오",
"Study": "연구",
"Style": "스타일",
"Summary": "요약",
"Suppress": "비활성화",
"Suppressed": "억제됨",
"Surface": "곡면",
"Surface, or faces to turn over": "곡면 또는 뒤집을 면",
"Swap Ends": "양 끝 바꾸기",
"TRIM": "트림",
"Tangent (G1)": "접선 (G1)",
"Tangent Chain": "접선 체인",
"Tangent chain": "접선 체인",
"Tap drill diameter": "탭 드릴 지름",
"Target": "대상",
"Target Body": "대상 바디",
"Tear": "찢김",
"Technical details": "기술 세부 정보",
"The dashed sketch is a saved plane guide. Select a new face to repair the attachment.": "점선 스케치는 저장된 평면 가이드입니다. 새 면을 선택하여 부착을 복구하세요.",
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "저장된 작성자 인스턴스가 없습니다. 다시 연결하기 전에 올바른 작성자 배치를 선택하세요.",
"Then": "Then",
@@ -491,32 +375,24 @@
"This child is shared. Deleting it removes it from every copy of its parent.": "이 하위 항목은 공유됩니다. 삭제하면 상위 항목의 모든 복사본에서 제거됩니다.",
"This child is shared. Position changes apply to every copy of its parent.": "이 하위 항목은 공유됩니다. 위치 변경은 상위 항목의 모든 복사본에 적용됩니다.",
"This face cannot provide a durable attachment. Choose another face or explicitly use a fixed plane.": "이 면은 안정적인 부착을 제공할 수 없습니다. 다른 면을 선택하거나 고정 평면을 명시적으로 사용하세요.",
"This image is not attached to GitHub yet. Copy or save it, then attach it on GitHub.": "이 이미지는 아직 GitHub에 첨부되지 않았습니다. 복사하거나 저장한 뒤 GitHub에서 첨부하세요.",
"To": "끝",
"To Offset": "오프셋까지",
"To edges": "끝 모서리",
"Tolerance": "공차",
"Too close": "너무 가까움",
"Tool Bodies": "공구 바디",
"Tools": "도구",
"Tools, clockwise from the top": "도구 (위쪽부터 시계 방향)",
"Trimming tool (plane, face or curves)": "트림 도구(평면, 면 또는 곡선)",
"Turn off section analysis before exporting the view.": "뷰를 내보내기 전에 단면 분석을 끄세요.",
"Type": "유형",
"UNTRIM": "트림 해제",
"Unit": "단위",
"Unsuppress": "비활성화 해제",
"Updating the preview": "미리보기 업데이트 중",
"Use active author placement": "활성 작성자 배치 사용",
"Use fixed plane": "고정 평면 사용",
"Use saved fixed plane": "저장된 고정 평면 사용",
"Use selected face as fixed plane": "선택한 면을 고정 평면으로 사용",
"Use the SolidWorks mouse mode": "SolidWorks 마우스 모드 사용",
"User Parameters": "사용자 매개변수",
"VIEW": "보기",
"VIEWS": "뷰",
"Value": "Value",
"Variable Radius": "가변 반지름",
"Velocity": "속도",
"Vertices": "꼭짓점",
"View navigator": "뷰 탐색기",
@@ -524,54 +400,19 @@
"Wall": "벽",
"Wall Location": "벽 위치",
"Wall Thickness": "벽 두께",
"What did you do just before the problem? A short list is enough": "문제가 생기기 직전에 무엇을 했나요? 짧은 목록이면 충분합니다",
"What did you expect to see?": "어떤 결과를 기대했나요?",
"What happened?": "무슨 일이 있었나요?",
"When": "When",
"Width": "너비",
"Will fail": "실패 예정",
"With gestures on, the right button no longer orbits the view: hold it and move to open the ring, then touch a tool's zone. A plain right-click still opens the menu.": "제스처를 켜면 오른쪽 버튼으로 더 이상 뷰가 회전하지 않습니다. 버튼을 누른 채 움직여 링을 열고 도구 영역에 닿으세요. 그냥 오른쪽 클릭하면 여전히 메뉴가 열립니다.",
"X": "X",
"X Angle": "X 각도",
"X angle": "X 각도",
"X distance": "X 거리",
"Y": "Y",
"Y Angle": "Y 각도",
"Y angle": "Y 각도",
"Y distance": "Y 거리",
"Z": "Z",
"Z Angle": "Z 각도",
"Z Distance": "Z 거리",
"Z angle": "Z 각도",
"Z distance": "Z 거리",
"Zebra": "제브라",
"ai.prefs.availability": "어시스턴트 제어",
"ai.prefs.background_commands": "에이전트 명령을 백그라운드에서 실행",
"ai.prefs.follow_camera": "카메라를 스케치로 돌리기",
"ai.prefs.follow_camera_off": "카메라 자동 따라가기가 꺼져 있습니다.",
"ai.prefs.follow_camera_on": "에이전트가 만드는 스케치를 향해 카메라가 회전합니다.",
"ai.prefs.intro": "에이전트, 권한, 그리고 작업이 표시되는 방식.",
"ai.prefs.make_default": "기본값으로 사용",
"ai.prefs.no_agents": "아직 에이전트가 없습니다. 에이전트를 추가하거나 새로 고쳐서 설치된 에이전트를 찾으세요.",
"ai.prefs.no_saved_approvals": "저장된 승인이 없습니다.",
"ai.prefs.override_all_warning": "이 에이전트는 묻지 않고 설계를 저장, 덮어쓰기, 내보내기 또는 교체할 수 있습니다.",
"ai.prefs.permission_all": "모두 허용",
"ai.prefs.permission_all_summary": "사용 가능한 SolidCraft 도구가 묻지 않고 실행됩니다.",
"ai.prefs.permission_ask": "변경 전에 묻기",
"ai.prefs.permission_ask_summary": "모든 설계 변경과 파일 작업을 승인합니다.",
"ai.prefs.permission_edits": "모델 편집 허용",
"ai.prefs.permission_edits_summary": "모델 편집은 실행되고, 파일 및 파괴적인 작업은 계속 묻습니다.",
"ai.prefs.permission_scope": "SolidCraft 도구에만 적용됩니다. 에이전트의 셸과 파일 편집은 자체 권한을 사용합니다.",
"ai.prefs.pointer_preview_hidden": "포인터 숨김",
"ai.prefs.pointer_preview_speed": "에이전트 포인터 · {speed}",
"ai.prefs.pointer_speed": "동작 속도",
"ai.prefs.privacy_note": "선택한 에이전트 또는 엔드포인트만 대화를 받습니다. 엔드포인트 키는 시스템 키체인에 저장됩니다.",
"ai.prefs.show_pointer": "에이전트 포인터 표시",
"ai.prefs.turn_limit_hint": "메시지 하나에 어시스턴트가 수행할 수 있는 설계 변경 도구 호출의 최대 횟수입니다.",
"angle, height": "각도, 높이",
"appearance.dielectric_ior": "유전체 굴절률",
"appearance.metallic": "금속성",
"appearance.only_available": "사용 가능한 외관만",
"appearance.pbr_help": "이 표면은 렌더링 작업 공간이나 제품 PNG에서 확인하세요. 굴절과 투과는 지원하지 않습니다.",
"appearance.pbr_opaque": "불투명 PBR",
"appearance.roughness": "거칠기",
@@ -733,7 +574,6 @@
"cmd.material_create": "새 물리 재질",
"cmd.material_delete": "물리 재질 삭제",
"cmd.material_edit": "물리 재질 편집",
"cmd.material_inspect": "물리 재질 검사",
"cmd.material_list": "재질 목록",
"cmd.material_properties_directional": "방향별 탄성 계수",
"cmd.material_properties_validate": "물리 재질 데이터 검증",
@@ -780,32 +620,13 @@
"cmd.plastic_rules": "플라스틱 규칙 목록",
"cmd.plastic_snap_fit": "스냅핏",
"cmd.render_backends": "렌더링 백엔드",
"cmd.render_camera_fit": "렌더 대상 화면 맞춤",
"cmd.render_camera_get": "렌더 카메라 확인",
"cmd.render_cancel": "렌더링 취소",
"cmd.render_export": "PNG 내보내기",
"cmd.render_hdr_inspect": "HDR 소스 검사",
"cmd.render_library_appearance_apply": "라이브러리 불투명 외관 적용",
"cmd.render_library_describe": "렌더 리소스 검사",
"cmd.render_library_environment_apply": "내장 HDR 환경 적용",
"cmd.render_library_list": "렌더 라이브러리 탐색",
"cmd.render_library_physical_prepare": "Prepare Physical Reference",
"cmd.render_library_studio_apply": "제작된 스튜디오 조명 적용",
"cmd.render_library_studio_prepare": "제작된 스튜디오 조명 준비",
"cmd.render_library_surface_bind": "Save Surface Binding",
"cmd.render_library_surface_prepare": "제작된 표면 맵 준비",
"cmd.render_library_surface_unbind": "Remove Surface Binding",
"cmd.render_library_targets": "렌더 리소스 대상 찾기",
"cmd.render_preflight": "렌더 요청 확인",
"cmd.render_result_pin": "표시된 렌더 결과 유지",
"cmd.render_scene": "렌더링 장면 검사",
"cmd.render_settings_apply": "렌더링 카메라 적용",
"cmd.render_settings_capture_hdr": "HDR과 렌더 설정 저장",
"cmd.render_settings_get": "렌더링 설정",
"cmd.render_settings_set": "렌더링 설정 저장",
"cmd.render_stage_backdrop": "물리 렌더 배경 저장",
"cmd.render_stage_clear": "Remove Render Stage",
"cmd.render_stage_ground": "Save Render Ground",
"cmd.render_start": "이미지 렌더링",
"cmd.render_status": "렌더링 진행 상황",
"cmd.select_clear": "선택 지우기",
@@ -1041,12 +862,6 @@
"help.licences": "라이선스",
"help.mit": "MIT 라이선스",
"help.notice": "NOTICE",
"help.solid.chamfer": "선택한 모서리를 지정한 거리만큼 평평하게 깎습니다. 날카로운 모서리를 다듬거나 핀 또는 나사가 쉽게 들어가도록 할 때 사용합니다.",
"help.solid.extrude": "스케치 프로파일을 지정한 거리만큼 3D로 밀어내 재료를 더하거나 제거합니다. 평면 윤곽을 블록, 보스 또는 포켓으로 만들 때 사용합니다.",
"help.solid.fillet": "선택한 모서리를 지정한 반지름의 매끄러운 원호로 둥글게 만듭니다. 강도, 편의성 또는 외관을 위해 날카로운 모서리를 부드럽게 할 때 사용합니다.",
"help.solid.hole": "면의 선택한 위치에 지정한 지름의 원형 구멍을 뚫습니다. 나사 여유 구멍, 핀 및 기타 단순한 원형 개구부에 사용합니다.",
"help.solid.revolve": "스케치 프로파일을 축을 중심으로 회전시켜 둥근 솔리드를 만듭니다. 샤프트, 손잡이, 그릇 및 중심선을 기준으로 둥근 모든 부품에 사용합니다.",
"help.solid.shell": "솔리드의 속을 비우고 지정한 두께의 벽을 남기며, 선택한 면을 열 수도 있습니다. 상자, 커버 및 기타 얇은 벽 부품에 사용합니다.",
"help.third_party": "타사 라이선스",
"home.back": "{name}(으)로 돌아가기",
"home.crashes": "충돌 보고서",
@@ -1156,15 +971,8 @@
"motion.to_end": "끝으로",
"motion.to_start": "처음으로",
"motion.track_name": "트랙 {number}",
"nav.alias.help": "Shift+Alt+왼쪽 드래그는 회전, Shift+Alt+가운데 드래그는 이동, Shift+Alt+오른쪽 드래그는 확대/축소입니다. 가운데 드래그로도 이동하고 오른쪽 드래그로도 회전합니다",
"nav.blender.help": "가운데 드래그는 회전, Shift+가운데 드래그는 이동, Ctrl+가운데 드래그는 확대/축소입니다. 오른쪽 드래그는 카메라에 영향을 주지 않습니다",
"nav.free_orbit.help": "마우스와 3D 마우스로 롤을 유지하며 양 극점을 넘어 회전합니다. Shift 축 스냅은 비활성화됩니다.",
"nav.free_orbit.label": "자유 회전",
"nav.fusion.help": "가운데 드래그는 이동, Shift+가운데 드래그는 회전, 오른쪽 드래그는 회전입니다",
"nav.inventor.help": "가운데 드래그는 이동, Shift+가운데 드래그는 회전, Ctrl+가운데 드래그는 확대/축소, 오른쪽 드래그는 회전입니다",
"nav.openscad.help": "빈 공간에서 왼쪽 드래그는 회전, Shift+왼쪽 드래그는 상자 선택, 스케치 형상 드래그는 편집입니다. 오른쪽 또는 가운데 드래그는 이동, Shift+오른쪽 또는 Shift+가운데 드래그는 확대/축소입니다",
"nav.solidworks.help": "가운데 드래그는 회전, Ctrl+가운데 드래그는 이동, Shift+가운데 드래그는 확대/축소, 오른쪽 드래그는 회전입니다",
"nav.tinkercad.help": "오른쪽 드래그는 회전, 가운데 또는 Shift+오른쪽 드래그는 이동입니다",
"nav.touchpad.help": "Shift를 누른 채 포인터를 움직이면 화면이 이동하고, Alt/Option을 누른 채 움직이면 회전합니다. 마우스 버튼은 필요 없습니다. 세로 스크롤로 확대/축소합니다.",
"nav.touchpad.label": "터치패드",
"nav.touchpad.orbit": "회전 (Alt/Option을 누르고 이동, 마우스 버튼 불필요)",
@@ -1187,130 +995,8 @@
"pattern.flip_direction": "방향 반전",
"pick a hole to seat it, or OK places it at the origin": "안착시킬 구멍을 선택하거나, 확인을 누르면 원점에 배치됩니다",
"pick one or two vertices, edges or faces": "꼭짓점, 모서리 또는 면을 하나나 두 개 선택하세요",
"prefs.add_item": "항목 추가",
"prefs.alt_frees_manipulators": "Alt / Option 키로 조작 화살표도 자유롭게 움직입니다",
"prefs.anti_aliasing_note": "울퉁불퉁한 모서리를 부드럽게 합니다.",
"prefs.applies_on_restart": "다시 시작하면 적용됩니다",
"prefs.assistant": "어시스턴트",
"prefs.axes": "축",
"prefs.axis": "축",
"prefs.bottom": "아래쪽",
"prefs.browser": "브라우저",
"prefs.built_in": "기본 제공",
"prefs.button_left": "왼쪽 드래그",
"prefs.button_middle": "가운데 드래그",
"prefs.button_right": "오른쪽 드래그",
"prefs.choose_font": "글꼴 파일 선택…",
"prefs.click": "클릭",
"prefs.contact_shading": "부드러운 접촉 음영",
"prefs.contact_shading_note": "부품이 가까워지는 곳의 부드러운 음영.",
"prefs.context_menu": "컨텍스트 메뉴",
"prefs.context_menu_ring": "컨텍스트 메뉴 · 길게 누르면 방사형 메뉴",
"prefs.cpu": "CPU (소프트웨어)",
"prefs.crash_reports": "충돌 보고서 열기…",
"prefs.crash_reports_note": "보고서는 문제를 재현하는 설계 및 명령과 함께 이 컴퓨터에 보관됩니다.",
"prefs.ctrl_cmd": "Ctrl / Cmd",
"prefs.curved_surfaces": "곡면",
"prefs.curves": "곡면",
"prefs.dead_zone_note": "빗금 친 영역이 데드존입니다. 그 안의 움직임은 무시됩니다.",
"prefs.default": "기본값",
"prefs.design": "설계 기본값",
"prefs.design_note": "새 설계가 시작할 때의 설정입니다. 열려 있는 설계는 자체 설정을 유지합니다.",
"prefs.devices": "3D 마우스",
"prefs.display": "표시",
"prefs.edges": "모서리 및 음영",
"prefs.extend": "확장",
"prefs.extra_navigation": "Ctrl+왼쪽 드래그로 빈 공간에서 회전하고, Space를 누른 채 이동합니다.",
"prefs.follows_os": "운영 체제 설정을 따름",
"prefs.font": "글꼴",
"prefs.font_fallback": "선택한 글꼴에 없는 글리프는 기본 제공 글꼴로 대체되므로 빈 상자로 표시되는 레이블이 없습니다.",
"prefs.font_sample": "The quick brown fox · Ø12.50 mm · 45.0° · 0123456789",
"prefs.font_unreadable": "선택한 글꼴을 읽을 수 없습니다.",
"prefs.general": "일반",
"prefs.general_note": "언어, 저장 및 업데이트.",
"prefs.gesture_orbit": "회전",
"prefs.gesture_pan": "이동",
"prefs.gesture_zoom": "확대/축소",
"prefs.graphics": "그래픽",
"prefs.graphics_note": "속도와 부드러움 중에서 선택합니다. 각 선택의 효과가 샘플에 표시됩니다.",
"prefs.graphics_presets_note": "프리셋은 아래의 선택 항목을 한 번에 설정합니다.",
"prefs.history": "이력",
"prefs.hold_stop_snapping": "누르고 있으면 스냅 중지",
"prefs.icons_only": "아이콘만",
"prefs.imported_file": "가져온 파일",
"prefs.input": "입력",
"prefs.interface": "인터페이스",
"prefs.interface_language": "인터페이스 언어",
"prefs.interface_note": "SolidCraft의 모양과 각 부분의 위치입니다.",
"prefs.interval_minutes": "{n}분",
"prefs.labels": "레이블",
"prefs.layout": "레이아웃",
"prefs.layout_note": "컴팩트는 패널을 창 가장자리에 고정합니다. 브라우저 반투명은 플로팅에서 사용할 수 있습니다. 어시스턴트 위치는 어시스턴트 페이지에서 바꿀 수 있습니다.",
"prefs.left": "왼쪽",
"prefs.left_click": "왼쪽 클릭",
"prefs.live_input": "실시간 입력",
"prefs.live_preview": "실시간 미리보기",
"prefs.minutes": "분",
"prefs.minutes_suffix": " min",
"prefs.mouse": "마우스 및 트랙패드",
"prefs.mouse_heading": "마우스",
"prefs.mouse_mapping": "마우스 매핑",
"prefs.navigation": "탐색",
"prefs.navigation_note": "마우스, 트랙패드, 3D 마우스로 뷰를 움직이는 방식입니다.",
"prefs.not_on_gpu": "이 GPU에서는 사용할 수 없음",
"prefs.on": "켜짐",
"prefs.opaque": "불투명",
"prefs.orbit_snap": "회전하는 동안 Shift를 누르면 축에 맞춰집니다.",
"prefs.orientation": "방향",
"prefs.overall_speed": "전체 속도",
"prefs.preview_choices": "선택 항목 미리보기",
"prefs.quality_default": "기본값",
"prefs.quality_fastest": "가장 빠름",
"prefs.quality_slowest": "가장 느림",
"prefs.quality_smoother": "더 부드럽게",
"prefs.reports_saved": "{n}개 저장됨",
"prefs.right_click": "오른쪽 클릭",
"prefs.saving": "자동 저장 및 복구",
"prefs.scale_note": "창에서 Ctrl + / Ctrl −로도 조절합니다. 버튼은 100%에서 28 px입니다.",
"prefs.scale_text": "배율 및 텍스트",
"prefs.select": "선택",
"prefs.select_one_item": "항목 하나 선택",
"prefs.selecting": "선택",
"prefs.shift": "Shift",
"prefs.shift_add_ctrl_toggle": "Shift는 추가만, Ctrl / Cmd는 토글",
"prefs.shift_add_note": "켬: Shift는 추가, Ctrl / Cmd는 토글. 끔(기본값): 둘 다 토글.",
"prefs.shortcuts": "단축키",
"prefs.sketch_selection": "스케치 및 선택",
"prefs.sketch_selection_heading": "스케치 및 선택",
"prefs.sketch_selection_note": "스케치가 스냅되는 방식과 클릭으로 선택하는 방식입니다.",
"prefs.sketching": "스케치",
"prefs.snap_drawing_note": "스케치 점을 그리거나 드래그하는 동안.",
"prefs.source": "소스",
"prefs.speed": "속도",
"prefs.stand_up_note": "STEP 또는 IGES 파일이 Z가 위쪽인 상태로 열린 후 X축 기준 90도 회전을 제안합니다.",
"prefs.status_done": "완료",
"prefs.status_error": "오류",
"prefs.status_selected": "선택됨",
"prefs.status_warning": "경고",
"prefs.supersampling_brief": "뷰를 더 크게 그린 뒤 줄여서 필터링합니다. 매우 선명하지만 더 무겁습니다.",
"prefs.tab_accent_note": "강조 색상이 활성 탭을 따라갑니다",
"prefs.theme_file": "테마 파일",
"prefs.timeline_half_hour": "−30분",
"prefs.timeline_now": "지금",
"prefs.timeline_quarter_hour": "−15",
"prefs.toggle_item": "항목을 추가하거나 다시 제거",
"prefs.toolbar": "도구 모음",
"prefs.translucent": "반투명",
"prefs.under_browser": "브라우저 아래",
"prefs.under_cursor": "커서 아래",
"prefs.units_precision": "단위 및 정밀도",
"prefs.updates": "업데이트",
"prefs.view": "뷰 동작",
"prefs.viewport": "뷰포트",
"prefs.wheel": "휠",
"prefs.wheel_trackpad": "휠 및 트랙패드",
"prefs.workspace": "작업 공간",
"prefs.zoom_toward_cursor": "확대/축소, 커서 방향으로",
"properties.title": "속성: {name}",
"recovery.days_ago": "{count}일 전",
"recovery.detail": "{file} · 피처 {features}개 · {when} 자동 저장",
@@ -1318,33 +1004,13 @@
"recovery.just_now": "방금",
"recovery.min_ago": "{count}분 전",
"recovery.never_saved": "저장된 적 없음",
"render.add_ground": "Add fitted ground to setup",
"render.background": "장면 배경",
"render.bloom_enable": "표시 블룸",
"render.bloom_help": "양의 선형 하이라이트를 세 번의 정규화 필터로 확산하는 출력 전용 효과입니다. 반경은 출력 픽셀 단위이며 sRGB 전에 더합니다. 렌즈·볼륨·수렴 개선이 아니며 원본 EXR은 변하지 않습니다.",
"render.bloom_opaque": "블룸에는 불투명 출력이 필요합니다. 투명 PNG를 몰래 합성하지 않고 거부합니다.",
"render.bloom_radius": "블룸 반경",
"render.bloom_strength": "블룸 강도",
"render.bloom_threshold": "선형 하이라이트 임계값",
"render.clear_stage": "Remove saved stage",
"render.cpu_budget": "CPU 리소스 프로필",
"render.cpu_budget_bodies": "바디 수 상한",
"render.cpu_budget_compact": "컴팩트 (기존 제한)",
"render.cpu_budget_details": "리소스 제한",
"render.cpu_budget_help": "더 큰 제한은 명시적으로 선택합니다. 해상도와 샘플 수를 자동으로 줄이지 않습니다.",
"render.cpu_budget_memory": "작업 메모리 상한",
"render.cpu_budget_not_guarantee": "이 값은 허용 상한이며 RAM 예약이나 속도 보장이 아닙니다. 형상, 필름, 보관된 미리보기가 메모리 예산을 공유합니다. 렌더링은 취소할 수 있습니다.",
"render.cpu_budget_resize": "설정이 CPU 프로필이나 카메라 샘플 예산을 초과합니다. 더 작은 크기를 명시적으로 적용하거나 더 큰 프로필을 선택하세요.",
"render.cpu_budget_studio": "스튜디오 (큰 이미지)",
"render.cpu_budget_triangles": "삼각형 수 상한",
"render.cpu_budget_workstation": "워크스테이션 (대규모 장면)",
"render.exr_pending": "GPU는 원본 선형 EXR을 보존할 수 있습니다. 압축, 다중 레이어 이미지 및 보조 렌더 패스는 아직 사용할 수 없습니다.",
"render.fit": "제품에 맞추기",
"render.image_cancelled": "렌더링 취소됨",
"render.image_cancelling": "취소 중",
"render.image_completed": "이미지 내보내기 준비됨",
"render.image_encoding": "PNG 인코딩 중",
"render.image_export": "완료된 이미지 내보내기",
"render.image_export": "PNG 내보내기",
"render.image_failed": "렌더링 실패",
"render.image_height": "높이",
"render.image_help": "CPU 미리 보기 이미지 · PNG · 근사 투명도",
@@ -1362,98 +1028,17 @@
"render.image_unknown": "렌더 상태를 확인할 수 없습니다",
"render.image_use_largest": "이 비율에서 가장 큰 크기 사용:",
"render.image_width": "너비",
"render.include_rig_cards": "Include passive cards and flags",
"render.library_applied": "리소스 변환을 적용했습니다. 시작 전에 전체 렌더 요청을 확인하세요.",
"render.library_apply_hdr": "이 HDR 환경과 함께 저장",
"render.library_apply_maps": "제작된 GPU 표면 맵 사용",
"render.library_apply_studio": "제작된 스튜디오 변환 저장",
"render.library_apply_uniform": "균일한 불투명 외관 적용",
"render.library_authored_help": "독자적으로 제작한 리소스는 표현용이며 측정된 공학 데이터가 아닙니다. 각 동작은 명시된 변환만 적용합니다.",
"render.library_blocked": "이 리소스에는 미해결 보정 또는 외부 태양 광원이 필요하여 아직 적용할 수 없습니다.",
"render.library_center": "리그 중심",
"render.library_emitters_only_ack": "광원만 사용하고 명시된 수동 반사판은 생략",
"render.library_environment_concepts": "환경 구상",
"render.library_extent": "대상 크기",
"render.library_hdr": "내장 HDR 환경",
"render.library_hdr_help": "원본 HDR을 저장 설정에 포함합니다. 제작된 경도 원점을 명시적으로 변환하며 저장 후 외부 파일이 필요하지 않습니다.",
"render.library_light_concepts": "조명 구상",
"render.library_maps_help": "Built-in maps can be saved with pinned sources and local occurrence mapping. Temporary optical overrides remain a separate mode.",
"render.library_material_concepts": "재질 구상",
"render.library_next": "다음 페이지",
"render.library_physical_profiles": "Physical reference profiles",
"render.library_previous": "이전 페이지",
"render.library_quality_concepts": "품질 사양",
"render.library_replace_environment": "기존 환경을 명시적으로 교체",
"render.library_replace_studio": "Replace saved lighting, environment and stage",
"render.library_results": "일치하는 리소스",
"render.library_rig_help": "Fit once at an explicit center and physical extent. Complete rigs include passive cards; calibration blockers still refuse. Conservative emitter/card clearance is checked before saving.",
"render.library_rigs": "제작된 조명 리그",
"render.library_save_maps": "Save maps to this setup",
"render.library_scene_bounds": "캡처된 장면 범위 사용",
"render.library_scenes": "장면 사양",
"render.library_search": "라이브러리 검색",
"render.library_selene": "SELENE 불투명 외관",
"render.library_specification_only": "사양만 제공되며 완전히 실행 가능한 프리셋이 아닙니다. 필요한 효과, 리소스 및 대상 역할은 명시적인 구현과 연결이 필요합니다.",
"render.library_surfaces": "제작된 PBR 표면",
"render.library_target": "대상 바디",
"render.library_title": "렌더 리소스 라이브러리",
"render.library_uniform_ack": "공간 변화나 고급 효과 없는 균일 근사 사용",
"render.material_color_replace": "기본 맵으로 바디 색상 교체",
"render.material_limits": "최대 128개 바디 설정, 분리된 유리 32개, 코팅 64개, 텍스처 연결 128개. 같은 맵은 메모리를 공유하며 실제 장치 및 전체 메모리 제한은 유지됩니다.",
"render.material_metallic_map": "금속성 맵 (선형)",
"render.material_roughness_map": "거칠기 맵 (선형)",
"render.material_v_up": "텍스처 V축은 이미지 위쪽",
"render.navigation_help": "카메라는 디자인에서와 같이 움직입니다(환경설정 › 탐색). 설정 저장은 현재 카메라를 기록합니다.",
"render.opaque_image_summary": "미리보기 PNG · 불투명 PBR · 블렌딩 투명도",
"render.opaque_preview_help": "불투명 PBR 및 단색 표시, 블렌딩 투명도를 지원합니다. 굴절, HDRI 및 최종 렌더링은 아직 지원하지 않습니다.",
"render.output_format_status": "PNG는 완료된 표시 이미지 그대로입니다. 선택적 GPU EXR은 표시 필터 전 선형 FLOAT32 방사 휘도를 보존합니다. 미리보기 캐시나 다른 작업으로 바꾸어 내보내지 않습니다.",
"render.passive_stage": "Render-only stage",
"render.passive_stage_help": "Finite passive panels belong only to rendered images. They do not add CAD bodies or mass. Placement is fixed after applying; intersecting product bounds refuse. CPU does not provide diffuse bounce lighting.",
"render.perspective": "원근 카메라",
"render.physical_acknowledge": "I understand this partial reference needs grade, condition and source review",
"render.physical_associations": "Physical candidates and roles",
"render.physical_copy_plan": "Copy plan with sources and omissions",
"render.physical_material_name": "Proposed reference material name",
"render.physical_omissions": "Properties not representable in the native card",
"render.physical_plan_only": "Read-only plan prepared. No material was created or assigned. Keep its extended profile and omissions when reviewing the native card.",
"render.physical_prepare_plan": "Prepare creation plan (does not apply)",
"render.physical_separate": "Appearance is not material identification. These are sourced references, not certified effective properties. Existing mass, engineering materials and optics are unchanged.",
"render.physical_unknown": "No numerical candidate is established. Grade, formulation or construction must be specified; unknown is not zero.",
"render.preview": "통합 미리보기",
"render.preview_help": "래스터 셰이딩과 블렌딩 투명도를 사용합니다. 굴절, HDRI 및 최종 렌더링은 아직 지원하지 않습니다.",
"render.reload_settings": "저장된 설정 다시 불러오기",
"render.replace_saved_surface": "Replace an existing saved binding for this target",
"render.restore_camera": "저장된 카메라 복원",
"render.result_reduced_display": "표시 미리보기를 뷰포트 장치 또는 캐시 제한에 맞춰 축소했습니다. 보관된 PNG와 내보내기는 전체 해상도를 유지합니다. 이 축소 보기에서는 원본 100% 검사가 불가능합니다.",
"render.return_design": "설계로 돌아가기",
"render.ribbon_backgrounds": "배경",
"render.ribbon_check": "검사",
"render.ribbon_check_help": "렌더에 제출할 현재 설정을 그대로 검증합니다. 엔진 전환, 축소, 리소스 교체를 몰래 수행하지 않습니다.",
"render.ribbon_edit_lights": "조명 편집",
"render.ribbon_environment_help": "HDR 조명, 카메라 전용 배경, 물리 무대는 독립된 설정이며 교체에는 명시적 확인이 필요합니다.",
"render.ribbon_finish_dialog": "먼저 파일 또는 확인 대화 상자를 완료하세요.",
"render.ribbon_frame": "화면 맞춤",
"render.ribbon_hdr_library": "HDR 라이브러리",
"render.ribbon_job_active": "렌더링이 진행 중입니다. 새 작업 전에 명시적으로 취소하세요.",
"render.ribbon_light_rigs": "조명 구성",
"render.ribbon_lights_help": "조명 구성을 검토하거나 지원 조명을 편집합니다. 명시적으로 적용하기 전에는 현재 설정을 교체하지 않습니다.",
"render.ribbon_materials": "재질",
"render.ribbon_materials_help": "공용 외관 검사기와 원본 표면 라이브러리를 엽니다. 광학 편집은 공학 속성을 임의로 변경하지 않습니다.",
"render.ribbon_no_active_job": "취소할 활성 렌더링이 없습니다.",
"render.ribbon_no_result": "이미지를 내보내려면 렌더링을 완료하세요.",
"render.ribbon_no_saved_camera": "카메라 복원 전에 샷을 저장하세요.",
"render.ribbon_saved_camera": "저장 카메라",
"render.ribbon_shots": "샷",
"render.ribbon_studios": "스튜디오",
"render.ribbon_studios_help": "구현된 스튜디오를 찾아보고 적용 전에 교체 항목을 검토합니다. 개념 장면은 구분됩니다.",
"render.ribbon_surface_maps": "표면 맵",
"render.save_setup": "설정 저장",
"render.saved_maps_runtime_conflict": "Saved surface bindings cannot be mixed with temporary optical overrides or an unsaved HDR. Save the HDR or remove the temporary inputs explicitly.",
"render.saved_surface_remove": "Remove saved binding",
"render.saved_surfaces": "Saved surface bindings",
"render.saved_surfaces_help": "Pinned built-in maps follow the selected occurrence in local millimeters. A replaced or missing target is an error, never an automatic reassignment.",
"render.setup_name": "설정 이름",
"render.stage_panel_count": "Saved panels",
"render.unavailable_preview": "이 설정은 사용할 수 없는 미리보기 프로필을 요청합니다.",
"render.unsaved": "저장하지 않은 미리보기 변경 사항",
"render.use_preview": "통합 미리보기 사용",
@@ -1530,7 +1115,6 @@
"thread.designation": "호칭",
"thread.iso_metric": "ISO 미터",
"thread.select_size": "크기 선택",
"timeline.feature_hint": "두 번 클릭하여 편집 · 드래그하여 순서 변경 · 마우스 오른쪽 버튼을 클릭하여 더 보기",
"timeline.group": "새 특성 폴더",
"timeline.move_many": "타임라인 항목 이동",
"timeline.move_to_group": "특성을 폴더로 이동",
@@ -1542,10 +1126,6 @@
"toolbar.customize_note": "{tab} 탭입니다. 명령을 선택하면 도구 모음에 해당 버튼이 추가됩니다. 패널의 드롭다운에서 행을 도구 모음으로 끌거나, 버튼을 도구 모음 밖으로 끌어 제거하거나, 드롭다운 행의 고정 핀을 사용할 수도 있습니다.",
"toolbar.image": "이미지",
"toolbar.render": "렌더링",
"toolbar.render_appearance": "외관",
"toolbar.render_camera": "카메라",
"toolbar.render_environment": "환경",
"toolbar.render_lighting": "조명",
"toolbar.render_scene": "장면",
"trace.points": "경로를 따라 {count}개 점",
"trace.stopped_by_contact": "샘플 {sample}에서 끝남: {a}이(가) {b}을(를) 통과합니다",
@@ -1780,8 +1360,6 @@
"ui.agent_chat_waiting": "확인 대기 중",
"ui.agent_cursor": "에이전트 커서",
"ui.agent_cursor_speed": "에이전트 커서 속도",
"ui.agent_off_thread": "에이전트가 명령을 실행하는 동안 창 반응성 유지",
"ui.agent_off_thread_note": "에이전트의 느린 명령(AI 패널 또는 MCP 클라이언트에서 실행)은 창과 별도로 계산됩니다. 뷰를 계속 회전하며 살펴볼 수 있고, 뷰의 칩에서 하나씩 중지할 수 있습니다. 끄면 각 명령이 창의 스레드에서 실행되며, 창은 명령이 끝날 때까지 기다립니다.",
"ui.ai_add": "추가",
"ui.ai_add_agent": "에이전트 추가…",
"ui.ai_add_agent_title": "에이전트 추가",
@@ -1837,6 +1415,7 @@
"ui.ai_permissions": "권한",
"ui.ai_preset": "프리셋",
"ui.ai_remove": "제거",
"ui.ai_request_limit": "메시지 하나에 24번 요청 후 멈췄습니다. 계속하려면 메시지를 하나 더 보내세요",
"ui.ai_reset": "재설정",
"ui.ai_settings": "AI 설정…",
"ui.ai_show_features": "AI 기능 표시",
@@ -2013,8 +1592,6 @@
"ui.filter_commands_or_keys": "명령 또는 키 필터",
"ui.findInTimeline": "타임라인에서 찾기",
"ui.findInWindow": "창에서 찾기",
"ui.fine": "세밀",
"ui.finest": "가장 세밀",
"ui.finish_sketch": "스케치 종료",
"ui.fit": "맞춤",
"ui.fix_unfix": "고정/고정 해제",
@@ -2112,17 +1689,10 @@
"ui.hideProfile": "윤곽 숨기기",
"ui.history_back": "한 단계 뒤로",
"ui.history_below_browser": "브라우저 아래",
"ui.history_edge_hint": "가장자리를 더블클릭",
"ui.history_first": "처음으로 이동",
"ui.history_forward": "한 단계 앞으로",
"ui.history_hide_names": "이름 숨기기",
"ui.history_last": "끝으로 이동",
"ui.history_marker_tip": "이력 마커: 드래그하여 모델을 되돌립니다. 새 피처는 여기에 추가됩니다",
"ui.history_names": "이름",
"ui.history_play": "끝까지 재생",
"ui.history_show_names": "이름 표시",
"ui.history_title": "기록",
"ui.history_toggle_names": "피처 이름 표시/숨기기",
"ui.hole": "구멍",
"ui.hole_tap_type": "탭 유형",
"ui.hole_type": "구멍 유형",
@@ -2246,7 +1816,6 @@
"ui.project_face_edges_sketch": "면 모서리를 새 스케치로 투영",
"ui.properties": "속성",
"ui.pull_direction": "빼기 방향",
"ui.quality": "품질",
"ui.quantity": "수량",
"ui.quantity_2": "수량 2",
"ui.quit": "종료",
@@ -2347,7 +1916,6 @@
"ui.spline": "스플라인",
"ui.split_body": "바디 분할",
"ui.stand_up_tip": "STEP 또는 IGES 파일이 Z 위쪽으로 열린 후 X축 기준 90도 회전을 제안",
"ui.standard": "표준",
"ui.start": "시작",
"ui.start_over": "처음부터 다시",
"ui.start_page": "시작 페이지",
@@ -2450,7 +2018,6 @@
"update.prefs_flatpak": "이 사본({version})은 Flatpak이 업데이트합니다: {command}를 실행하거나 소프트웨어 센터를 사용하세요. SolidCraft는 직접 확인하지 않습니다.",
"update.prefs_label": "업데이트",
"update.prefs_note": "SolidCraft {version} ({kind})입니다. 확인 시 GitHub에 최신 릴리스만 요청하며 다른 정보는 보내지 않습니다.",
"update.prefs_web": "페이지를 새로고침하면 최신 웹 앱을 가져옵니다. 네트워크를 사용할 수 없거나 업데이트 다운로드가 제한 시간 안에 완료되지 않으면 저장된 버전이 열립니다. 진행 중인 다운로드는 다음 새로고침을 위해 계속됩니다.",
"update.release_page": "릴리스 페이지 열기",
"update.restart": "지금 다시 시작",
"update.restart_later": "나중에",
+1 -11
View File
@@ -72,7 +72,6 @@
"Choose…": "Pilih…",
"Circular": "Bulat",
"Circumference": "Lilitan",
"Clockwise": "Ikut arah jam",
"Close draft": "Tutup draf",
"Collisions": "Perlanggaran",
"Colour": "Warna",
@@ -323,7 +322,6 @@
"Search commands": "Cari arahan",
"Search this library": "Cari dalam koleksi ini",
"Section": "Keratan",
"Section position": "Kedudukan keratan",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Pilih Lain: tahan butang kiri pada model tanpa bergerak (atau klik kanan › Pilih Lain…) untuk senarai objek di bawah kursor. Klik tempat sama untuk objek seterusnya. Klik dua kali butang tengah untuk ke Utama (Tetapan › Navigasi).",
"Select bodies (a face picks its body)": "Pilih jasad (klik muka untuk pilih jasadnya)",
"Select bodies or faces instead": "Pilih jasad atau muka",
@@ -335,7 +333,6 @@
"Show Agent Cursor": "Papar Kursor Ejen",
"Show in folder": "Papar dalam folder",
"Show the pointer an AI agent moves (#35)": "Papar penuding yang digerakkan oleh ejen AI (#35)",
"Show tool at cursor": "Tunjukkan alat di sebelah kursor",
"Signed axes": "Paksi bertanda",
"Size": "Saiz",
"Sketch constraints": "Kekangan lakaran",
@@ -348,11 +345,9 @@
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft ditutup sebelum reka bentuk disimpan. Buka simpanan automatik terakhirnya.",
"Specialise": "Khususkan",
"Speed": "Kelajuan",
"Square": "Segi empat sama",
"Stand it up": "Tegakkan",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Tegakkan: suku putaran pada X, satu ciri Alih yang boleh dibatalkan.",
"Stand this part up?": "Tegakkan bahagian ini?",
"Start angle": "Sudut mula",
"Start bulge": "Bonjolan permulaan",
"Start continuity": "Keselanjaran permulaan",
"Step": "Langkah",
@@ -417,7 +412,6 @@
"angle, height": "sudut, ketinggian",
"appearance.dielectric_ior": "IOR Dielektrik",
"appearance.metallic": "Sifat Logam",
"appearance.only_available": "Rupa yang tersedia sahaja",
"appearance.pbr_help": "Lihat dalam Render atau PNG produk. Tiada pembiasan atau penembusan cahaya.",
"appearance.pbr_opaque": "PBR Legap",
"appearance.roughness": "Kekasaran",
@@ -579,7 +573,6 @@
"cmd.material_create": "Bahan Fizikal Baharu",
"cmd.material_delete": "Padam Bahan Fizikal",
"cmd.material_edit": "Sunting Bahan Fizikal",
"cmd.material_inspect": "Periksa Bahan Fizikal",
"cmd.material_list": "Senarai Bahan",
"cmd.material_properties_directional": "Modulus Keanjalan Berarah",
"cmd.material_properties_validate": "Sahkan Data Bahan Fizikal",
@@ -1416,6 +1409,7 @@
"ui.ai_permissions": "Kebenaran",
"ui.ai_preset": "Pratetap",
"ui.ai_remove": "Buang",
"ui.ai_request_limit": "berhenti selepas 24 permintaan untuk satu mesej; hantar mesej lain untuk meneruskan",
"ui.ai_reset": "Set Semula",
"ui.ai_settings": "Tetapan AI…",
"ui.ai_show_features": "Papar ciri AI",
@@ -1592,8 +1586,6 @@
"ui.filter_commands_or_keys": "Tapis arahan atau kekunci",
"ui.findInTimeline": "Cari dalam Garis Masa",
"ui.findInWindow": "Cari dalam Tetingkap",
"ui.fine": "Halus",
"ui.finest": "Paling halus",
"ui.finish_sketch": "Tamatkan Lakaran",
"ui.fit": "Muatkan",
"ui.fix_unfix": "Tetap/Lepas",
@@ -1818,7 +1810,6 @@
"ui.project_face_edges_sketch": "Unjur tepi muka ke dalam lakaran baharu",
"ui.properties": "Sifat",
"ui.pull_direction": "Arah tarikan",
"ui.quality": "Kualiti",
"ui.quantity": "Kuantiti",
"ui.quantity_2": "Kuantiti 2",
"ui.quit": "Keluar",
@@ -1913,7 +1904,6 @@
"ui.spline": "Splin",
"ui.split_body": "Belah Jasad",
"ui.stand_up_tip": "Selepas buka STEP atau IGES dengan Z ke atas, cadangkan suku putaran pada X",
"ui.standard": "Standard",
"ui.start": "Mula",
"ui.start_over": "Mula semula",
"ui.start_page": "Halaman mula",
File diff suppressed because it is too large Load Diff
+1 -11
View File
@@ -72,7 +72,6 @@
"Choose…": "Kiezen…",
"Circular": "Cirkelvormig",
"Circumference": "Omtrek",
"Clockwise": "Met de klok mee",
"Close draft": "Concept sluiten",
"Collisions": "Botsingen",
"Colour": "Kleur",
@@ -322,7 +321,6 @@
"Search commands": "Opdrachten zoeken",
"Search this library": "Deze bibliotheek doorzoeken",
"Section": "Doorsnede",
"Section position": "Positie van de doorsnede",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Overige selecteren: houd de linkermuisknop stil op het model (of klik met rechts › Overige selecteren…) om alles onder de cursor weer te geven; klik opnieuw op dezelfde plek om het volgende item daar te selecteren. Dubbelklik met de middelste knop om naar Home te gaan (Voorkeuren › Navigatie).",
"Select bodies (a face picks its body)": "Lichamen selecteren (een vlak kiest zijn lichaam)",
"Select bodies or faces instead": "In plaats daarvan lichamen of vlakken selecteren",
@@ -334,7 +332,6 @@
"Show Agent Cursor": "Agentcursor tonen",
"Show in folder": "Tonen in map",
"Show the pointer an AI agent moves (#35)": "De aanwijzer tonen die een AI-agent verplaatst (#35)",
"Show tool at cursor": "Gereedschap bij de cursor tonen",
"Signed axes": "Assen met teken",
"Size": "Grootte",
"Sketch constraints": "Schetsbeperkingen",
@@ -347,11 +344,9 @@
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft is gesloten voordat deze ontwerpen zijn opgeslagen. Hun laatste automatische opslag kan worden geopend.",
"Specialise": "Specialiseren",
"Speed": "Snelheid",
"Square": "Vierkant",
"Stand it up": "Rechtop zetten",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Rechtop zetten: een kwartslag om X, als één Verplaatsen-kenmerk dat u ongedaan kunt maken.",
"Stand this part up?": "Dit onderdeel rechtop zetten?",
"Start angle": "Beginhoek",
"Start bulge": "Beginbolling",
"Start continuity": "Begincontinuïteit",
"Step": "Stap",
@@ -416,7 +411,6 @@
"angle, height": "hoek, hoogte",
"appearance.dielectric_ior": "Diëlektrische IOR",
"appearance.metallic": "Metaalachtig",
"appearance.only_available": "Alleen beschikbare verschijningen",
"appearance.pbr_help": "Bekijk dit oppervlak in Render of een product-PNG. Geen breking of transmissie.",
"appearance.pbr_opaque": "Ondoorzichtige PBR",
"appearance.roughness": "Ruwheid",
@@ -578,7 +572,6 @@
"cmd.material_create": "Nieuw fysiek materiaal",
"cmd.material_delete": "Fysiek materiaal verwijderen",
"cmd.material_edit": "Fysiek materiaal bewerken",
"cmd.material_inspect": "Fysiek materiaal inspecteren",
"cmd.material_list": "Materialen weergeven",
"cmd.material_properties_directional": "Richtingsafhankelijke elasticiteitsmodulus",
"cmd.material_properties_validate": "Gegevens fysiek materiaal valideren",
@@ -1359,6 +1352,7 @@
"ui.ai_permissions": "Machtigingen",
"ui.ai_preset": "Voorinstelling",
"ui.ai_remove": "Verwijderen",
"ui.ai_request_limit": "gestopt na 24 verzoeken voor één bericht; stuur nog een bericht om verder te gaan",
"ui.ai_reset": "Herstellen",
"ui.ai_settings": "AI-instellingen…",
"ui.ai_show_features": "AI-functies tonen",
@@ -1534,8 +1528,6 @@
"ui.filter_commands_or_keys": "Opdrachten of toetsen filteren",
"ui.findInTimeline": "Zoeken in tijdlijn",
"ui.findInWindow": "Zoeken in venster",
"ui.fine": "Fijn",
"ui.finest": "Zeer fijn",
"ui.finish_sketch": "Schets voltooien",
"ui.fit": "Passend",
"ui.fix_unfix": "Vastzetten/losmaken",
@@ -1760,7 +1752,6 @@
"ui.project_face_edges_sketch": "Vlakranden projecteren in nieuwe schetsen",
"ui.properties": "Eigenschappen",
"ui.pull_direction": "Trekrichting",
"ui.quality": "Kwaliteit",
"ui.quantity": "Aantal",
"ui.quantity_2": "Aantal 2",
"ui.quit": "Afsluiten",
@@ -1855,7 +1846,6 @@
"ui.spline": "Spline",
"ui.split_body": "Lichaam splitsen",
"ui.stand_up_tip": "Zet na het openen van een STEP- of IGES-bestand met Z omhoog een kwartslag om X aan",
"ui.standard": "Standaard",
"ui.start": "Start",
"ui.start_over": "Opnieuw beginnen",
"ui.start_page": "Startpagina",
-24
View File
@@ -1,24 +0,0 @@
# Pending UI strings
A pull request that adds UI strings adds **one new file here** instead of editing
`assets/locales/<locale>.json`. Two pull requests then never touch the same file, so they never
conflict.
- Name it after the change, lowercase with dashes: `sketch-trim-hints.json`.
- Contents: a map from locale code to `{"message id": "text"}`. Give English; add other locales
you can translate (a locale you leave out falls back to English, as before):
```json
{
"en": { "sketch.trim.hint": "Click a segment to trim it" },
"de": { "sketch.trim.hint": "Segment zum Kürzen anklicken" }
}
```
- The id must not already be in that locale's catalog, nor in another pending file; the build and
`cargo xtask lists` fail otherwise. To change an existing message, edit the catalog itself.
- Don't sort or fold anything. The app, `cargo xtask i18n` and the tests read these files as part of
the catalogs, so your strings work in your own pull request.
- After a merge, the Generated files workflow runs `cargo xtask fold-pending` on `main`: it adds the
messages to the catalogs (sorted, one per line) and deletes the files. Never run it in a pull
request.
+1 -11
View File
@@ -73,7 +73,6 @@
"Choose…": "Wybierz…",
"Circular": "Kołowy",
"Circumference": "Obwód",
"Clockwise": "Zgodnie z ruchem wskazówek zegara",
"Close draft": "Zamknij szkic zgłoszenia",
"Collisions": "Kolizje",
"Colour": "Kolor",
@@ -340,7 +339,6 @@
"Search commands": "Szukaj poleceń",
"Search this library": "Przeszukaj tę bibliotekę",
"Section": "Przekrój",
"Section position": "Położenie przekroju",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Wybierz inny: przytrzymaj nieruchomo lewy przycisk na modelu (lub prawy przycisk › Wybierz inny…), aby wyświetlić wszystko pod kursorem; kliknij ponownie w tym samym miejscu, aby zaznaczyć kolejny obiekt. Dwukrotne kliknięcie środkowym przyciskiem przechodzi do widoku głównego (Preferencje › Nawigacja).",
"Select bodies (a face picks its body)": "Zaznacz bryły (ściana wybiera swoją bryłę)",
"Select bodies or faces instead": "Zamiast tego zaznacz bryły lub ściany",
@@ -352,7 +350,6 @@
"Show Agent Cursor": "Pokaż kursor agenta",
"Show in folder": "Pokaż w folderze",
"Show the pointer an AI agent moves (#35)": "Pokaż wskaźnik poruszany przez agenta AI (#35)",
"Show tool at cursor": "Pokaż narzędzie przy kursorze",
"Signed axes": "Osie ze znakiem",
"Size": "Rozmiar",
"Sketch constraints": "Wiązania szkicu",
@@ -365,11 +362,9 @@
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft zamknął się, zanim te projekty zostały zapisane. Można otworzyć ich ostatni autozapis.",
"Specialise": "Uszczegółów",
"Speed": "Prędkość",
"Square": "Kwadrat",
"Stand it up": "Postaw",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Postaw: obrót o ćwierć obrotu wokół osi X, jako jedna funkcja Przesuń, którą można cofnąć.",
"Stand this part up?": "Postawić tę część?",
"Start angle": "Kąt początkowy",
"Start bulge": "Wybrzuszenie początku",
"Start continuity": "Ciągłość początku",
"Step": "Krok",
@@ -465,7 +460,6 @@
"angle, height": "kąt, wysokość",
"appearance.dielectric_ior": "Współczynnik załamania dielektryka (IOR)",
"appearance.metallic": "Metaliczność",
"appearance.only_available": "Tylko dostępne wyglądy",
"appearance.pbr_help": "Zobacz tę powierzchnię w renderowaniu lub w obrazie PNG produktu. Bez załamania i przepuszczania światła.",
"appearance.pbr_opaque": "Nieprzezroczysty PBR",
"appearance.roughness": "Chropowatość",
@@ -627,7 +621,6 @@
"cmd.material_create": "Nowy materiał fizyczny",
"cmd.material_delete": "Usuń materiał fizyczny",
"cmd.material_edit": "Edytuj materiał fizyczny",
"cmd.material_inspect": "Sprawdź materiał fizyczny",
"cmd.material_list": "Lista materiałów",
"cmd.material_properties_directional": "Kierunkowy moduł sprężystości",
"cmd.material_properties_validate": "Sprawdź dane materiału fizycznego",
@@ -1602,6 +1595,7 @@
"ui.ai_permissions": "Uprawnienia",
"ui.ai_preset": "Ustawienie wstępne",
"ui.ai_remove": "Usuń",
"ui.ai_request_limit": "zatrzymano po 24 żądaniach dla jednej wiadomości; wyślij kolejną wiadomość, aby kontynuować",
"ui.ai_reset": "Resetuj",
"ui.ai_settings": "Ustawienia AI…",
"ui.ai_show_features": "Pokaż funkcje AI",
@@ -1778,8 +1772,6 @@
"ui.filter_commands_or_keys": "Filtruj polecenia lub klawisze",
"ui.findInTimeline": "Znajdź na osi czasu",
"ui.findInWindow": "Znajdź w oknie",
"ui.fine": "Dokładna",
"ui.finest": "Najdokładniejsza",
"ui.finish_sketch": "Zakończ szkic",
"ui.fit": "Dopasuj",
"ui.fix_unfix": "Ustal/zwolnij",
@@ -2011,7 +2003,6 @@
"ui.project_face_edges_sketch": "Rzutuj krawędzie ściany do nowych szkiców",
"ui.properties": "Właściwości",
"ui.pull_direction": "Kierunek wyjmowania",
"ui.quality": "Jakość",
"ui.quantity": "Liczba",
"ui.quantity_2": "Liczba 2",
"ui.quit": "Zakończ",
@@ -2106,7 +2097,6 @@
"ui.spline": "Splajn",
"ui.split_body": "Podziel bryłę",
"ui.stand_up_tip": "Gdy plik STEP lub IGES otworzy się z osią Z w górę, proponuj obrót o ćwierć obrotu wokół osi X",
"ui.standard": "Standardowa",
"ui.start": "Start",
"ui.start_over": "Zacznij od nowa",
"ui.start_page": "Strona startowa",
+1 -345
View File
@@ -14,15 +14,11 @@
"Active": "Ativo",
"Active rule": "Regra ativa",
"Add": "Adicionar",
"Add Column": "Adicionar coluna",
"Add Key": "Adicionar chave",
"Add Mid Radius": "Adicionar raio intermediário",
"Add Row": "Adicionar linha",
"Add a tool": "Adicionar ferramenta",
"Add condition": "Adicionar condição",
"Add or edit rule": "Adicionar ou editar regra",
"Add to New Folder": "Adicionar à nova pasta",
"Advanced": "Avançado",
"Against": "Contra",
"Alignment": "Alinhamento",
"All Sketch Points": "Todos os pontos do esboço",
@@ -32,11 +28,9 @@
"Also deletes": "Também exclui",
"Angle 2": "Ângulo 2",
"Animate": "Animar",
"App/system information and command settings are included. Review private details before sharing.": "Informações do aplicativo/sistema e configurações estão incluídas. Revise detalhes privados antes de compartilhar.",
"Appearance name": "Nome da aparência",
"Applied Radius": "Raio aplicado",
"Apply To": "Aplicar a",
"Apply library appearance": "Aplicar aparência da biblioteca",
"Apply to selection": "Aplicar à seleção",
"Apply your appearance first. Exports assigned appearances and materials created in this design.": "Aplique a aparência primeiro. Exporta as aparências atribuídas e os materiais criados neste projeto.",
"Arc length": "Comprimento do arco",
@@ -47,18 +41,13 @@
"BLEND SURFACE": "SUPERFÍCIE DE TRANSIÇÃO",
"BOUNDARY SURFACE": "SUPERFÍCIE DE CONTORNO",
"BROWSER": "NAVEGADOR",
"Bend Edges": "Arestas de dobra",
"Bend Line Position": "Posição da linha de dobra",
"Bend Position": "Posição da dobra",
"Bend Radius": "Raio de dobra",
"Bend Table": "Tabela de dobras",
"Bends": "Dobras",
"Bodies": "Corpos",
"Bodies (optional)": "Corpos (opcional)",
"Body": "Corpo",
"Body (last if none)": "Corpo (o último, se nenhum)",
"Body to split": "Corpo a dividir",
"Body, or faces to take out": "Corpo, ou faces a remover",
"Boundary": "Contorno",
"Bounding box": "Caixa delimitadora",
"CHECK": "VERIFICAR",
@@ -70,9 +59,7 @@
"CSV or JSON: updates existing parameters, adds new ones": "CSV ou JSON: atualiza parâmetros existentes e adiciona novos",
"Capture All Positions": "Capturar todas as posições",
"Capture Pos.": "Capturar pos.",
"Catch Depth": "Profundidade do encaixe",
"Catch Direction": "Direção do encaixe",
"Catch Length": "Comprimento do encaixe",
"Cells": "Células",
"Center X": "Centro X",
"Center Y": "Centro Y",
@@ -81,31 +68,22 @@
"Chamfer Type": "Tipo de chanfro",
"Check collisions": "Verificar colisões",
"Checked": "Marcado",
"Choose Variable radius or Spline in Type.": "Escolha Raio variável ou Spline em Tipo.",
"Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.": "Escolha um novo plano. A geometria e as cotas do esboço permanecem inalteradas em suas coordenadas bidimensionais.",
"Choose…": "Escolher…",
"Circular": "Circular",
"Circumference": "Circunferência",
"Clockwise": "Horário",
"Close draft": "Fechar rascunho",
"Collapse": "Recolher",
"Collisions": "Colisões",
"Colour": "Cor",
"Command": "Comando",
"Comment": "Comentário",
"Component 1": "Componente 1",
"Component 2": "Componente 2",
"Component1": "Componente1",
"Components": "Componentes",
"Config Table": "Tabela de config.",
"Configuration": "Configuração",
"Configuration rules": "Regras de configuração",
"Contact": "Contato",
"Contact Sets": "Conjuntos de contato",
"Contact sets": "Conjuntos de contato",
"Continue on GitHub": "Continuar no GitHub",
"Continuity": "Continuidade",
"Control Radius": "Raio de controle",
"Copy Error Message": "Copiar mensagem de erro",
"Copy Warning Message": "Copiar mensagem de aviso",
"Copy all licences": "Copiar todas as licenças",
@@ -115,11 +93,8 @@
"Corner Relief": "Alívio de canto",
"Corner Relief Shape": "Forma do alívio de canto",
"Counterbore depth": "Profundidade do rebaixo",
"Counterbore Ø": "Ø do rebaixo",
"Countersink angle": "Ângulo do escareado",
"Countersink Ø": "Ø do escareado",
"Create Issue — review draft": "Criar issue — revisar rascunho",
"Create Issue…": "Criar issue…",
"Cross curves": "Curvas transversais",
"Cube": "Cubo",
"Current orthographic view (1:1 mm)": "Vista ortográfica atual (1:1 mm)",
@@ -127,28 +102,18 @@
"Curv. Comb": "Pente de curv.",
"Curvature (G2)": "Curvatura (G2)",
"Curvature change (G3)": "Variação de curvatura (G3)",
"Custom": "Personalizado",
"Customize Toolbar": "Personalizar barra de ferramentas",
"Customize…": "Personalizar…",
"Cylindrical face": "Face cilíndrica",
"Delete": "Excluir",
"Delete Selected Features…": "Excluir recursos selecionados…",
"Delete Selected Folders": "Excluir pastas selecionadas",
"Delete all": "Excluir tudo",
"Delete rule": "Excluir regra",
"Delete the row": "Excluir a linha",
"Delete the set": "Excluir o conjunto",
"Density": "Densidade",
"Dependents": "Dependentes",
"Describe the problem you saw": "Descreva o problema que você viu",
"Describe what happened before continuing.": "Descreva o que aconteceu antes de continuar.",
"Designs you open or save appear here.": "Os projetos que você abrir ou salvar aparecem aqui.",
"Determined by To Object. Choose Angle to edit the angular extent.": "Determinado por Até o objeto. Escolha Ângulo para editar a extensão angular.",
"Diameter": "Diâmetro",
"Direction": "Direção",
"Discard": "Descartar",
"Distance": "Distância",
"Distance 1": "Distância 1",
"Do not export hidden edges": "Não exportar arestas ocultas",
"Don't ask again": "Não perguntar novamente",
"Double-click to rename": "Clique duas vezes para renomear",
@@ -160,65 +125,49 @@
"Edit Profile Sketch": "Editar esboço do perfil",
"Enable rule": "Ativar regra",
"Enabled": "Ativado",
"End Radius": "Raio final",
"End bulge": "Abaulamento final",
"End continuity": "Continuidade final",
"Expand": "Expandir",
"Expected result (optional)": "Resultado esperado (opcional)",
"Explode": "Explodir",
"Explode Text": "Decompor texto",
"Export Animation…": "Exportar animação…",
"Export Bend Table…": "Exportar tabela de dobras…",
"Export CSV…": "Exportar CSV…",
"Export Current View as DXF": "Exportar vista atual como DXF",
"Export Current View as DXF…": "Exportar vista atual como DXF…",
"Export DXF": "Exportar DXF",
"Export DXF…": "Exportar DXF…",
"Export JSON…": "Exportar JSON…",
"Export design library...": "Exportar biblioteca do projeto...",
"Export hidden edges as dashed lines": "Exportar arestas ocultas como linhas tracejadas",
"Exported by SolidWorks, which usually saves Y up": "Exportado pelo SolidWorks, que normalmente salva com Y para cima",
"Expression": "Expressão",
"Extend to meet": "Estender até encontrar",
"Extents": "Extensões",
"FILES": "ARQUIVOS",
"FLAT": "PLANIFICAÇÃO",
"Face": "Face",
"Face Appearance": "Aparência da face",
"Face attachment lost": "Fixação à face perdida",
"Faces": "Faces",
"Faces / edges / vertices": "Faces / arestas / vértices",
"Faces to split": "Faces a dividir",
"Fastener": "Fixador",
"Favorites": "Favoritos",
"Favourite": "Favorito",
"Feature": "Recurso",
"Files from many CAD programs have Y up, so they open lying on their back here (Z up).": "Arquivos de muitos programas CAD usam Y para cima, então abrem deitados aqui (Z para cima).",
"Filter": "Filtro",
"Filter…": "Filtrar…",
"Find in Browser": "Localizar no navegador",
"Finish": "Acabamento",
"First plane": "Primeiro plano",
"Fit and G2 corners require Constant radius.": "Ajuste e cantos G2 exigem Raio constante.",
"Fixed Face": "Face fixa",
"Flat Pattern": "Planificação",
"Flat size": "Tamanho planificado",
"Follow camera (Look At sketches)": "Seguir a câmera (Olhar para os esboços)",
"Follow the camera (Look At sketches)": "Seguir a câmera (Olhar para os esboços)",
"Font": "Fonte",
"From": "De",
"From Bodies": "A partir de corpos",
"From edges": "Arestas de origem",
"Full Length": "Comprimento total",
"G2 corners require a constant radius and Exact fit. G2 corner construction is not supported yet.": "Cantos G2 exigem raio constante e Ajuste exato. A construção de cantos G2 ainda não é suportada.",
"Gap": "Folga",
"Grid": "Grade",
"HISTORY": "HISTÓRICO",
"Height": "Altura",
"Hidden edges": "Arestas ocultas",
"History layout": "Layout do histórico",
"History names": "Nomes no histórico",
"Hole (optional)": "Furo (opcional)",
"Holes": "Furos",
"Holes (edges on them)": "Furos (arestas neles)",
"Home view": "Vista inicial",
@@ -229,20 +178,14 @@
"In place (surfaces)": "No lugar (superfícies)",
"In plane": "No plano",
"Inside": "Dentro",
"Instant": "Instantâneo",
"Interface font": "Fonte da interface",
"Interferes": "Interfere",
"Invert": "Inverter",
"Isocurve": "Isocurva",
"Issue title": "Título da issue",
"Items": "Itens",
"JOINTS": "JUNTAS",
"Joint Origin1": "Origem da junta1",
"Keep (points on it)": "Manter (pontos sobre ela)",
"Keep Tools": "Manter ferramentas",
"Keep of the other surface": "Manter da outra superfície",
"Key a joint's value at this step": "Criar um quadro-chave com o valor de uma junta neste passo",
"Keys": "Quadros-chave",
"LIBRARY": "BIBLIOTECA",
"LINKS": "VÍNCULOS",
"Later": "Depois",
@@ -253,14 +196,9 @@
"Letter Spacing": "Espaçamento entre letras",
"Library": "Biblioteca",
"Library file": "Arquivo de biblioteca",
"Limits": "Limites",
"Line Spacing": "Espaçamento entre linhas",
"Line Type": "Tipo de linha",
"Listing the installed fonts…": "Listando as fontes instaladas…",
"Load library": "Carregar biblioteca",
"Local library": "Biblioteca local",
"Loop": "Laço",
"M5 or M5x0.5": "M5 ou M5x0.5",
"MANAGE": "GERENCIAR",
"MATERIALS": "MATERIAIS",
"MODEL": "MODELO",
@@ -271,20 +209,14 @@
"Matched restrictions check the final values.": "As restrições cujas condições são atendidas verificam os valores finais.",
"Material": "Material",
"Max": "Máx.",
"Maximum": "Máximo",
"Mesh body": "Corpo de malha",
"Mid Radius": "Raio intermediário",
"Middle double-click": "Clique duplo do meio",
"Min": "Mín.",
"Minimum": "Mínimo",
"Mirror Plane": "Plano de espelho",
"Mitre": "Meia-esquadria",
"Model Parameters": "Parâmetros do modelo",
"Modeled": "Modelado",
"More toolbar panels": "Mais painéis da barra de ferramentas",
"Motion": "Movimento",
"Mouse gestures": "Gestos do mouse",
"Mouse gestures only work with the SolidWorks mouse mode (the wheel orbits, Ctrl + wheel pans).": "Os gestos do mouse só funcionam no modo de mouse SolidWorks (a roda orbita, Ctrl + roda desloca).",
"Move / Copy": "Mover / Copiar",
"Move Type": "Tipo de movimento",
"Move down": "Mover para baixo",
@@ -302,22 +234,15 @@
"No matching appearance presets.": "Nenhuma predefinição de aparência correspondente.",
"No matching appearances": "Nenhuma aparência correspondente",
"No visible bodies or sketches to export.": "Nenhum corpo ou esboço visível para exportar.",
"Nominal diameter": "Diâmetro nominal",
"None": "Nenhum",
"Not available yet": "Ainda não disponível",
"Notch": "Entalhe",
"OUTPUT": "SAÍDA",
"Of": "De",
"On your computer": "No seu computador",
"Opacity": "Opacidade",
"Open GitHub draft": "Abrir rascunho no GitHub",
"Open library...": "Abrir biblioteca...",
"Open local libraries in the desktop app. You can export this design's library here.": "Abra bibliotecas locais no aplicativo para desktop. Você pode exportar a biblioteca deste projeto aqui.",
"Open-source parametric 3D CAD in Rust. Licensed MIT OR Apache-2.0.": "CAD 3D paramétrico de código aberto em Rust. Licenciado sob MIT OU Apache-2.0.",
"Opens your browser and copies the report text for you to paste into GitHub.": "Abre o navegador e copia o texto do relatório para você colar no GitHub.",
"Opens your browser. Review and submit your report on GitHub.": "Abre o navegador. Revise e envie seu relatório no GitHub.",
"Operator": "Operador",
"Orbit (right drag)": "Orbitar (arrastar com o botão direito)",
"Other side": "Outro lado",
"Other surface (mutual trim)": "Outra superfície (aparo mútuo)",
"Outside": "Fora",
@@ -332,11 +257,8 @@
"Pick the tool bodies": "Selecione os corpos de ferramenta",
"Picked": "Selecionado",
"Picked point": "Ponto selecionado",
"Pitch": "Passo",
"Pivot": "Pivô",
"Place at the origin instead": "Colocar na origem",
"Plastic Rules": "Regras de plástico",
"Play the joint's motion in the view": "Reproduzir o movimento da junta na vista",
"Playback": "Reprodução",
"Plot": "Gráfico",
"Position": "Posição",
@@ -344,18 +266,14 @@
"Position (G0)": "Posição (G0)",
"Position along the edge, 0 at the start end, 1 at the other": "Posição ao longo da aresta: 0 na extremidade inicial, 1 na outra",
"Preferences › Design turns the offer back on": "Preferências › Projeto reativa a oferta",
"Preview": "Visualização",
"Preview Row": "Visualizar linha",
"Preview leaves the current design unchanged.": "A visualização não altera o projeto atual.",
"Profile / Edges": "Perfil / Arestas",
"Profiles or curves": "Perfis ou curvas",
"Profiles/Curves": "Perfis/Curvas",
"Quantity": "Quantidade",
"RECENT": "RECENTES",
"REFERENCE": "REFERÊNCIA",
"RULED SURFACE": "SUPERFÍCIE REGRADA",
"RULES": "REGRAS",
"Radius": "Raio",
"Radius Fit": "Ajuste de raio",
"Ratio": "Razão",
"Reattach selected face": "Fixar novamente na face selecionada",
@@ -364,8 +282,6 @@
"Rectangular": "Retangular",
"Reference face": "Face de referência",
"Remove Tools": "Remover ferramentas",
"Remove condition": "Remover condição",
"Remove from Folder": "Remover da pasta",
"Remove from list": "Remover da lista",
"Remove override": "Remover substituição",
"Remove private design details or paths before sharing.": "Remova detalhes privados do projeto ou caminhos antes de compartilhar.",
@@ -373,18 +289,12 @@
"Remove the column": "Remover a coluna",
"Remove the track": "Remover a trilha",
"Remove this point": "Remover este ponto",
"Rename Feature": "Renomear recurso",
"Rename Folder": "Renomear pasta",
"Replace existing library file": "Substituir o arquivo de biblioteca existente",
"Report a bug — review a screenshot and open a GitHub draft": "Relatar um bug — revise uma captura de tela e abra um rascunho no GitHub",
"Report text copied. Paste it into GitHub and submit your report there.": "Texto do relatório copiado. Cole-o no GitHub e envie seu relatório por lá.",
"Reports are public. A GitHub account is required.": "Os relatórios são públicos. É necessária uma conta no GitHub.",
"Reset Toolbar": "Redefinir barra de ferramentas",
"Resize Part": "Redimensionar peça",
"Restore View": "Restaurar vista",
"Restore outer boundary": "Restaurar contorno externo",
"Restore the inherited component or physical material appearance": "Restaurar a aparência herdada do componente ou do material físico",
"Restore this studio's tools": "Restaurar as ferramentas deste ambiente",
"Result": "Resultado",
"Retake screenshot": "Refazer captura de tela",
"Reverse": "Inverter",
@@ -394,14 +304,7 @@
"Revert Pos.": "Reverter pos.",
"Revert Position": "Reverter posição",
"Review the report and screenshot before opening GitHub.": "Revise o relatório e a captura de tela antes de abrir o GitHub.",
"Review your report on GitHub and click Submit new issue to publish it.": "Revise seu relatório no GitHub e clique em Submit new issue para publicá-lo.",
"Rib Length": "Comprimento da nervura",
"Rib Offset": "Deslocamento da nervura",
"Rib Thickness": "Espessura da nervura",
"Ribs": "Nervuras",
"Rim Edge": "Aresta da borda",
"Ring of tools on the right button": "Anel de ferramentas no botão direito",
"Roll Back Here": "Retroceder até aqui",
"Roll History Marker Here": "Mover o marcador do histórico para cá",
"Round": "Arredondado",
"Rule": "Regra",
@@ -411,34 +314,25 @@
"SURFACE QUALITY": "QUALIDADE DA SUPERFÍCIE",
"Save Rule": "Salvar regra",
"Save View": "Salvar vista",
"Save screenshot…": "Salvar captura de tela…",
"Save the study as an animated GIF, seen as the viewport shows it": "Salvar o estudo como GIF animado, como a janela de visualização o mostra",
"Saved preview. Geometry is being checked before editing.": "Visualização salva. A geometria está sendo verificada antes da edição.",
"Scale factor": "Fator de escala",
"Scene": "Cena",
"Screenshot (optional)": "Captura de tela (opcional)",
"Search": "Pesquisar",
"Search commands": "Pesquisar comandos",
"Search this library": "Pesquisar nesta biblioteca",
"Second plane": "Segundo plano",
"Section": "Seção",
"Section position": "Posição da seção",
"Sections (one sketch each)": "Seções (um esboço cada)",
"Select Other": "Selecionar outro",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Selecionar outro: mantenha o botão esquerdo parado sobre o modelo (ou clique com o botão direito › Selecionar outro…) para listar tudo sob o cursor; clique de novo no mesmo ponto para selecionar o próximo item ali. Clique duas vezes com o botão do meio para ir ao Início (Preferências › Navegação).",
"Select bodies (a face picks its body)": "Selecione corpos (uma face seleciona seu corpo)",
"Select bodies or faces instead": "Selecionar corpos ou faces em vez disso",
"Select edges": "Selecione arestas",
"Selection filter": "Filtro de seleção",
"Semantic faces share their appearance across instances.": "Faces semânticas compartilham a aparência entre instâncias.",
"Send your report on GitHub": "Envie seu relatório no GitHub",
"Shaded": "Sombreado",
"Shape": "Forma",
"Sheet Rules": "Regras de chapa",
"Show Agent Cursor": "Mostrar cursor do agente",
"Show in folder": "Mostrar na pasta",
"Show the pointer an AI agent moves (#35)": "Mostrar o ponteiro que um agente de IA move (#35)",
"Show tool at cursor": "Mostrar a ferramenta junto ao cursor",
"Signed axes": "Eixos com sinal",
"Size": "Tamanho",
"Sketch constraints": "Restrições do esboço",
@@ -451,37 +345,24 @@
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "O SolidCraft fechou antes de estes projetos serem salvos. O último salvamento automático deles pode ser aberto.",
"Specialise": "Especializar",
"Speed": "Velocidade",
"Splitting tool": "Ferramenta de divisão",
"Square": "Quadrado",
"Stand it up": "Colocar em pé",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Colocar em pé: um quarto de volta em torno de X, como um recurso Mover que pode ser desfeito.",
"Stand this part up?": "Colocar esta peça em pé?",
"Start angle": "Ângulo inicial",
"Start bulge": "Abaulamento inicial",
"Start continuity": "Continuidade inicial",
"Step": "Passo",
"Steps": "Passos",
"Steps to reproduce (optional)": "Passos para reproduzir (opcional)",
"Stitch the kept pieces": "Costurar as partes mantidas",
"Stitch to both bodies": "Costurar aos dois corpos",
"Studio": "Ambiente",
"Study": "Estudo",
"Style": "Estilo",
"Summary": "Resumo",
"Suppress": "Suprimir",
"Suppressed": "Suprimido",
"Surface": "Superfície",
"Surface, or faces to turn over": "Superfície, ou faces a inverter",
"Swap Ends": "Trocar extremidades",
"TRIM": "APARAR",
"Tangent (G1)": "Tangente (G1)",
"Tangent Chain": "Cadeia tangente",
"Tangent chain": "Cadeia tangente",
"Tap drill diameter": "Diâmetro da broca para macho",
"Target": "Alvo",
"Target Body": "Corpo-alvo",
"Tear": "Rasgo",
"Technical details": "Detalhes técnicos",
"The dashed sketch is a saved plane guide. Select a new face to repair the attachment.": "O esboço tracejado é uma guia de plano salva. Selecione uma nova face para reparar a fixação.",
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "A ocorrência de autoria salva não existe mais. Selecione um posicionamento de autoria válido antes de vincular novamente.",
"Then": "Então",
@@ -493,32 +374,24 @@
"This child is shared. Deleting it removes it from every copy of its parent.": "Este filho é compartilhado. Excluí-lo o remove de todas as cópias do pai.",
"This child is shared. Position changes apply to every copy of its parent.": "Este filho é compartilhado. Mudanças de posição se aplicam a todas as cópias do pai.",
"This face cannot provide a durable attachment. Choose another face or explicitly use a fixed plane.": "Esta face não pode fornecer uma fixação duradoura. Escolha outra face ou use explicitamente um plano fixo.",
"This image is not attached to GitHub yet. Copy or save it, then attach it on GitHub.": "Esta imagem ainda não está anexada no GitHub. Copie ou salve-a e depois anexe-a no GitHub.",
"To": "Para",
"To Offset": "Deslocamento final",
"To edges": "Arestas de destino",
"Tolerance": "Tolerância",
"Too close": "Muito próximo",
"Tool Bodies": "Corpos-ferramenta",
"Tools": "Ferramentas",
"Tools, clockwise from the top": "Ferramentas, em sentido horário a partir do topo",
"Trimming tool (plane, face or curves)": "Ferramenta de aparo (plano, face ou curvas)",
"Turn off section analysis before exporting the view.": "Desative a análise de seção antes de exportar a vista.",
"Type": "Tipo",
"UNTRIM": "DESAPARAR",
"Unit": "Unidade",
"Unsuppress": "Reativar",
"Updating the preview": "Atualizando a visualização",
"Use active author placement": "Usar o posicionamento de autoria ativo",
"Use fixed plane": "Usar plano fixo",
"Use saved fixed plane": "Usar o plano fixo salvo",
"Use selected face as fixed plane": "Usar a face selecionada como plano fixo",
"Use the SolidWorks mouse mode": "Usar o modo de mouse SolidWorks",
"User Parameters": "Parâmetros do usuário",
"VIEW": "VISTA",
"VIEWS": "VISTAS",
"Value": "Valor",
"Variable Radius": "Raio variável",
"Velocity": "Velocidade",
"Vertices": "Vértices",
"View navigator": "Navegador de vistas",
@@ -526,54 +399,19 @@
"Wall": "Parede",
"Wall Location": "Posição da parede",
"Wall Thickness": "Espessura da parede",
"What did you do just before the problem? A short list is enough": "O que você fez logo antes do problema? Uma lista curta basta",
"What did you expect to see?": "O que você esperava ver?",
"What happened?": "O que aconteceu?",
"When": "Quando",
"Width": "Largura",
"Will fail": "Vai falhar",
"With gestures on, the right button no longer orbits the view: hold it and move to open the ring, then touch a tool's zone. A plain right-click still opens the menu.": "Com os gestos ativados, o botão direito não orbita mais a vista: segure-o e mova para abrir o anel, depois toque na zona de uma ferramenta. Um clique direito simples ainda abre o menu.",
"X": "X",
"X Angle": "Ângulo X",
"X angle": "Ângulo X",
"X distance": "Distância X",
"Y": "Y",
"Y Angle": "Ângulo Y",
"Y angle": "Ângulo Y",
"Y distance": "Distância Y",
"Z": "Z",
"Z Angle": "Ângulo Z",
"Z Distance": "Distância Z",
"Z angle": "Ângulo Z",
"Z distance": "Distância Z",
"Zebra": "Zebra",
"ai.prefs.availability": "Controles do Assistente",
"ai.prefs.background_commands": "Executar comandos do agente em segundo plano",
"ai.prefs.follow_camera": "Virar a câmera para os esboços",
"ai.prefs.follow_camera_off": "O acompanhamento automático da câmera está desativado.",
"ai.prefs.follow_camera_on": "A câmera se vira para os esboços que o agente cria.",
"ai.prefs.intro": "Agentes, permissões e como o trabalho deles aparece.",
"ai.prefs.make_default": "Usar como padrão",
"ai.prefs.no_agents": "Nenhum agente ainda. Adicione um agente ou atualize para encontrar agentes instalados.",
"ai.prefs.no_saved_approvals": "Nenhuma aprovação salva.",
"ai.prefs.override_all_warning": "Este agente pode salvar, sobrescrever, exportar ou substituir o projeto sem perguntar.",
"ai.prefs.permission_all": "Permitir tudo",
"ai.prefs.permission_all_summary": "As ferramentas disponíveis do SolidCraft são executadas sem perguntar.",
"ai.prefs.permission_ask": "Perguntar antes de alterar",
"ai.prefs.permission_ask_summary": "Aprovar cada alteração no projeto e cada ação de arquivo.",
"ai.prefs.permission_edits": "Permitir edições no modelo",
"ai.prefs.permission_edits_summary": "Edições no modelo são executadas; ações de arquivo e destrutivas ainda perguntam.",
"ai.prefs.permission_scope": "Só para ferramentas do SolidCraft. O shell e as edições de arquivos do agente usam as permissões dele.",
"ai.prefs.pointer_preview_hidden": "Ponteiro oculto",
"ai.prefs.pointer_preview_speed": "Ponteiro do agente · {speed}",
"ai.prefs.pointer_speed": "Velocidade do movimento",
"ai.prefs.privacy_note": "Só o agente ou endpoint que você escolher recebe a conversa. As chaves de endpoint ficam guardadas no chaveiro do sistema.",
"ai.prefs.show_pointer": "Mostrar ponteiro do agente",
"ai.prefs.turn_limit_hint": "Número máximo de chamadas de ferramenta que alteram o projeto que o Assistente pode fazer por mensagem.",
"angle, height": "ângulo, altura",
"appearance.dielectric_ior": "IOR dielétrico",
"appearance.metallic": "Metálico",
"appearance.only_available": "Somente aparências disponíveis",
"appearance.pbr_help": "Veja esta superfície em Renderizar ou em um PNG de produto. Sem refração nem transmissão.",
"appearance.pbr_opaque": "PBR opaco",
"appearance.roughness": "Rugosidade",
@@ -616,10 +454,8 @@
"cmd.component_activate": "Ativar componente",
"cmd.component_break_link": "Quebrar vínculo",
"cmd.component_capture_position": "Capturar posição",
"cmd.component_copies": "Encontrar cópias idênticas",
"cmd.component_create": "Novo componente",
"cmd.component_delete": "Excluir componente",
"cmd.component_extract": "Extrair para arquivo",
"cmd.component_from_bodies": "Criar componentes a partir de corpos",
"cmd.component_insert_design": "Inserir projeto",
"cmd.component_links": "Listar vínculos",
@@ -632,7 +468,6 @@
"cmd.component_properties": "Propriedades físicas do componente",
"cmd.component_relink": "Revincular origem",
"cmd.component_rename": "Renomear componente",
"cmd.component_replace_with_link": "Substituir por vínculo",
"cmd.component_revert_position": "Reverter posição",
"cmd.component_unlock": "Desbloquear conteúdo",
"cmd.component_update_links": "Atualizar vínculos",
@@ -738,7 +573,6 @@
"cmd.material_create": "Novo material físico",
"cmd.material_delete": "Excluir material físico",
"cmd.material_edit": "Editar material físico",
"cmd.material_inspect": "Inspecionar material físico",
"cmd.material_list": "Listar materiais",
"cmd.material_properties_directional": "Módulo de elasticidade direcional",
"cmd.material_properties_validate": "Validar dados do material físico",
@@ -755,7 +589,6 @@
"cmd.motion_plot": "Plotar estudo de movimento",
"cmd.motion_study": "Estudo de movimento",
"cmd.motion_trace": "Traçado de movimento",
"cmd.motion_trace_edit": "Editar traçado de movimento",
"cmd.motion_trace_keep": "Manter traçado de movimento",
"cmd.occurrence_copy": "Colar (instância)",
"cmd.occurrence_ground": "Fixar no solo",
@@ -926,20 +759,12 @@
"cmd.timeline_suppress": "Suprimir recurso",
"cmd.view_ambient_occlusion": "Oclusão de ambiente",
"cmd.view_apply": "Restaurar vista nomeada",
"cmd.view_back": "Vista traseira",
"cmd.view_bottom": "Vista inferior",
"cmd.view_delete": "Excluir vista nomeada",
"cmd.view_front": "Vista frontal",
"cmd.view_graphics_preset": "Predefinição gráfica",
"cmd.view_isometric": "Vista isométrica",
"cmd.view_left": "Vista esquerda",
"cmd.view_list": "Listar vistas nomeadas",
"cmd.view_navigate": "Navegar na vista",
"cmd.view_normal_to": "Normal à seleção",
"cmd.view_rename": "Renomear vista nomeada",
"cmd.view_right": "Vista direita",
"cmd.view_save": "Nova vista nomeada",
"cmd.view_top": "Vista superior",
"color.copy": "Clique para copiar os valores da cor",
"color.edit": "Clique para editar a cor",
"color.gamma": "Mostrando valores de cor no espaço gama 0-255",
@@ -1035,12 +860,6 @@
"help.licences": "Licenças",
"help.mit": "Licença MIT",
"help.notice": "NOTICE",
"help.solid.chamfer": "Corta as arestas selecionadas com um bisel plano na distância que você definir. Use para quebrar arestas vivas ou facilitar a entrada de um pino ou parafuso.",
"help.solid.extrude": "Empurra um perfil de esboço para o 3D ao longo de uma distância, adicionando ou removendo material. Use para transformar um contorno plano em um bloco, um ressalto ou um rebaixo.",
"help.solid.fillet": "Arredonda as arestas selecionadas com uma concordância circular suave do raio que você definir. Use para suavizar arestas vivas, por resistência, conforto ou aparência.",
"help.solid.hole": "Faz um furo redondo do diâmetro que você definir em um ponto escolhido de uma face. Use para folgas de parafuso, pinos e outras aberturas redondas simples.",
"help.solid.revolve": "Gira um perfil de esboço em torno de um eixo para criar um sólido redondo. Use para eixos, botões, tigelas e qualquer peça redonda em torno de uma linha central.",
"help.solid.shell": "Esvazia um sólido, deixando paredes da espessura que você definir, e pode abrir as faces que você escolher. Use para caixas, tampas e outras peças de parede fina.",
"help.third_party": "Licenças de terceiros",
"home.back": "Voltar para {name}",
"home.crashes": "Relatórios de falhas",
@@ -1150,15 +969,6 @@
"motion.to_end": "Para o fim",
"motion.to_start": "Para o início",
"motion.track_name": "Trilha {number}",
"nav.alias.help": "Shift+Alt+arrastar com o esquerdo orbita, Shift+Alt+meio desloca, Shift+Alt+direito aplica zoom; arrastar com o meio também desloca e arrastar com o direito orbita",
"nav.blender.help": "Arrastar com o meio orbita, Shift+meio desloca, Ctrl+meio aplica zoom; arrastar com o direito não mexe na câmera",
"nav.free_orbit.help": "O mouse e o mouse 3D giram passando pelos dois polos e preservam o giro lateral (roll). O encaixe em eixo com Shift fica desativado.",
"nav.free_orbit.label": "Órbita livre",
"nav.fusion.help": "Arrastar com o meio desloca, Shift+meio orbita, arrastar com o direito orbita",
"nav.inventor.help": "Arrastar com o meio desloca, Shift+meio orbita, Ctrl+meio aplica zoom, arrastar com o direito orbita",
"nav.openscad.help": "Arrastar com o esquerdo no espaço vazio orbita, Shift+arrastar com o esquerdo seleciona por caixa, arrastar a geometria do esboço a edita; arrastar com o direito ou o meio desloca, Shift+arrastar com o direito ou o meio aplica zoom",
"nav.solidworks.help": "Arrastar com o meio orbita, Ctrl+meio desloca, Shift+meio aplica zoom, arrastar com o direito orbita",
"nav.tinkercad.help": "Arrastar com o direito orbita, arrastar com o meio ou Shift+direito desloca",
"nav.touchpad.help": "Segure Shift e mova o ponteiro para deslocar; segure Alt/Option e mova para orbitar. Não é preciso botão do mouse. A rolagem vertical aplica zoom.",
"nav.touchpad.label": "Touchpad",
"nav.touchpad.orbit": "Orbitar (segure Alt/Option e mova; sem botão do mouse)",
@@ -1181,130 +991,8 @@
"pattern.flip_direction": "Inverter direção",
"pick a hole to seat it, or OK places it at the origin": "selecione um furo para assentá-la, ou OK a coloca na origem",
"pick one or two vertices, edges or faces": "selecione um ou dois vértices, arestas ou faces",
"prefs.add_item": "Adicionar o item",
"prefs.alt_frees_manipulators": "Alt / Option também libera as setas do manipulador",
"prefs.anti_aliasing_note": "Suaviza bordas serrilhadas.",
"prefs.applies_on_restart": "Aplica-se ao reiniciar",
"prefs.assistant": "Assistente",
"prefs.axes": "Eixos",
"prefs.axis": "Eixo",
"prefs.bottom": "Embaixo",
"prefs.browser": "Navegador",
"prefs.built_in": "Integrado",
"prefs.button_left": "Arrastar com o esquerdo",
"prefs.button_middle": "Arrastar com o meio",
"prefs.button_right": "Arrastar com o direito",
"prefs.choose_font": "Escolher arquivo de fonte…",
"prefs.click": "Clique",
"prefs.contact_shading": "Sombreamento suave de contato",
"prefs.contact_shading_note": "Sombreamento suave onde as peças se aproximam.",
"prefs.context_menu": "Menu de contexto",
"prefs.context_menu_ring": "Menu de contexto · segure para o menu radial",
"prefs.cpu": "CPU (software)",
"prefs.crash_reports": "Abrir relatórios de falhas…",
"prefs.crash_reports_note": "Os relatórios ficam guardados neste computador, com o projeto e os comandos que reproduzem o problema.",
"prefs.ctrl_cmd": "Ctrl / Cmd",
"prefs.curved_surfaces": "Superfícies curvas",
"prefs.curves": "Superfícies curvas",
"prefs.dead_zone_note": "A faixa hachurada é a zona morta: o movimento dentro dela é ignorado.",
"prefs.default": "padrão",
"prefs.design": "Padrões do projeto",
"prefs.design_note": "Com o que os novos projetos começam. Projetos abertos mantêm as próprias configurações.",
"prefs.devices": "Mouse 3D",
"prefs.display": "Exibição",
"prefs.edges": "Arestas e sombreamento",
"prefs.extend": "Extensões",
"prefs.extra_navigation": "Ctrl+arrastar com o esquerdo no espaço vazio orbita; segure Espaço para deslocar.",
"prefs.follows_os": "Segue o sistema",
"prefs.font": "Fonte",
"prefs.font_fallback": "Os glifos que faltam na fonte escolhida usam a fonte integrada, então nenhum rótulo mostra uma caixa vazia.",
"prefs.font_sample": "Vovô viu o ímã, a maçã e o pinguim · Ø12.50 mm · 45.0° · 0123456789",
"prefs.font_unreadable": "Não foi possível ler a fonte selecionada.",
"prefs.general": "Geral",
"prefs.general_note": "Idioma, salvamento e atualizações.",
"prefs.gesture_orbit": "Orbitar",
"prefs.gesture_pan": "Deslocar",
"prefs.gesture_zoom": "Zoom",
"prefs.graphics": "Gráficos",
"prefs.graphics_note": "Troque velocidade por suavidade. Cada opção mostra seu efeito em uma amostra.",
"prefs.graphics_presets_note": "As predefinições ajustam as opções abaixo em conjunto.",
"prefs.history": "Histórico",
"prefs.hold_stop_snapping": "Segure para desativar o encaixe",
"prefs.icons_only": "Só ícones",
"prefs.imported_file": "Arquivo importado",
"prefs.input": "Entrada",
"prefs.interface": "Interface",
"prefs.interface_language": "Idioma da interface",
"prefs.interface_note": "A aparência do SolidCraft e onde ficam suas partes.",
"prefs.interval_minutes": "{n} min",
"prefs.labels": "Rótulos",
"prefs.layout": "Layout",
"prefs.layout_note": "Compacto encaixa os painéis nas bordas da janela. A translucidez do navegador está disponível em Flutuante. Mude o lado do Assistente na página Assistente.",
"prefs.left": "Esquerda",
"prefs.left_click": "Clique esquerdo",
"prefs.live_input": "Entrada ao vivo",
"prefs.live_preview": "Visualização ao vivo",
"prefs.minutes": "minutos",
"prefs.minutes_suffix": " min",
"prefs.mouse": "Mouse e trackpad",
"prefs.mouse_heading": "Mouse",
"prefs.mouse_mapping": "Mapeamento do mouse",
"prefs.navigation": "Navegação",
"prefs.navigation_note": "Como o mouse, o trackpad e o mouse 3D movem a vista.",
"prefs.not_on_gpu": "indisponível nesta GPU",
"prefs.on": "Ativado",
"prefs.opaque": "Opaco",
"prefs.orbit_snap": "Segure Shift ao orbitar para encaixar em um eixo.",
"prefs.orientation": "Orientação",
"prefs.overall_speed": "Velocidade geral",
"prefs.preview_choices": "Prévia das suas escolhas",
"prefs.quality_default": "padrão",
"prefs.quality_fastest": "mais rápido",
"prefs.quality_slowest": "mais lento",
"prefs.quality_smoother": "mais suave",
"prefs.reports_saved": "{n} salvos",
"prefs.right_click": "Clique direito",
"prefs.saving": "Salvamento automático e recuperação",
"prefs.scale_note": "Também Ctrl + / Ctrl − na janela. Os botões têm 28 px em 100%.",
"prefs.scale_text": "Escala e texto",
"prefs.select": "Selecionar",
"prefs.select_one_item": "Selecionar um item",
"prefs.selecting": "Seleção",
"prefs.shift": "Shift",
"prefs.shift_add_ctrl_toggle": "Shift só adiciona; Ctrl / Cmd alterna",
"prefs.shift_add_note": "Ativado: Shift adiciona, Ctrl / Cmd alterna. Desativado (padrão): ambos alternam.",
"prefs.shortcuts": "Atalhos",
"prefs.sketch_selection": "Esboço e seleção",
"prefs.sketch_selection_heading": "Esboço e seleção",
"prefs.sketch_selection_note": "Como os esboços encaixam e como os cliques selecionam.",
"prefs.sketching": "Criação de esboços",
"prefs.snap_drawing_note": "Ao desenhar ou arrastar um ponto do esboço.",
"prefs.source": "Origem",
"prefs.speed": "Velocidade",
"prefs.stand_up_note": "Depois que um arquivo STEP ou IGES abrir com Z para cima, oferecer um quarto de volta em torno de X.",
"prefs.status_done": "Concluído",
"prefs.status_error": "Erro",
"prefs.status_selected": "Selecionado",
"prefs.status_warning": "Aviso",
"prefs.supersampling_brief": "Desenha a vista maior e a reduz com filtragem: muito nítida, e mais pesada.",
"prefs.tab_accent_note": "A cor de destaque acompanha a aba ativa",
"prefs.theme_file": "arquivo de tema",
"prefs.timeline_half_hour": "−30 min",
"prefs.timeline_now": "agora",
"prefs.timeline_quarter_hour": "−15",
"prefs.toggle_item": "Adicionar o item, ou retirá-lo de novo",
"prefs.toolbar": "Barra de ferramentas",
"prefs.translucent": "Translúcido",
"prefs.under_browser": "Abaixo do navegador",
"prefs.under_cursor": "Sob o cursor",
"prefs.units_precision": "Unidades e precisão",
"prefs.updates": "Atualizações",
"prefs.view": "Comportamento da vista",
"prefs.viewport": "Janela de visualização",
"prefs.wheel": "Roda",
"prefs.wheel_trackpad": "Roda e trackpad",
"prefs.workspace": "Espaço de trabalho",
"prefs.zoom_toward_cursor": "Zoom em direção ao cursor",
"properties.title": "Propriedades: {name}",
"recovery.days_ago": "há {count} dias",
"recovery.detail": "{file} · {features} recursos · salvo automaticamente {when}",
@@ -1382,12 +1070,9 @@
"settings.msaa_applies_on_restart": "O antisserrilhamento vale na próxima vez que o SolidCraft iniciar",
"settings.msaa_reset": "{setting}: {requested}× não é suportado por este dispositivo gráfico; agora é {reset}× (suportados: {supported}).",
"settings.msaa_unsupported": "Não suportado por este dispositivo gráfico",
"settings.reference_geometry_through_bodies": "Mostrar geometria de referência através dos corpos",
"settings.reference_geometry_through_bodies_help": "Desenhar contornos de esboços inativos e planos de referência sobre os corpos sombreados",
"settings.reset_intro": "Estas configurações não funcionam neste computador e impediriam o SolidCraft de iniciar, por isso foram alteradas:",
"settings.reset_title": "As configurações foram alteradas",
"settings.supersampling_note": "Desenha a vista em resolução maior e a filtra para baixo: arestas, linhas e grade mais suaves em telas abaixo de 4K. Custa tempo de GPU: cerca de 2,25× os pixels em 1,5× e 4× em 2×.",
"sheet.no_body": "não há corpo de chapa metálica: comece um com Flange > Base, ou transforme uma placa em chapa metálica com Converter em chapa metálica (selecione a face plana grande dela)",
"shortcut.json_error": "os parâmetros devem estar no formato JSON: {error}",
"shortcut.no_command": "o comando `{id}` não existe; o texto antes do JSON deve ser um ID de comando",
"shortcut.pin": "Fixar no topo da caixa S",
@@ -1424,9 +1109,6 @@
"thread.designation": "Designação",
"thread.iso_metric": "Métrica ISO",
"thread.select_size": "Selecionar tamanho",
"timeline.delete_after_marker_one": "Excluir 1 recurso após o marcador do histórico",
"timeline.delete_after_marker_other": "Excluir {count} recursos após o marcador do histórico",
"timeline.delete_many": "Excluir {count} recursos selecionados",
"timeline.group": "Nova pasta de recursos",
"timeline.move_many": "Mover itens da linha do tempo",
"timeline.move_to_group": "Mover recursos para a pasta",
@@ -1672,15 +1354,11 @@
"ui.agent_chat_waiting": "Aguardando sua resposta",
"ui.agent_cursor": "Cursor do agente",
"ui.agent_cursor_speed": "Velocidade do cursor do agente",
"ui.agent_off_thread": "Manter a janela respondendo enquanto os agentes executam comandos",
"ui.agent_off_thread_note": "Os comandos lentos de um agente (do painel de IA ou de um cliente MCP) são calculados ao lado da janela: você pode continuar girando e inspecionando a vista, e parar um deles pelo indicador na vista. Desativado: cada um roda na thread da janela, e a janela espera ele terminar.",
"ui.ai_add": "Adicionar",
"ui.ai_add_agent": "Adicionar agente…",
"ui.ai_add_agent_title": "Adicionar agente",
"ui.ai_add_anthropic": "API compatível com Anthropic",
"ui.ai_add_api": "API compatível com OpenAI",
"ui.ai_add_custom": "Comando personalizado",
"ui.ai_add_endpoint": "Endpoint de API",
"ui.ai_add_installed": "Agente instalado",
"ui.ai_add_installed_note": "Uma entrada própria para um agente instalado, por exemplo com outros argumentos. Os agentes instalados são listados por si mesmos.",
"ui.ai_added": "adicionado",
@@ -1709,7 +1387,6 @@
"ui.ai_key_needs_https": "Uma chave só é enviada por HTTPS ou para este computador.",
"ui.ai_key_note": "Guardada no chaveiro do sistema, nunca nas configurações do SolidCraft. Deixe em branco para um servidor local.",
"ui.ai_keychain_error": "O chaveiro do sistema recusou: {error}",
"ui.ai_kind": "Tipo",
"ui.ai_mode_all": "Permitir tudo",
"ui.ai_mode_all_note": "Nada pergunta antes. Os relatórios de falha continuam fora de alcance e os limites de pasta ainda valem.",
"ui.ai_mode_all_warning": "O agente pode salvar, sobrescrever, exportar e substituir seu projeto sem perguntar. Use isto só com um agente em que você confie e em projetos que você já salvou.",
@@ -1731,8 +1408,8 @@
"ui.ai_panel": "Painel",
"ui.ai_permissions": "Permissões",
"ui.ai_preset": "Predefinição",
"ui.ai_protocol": "Protocolo",
"ui.ai_remove": "Remover",
"ui.ai_request_limit": "parou após 24 requisições para uma mensagem; envie outra mensagem para continuar",
"ui.ai_reset": "Redefinir",
"ui.ai_settings": "Configurações de IA…",
"ui.ai_show_features": "Mostrar recursos de IA",
@@ -1741,8 +1418,6 @@
"ui.ai_test": "Testar",
"ui.ai_test_acp_ok": "Responde: {agent}",
"ui.ai_test_acp_tip": "Inicia o agente por um instante e pede uma sessão; depois o encerra",
"ui.ai_test_answered": "Acessado: {model} respondeu",
"ui.ai_test_anthropic_tip": "Envia uma mensagem curta ao endpoint",
"ui.ai_test_api_tip": "Pede ao endpoint a lista de modelos",
"ui.ai_test_no_model": "Acessado: {count} modelos, mas não {model}",
"ui.ai_test_ok": "Acessado: {count} modelos",
@@ -1901,9 +1576,6 @@
"ui.export_keymap_tip": "Teclas, favoritos da caixa S e a barra de ferramentas, como um arquivo",
"ui.extend": "Estender",
"ui.extent_type": "Tipo de extensão",
"ui.extract_component": "Extrair para arquivo…",
"ui.extract_design_changed": "O projeto mudou enquanto um arquivo era escolhido. Inicie Extrair para arquivo de novo.",
"ui.extract_done": "O componente agora é um projeto próprio, vinculado aqui.",
"ui.extrude": "Extrudar",
"ui.factor": "Fator",
"ui.feature_history": "Histórico de recursos",
@@ -1914,8 +1586,6 @@
"ui.filter_commands_or_keys": "Filtrar comandos ou teclas",
"ui.findInTimeline": "Localizar na linha do tempo",
"ui.findInWindow": "Localizar na janela",
"ui.fine": "Fina",
"ui.finest": "Muito fina",
"ui.finish_sketch": "Concluir esboço",
"ui.fit": "Ajustar",
"ui.fix_unfix": "Fixar/Liberar",
@@ -2013,17 +1683,10 @@
"ui.hideProfile": "Ocultar perfil",
"ui.history_back": "Voltar uma etapa",
"ui.history_below_browser": "Abaixo do navegador",
"ui.history_edge_hint": "clique duas vezes na borda",
"ui.history_first": "Ir para o início",
"ui.history_forward": "Avançar uma etapa",
"ui.history_hide_names": "Ocultar nomes",
"ui.history_last": "Ir para o fim",
"ui.history_marker_tip": "Marcador do histórico: arraste para retroceder o modelo; novos recursos entram aqui",
"ui.history_names": "Nomes",
"ui.history_play": "Reproduzir até o fim",
"ui.history_show_names": "Mostrar nomes",
"ui.history_title": "Histórico",
"ui.history_toggle_names": "Mostrar ou ocultar os nomes dos recursos",
"ui.hole": "Furo",
"ui.hole_tap_type": "Tipo de rosca do furo",
"ui.hole_type": "Tipo de furo",
@@ -2045,10 +1708,6 @@
"ui.length_decimals": "Casas decimais de comprimento",
"ui.light": "Claro",
"ui.line": "Linha",
"ui.link_copies": "Vincular cópias idênticas",
"ui.link_copies_keep": "Manter",
"ui.link_copies_link": "Vincular",
"ui.link_copies_text": "Estes componentes constroem os mesmos corpos que {name}. Torná-los posicionamentos do vínculo dele?",
"ui.lock": "Bloquear",
"ui.loft": "Loft",
"ui.look_at": "Olhar para",
@@ -2151,7 +1810,6 @@
"ui.project_face_edges_sketch": "Projetar arestas da face em novos esboços",
"ui.properties": "Propriedades",
"ui.pull_direction": "Direção de extração",
"ui.quality": "Qualidade",
"ui.quantity": "Quantidade",
"ui.quantity_2": "Quantidade 2",
"ui.quit": "Sair",
@@ -2173,7 +1831,6 @@
"ui.render": "Renderizar",
"ui.renderer": "Renderizador",
"ui.repeat": "Repetir",
"ui.replace_with_link_done": "As cópias agora são posicionamentos do projeto vinculado.",
"ui.report_a_bug": "Relatar um bug",
"ui.reset": "Redefinir",
"ui.reset_all": "Redefinir tudo",
@@ -2247,7 +1904,6 @@
"ui.spline": "Spline",
"ui.split_body": "Dividir corpo",
"ui.stand_up_tip": "Depois que um arquivo STEP ou IGES abrir com Z para cima, oferecer um quarto de volta em torno de X",
"ui.standard": "Padrão",
"ui.start": "Início",
"ui.start_over": "Recomeçar",
"ui.start_page": "Página inicial",
+1 -11
View File
@@ -72,7 +72,6 @@
"Choose…": "Alegeți…",
"Circular": "Circular",
"Circumference": "Circumferință",
"Clockwise": "În sens orar",
"Close draft": "Închide ciorna",
"Collisions": "Coliziuni",
"Colour": "Culoare",
@@ -322,7 +321,6 @@
"Search commands": "Caută comenzi",
"Search this library": "Caută în această bibliotecă",
"Section": "Secțiune",
"Section position": "Poziția secțiunii",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Selectează altul: țineți apăsat butonul stâng nemișcat pe model (sau clic dreapta › Selectează altul…) pentru a lista tot ce se află sub cursor; faceți clic din nou în același loc pentru a selecta următorul element. Dublu clic pe butonul din mijloc pentru a merge Acasă (Preferințe › Navigare).",
"Select bodies (a face picks its body)": "Selectați corpuri (o față alege corpul ei)",
"Select bodies or faces instead": "Selectați în schimb corpuri sau fețe",
@@ -334,7 +332,6 @@
"Show Agent Cursor": "Afișează cursorul agentului",
"Show in folder": "Afișează în dosar",
"Show the pointer an AI agent moves (#35)": "Afișează indicatorul pe care îl mută un agent AI (#35)",
"Show tool at cursor": "Afișează instrumentul lângă cursor",
"Signed axes": "Axe cu semn",
"Size": "Dimensiune",
"Sketch constraints": "Constrângerile schiței",
@@ -347,11 +344,9 @@
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft s-a închis înainte ca aceste proiecte să fie salvate. Ultima lor salvare automată poate fi deschisă.",
"Specialise": "Specializează",
"Speed": "Viteză",
"Square": "Pătrat",
"Stand it up": "Ridică-l în picioare",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Ridică-l în picioare: un sfert de rotație în jurul lui X, ca o singură operație de mutare ce poate fi anulată.",
"Stand this part up?": "Ridicați această piesă în picioare?",
"Start angle": "Unghi de început",
"Start bulge": "Bombare inițială",
"Start continuity": "Continuitate inițială",
"Step": "Pas",
@@ -416,7 +411,6 @@
"angle, height": "unghi, înălțime",
"appearance.dielectric_ior": "IOR dielectric",
"appearance.metallic": "Metalic",
"appearance.only_available": "Doar aspecte disponibile",
"appearance.pbr_help": "Vizualizați această suprafață în Randare sau într-un PNG de produs. Fără refracție sau transmisie.",
"appearance.pbr_opaque": "PBR opac",
"appearance.roughness": "Rugozitate",
@@ -578,7 +572,6 @@
"cmd.material_create": "Material fizic nou",
"cmd.material_delete": "Șterge materialul fizic",
"cmd.material_edit": "Editează materialul fizic",
"cmd.material_inspect": "Inspectează materialul fizic",
"cmd.material_list": "Listează materialele",
"cmd.material_properties_directional": "Modul de elasticitate direcțional",
"cmd.material_properties_validate": "Validează datele materialului fizic",
@@ -1359,6 +1352,7 @@
"ui.ai_permissions": "Permisiuni",
"ui.ai_preset": "Presetare",
"ui.ai_remove": "Elimină",
"ui.ai_request_limit": "oprit după 24 de cereri pentru un mesaj; trimiteți alt mesaj pentru a continua",
"ui.ai_reset": "Resetează",
"ui.ai_settings": "Setări AI…",
"ui.ai_show_features": "Afișează funcțiile AI",
@@ -1534,8 +1528,6 @@
"ui.filter_commands_or_keys": "Filtrează comenzi sau taste",
"ui.findInTimeline": "Găsește în cronologie",
"ui.findInWindow": "Găsește în fereastră",
"ui.fine": "Fină",
"ui.finest": "Foarte fină",
"ui.finish_sketch": "Finalizează schița",
"ui.fit": "Potrivire",
"ui.fix_unfix": "Fixează/Defixează",
@@ -1760,7 +1752,6 @@
"ui.project_face_edges_sketch": "Proiectează muchiile feței în schițe noi",
"ui.properties": "Proprietăți",
"ui.pull_direction": "Direcția de extragere",
"ui.quality": "Calitate",
"ui.quantity": "Cantitate",
"ui.quantity_2": "Cantitatea 2",
"ui.quit": "Ieșire",
@@ -1855,7 +1846,6 @@
"ui.spline": "Spline",
"ui.split_body": "Împarte corpul",
"ui.stand_up_tip": "După ce un fișier STEP sau IGES se deschide cu Z în sus, oferă un sfert de rotație în jurul lui X",
"ui.standard": "Standard",
"ui.start": "Început",
"ui.start_over": "Reîncepe",
"ui.start_page": "Pagina de start",
+1 -11
View File
@@ -72,7 +72,6 @@
"Choose…": "Выбрать…",
"Circular": "Круговой",
"Circumference": "Окружность",
"Clockwise": "По часовой стрелке",
"Close draft": "Закрыть анализ уклонов",
"Collisions": "Столкновения",
"Colour": "Цвет",
@@ -322,7 +321,6 @@
"Search commands": "Поиск команд",
"Search this library": "Поиск в этой библиотеке",
"Section": "Сечение",
"Section position": "Положение сечения",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Выбрать другое: удерживайте левую кнопку на модели (или правый щелчок › Выбрать другое…), чтобы увидеть всё под курсором; повторный щелчок в том же месте выбирает следующий объект. Двойной щелчок средней кнопкой — «Главная» (Настройки › Навигация).",
"Select bodies (a face picks its body)": "Выберите тела (выбор грани выбирает её тело)",
"Select bodies or faces instead": "Вместо этого выберите тела или грани",
@@ -334,7 +332,6 @@
"Show Agent Cursor": "Показывать курсор агента",
"Show in folder": "Показать в папке",
"Show the pointer an AI agent moves (#35)": "Показывать указатель, которым двигается ИИ-агент (#35)",
"Show tool at cursor": "Показывать инструмент у курсора",
"Signed axes": "Знаковые оси",
"Size": "Размер",
"Sketch constraints": "Ограничения эскиза",
@@ -347,11 +344,9 @@
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft закрылся до сохранения этих дизайнов. Можно открыть последнюю автосохранённую версию.",
"Specialise": "Специализировать",
"Speed": "Скорость",
"Square": "Квадрат",
"Stand it up": "Поставить вертикально",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Поставить вертикально: четверть оборота вокруг X одной операцией «Переместить», которую можно отменить.",
"Stand this part up?": "Поставить эту деталь вертикально?",
"Start angle": "Начальный угол",
"Start bulge": "Выпуклость в начале",
"Start continuity": "Непрерывность в начале",
"Step": "Шаг",
@@ -416,7 +411,6 @@
"angle, height": "угол, высота",
"appearance.dielectric_ior": "Показатель преломления диэлектрика",
"appearance.metallic": "Металличность",
"appearance.only_available": "Только доступные внешние виды",
"appearance.pbr_help": "Просмотр поверхности во вкладке Рендер или на PNG изделия. Без преломления и пропускания света.",
"appearance.pbr_opaque": "Непрозрачный PBR",
"appearance.roughness": "Шероховатость",
@@ -578,7 +572,6 @@
"cmd.material_create": "Новый физический материал",
"cmd.material_delete": "Удалить физический материал",
"cmd.material_edit": "Изменить физический материал",
"cmd.material_inspect": "Проверить физический материал",
"cmd.material_list": "Список материалов",
"cmd.material_properties_directional": "Направленный модуль упругости",
"cmd.material_properties_validate": "Проверить данные физического материала",
@@ -1415,6 +1408,7 @@
"ui.ai_permissions": "Разрешения",
"ui.ai_preset": "Пресет",
"ui.ai_remove": "Удалить",
"ui.ai_request_limit": "остановлено после 24 запросов на одно сообщение; отправьте ещё одно сообщение, чтобы продолжить",
"ui.ai_reset": "Сбросить",
"ui.ai_settings": "Настройки ИИ…",
"ui.ai_show_features": "Показывать функции ИИ",
@@ -1591,8 +1585,6 @@
"ui.filter_commands_or_keys": "Фильтр команд или клавиш",
"ui.findInTimeline": "Найти на шкале",
"ui.findInWindow": "Найти в окне",
"ui.fine": "Высокое",
"ui.finest": "Максимальное",
"ui.finish_sketch": "Завершить эскиз",
"ui.fit": "Вписать",
"ui.fix_unfix": "Зафиксировать/Освободить",
@@ -1817,7 +1809,6 @@
"ui.project_face_edges_sketch": "Проецировать рёбра граней в новые эскизы",
"ui.properties": "Свойства",
"ui.pull_direction": "Направление вытягивания",
"ui.quality": "Качество",
"ui.quantity": "Количество",
"ui.quantity_2": "Количество 2",
"ui.quit": "Выход",
@@ -1912,7 +1903,6 @@
"ui.spline": "Сплайн",
"ui.split_body": "Разделить тело",
"ui.stand_up_tip": "Если файл STEP или IGES открывается с Z вверх, предложить поворот на четверть оборота вокруг X",
"ui.standard": "Стандартное",
"ui.start": "Старт",
"ui.start_over": "Начать заново",
"ui.start_page": "Начальная страница",
+1 -11
View File
@@ -72,7 +72,6 @@
"Choose…": "Välj…",
"Circular": "Cirkulär",
"Circumference": "Omkrets",
"Clockwise": "Medurs",
"Close draft": "Stäng utkast",
"Collisions": "Kollisioner",
"Colour": "Färg",
@@ -322,7 +321,6 @@
"Search commands": "Sök kommandon",
"Search this library": "Sök i detta bibliotek",
"Section": "Sektion",
"Section position": "Profilens läge",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Markera annan: håll vänsterknappen stilla på modellen (eller högerklicka › Markera annan…) för att lista allt under markören; klicka igen på samma plats för att markera nästa objekt där. Dubbelklicka med mittenknappen för att gå till Hem (Inställningar › Navigering).",
"Select bodies (a face picks its body)": "Markera kroppar (en yta väljer sin kropp)",
"Select bodies or faces instead": "Markera kroppar eller ytor istället",
@@ -334,7 +332,6 @@
"Show Agent Cursor": "Visa agentmarkör",
"Show in folder": "Visa i mapp",
"Show the pointer an AI agent moves (#35)": "Visa pekaren som en AI-agent flyttar (#35)",
"Show tool at cursor": "Visa verktyget vid markören",
"Signed axes": "Axlar med tecken",
"Size": "Storlek",
"Sketch constraints": "Skissvillkor",
@@ -347,11 +344,9 @@
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft stängdes innan dessa konstruktioner sparades. Deras senaste autospar kan öppnas.",
"Specialise": "Specialisera",
"Speed": "Hastighet",
"Square": "Kvadrat",
"Stand it up": "Res upp den",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Res upp den: ett kvartsvarv runt X, som en enda Flytta-funktion som du kan ångra.",
"Stand this part up?": "Resa upp denna del?",
"Start angle": "Startvinkel",
"Start bulge": "Startbukt",
"Start continuity": "Startkontinuitet",
"Step": "Steg",
@@ -416,7 +411,6 @@
"angle, height": "vinkel, höjd",
"appearance.dielectric_ior": "Dielektriskt IOR",
"appearance.metallic": "Metallisk",
"appearance.only_available": "Endast tillgängliga utseenden",
"appearance.pbr_help": "Visa den här ytan i Rendering eller en produkt-PNG. Ingen refraktion eller transmission.",
"appearance.pbr_opaque": "Ogenomskinlig PBR",
"appearance.roughness": "Skrovlighet",
@@ -578,7 +572,6 @@
"cmd.material_create": "Nytt fysiskt material",
"cmd.material_delete": "Ta bort fysiskt material",
"cmd.material_edit": "Redigera fysiskt material",
"cmd.material_inspect": "Granska fysiskt material",
"cmd.material_list": "Lista material",
"cmd.material_properties_directional": "Riktningsberoende elasticitetsmodul",
"cmd.material_properties_validate": "Validera fysiska materialdata",
@@ -1359,6 +1352,7 @@
"ui.ai_permissions": "Behörigheter",
"ui.ai_preset": "Förinställning",
"ui.ai_remove": "Ta bort",
"ui.ai_request_limit": "stoppades efter 24 förfrågningar för ett meddelande; skicka ett nytt meddelande för att fortsätta",
"ui.ai_reset": "Återställ",
"ui.ai_settings": "AI-inställningar…",
"ui.ai_show_features": "Visa AI-funktioner",
@@ -1534,8 +1528,6 @@
"ui.filter_commands_or_keys": "Filtrera kommandon eller tangenter",
"ui.findInTimeline": "Sök i tidslinjen",
"ui.findInWindow": "Sök i fönstret",
"ui.fine": "Fin",
"ui.finest": "Finast",
"ui.finish_sketch": "Avsluta skiss",
"ui.fit": "Anpassa",
"ui.fix_unfix": "Fäst/Lös",
@@ -1760,7 +1752,6 @@
"ui.project_face_edges_sketch": "Projicera ytkanter i nya skisser",
"ui.properties": "Egenskaper",
"ui.pull_direction": "Dragriktning",
"ui.quality": "Kvalitet",
"ui.quantity": "Antal",
"ui.quantity_2": "Antal 2",
"ui.quit": "Avsluta",
@@ -1855,7 +1846,6 @@
"ui.spline": "Spline",
"ui.split_body": "Dela kropp",
"ui.stand_up_tip": "När en STEP- eller IGES-fil öppnas med Z uppåt, erbjud ett kvartsvarv runt X",
"ui.standard": "Standard",
"ui.start": "Start",
"ui.start_over": "Börja om",
"ui.start_page": "Startsida",
+1 -11
View File
@@ -72,7 +72,6 @@
"Choose…": "Seç…",
"Circular": "Dairesel",
"Circumference": "Çevre",
"Clockwise": "Saat yönünde",
"Close draft": "Taslağı kapat",
"Collisions": "Çarpışmalar",
"Colour": "Renk",
@@ -322,7 +321,6 @@
"Search commands": "Komutlarda ara",
"Search this library": "Bu kitaplıkta ara",
"Section": "Kesit",
"Section position": "Kesit konumu",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Diğerini seç: İmlecin altındaki her şeyi listelemek için sol tuşu model üzerinde hareket ettirmeden basılı tutun (veya sağ tık › Diğerini seç…); oradaki bir sonraki öğeyi seçmek için aynı noktaya tekrar tıklayın. Başlangıç görünümüne dönmek için orta tuşla çift tıklayın (Ayarlar › Gezinme).",
"Select bodies (a face picks its body)": "Gövdeleri seçin (bir yüz kendi gövdesini seçer)",
"Select bodies or faces instead": "Bunun yerine gövde veya yüz seçin",
@@ -334,7 +332,6 @@
"Show Agent Cursor": "Ajan imlecini göster",
"Show in folder": "Klasörde göster",
"Show the pointer an AI agent moves (#35)": "Bir AI ajanının hareket ettirdiği imleci göster (#35)",
"Show tool at cursor": "Aracı imlecin yanında göster",
"Signed axes": "İşaretli eksenler",
"Size": "Boyut",
"Sketch constraints": "Eskiz kısıtlamaları",
@@ -347,11 +344,9 @@
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft bu tasarımlar kaydedilmeden önce kapandı. Son otomatik kayıtları açılabilir.",
"Specialise": "Özelleştir",
"Speed": "Hız",
"Square": "Kare",
"Stand it up": "Dik çevir",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Dik çevir: X etrafında çeyrek tur; geri alınabilen tek bir Taşı özelliği olarak eklenir.",
"Stand this part up?": "Bu parça dik çevrilsin mi?",
"Start angle": "Başlangıç açısı",
"Start bulge": "Başlangıç şişkinliği",
"Start continuity": "Başlangıç sürekliliği",
"Step": "Adım",
@@ -416,7 +411,6 @@
"angle, height": "açı, yükseklik",
"appearance.dielectric_ior": "Dielektrik kırılma indisi",
"appearance.metallic": "Metaliklik",
"appearance.only_available": "Yalnızca kullanılabilir görünümler",
"appearance.pbr_help": "Bu yüzeyi Render'da veya bir ürün PNG'sinde görüntüleyin. Kırılma veya geçirgenlik yok.",
"appearance.pbr_opaque": "Opak PBR",
"appearance.roughness": "Pürüzlülük",
@@ -578,7 +572,6 @@
"cmd.material_create": "Yeni fiziksel malzeme",
"cmd.material_delete": "Fiziksel malzemeyi sil",
"cmd.material_edit": "Fiziksel malzemeyi düzenle",
"cmd.material_inspect": "Fiziksel malzemeyi incele",
"cmd.material_list": "Malzemeleri listele",
"cmd.material_properties_directional": "Yöne bağlı elastisite modülü",
"cmd.material_properties_validate": "Fiziksel malzeme verilerini kontrol et",
@@ -1415,6 +1408,7 @@
"ui.ai_permissions": "İzinler",
"ui.ai_preset": "Şablon",
"ui.ai_remove": "Kaldır",
"ui.ai_request_limit": "bir mesaj için 24 istekten sonra durduruldu; devam etmek için başka bir mesaj gönderin",
"ui.ai_reset": "Sıfırla",
"ui.ai_settings": "AI ayarları…",
"ui.ai_show_features": "AI özelliklerini göster",
@@ -1591,8 +1585,6 @@
"ui.filter_commands_or_keys": "Komutları veya tuşları filtrele",
"ui.findInTimeline": "Zaman çizelgesinde bul",
"ui.findInWindow": "Pencerede bul",
"ui.fine": "İnce",
"ui.finest": "En ince",
"ui.finish_sketch": "Eskizi bitir",
"ui.fit": "Sığdır",
"ui.fix_unfix": "Sabitle/Çöz",
@@ -1817,7 +1809,6 @@
"ui.project_face_edges_sketch": "Yüz kenarlarını yeni eskizlere yansıt",
"ui.properties": "Özellikler",
"ui.pull_direction": "Kalıptan çıkarma yönü",
"ui.quality": "Kalite",
"ui.quantity": "Adet",
"ui.quantity_2": "Adet 2",
"ui.quit": "Çıkış",
@@ -1912,7 +1903,6 @@
"ui.spline": "Spline",
"ui.split_body": "Gövdeyi böl",
"ui.stand_up_tip": "Bir STEP veya IGES dosyası Z yukarı olarak açıldığında X etrafında çeyrek tur döndürmeyi öner",
"ui.standard": "Standart",
"ui.start": "Başlangıç",
"ui.start_over": "Baştan başla",
"ui.start_page": "Başlangıç sayfası",
+2 -314
View File
@@ -14,15 +14,11 @@
"Active": "Активна",
"Active rule": "Активне правило",
"Add": "Додати",
"Add Column": "Додати стовпець",
"Add Key": "Додати ключовий кадр",
"Add Key": "Додати клавішу",
"Add Mid Radius": "Додати середній радіус",
"Add Row": "Додати рядок",
"Add a tool": "Додати інструмент",
"Add condition": "Додати умову",
"Add or edit rule": "Додати або змінити правило",
"Add to New Folder": "Додати до нової теки",
"Advanced": "Додатково",
"Against": "Проти",
"Alignment": "Вирівнювання",
"All Sketch Points": "Усі точки ескізу",
@@ -32,11 +28,9 @@
"Also deletes": "Також видаляє",
"Angle 2": "Кут 2",
"Animate": "Анімувати",
"App/system information and command settings are included. Review private details before sharing.": "Додано відомості про програму й систему та налаштування команд. Перегляньте приватні дані перед поширенням.",
"Appearance name": "Назва вигляду",
"Applied Radius": "Застосований радіус",
"Apply To": "Застосувати до",
"Apply library appearance": "Застосувати вигляд із бібліотеки",
"Apply to selection": "Застосувати до виділення",
"Apply your appearance first. Exports assigned appearances and materials created in this design.": "Спочатку застосуйте вигляд. Експортує призначені вигляди та матеріали, створені в цьому проєкті.",
"Arc length": "Довжина дуги",
@@ -47,18 +41,13 @@
"BLEND SURFACE": "ПОВЕРХНЯ ПЕРЕХОДУ",
"BOUNDARY SURFACE": "ПОВЕРХНЯ ЗА МЕЖАМИ",
"BROWSER": "БРАУЗЕР",
"Bend Edges": "Ребра згину",
"Bend Line Position": "Положення лінії згину",
"Bend Position": "Положення згину",
"Bend Radius": "Радіус згину",
"Bend Table": "Таблиця згинів",
"Bends": "Згини",
"Bodies": "Тіла",
"Bodies (optional)": "Тіла (необов'язково)",
"Body": "Тіло",
"Body (last if none)": "Тіло (останнє, якщо не вибрано)",
"Body to split": "Тіло для розділення",
"Body, or faces to take out": "Тіло або грані для вилучення",
"Boundary": "Межа",
"Bounding box": "Обмежувальна рамка",
"CHECK": "ПЕРЕВІРКА",
@@ -70,9 +59,7 @@
"CSV or JSON: updates existing parameters, adds new ones": "CSV або JSON: оновлює наявні параметри, додає нові",
"Capture All Positions": "Зафіксувати всі положення",
"Capture Pos.": "Зафікс. полож.",
"Catch Depth": "Глибина зачеплення",
"Catch Direction": "Напрямок зачеплення",
"Catch Length": "Довжина зачеплення",
"Cells": "Комірки",
"Center X": "Центр X",
"Center Y": "Центр Y",
@@ -81,31 +68,22 @@
"Chamfer Type": "Тип фаски",
"Check collisions": "Перевіряти зіткнення",
"Checked": "Перевірено",
"Choose Variable radius or Spline in Type.": "Виберіть у полі «Тип» значення «Variable radius» або «Spline».",
"Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.": "Виберіть нову площину. Геометрія та розміри ескізу залишаються незмінними у своїх двовимірних координатах.",
"Choose…": "Вибрати…",
"Circular": "Круговий",
"Circumference": "Довжина кола",
"Clockwise": "За годинниковою стрілкою",
"Close draft": "Закрити чернетку",
"Collapse": "Згорнути",
"Collisions": "Зіткнення",
"Colour": "Колір",
"Command": "Команда",
"Comment": "Коментар",
"Component 1": "Компонент 1",
"Component 2": "Компонент 2",
"Component1": "Компонент1",
"Components": "Компоненти",
"Config Table": "Табл. конфігурацій",
"Configuration": "Конфігурація",
"Configuration rules": "Правила конфігурації",
"Contact": "Контакт",
"Contact Sets": "Набори контактів",
"Contact sets": "Набори контактів",
"Continue on GitHub": "Продовжити на GitHub",
"Continuity": "Неперервність",
"Control Radius": "Керівний радіус",
"Copy Error Message": "Копіювати повідомлення про помилку",
"Copy Warning Message": "Копіювати попереджувальне повідомлення",
"Copy all licences": "Копіювати всі ліцензії",
@@ -115,11 +93,8 @@
"Corner Relief": "Полегшення кута",
"Corner Relief Shape": "Форма полегшення кута",
"Counterbore depth": "Глибина циліндричного розширення",
"Counterbore Ø": "Ø циліндричного розширення",
"Countersink angle": "Кут зенкування",
"Countersink Ø": "Ø зенкування",
"Create Issue — review draft": "Створити повідомлення про проблему — перегляд чернетки",
"Create Issue…": "Створити повідомлення про проблему…",
"Cross curves": "Поперечні криві",
"Cube": "Куб",
"Current orthographic view (1:1 mm)": "Поточний ортографічний вид (1:1 mm)",
@@ -127,28 +102,18 @@
"Curv. Comb": "Гребінь кривини",
"Curvature (G2)": "Кривина (G2)",
"Curvature change (G3)": "Зміна кривини (G3)",
"Custom": "Власний",
"Customize Toolbar": "Налаштувати панель інструментів",
"Customize…": "Налаштувати…",
"Cylindrical face": "Циліндрична грань",
"Delete": "Видалити",
"Delete Selected Features…": "Видалити вибрані операції…",
"Delete Selected Folders": "Видалити вибрані теки",
"Delete all": "Видалити все",
"Delete rule": "Видалити правило",
"Delete the row": "Видалити рядок",
"Delete the set": "Видалити набір",
"Density": "Густина",
"Dependents": "Залежні",
"Describe the problem you saw": "Опишіть проблему, яку ви побачили",
"Describe what happened before continuing.": "Опишіть, що сталося, перш ніж продовжити.",
"Designs you open or save appear here.": "Проєкти, які ви відкриваєте чи зберігаєте, з'являються тут.",
"Determined by To Object. Choose Angle to edit the angular extent.": "Визначається об'єктом призначення. Виберіть «Кут», щоб редагувати кутову протяжність.",
"Diameter": "Діаметр",
"Direction": "Напрямок",
"Discard": "Відхилити",
"Distance": "Відстань",
"Distance 1": "Відстань 1",
"Do not export hidden edges": "Не експортувати приховані ребра",
"Don't ask again": "Більше не питати",
"Double-click to rename": "Двічі клацніть, щоб перейменувати",
@@ -160,22 +125,17 @@
"Edit Profile Sketch": "Редагувати ескіз профілю",
"Enable rule": "Увімкнути правило",
"Enabled": "Увімкнено",
"End Radius": "Кінцевий радіус",
"End bulge": "Роздуття кінця",
"End continuity": "Неперервність кінця",
"Expand": "Розгорнути",
"Expected result (optional)": "Очікуваний результат (необов'язково)",
"Explode": "Рознести",
"Explode Text": "Розібрати текст",
"Export Animation…": "Експортувати анімацію…",
"Export Bend Table…": "Експортувати таблицю згинів…",
"Export CSV…": "Експорт CSV…",
"Export Current View as DXF": "Експортувати поточний вид як DXF",
"Export Current View as DXF…": "Експортувати поточний вид як DXF…",
"Export DXF": "Експорт DXF",
"Export DXF…": "Експорт DXF…",
"Export JSON…": "Експорт JSON…",
"Export design library...": "Експортувати бібліотеку проєкту...",
"Export hidden edges as dashed lines": "Експортувати приховані ребра як штрихові лінії",
"Expression": "Вираз",
"Extend to meet": "Подовжити до перетину",
@@ -183,41 +143,31 @@
"FILES": "ФАЙЛИ",
"FLAT": "РОЗГОРТКА",
"Face": "Грань",
"Face Appearance": "Вигляд грані",
"Face attachment lost": "Втрачено прив'язку до грані",
"Faces": "Грані",
"Faces / edges / vertices": "Грані / ребра / вершини",
"Faces to split": "Грані для розділення",
"Fastener": "Кріплення",
"Favorites": "Обране",
"Favourite": "Обране",
"Feature": "Операція",
"Files from many CAD programs have Y up, so they open lying on their back here (Z up).": "Файли з багатьох CAD-програм мають напрямок Y вгору, тому тут вони відкриваються лежачи на спині (Z вгору).",
"Filter": "Фільтр",
"Filter…": "Фільтр…",
"Find in Browser": "Знайти в браузері",
"Finish": "Оздоблення",
"First plane": "Перша площина",
"Fit and G2 corners require Constant radius.": "Для підгонки та кутів G2 потрібен тип «Constant radius».",
"Fixed Face": "Фіксована грань",
"Flat Pattern": "Розгортка",
"Flat size": "Розмір розгортки",
"Follow camera (Look At sketches)": "Стежити за камерою (ескізи наведення)",
"Follow the camera (Look At sketches)": "Стежити за камерою (ескізи наведення)",
"Font": "Шрифт",
"From": "Від",
"From Bodies": "З тіл",
"From edges": "Початкові ребра",
"Full Length": "Повна довжина",
"G2 corners require a constant radius and Exact fit. G2 corner construction is not supported yet.": "Кути G2 потребують постійного радіуса та підгонки «Exact». Побудова кутів G2 ще не підтримується.",
"Gap": "Зазор",
"Grid": "Сітка",
"HISTORY": "ІСТОРІЯ",
"Height": "Висота",
"Hidden edges": "Приховані ребра",
"History layout": "Розташування історії",
"History names": "Назви в історії",
"Hole (optional)": "Отвір (необов'язково)",
"Holes": "Отвори",
"Holes (edges on them)": "Отвори (ребра на них)",
"Home view": "Головний вид",
@@ -228,20 +178,14 @@
"In place (surfaces)": "На місці (поверхні)",
"In plane": "У площині",
"Inside": "Усередину",
"Instant": "Миттєво",
"Interface font": "Шрифт інтерфейсу",
"Interferes": "Перетинається",
"Invert": "Інвертувати",
"Isocurve": "Ізокрива",
"Issue title": "Заголовок повідомлення",
"Items": "Елементи",
"JOINTS": "З’ЄДНАННЯ",
"Joint Origin1": "Початок з'єднання1",
"Keep (points on it)": "Залишити (точки на ньому)",
"Keep Tools": "Зберегти інструменти",
"Keep of the other surface": "Залишити від іншої поверхні",
"Key a joint's value at this step": "Вставити ключовий кадр зі значенням з'єднання на цьому кроці",
"Keys": "Ключові кадри",
"LIBRARY": "БІБЛІОТЕКА",
"LINKS": "ПОСИЛАННЯ",
"Later": "Пізніше",
@@ -252,14 +196,9 @@
"Letter Spacing": "Міжлітерний інтервал",
"Library": "Бібліотека",
"Library file": "Файл бібліотеки",
"Limits": "Межі",
"Line Spacing": "Міжрядковий інтервал",
"Line Type": "Тип лінії",
"Listing the installed fonts…": "Пошук встановлених шрифтів…",
"Load library": "Завантажити бібліотеку",
"Local library": "Локальна бібліотека",
"Loop": "Контур",
"M5 or M5x0.5": "M5 або M5x0.5",
"MANAGE": "КЕРУВАННЯ",
"MATERIALS": "МАТЕРІАЛИ",
"MODEL": "МОДЕЛЬ",
@@ -270,20 +209,14 @@
"Matched restrictions check the final values.": "Обмеження, умови яких виконано, перевіряють кінцеві значення.",
"Material": "Матеріал",
"Max": "Макс.",
"Maximum": "Максимум",
"Mesh body": "Тіло сітки",
"Mid Radius": "Середній радіус",
"Middle double-click": "Подвійне клацання середньою кнопкою",
"Min": "Мін.",
"Minimum": "Мінімум",
"Mirror Plane": "Площина дзеркала",
"Mitre": "Скіс",
"Model Parameters": "Параметри моделі",
"Modeled": "Змодельовано",
"More toolbar panels": "Більше панелей інструментів",
"Motion": "Рух",
"Mouse gestures": "Жести мишею",
"Mouse gestures only work with the SolidWorks mouse mode (the wheel orbits, Ctrl + wheel pans).": "Жести мишею працюють лише в режимі миші SolidWorks (колесо обертає, Ctrl + колесо панорамує).",
"Move / Copy": "Перемістити / копіювати",
"Move Type": "Тип переміщення",
"Move down": "Перемістити вниз",
@@ -301,22 +234,15 @@
"No matching appearance presets.": "Немає відповідних наборів вигляду.",
"No matching appearances": "Немає відповідних виглядів",
"No visible bodies or sketches to export.": "Немає видимих тіл або ескізів для експорту.",
"Nominal diameter": "Номінальний діаметр",
"None": "Немає",
"Not available yet": "Ще недоступно",
"Notch": "Виріз",
"OUTPUT": "ВИВЕДЕННЯ",
"Of": "Ступінь вільності",
"On your computer": "На цьому комп'ютері",
"Opacity": "Непрозорість",
"Open GitHub draft": "Відкрити чернетку GitHub",
"Open library...": "Відкрити бібліотеку...",
"Open local libraries in the desktop app. You can export this design's library here.": "Відкривайте локальні бібліотеки в настільному застосунку. Тут можна експортувати бібліотеку цього проєкту.",
"Open-source parametric 3D CAD in Rust. Licensed MIT OR Apache-2.0.": "Параметричний 3D-CAD з відкритим вихідним кодом на Rust. Ліцензовано за MIT OR Apache-2.0.",
"Opens your browser and copies the report text for you to paste into GitHub.": "Відкриває браузер і копіює текст звіту, щоб ви вставили його на GitHub.",
"Opens your browser. Review and submit your report on GitHub.": "Відкриває браузер. Перегляньте й надішліть звіт на GitHub.",
"Operator": "Оператор",
"Orbit (right drag)": "Обертання (перетягування правою кнопкою)",
"Other side": "Інший бік",
"Other surface (mutual trim)": "Інша поверхня (взаємне обрізання)",
"Outside": "Назовні",
@@ -331,11 +257,8 @@
"Pick the tool bodies": "Виберіть тіла інструмента",
"Picked": "Вибрано",
"Picked point": "Вибрана точка",
"Pitch": "Крок",
"Pivot": "Точка обертання",
"Place at the origin instead": "Натомість розмістити в початку координат",
"Plastic Rules": "Правила пластику",
"Play the joint's motion in the view": "Відтворити рух з'єднання у виді",
"Playback": "Відтворення",
"Plot": "Графік",
"Position": "Положення",
@@ -343,18 +266,14 @@
"Position (G0)": "Положення (G0)",
"Position along the edge, 0 at the start end, 1 at the other": "Положення вздовж ребра: 0 на початку, 1 на іншому кінці",
"Preferences › Design turns the offer back on": "Налаштування › Дизайн повертає пропозицію",
"Preview": "Попередній перегляд",
"Preview Row": "Рядок перегляду",
"Preview leaves the current design unchanged.": "Попередній перегляд не змінює поточний проєкт.",
"Profile / Edges": "Профіль / ребра",
"Profiles or curves": "Профілі або криві",
"Profiles/Curves": "Профілі/криві",
"Quantity": "Величина",
"RECENT": "НЕДАВНІ",
"REFERENCE": "ДОПОМІЖНА ГЕОМЕТРІЯ",
"RULED SURFACE": "ЛІНІЙЧАСТА ПОВЕРХНЯ",
"RULES": "ПРАВИЛА",
"Radius": "Радіус",
"Radius Fit": "Підгонка за радіусом",
"Ratio": "Співвідношення",
"Reattach selected face": "Повторно приєднати вибрану грань",
@@ -363,8 +282,6 @@
"Rectangular": "Прямокутний",
"Reference face": "Опорна грань",
"Remove Tools": "Прибрати інструменти",
"Remove condition": "Прибрати умову",
"Remove from Folder": "Прибрати з теки",
"Remove from list": "Прибрати зі списку",
"Remove override": "Прибрати перевизначення",
"Remove private design details or paths before sharing.": "Перед публікацією приберіть приватні дані проєкту або шляхи.",
@@ -372,18 +289,12 @@
"Remove the column": "Прибрати стовпець",
"Remove the track": "Прибрати доріжку",
"Remove this point": "Прибрати цю точку",
"Rename Feature": "Перейменувати операцію",
"Rename Folder": "Перейменувати теку",
"Replace existing library file": "Замінити наявний файл бібліотеки",
"Report a bug — review a screenshot and open a GitHub draft": "Повідомити про помилку — переглянути знімок екрана й відкрити чернетку GitHub",
"Report text copied. Paste it into GitHub and submit your report there.": "Текст звіту скопійовано. Вставте його на GitHub і надішліть звіт там.",
"Reports are public. A GitHub account is required.": "Звіти загальнодоступні. Потрібен обліковий запис GitHub.",
"Reset Toolbar": "Скинути панель інструментів",
"Resize Part": "Змінити розмір деталі",
"Restore View": "Відновити вид",
"Restore outer boundary": "Відновити зовнішню межу",
"Restore the inherited component or physical material appearance": "Відновити успадкований вигляд компонента або фізичного матеріалу",
"Restore this studio's tools": "Відновити інструменти цієї студії",
"Result": "Результат",
"Retake screenshot": "Повторити знімок екрана",
"Reverse": "Обернути",
@@ -393,14 +304,7 @@
"Revert Pos.": "Скасув. полож.",
"Revert Position": "Повернути положення",
"Review the report and screenshot before opening GitHub.": "Перегляньте звіт і знімок екрана перед відкриттям GitHub.",
"Review your report on GitHub and click Submit new issue to publish it.": "Перегляньте звіт на GitHub і натисніть Submit new issue, щоб опублікувати його.",
"Rib Length": "Довжина ребра",
"Rib Offset": "Зсув ребра",
"Rib Thickness": "Товщина ребра",
"Ribs": "Ребра",
"Rim Edge": "Крайове ребро",
"Ring of tools on the right button": "Кільце інструментів на правій кнопці",
"Roll Back Here": "Відкотити сюди",
"Roll History Marker Here": "Перемістити маркер історії сюди",
"Round": "Круглий",
"Rule": "Правило",
@@ -410,34 +314,25 @@
"SURFACE QUALITY": "ЯКІСТЬ ПОВЕРХНІ",
"Save Rule": "Зберегти правило",
"Save View": "Зберегти вид",
"Save screenshot…": "Зберегти знімок екрана…",
"Save the study as an animated GIF, seen as the viewport shows it": "Зберегти дослідження як анімований GIF, як його показує вікно перегляду",
"Saved preview. Geometry is being checked before editing.": "Збережений перегляд. Геометрію перевіряють перед редагуванням.",
"Scale factor": "Коефіцієнт масштабу",
"Scene": "Сцена",
"Screenshot (optional)": "Знімок екрана (необов'язково)",
"Search": "Пошук",
"Search commands": "Пошук команд",
"Search this library": "Пошук у цій бібліотеці",
"Second plane": "Друга площина",
"Section": "Переріз",
"Section position": "Положення перерізу",
"Sections (one sketch each)": "Перерізи (по одному ескізу)",
"Select Other": "Вибрати інше",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Вибір іншого: утримуйте ліву кнопку нерухомо на моделі (або клацніть правою › Вибрати інше…), щоб побачити список усього під курсором; клацніть ще раз у тому ж місці, щоб вибрати наступний об'єкт там. Двічі клацніть середньою кнопкою, щоб перейти до головного виду (Налаштування › Навігація).",
"Select bodies (a face picks its body)": "Виділяти тіла (грань виділяє своє тіло)",
"Select bodies or faces instead": "Натомість виділіть тіла або грані",
"Select edges": "Виберіть ребра",
"Selection filter": "Фільтр виділення",
"Semantic faces share their appearance across instances.": "Семантичні грані спільно використовують свій вигляд між екземплярами.",
"Send your report on GitHub": "Надіслати звіт на GitHub",
"Shaded": "Затінений",
"Shape": "Форма",
"Sheet Rules": "Правила листа",
"Show Agent Cursor": "Показати курсор агента",
"Show in folder": "Показати в теці",
"Show the pointer an AI agent moves (#35)": "Показувати вказівник, яким рухає ШІ-агент (#35)",
"Show tool at cursor": "Показувати інструмент біля курсора",
"Signed axes": "Підписані осі",
"Size": "Розмір",
"Sketch constraints": "Обмеження ескізу",
@@ -450,37 +345,24 @@
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft закрився до того, як ці проєкти було збережено. Можна відкрити їхнє останнє автозбереження.",
"Specialise": "Спеціалізувати",
"Speed": "Швидкість",
"Splitting tool": "Інструмент розділення",
"Square": "Квадрат",
"Stand it up": "Вирівняти",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Вирівняти: поворот на чверть оберту навколо X як одна операція переміщення, яку можна скасувати.",
"Stand this part up?": "Вирівняти цю деталь?",
"Start angle": "Початковий кут",
"Start bulge": "Роздуття початку",
"Start continuity": "Неперервність початку",
"Step": "Крок",
"Steps": "Кроки",
"Steps to reproduce (optional)": "Кроки відтворення (необов'язково)",
"Stitch the kept pieces": "Зшити залишені частини",
"Stitch to both bodies": "Зшити з обома тілами",
"Studio": "Студія",
"Study": "Дослідження",
"Style": "Стиль",
"Summary": "Короткий опис",
"Suppress": "Придушити",
"Suppressed": "Придушено",
"Surface": "Поверхня",
"Surface, or faces to turn over": "Поверхня або грані, щоб обернути нормаль",
"Swap Ends": "Поміняти кінці місцями",
"TRIM": "ОБРІЗАННЯ",
"Tangent (G1)": "Дотик (G1)",
"Tangent Chain": "Дотичний ланцюг",
"Tangent chain": "Дотичний ланцюг",
"Tap drill diameter": "Діаметр свердла під різьбу",
"Target": "Цільова грань або площина",
"Target Body": "Цільове тіло",
"Tear": "Розрив",
"Technical details": "Технічні подробиці",
"The dashed sketch is a saved plane guide. Select a new face to repair the attachment.": "Штриховий ескіз — це збережений орієнтир площини. Виберіть нову грань, щоб відновити прив'язку.",
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "Збережений вихідний екземпляр відсутній. Перед повторною прив'язкою виберіть дійсне вихідне розміщення.",
"Then": "Потім",
@@ -492,32 +374,24 @@
"This child is shared. Deleting it removes it from every copy of its parent.": "Цей дочірній об'єкт спільний. Його видалення прибирає його з кожної копії батьківського.",
"This child is shared. Position changes apply to every copy of its parent.": "Цей дочірній об'єкт спільний. Зміни положення застосовуються до кожної копії батьківського.",
"This face cannot provide a durable attachment. Choose another face or explicitly use a fixed plane.": "Ця грань не може забезпечити надійну прив'язку. Виберіть іншу грань або явно використайте фіксовану площину.",
"This image is not attached to GitHub yet. Copy or save it, then attach it on GitHub.": "Це зображення ще не додано до GitHub. Скопіюйте або збережіть його, а потім додайте на GitHub.",
"To": "До",
"To Offset": "До зсуву",
"To edges": "Кінцеві ребра",
"Tolerance": "Допуск",
"Too close": "Занадто близько",
"Tool Bodies": "Тіла інструмента",
"Tools": "Інструменти",
"Tools, clockwise from the top": "Інструменти за годинниковою стрілкою згори",
"Trimming tool (plane, face or curves)": "Інструмент обрізання (площина, грань або криві)",
"Turn off section analysis before exporting the view.": "Вимкніть аналіз перерізу перед експортом виду.",
"Type": "Тип",
"UNTRIM": "ЗНЯТИ ОБРІЗАННЯ",
"Unit": "Одиниця",
"Unsuppress": "Скасувати придушення",
"Updating the preview": "Оновлення попереднього перегляду",
"Use active author placement": "Використати активне вихідне розміщення",
"Use fixed plane": "Використати фіксовану площину",
"Use saved fixed plane": "Використати збережену фіксовану площину",
"Use selected face as fixed plane": "Використати вибрану грань як фіксовану площину",
"Use the SolidWorks mouse mode": "Використовувати режим миші SolidWorks",
"User Parameters": "Параметри користувача",
"VIEW": "ВИД",
"VIEWS": "ВИГЛЯДИ",
"Value": "Значення",
"Variable Radius": "Змінний радіус",
"Velocity": "Швидкість",
"Vertices": "Вершини",
"View navigator": "Навігатор виду",
@@ -525,54 +399,19 @@
"Wall": "Стінка",
"Wall Location": "Розташування стінки",
"Wall Thickness": "Товщина стінки",
"What did you do just before the problem? A short list is enough": "Що ви робили безпосередньо перед проблемою? Короткого переліку достатньо",
"What did you expect to see?": "Що ви очікували побачити?",
"What happened?": "Що сталося?",
"When": "Коли",
"Width": "Ширина",
"Will fail": "Зазнає невдачі",
"With gestures on, the right button no longer orbits the view: hold it and move to open the ring, then touch a tool's zone. A plain right-click still opens the menu.": "Коли жести ввімкнено, права кнопка більше не обертає вид: утримуйте її та рухайте, щоб відкрити кільце, а потім наведіть на зону інструмента. Звичайне клацання правою кнопкою так само відкриває меню.",
"X": "X",
"X Angle": "Кут X",
"X angle": "Кут X",
"X distance": "Відстань X",
"Y": "Y",
"Y Angle": "Кут Y",
"Y angle": "Кут Y",
"Y distance": "Відстань Y",
"Z": "Z",
"Z Angle": "Кут Z",
"Z Distance": "Відстань Z",
"Z angle": "Кут Z",
"Z distance": "Відстань Z",
"Zebra": "Зебра",
"ai.prefs.availability": "Керування асистентом",
"ai.prefs.background_commands": "Виконувати команди агента у фоновому режимі",
"ai.prefs.follow_camera": "Повертати камеру до ескізів",
"ai.prefs.follow_camera_off": "Автоматичне стеження камери вимкнено.",
"ai.prefs.follow_camera_on": "Камера повертається до ескізів, які створює агент.",
"ai.prefs.intro": "Агенти, дозволи та те, як показується їхня робота.",
"ai.prefs.make_default": "Використовувати за замовчуванням",
"ai.prefs.no_agents": "Агентів ще немає. Додайте агента або оновіть список, щоб знайти встановлених.",
"ai.prefs.no_saved_approvals": "Немає збережених дозволів.",
"ai.prefs.override_all_warning": "Цей агент може зберігати, перезаписувати, експортувати або замінювати проєкт без запиту.",
"ai.prefs.permission_all": "Дозволяти все",
"ai.prefs.permission_all_summary": "Доступні інструменти SolidCraft працюють без запиту.",
"ai.prefs.permission_ask": "Запитувати перед змінами",
"ai.prefs.permission_ask_summary": "Підтверджувати кожну зміну проєкту та дію з файлами.",
"ai.prefs.permission_edits": "Дозволяти редагування моделі",
"ai.prefs.permission_edits_summary": "Редагування моделі виконується; для дій з файлами й руйнівних дій усе ще запитують дозвіл.",
"ai.prefs.permission_scope": "Лише для інструментів SolidCraft. Оболонка агента та редагування файлів підкоряються його власним дозволам.",
"ai.prefs.pointer_preview_hidden": "Вказівник приховано",
"ai.prefs.pointer_preview_speed": "Вказівник агента · {speed}",
"ai.prefs.pointer_speed": "Швидкість руху",
"ai.prefs.privacy_note": "Розмову отримує лише вибраний вами агент або кінцева точка. Ключі кінцевих точок зберігаються в системному сховищі ключів.",
"ai.prefs.show_pointer": "Показати вказівник агента",
"ai.prefs.turn_limit_hint": "Максимальна кількість викликів інструментів, що змінюють проєкт, які Асистент може зробити за одне повідомлення.",
"angle, height": "кут, висота",
"appearance.dielectric_ior": "Діелектричний IOR",
"appearance.metallic": "Металевість",
"appearance.only_available": "Лише доступні зовнішні вигляди",
"appearance.pbr_help": "Перегляньте цю поверхню в режимі рендерингу або як PNG-зображення виробу. Без заломлення чи пропускання.",
"appearance.pbr_opaque": "Непрозорий PBR",
"appearance.roughness": "Шорсткість",
@@ -734,7 +573,6 @@
"cmd.material_create": "Новий фізичний матеріал",
"cmd.material_delete": "Видалити фізичний матеріал",
"cmd.material_edit": "Редагувати фізичний матеріал",
"cmd.material_inspect": "Перевірити фізичний матеріал",
"cmd.material_list": "Список матеріалів",
"cmd.material_properties_directional": "Спрямований модуль пружності",
"cmd.material_properties_validate": "Перевірити дані фізичного матеріалу",
@@ -925,7 +763,6 @@
"cmd.view_graphics_preset": "Набір графіки",
"cmd.view_list": "Список іменованих видів",
"cmd.view_navigate": "Навігація видом",
"cmd.view_normal_to": "Перпендикулярно до виділення",
"cmd.view_rename": "Перейменувати іменований вид",
"cmd.view_save": "Новий іменований вид",
"color.copy": "Клацніть, щоб скопіювати значення кольору",
@@ -1023,12 +860,6 @@
"help.licences": "Ліцензії",
"help.mit": "Ліцензія MIT",
"help.notice": "NOTICE",
"help.solid.chamfer": "Зрізає вибрані ребра плоским скосом на заданій відстані. Використовуйте, щоб притупити гострі ребра або полегшити вставляння штифта чи гвинта.",
"help.solid.extrude": "Витягує профіль ескізу в 3D на задану відстань, додаючи або видаляючи матеріал. Використовуйте, щоб перетворити плоский контур на брусок, бобишку або кишеню.",
"help.solid.fillet": "Закруглює вибрані ребра плавним круговим переходом заданого радіуса. Використовуйте, щоб пом'якшити гострі ребра для міцності, зручності чи вигляду.",
"help.solid.hole": "Просвердлює круглий отвір заданого діаметра в обраній точці на грані. Використовуйте для отворів під гвинти, штифтів та інших простих круглих отворів.",
"help.solid.revolve": "Обертає профіль ескізу навколо осі, утворюючи кругле тіло. Використовуйте для валів, ручок, чаш та будь-яких деталей, круглих навколо осьової лінії.",
"help.solid.shell": "Робить тіло порожнистим, залишаючи стінки заданої товщини, і може відкривати вибрані грані. Використовуйте для коробок, кришок та інших тонкостінних деталей.",
"help.third_party": "Ліцензії сторонніх компонентів",
"home.back": "Назад до {name}",
"home.crashes": "Звіти про збої",
@@ -1138,15 +969,6 @@
"motion.to_end": "У кінець",
"motion.to_start": "На початок",
"motion.track_name": "Доріжка {number}",
"nav.alias.help": "Shift+Alt+ліва обертає, Shift+Alt+середня панорамує, Shift+Alt+права масштабує; середня також панорамує, права обертає",
"nav.blender.help": "Середня обертає, Shift+середня панорамує, Ctrl+середня масштабує; права не рухає камеру",
"nav.free_orbit.help": "Миша та 3D-миша обертають через обидва полюси й зберігають крен. Прив'язування до осей через Shift вимкнено.",
"nav.free_orbit.label": "Вільне обертання",
"nav.fusion.help": "Середня панорамує, Shift+середня обертає, права обертає",
"nav.inventor.help": "Середня панорамує, Shift+середня обертає, Ctrl+середня масштабує, права обертає",
"nav.openscad.help": "Ліва обертає по порожньому місцю, Shift+ліва виділяє рамкою, геометрію ескізу перетягують для редагування; права або середня панорамує, Shift+права або Shift+середня масштабує",
"nav.solidworks.help": "Середня обертає, Ctrl+середня панорамує, Shift+середня масштабує, права обертає",
"nav.tinkercad.help": "Права обертає, середня або Shift+права панорамує",
"nav.touchpad.help": "Утримуйте Shift і рухайте вказівник, щоб панорамувати; утримуйте Alt/Option і рухайте, щоб обертати. Кнопка миші не потрібна. Вертикальна прокрутка масштабує.",
"nav.touchpad.label": "Трекпад",
"nav.touchpad.orbit": "Обертання (утримуйте Alt/Option і рухайте; без кнопки миші)",
@@ -1169,130 +991,8 @@
"pattern.flip_direction": "Змінити напрямок",
"pick a hole to seat it, or OK places it at the origin": "виберіть отвір для посадки, або OK розмістить його в початку координат",
"pick one or two vertices, edges or faces": "виберіть одну-дві вершини, ребра або грані",
"prefs.add_item": "Додати елемент",
"prefs.alt_frees_manipulators": "Alt / Option також дає вільне перетягування стрілок маніпулятора",
"prefs.anti_aliasing_note": "Згладжує нерівні краї.",
"prefs.applies_on_restart": "Застосовується після перезапуску",
"prefs.assistant": "Асистент",
"prefs.axes": "Осі",
"prefs.axis": "Вісь",
"prefs.bottom": "Низ",
"prefs.browser": "Браузер",
"prefs.built_in": "Вбудована",
"prefs.button_left": "Перетягування лівою кнопкою",
"prefs.button_middle": "Перетягування середньою кнопкою",
"prefs.button_right": "Перетягування правою кнопкою",
"prefs.choose_font": "Вибрати файл шрифту…",
"prefs.click": "Клацання",
"prefs.contact_shading": "М'яке контактне затінення",
"prefs.contact_shading_note": "М'яке затінення там, де деталі зближуються.",
"prefs.context_menu": "Контекстне меню",
"prefs.context_menu_ring": "Контекстне меню · утримуйте для кругового меню",
"prefs.cpu": "ЦП (програмний)",
"prefs.crash_reports": "Відкрити звіти про збої…",
"prefs.crash_reports_note": "Звіти зберігаються на цьому комп'ютері разом із проєктом і командами, що відтворюють проблему.",
"prefs.ctrl_cmd": "Ctrl / Cmd",
"prefs.curved_surfaces": "Криві поверхні",
"prefs.curves": "Криві поверхні",
"prefs.dead_zone_note": "Заштрихована смуга — це мертва зона: рух у її межах ігнорується.",
"prefs.default": "типово",
"prefs.design": "Типові параметри проєкту",
"prefs.design_note": "З чого починаються нові проєкти. Відкриті проєкти зберігають власні параметри.",
"prefs.devices": "3D-миша",
"prefs.display": "Відображення",
"prefs.edges": "Ребра та затінення",
"prefs.extend": "Розширення",
"prefs.extra_navigation": "Ctrl+перетягування лівою на порожньому місці обертає; утримуйте Space, щоб панорамувати.",
"prefs.follows_os": "Як у системі",
"prefs.font": "Шрифт",
"prefs.font_fallback": "Символи, яких немає у вибраному шрифті, беруться з вбудованого, тож жоден напис не показує порожній квадрат.",
"prefs.font_sample": "Ґедзь, що їв фінік, з'їв і юшку · Ø12.50 mm · 45.0° · 0123456789",
"prefs.font_unreadable": "Не вдалося прочитати вибраний шрифт.",
"prefs.general": "Загальні",
"prefs.general_note": "Мова, збереження та оновлення.",
"prefs.gesture_orbit": "Обертання",
"prefs.gesture_pan": "Панорамування",
"prefs.gesture_zoom": "Масштаб",
"prefs.graphics": "Графіка",
"prefs.graphics_note": "Обміняйте швидкість на плавність. Кожен варіант показує свій ефект на зразку.",
"prefs.graphics_presets_note": "Набори задають наведені нижче параметри разом.",
"prefs.history": "Історія",
"prefs.hold_stop_snapping": "Утримуйте, щоб вимкнути прив'язування",
"prefs.icons_only": "Лише піктограми",
"prefs.imported_file": "Імпортований файл",
"prefs.input": "Введення",
"prefs.interface": "Інтерфейс",
"prefs.interface_language": "Мова інтерфейсу",
"prefs.interface_note": "Як виглядає SolidCraft і де розташовані його частини.",
"prefs.interval_minutes": "{n} хв",
"prefs.labels": "Підписи",
"prefs.layout": "Компонування",
"prefs.layout_note": "«Компактне» прикріплює панелі до країв вікна. Прозорість браузера доступна в режимі «Плаваюче». Сторону Асистента можна змінити на сторінці «Асистент».",
"prefs.left": "Ліворуч",
"prefs.left_click": "Клацання лівою",
"prefs.live_input": "Поточне введення",
"prefs.live_preview": "Попередній перегляд у реальному часі",
"prefs.minutes": "хв",
"prefs.minutes_suffix": " min",
"prefs.mouse": "Миша й трекпад",
"prefs.mouse_heading": "Миша",
"prefs.mouse_mapping": "Призначення кнопок миші",
"prefs.navigation": "Навігація",
"prefs.navigation_note": "Як миша, трекпад і 3D-миша рухають вид.",
"prefs.not_on_gpu": "не на цьому GPU",
"prefs.on": "Увімкнено",
"prefs.opaque": "Непрозорий",
"prefs.orbit_snap": "Утримуйте Shift під час обертання, щоб прив'язатися до осі.",
"prefs.orientation": "Орієнтація",
"prefs.overall_speed": "Загальна швидкість",
"prefs.preview_choices": "Перегляд ваших виборів",
"prefs.quality_default": "типово",
"prefs.quality_fastest": "найшвидше",
"prefs.quality_slowest": "найповільніше",
"prefs.quality_smoother": "плавніше",
"prefs.reports_saved": "Збережено: {n}",
"prefs.right_click": "Клацання правою",
"prefs.saving": "Автозбереження й відновлення",
"prefs.scale_note": "Також Ctrl + / Ctrl − у вікні. Кнопки мають 28 px за 100%.",
"prefs.scale_text": "Масштаб і текст",
"prefs.select": "Вибрати",
"prefs.select_one_item": "Вибрати один елемент",
"prefs.selecting": "Вибір",
"prefs.shift": "Shift",
"prefs.shift_add_ctrl_toggle": "Shift лише додає; Ctrl / Cmd перемикає",
"prefs.shift_add_note": "Увімкнено: Shift додає, Ctrl / Cmd перемикає. Вимкнено (типово): обидва перемикають.",
"prefs.shortcuts": "Комбінації клавіш",
"prefs.sketch_selection": "Ескіз і вибір",
"prefs.sketch_selection_heading": "Ескіз і вибір",
"prefs.sketch_selection_note": "Як ескізи прив'язуються й як клацання вибирають.",
"prefs.sketching": "Ескізування",
"prefs.snap_drawing_note": "Під час малювання або перетягування точки ескізу.",
"prefs.source": "Джерело",
"prefs.speed": "Швидкість",
"prefs.stand_up_note": "Коли файл STEP або IGES відкрито з віссю Z угору, пропонувати поворот на чверть оберту навколо X.",
"prefs.status_done": "Готово",
"prefs.status_error": "Помилка",
"prefs.status_selected": "Вибрано",
"prefs.status_warning": "Попередження",
"prefs.supersampling_brief": "Малює вид у більшому розмірі й зменшує його: дуже чітко, але потребує більше ресурсів.",
"prefs.tab_accent_note": "Акцентний колір відповідає активній вкладці",
"prefs.theme_file": "файл теми",
"prefs.timeline_half_hour": "−30 хв",
"prefs.timeline_now": "зараз",
"prefs.timeline_quarter_hour": "−15",
"prefs.toggle_item": "Додати або прибрати елемент",
"prefs.toolbar": "Панель інструментів",
"prefs.translucent": "Напівпрозорий",
"prefs.under_browser": "Під браузером",
"prefs.under_cursor": "Під курсором",
"prefs.units_precision": "Одиниці й точність",
"prefs.updates": "Оновлення",
"prefs.view": "Поведінка виду",
"prefs.viewport": "Вікно перегляду",
"prefs.wheel": "Колесо",
"prefs.wheel_trackpad": "Колесо й трекпад",
"prefs.workspace": "Робоча область",
"prefs.zoom_toward_cursor": "Масштаб до курсора",
"properties.title": "Властивості: {name}",
"recovery.days_ago": "{count} дн. тому",
"recovery.detail": "{file} · елементів: {features} · автозбережено {when}",
@@ -1654,8 +1354,6 @@
"ui.agent_chat_waiting": "Очікує на вашу відповідь",
"ui.agent_cursor": "Курсор агента",
"ui.agent_cursor_speed": "Швидкість курсора агента",
"ui.agent_off_thread": "Підтримувати чуйність вікна, поки агенти виконують команди",
"ui.agent_off_thread_note": "Повільні команди агента (з панелі ШІ або клієнта MCP) обчислюються паралельно з вікном: ви можете далі обертати й оглядати вид і зупинити команду з індикатора у виді. Вимкнено: кожна виконується в потоці вікна, і вікно чекає на неї.",
"ui.ai_add": "Додати",
"ui.ai_add_agent": "Додати агента…",
"ui.ai_add_agent_title": "Додати агента",
@@ -1711,6 +1409,7 @@
"ui.ai_permissions": "Дозволи",
"ui.ai_preset": "Набір",
"ui.ai_remove": "Видалити",
"ui.ai_request_limit": "зупинено після 24 запитів на одне повідомлення; надішліть інше повідомлення, щоб продовжити",
"ui.ai_reset": "Скинути",
"ui.ai_settings": "Налаштування ШІ…",
"ui.ai_show_features": "Показувати функції ШІ",
@@ -1887,8 +1586,6 @@
"ui.filter_commands_or_keys": "Фільтрувати команди або клавіші",
"ui.findInTimeline": "Знайти в часовому ланцюжку",
"ui.findInWindow": "Знайти у вікні",
"ui.fine": "Висока",
"ui.finest": "Максимальна",
"ui.finish_sketch": "Завершити ескіз",
"ui.fit": "Вписати",
"ui.fix_unfix": "Закріпити/розкріпити",
@@ -1986,17 +1683,10 @@
"ui.hideProfile": "Приховати профіль",
"ui.history_back": "На один крок назад",
"ui.history_below_browser": "Під браузером",
"ui.history_edge_hint": "двічі клацніть край",
"ui.history_first": "Перейти на початок",
"ui.history_forward": "На один крок уперед",
"ui.history_hide_names": "Приховати назви",
"ui.history_last": "Перейти в кінець",
"ui.history_marker_tip": "Маркер історії: перетягніть, щоб відкотити модель назад; нові елементи додаються тут",
"ui.history_names": "Назви",
"ui.history_play": "Відтворити до кінця",
"ui.history_show_names": "Показати назви",
"ui.history_title": "Історія",
"ui.history_toggle_names": "Показувати або приховувати назви елементів",
"ui.hole": "Отвір",
"ui.hole_tap_type": "Тип різьби отвору",
"ui.hole_type": "Тип отвору",
@@ -2120,7 +1810,6 @@
"ui.project_face_edges_sketch": "Проєктувати ребра граней у нові ескізи",
"ui.properties": "Властивості",
"ui.pull_direction": "Напрямок витягування",
"ui.quality": "Якість",
"ui.quantity": "Кількість",
"ui.quantity_2": "Кількість 2",
"ui.quit": "Вихід",
@@ -2215,7 +1904,6 @@
"ui.spline": "Сплайн",
"ui.split_body": "Розділити тіло",
"ui.stand_up_tip": "Після відкриття файлу STEP або IGES з напрямком Z вгору пропонувати поворот на чверть оберту навколо X",
"ui.standard": "Стандартна",
"ui.start": "Початок",
"ui.start_over": "Почати спочатку",
"ui.start_page": "Початкова сторінка",
+1 -11
View File
@@ -72,7 +72,6 @@
"Choose…": "Chọn…",
"Circular": "Tròn",
"Circumference": "Chu vi",
"Clockwise": "Theo chiều kim đồng hồ",
"Close draft": "Đóng bản nháp",
"Collisions": "Va chạm",
"Colour": "Màu sắc",
@@ -323,7 +322,6 @@
"Search commands": "Tìm kiếm lệnh",
"Search this library": "Tìm trong thư viện này",
"Section": "Mặt cắt",
"Section position": "Vị trí mặt cắt",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Chọn đối tượng khác: giữ nút chuột trái đứng yên trên mô hình (hoặc nhấp chuột phải › Chọn đối tượng khác…) để liệt kê mọi đối tượng dưới con trỏ; nhấp lại vào cùng vị trí đó để chọn đối tượng kế tiếp. Nhấp đúp chuột giữa để về Góc nhìn chuẩn (Tùy chọn cài đặt › Điều hướng).",
"Select bodies (a face picks its body)": "Chọn thân khối (chọn một mặt sẽ chọn cả thân khối)",
"Select bodies or faces instead": "Chọn thân khối hoặc bề mặt thay thế",
@@ -335,7 +333,6 @@
"Show Agent Cursor": "Hiển thị con trỏ của AI Agent",
"Show in folder": "Hiển thị trong thư mục",
"Show the pointer an AI agent moves (#35)": "Hiển thị con trỏ do AI Agent điều khiển (#35)",
"Show tool at cursor": "Hiển thị công cụ cạnh con trỏ",
"Signed axes": "Các trục có dấu (+/-)",
"Size": "Kích thước",
"Sketch constraints": "Ràng buộc phác thảo",
@@ -348,11 +345,9 @@
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft đã đóng trước khi các thiết kế này được lưu. Bạn có thể mở bản tự động lưu gần nhất của chúng.",
"Specialise": "Chuyên biệt hóa",
"Speed": "Tốc độ",
"Square": "Hình vuông",
"Stand it up": "Dựng đứng đối tượng",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Dựng đứng đối tượng: xoay 90° quanh trục X, dưới dạng một tính năng Di chuyển có thể hoàn tác.",
"Stand this part up?": "Dựng đứng chi tiết này lên?",
"Start angle": "Góc bắt đầu",
"Start bulge": "Độ phình đầu",
"Start continuity": "Độ liên tục đầu",
"Step": "Bước",
@@ -417,7 +412,6 @@
"angle, height": "góc, chiều cao",
"appearance.dielectric_ior": "Chỉ số khúc xạ điện môi (IOR)",
"appearance.metallic": "Độ kim loại",
"appearance.only_available": "Chỉ các kiểu bề ngoài khả dụng",
"appearance.pbr_help": "Xem bề mặt này trong chế độ Kết xuất hoặc ảnh PNG sản phẩm. Không có khúc xạ hay truyền sáng.",
"appearance.pbr_opaque": "PBR không trong suốt",
"appearance.roughness": "Độ nhám bề mặt",
@@ -579,7 +573,6 @@
"cmd.material_create": "Tạo vật liệu vật lý mới",
"cmd.material_delete": "Xóa vật liệu vật lý",
"cmd.material_edit": "Chỉnh sửa vật liệu vật lý",
"cmd.material_inspect": "Kiểm tra vật liệu vật lý",
"cmd.material_list": "Danh sách vật liệu",
"cmd.material_properties_directional": "Mô đun đàn hồi theo hướng",
"cmd.material_properties_validate": "Xác thực dữ liệu thuộc tính vật liệu",
@@ -1416,6 +1409,7 @@
"ui.ai_permissions": "Quyền hạn",
"ui.ai_preset": "Mẫu cấu hình sẵn",
"ui.ai_remove": "Xóa",
"ui.ai_request_limit": "đã dừng sau 24 yêu cầu cho một tin nhắn; gửi tin nhắn khác để tiếp tục",
"ui.ai_reset": "Đặt lại",
"ui.ai_settings": "AI cài đặt…",
"ui.ai_show_features": "Hiển thị các tính năng AI",
@@ -1592,8 +1586,6 @@
"ui.filter_commands_or_keys": "Lọc lệnh hoặc phím tắt",
"ui.findInTimeline": "Tìm trong lịch sử thiết kế",
"ui.findInWindow": "Tìm trong cửa sổ",
"ui.fine": "Mịn",
"ui.finest": "Mịn nhất",
"ui.finish_sketch": "Hoàn tất phác thảo",
"ui.fit": "Vừa vặn (Fit)",
"ui.fix_unfix": "Cố định/Uncố định",
@@ -1818,7 +1810,6 @@
"ui.project_face_edges_sketch": "Chiếu các cạnh bề mặt vào phác thảo mới",
"ui.properties": "Thuộc tính cơ lý",
"ui.pull_direction": "Hướng kéo",
"ui.quality": "Chất lượng",
"ui.quantity": "Số lượng",
"ui.quantity_2": "Số lượng 2",
"ui.quit": "Thoát ứng dụng",
@@ -1913,7 +1904,6 @@
"ui.spline": "Đường cong Spline",
"ui.split_body": "Chia tách khối",
"ui.stand_up_tip": "Sau khi mở tệp STEP hoặc IGES với trục Z hướng lên, đề xuất xoay 90° quanh trục X",
"ui.standard": "Tiêu chuẩn",
"ui.start": "Bắt đầu",
"ui.start_over": "Bắt đầu lại",
"ui.start_page": "Trang bắt đầu",
+1 -11
View File
@@ -72,7 +72,6 @@
"Choose…": "選擇…",
"Circular": "環形",
"Circumference": "周長",
"Clockwise": "順時針",
"Close draft": "關閉草稿",
"Collisions": "碰撞",
"Colour": "顏色",
@@ -322,7 +321,6 @@
"Search commands": "搜尋命令",
"Search this library": "搜尋此零件庫",
"Section": "剖面",
"Section position": "截面位置",
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "「選擇其他」:在模型上按住左鍵(或右鍵點擊 › 選擇其他…)列出游標下的所有物件;在同一位置再次點擊可選擇該處的下一個物件。雙擊中鍵轉到主檢視(偏好設定 › 導覽)。",
"Select bodies (a face picks its body)": "選擇實體(點擊面即選擇其所屬實體)",
"Select bodies or faces instead": "改為選擇實體或面",
@@ -334,7 +332,6 @@
"Show Agent Cursor": "顯示代理游標",
"Show in folder": "在資料夾中顯示",
"Show the pointer an AI agent moves (#35)": "顯示 AI 代理移動的指標(#35)",
"Show tool at cursor": "在游標旁顯示工具",
"Signed axes": "帶符號軸",
"Size": "大小",
"Sketch constraints": "草圖拘束條件",
@@ -347,11 +344,9 @@
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft 在這些設計儲存前已關閉。可以開啟它們的上次自動儲存。",
"Specialise": "專用操作",
"Speed": "速度",
"Square": "正方形",
"Stand it up": "將其立起",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "將其立起:繞 X 軸旋轉四分之一週,作為一個可復原的移動特徵。",
"Stand this part up?": "立起此零件?",
"Start angle": "起始角度",
"Start bulge": "起始鼓度",
"Start continuity": "起始連續性",
"Step": "步驟",
@@ -416,7 +411,6 @@
"angle, height": "角度、高度",
"appearance.dielectric_ior": "介電折射率",
"appearance.metallic": "金屬度",
"appearance.only_available": "僅顯示可用外觀",
"appearance.pbr_help": "在彩現工作區或產品 PNG 中檢視此表面。不支援折射或透射。",
"appearance.pbr_opaque": "不透明 PBR",
"appearance.roughness": "粗糙度",
@@ -578,7 +572,6 @@
"cmd.material_create": "新建物理材質",
"cmd.material_delete": "刪除物理材質",
"cmd.material_edit": "編輯物理材質",
"cmd.material_inspect": "檢查物理材質",
"cmd.material_list": "列出材質",
"cmd.material_properties_directional": "方向彈性模量",
"cmd.material_properties_validate": "驗證物理材質資料",
@@ -1359,6 +1352,7 @@
"ui.ai_permissions": "權限",
"ui.ai_preset": "預設",
"ui.ai_remove": "移除",
"ui.ai_request_limit": "每條訊息 24 次請求後已停止;再傳送一條訊息即可繼續",
"ui.ai_reset": "重設",
"ui.ai_settings": "AI 設定…",
"ui.ai_show_features": "顯示 AI 功能",
@@ -1534,8 +1528,6 @@
"ui.filter_commands_or_keys": "篩選命令或按鍵",
"ui.findInTimeline": "在時間軸中查詢",
"ui.findInWindow": "在視窗中查詢",
"ui.fine": "精細",
"ui.finest": "最精細",
"ui.finish_sketch": "完成草圖",
"ui.fit": "適合",
"ui.fix_unfix": "固定/取消固定",
@@ -1760,7 +1752,6 @@
"ui.project_face_edges_sketch": "將面的邊投影到新草圖",
"ui.properties": "屬性",
"ui.pull_direction": "拔模方向",
"ui.quality": "品質",
"ui.quantity": "數量",
"ui.quantity_2": "數量 2",
"ui.quit": "結束",
@@ -1855,7 +1846,6 @@
"ui.spline": "雲形線",
"ui.split_body": "分割實體",
"ui.stand_up_tip": "當 Z 朝上的 STEP 或 IGES 檔案開啟後,提示繞 X 軸旋轉四分之一週",
"ui.standard": "標準",
"ui.start": "開始",
"ui.start_over": "重新開始",
"ui.start_page": "起始頁面",
+2 -769
View File
File diff suppressed because it is too large Load Diff
+1 -4
View File
@@ -8,7 +8,7 @@
Per-machine install with a setup wizard: Welcome, install folder (Program Files\SolidCraft by
default, remembered for upgrades), Ready, progress, and a Finish page with a ticked "Launch
SolidCraft" box. Installs a Start Menu shortcut, solidcraft-cli (and adds its folder to the system PATH), the licence files (including
SolidCraft" box. Installs a Start Menu shortcut, solidcraft-cli, the licence files (including
THIRD-PARTY-LICENSES.txt), makes SolidCraft the owner of `.solidcraft` (and of the pre-rename `.solvecraft`), and offers it under
"Open with" for STEP, IGES, 3MF and STL. `msiexec /qn` installs silently.
Adapted from PhotoCraft's (storytold/photocraft, Apache-2.0; see ATTRIBUTION.md).
@@ -122,9 +122,6 @@
<Component Id="SolidcraftCli">
<File Id="SolidcraftCliExe" Source="$(var.BinDir)\solidcraft-cli.exe" KeyPath="yes" />
<!-- The install folder on the system PATH, so MCP clients and shells find `solidcraft-cli` by name;
removed again on uninstall. -->
<Environment Id="SolidcraftPath" Name="PATH" Value="[INSTALLFOLDER]" Action="set" Part="last" System="yes" Permanent="no" />
</Component>
<!-- Compatibility shim (docs/compatibility.md): the old CLI name, forwarding to solidcraft-cli. -->
<Component Id="SolidcraftCliAlias">
-6
View File
@@ -1,6 +0,0 @@
# Recipe and generator receipts hash canonical bytes on every platform.
*.json text eol=lf
*.py text eol=lf
*.md text eol=lf
*.png binary
*.hdr binary
-14
View File
@@ -1,14 +0,0 @@
# Runtime render catalog exports
These JSON files are necessary runtime exports of the editable authoring catalogs in
`docs/render-materials/`. Do not edit both copies. `python -B tools/render-library/generate.py`
copies the original bytes and updates the Rust lookup; `--check` verifies exact equality.
The application includes these asset exports, never files from the documentation tree.
The 192 texture maps and 24 HDR files are separate from this metadata. They are included
only with the engine's `bundled-render-library` feature. Desktop builds enable it by
default. Standalone CLI and web builds do not; use their same-named feature to opt in.
Discovery and uniform appearances remain available without the image pack. Requests
requiring missing bytes fail explicitly before document mutation; no network is used.
GPU/UI test targets opt in deliberately. Exercise the engine's no-pack negative tests
separately from its feature-enabled resource tests.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-759
View File
@@ -1,759 +0,0 @@
{
"metadata": {
"schema_version": "0.2.0",
"language": "en",
"status": "specified_unbound",
"provenance_id": "ORIGINAL-2026-10-CATALOG"
},
"items": [
{
"id": "Q-001",
"name": "Interactive Draft",
"status": "specified_unbound",
"goal": "preview",
"resolution": {
"width": 960,
"height": 540
},
"integrator_intent": "path_tracing",
"sampling": {
"minimum_samples": 8,
"maximum_samples": 64,
"relative_error_target": 0.12,
"error_estimator": "Versioned relative standard error of scene-linear luminance with a documented dark-signal floor; adapter must bind an estimator before use.",
"maximum_samples_action": "Finish with an unconverged flag when the target is not met.",
"scope": "Estimated Monte Carlo uncertainty for the bound estimator, not total optical error, finite-depth bias, rare-path coverage or temporal error.",
"stopping_rule_binding": "Require a versioned pixel/tile aggregation rule and dark-signal floor before execution."
},
"transport": {
"maximum_depth": 4,
"maximum_transmission_depth": 2,
"russian_roulette_after_depth": 3,
"roulette_policy": "Compensated stochastic termination; finite depth is a declared truncation."
},
"denoising": {
"enabled": false,
"preserve_raw": true,
"temporal_validation_required": false
},
"motion": {
"enabled": false,
"shutter_fraction": 0.0,
"temporal_samples": 0,
"requires_approved_bake": false,
"sample_budget_policy": "No time integration.",
"shutter_alignment": "instantaneous",
"frame_rate_binding": "not_applicable"
},
"output": {
"master": "linear_buffer",
"presentation": "png",
"color_transform": "project_declared",
"exposure_policy": "manual_locked"
},
"seed": 21026,
"required_capabilities": [
"CAP-001",
"CAP-002",
"CAP-003",
"CAP-004",
"CAP-005",
"CAP-006",
"CAP-057",
"CAP-058",
"CAP-059",
"CAP-073",
"CAP-081",
"CAP-083",
"CAP-085"
],
"budget_policy": "Resolve memory and time against the selected executor; never switch devices or downgrade silently.",
"baseline_compatibility": "Future authoring target; resolve every field against the current executor before use.",
"validation": [
"Measure convergence in dark regolith and bright metal or glass regions.",
"Compare raw and finished results; preserve labels, fibers and thin wires.",
"Report elapsed time, peak memory and incomplete quality targets."
],
"provenance_id": "ORIGINAL-2026-10-CATALOG",
"seed_policy": "Base seed; bind a versioned frame/pixel/sample/time-stratum stream policy and record it in the render manifest."
},
{
"id": "Q-002",
"name": "Balanced Product",
"status": "specified_unbound",
"goal": "presentation",
"resolution": {
"width": 1600,
"height": 900
},
"integrator_intent": "path_tracing",
"sampling": {
"minimum_samples": 32,
"maximum_samples": 512,
"relative_error_target": 0.035,
"error_estimator": "Versioned relative standard error of scene-linear luminance with a documented dark-signal floor; adapter must bind an estimator before use.",
"maximum_samples_action": "Finish with an unconverged flag when the target is not met.",
"scope": "Estimated Monte Carlo uncertainty for the bound estimator, not total optical error, finite-depth bias, rare-path coverage or temporal error.",
"stopping_rule_binding": "Require a versioned pixel/tile aggregation rule and dark-signal floor before execution."
},
"transport": {
"maximum_depth": 8,
"maximum_transmission_depth": 6,
"russian_roulette_after_depth": 3,
"roulette_policy": "Compensated stochastic termination; finite depth is a declared truncation."
},
"denoising": {
"enabled": true,
"preserve_raw": true,
"temporal_validation_required": false
},
"motion": {
"enabled": false,
"shutter_fraction": 0.0,
"temporal_samples": 0,
"requires_approved_bake": false,
"sample_budget_policy": "No time integration.",
"shutter_alignment": "instantaneous",
"frame_rate_binding": "not_applicable"
},
"output": {
"master": "linear_buffer",
"presentation": "png",
"color_transform": "project_declared",
"exposure_policy": "manual_locked"
},
"seed": 21026,
"required_capabilities": [
"CAP-001",
"CAP-002",
"CAP-003",
"CAP-004",
"CAP-005",
"CAP-006",
"CAP-057",
"CAP-058",
"CAP-059",
"CAP-065",
"CAP-073",
"CAP-081",
"CAP-083",
"CAP-085"
],
"budget_policy": "Resolve memory and time against the selected executor; never switch devices or downgrade silently.",
"baseline_compatibility": "Future authoring target; resolve every field against the current executor before use.",
"validation": [
"Measure convergence in dark regolith and bright metal or glass regions.",
"Compare raw and finished results; preserve labels, fibers and thin wires.",
"Report elapsed time, peak memory and incomplete quality targets."
],
"provenance_id": "ORIGINAL-2026-10-CATALOG",
"seed_policy": "Base seed; bind a versioned frame/pixel/sample/time-stratum stream policy and record it in the render manifest."
},
{
"id": "Q-003",
"name": "Product Portfolio",
"status": "specified_unbound",
"goal": "presentation",
"resolution": {
"width": 2560,
"height": 1440
},
"integrator_intent": "path_tracing",
"sampling": {
"minimum_samples": 64,
"maximum_samples": 2048,
"relative_error_target": 0.015,
"error_estimator": "Versioned relative standard error of scene-linear luminance with a documented dark-signal floor; adapter must bind an estimator before use.",
"maximum_samples_action": "Finish with an unconverged flag when the target is not met.",
"scope": "Estimated Monte Carlo uncertainty for the bound estimator, not total optical error, finite-depth bias, rare-path coverage or temporal error.",
"stopping_rule_binding": "Require a versioned pixel/tile aggregation rule and dark-signal floor before execution."
},
"transport": {
"maximum_depth": 12,
"maximum_transmission_depth": 10,
"russian_roulette_after_depth": 3,
"roulette_policy": "Compensated stochastic termination; finite depth is a declared truncation."
},
"denoising": {
"enabled": true,
"preserve_raw": true,
"temporal_validation_required": false
},
"motion": {
"enabled": false,
"shutter_fraction": 0.0,
"temporal_samples": 0,
"requires_approved_bake": false,
"sample_budget_policy": "No time integration.",
"shutter_alignment": "instantaneous",
"frame_rate_binding": "not_applicable"
},
"output": {
"master": "linear_buffer",
"presentation": "png",
"color_transform": "project_declared",
"exposure_policy": "manual_locked"
},
"seed": 21026,
"required_capabilities": [
"CAP-001",
"CAP-002",
"CAP-003",
"CAP-004",
"CAP-005",
"CAP-006",
"CAP-057",
"CAP-058",
"CAP-059",
"CAP-065",
"CAP-073",
"CAP-081",
"CAP-083",
"CAP-085"
],
"budget_policy": "Resolve memory and time against the selected executor; never switch devices or downgrade silently.",
"baseline_compatibility": "Future authoring target; resolve every field against the current executor before use.",
"validation": [
"Measure convergence in dark regolith and bright metal or glass regions.",
"Compare raw and finished results; preserve labels, fibers and thin wires.",
"Report elapsed time, peak memory and incomplete quality targets."
],
"provenance_id": "ORIGINAL-2026-10-CATALOG",
"seed_policy": "Base seed; bind a versioned frame/pixel/sample/time-stratum stream policy and record it in the render manifest."
},
{
"id": "Q-004",
"name": "Glass and Layers",
"status": "specified_unbound",
"goal": "optics",
"resolution": {
"width": 2560,
"height": 1440
},
"integrator_intent": "path_tracing",
"sampling": {
"minimum_samples": 128,
"maximum_samples": 4096,
"relative_error_target": 0.01,
"error_estimator": "Versioned relative standard error of scene-linear luminance with a documented dark-signal floor; adapter must bind an estimator before use.",
"maximum_samples_action": "Finish with an unconverged flag when the target is not met.",
"scope": "Estimated Monte Carlo uncertainty for the bound estimator, not total optical error, finite-depth bias, rare-path coverage or temporal error.",
"stopping_rule_binding": "Require a versioned pixel/tile aggregation rule and dark-signal floor before execution."
},
"transport": {
"maximum_depth": 20,
"maximum_transmission_depth": 16,
"russian_roulette_after_depth": 3,
"roulette_policy": "Compensated stochastic termination; finite depth is a declared truncation."
},
"denoising": {
"enabled": true,
"preserve_raw": true,
"temporal_validation_required": false
},
"motion": {
"enabled": false,
"shutter_fraction": 0.0,
"temporal_samples": 0,
"requires_approved_bake": false,
"sample_budget_policy": "No time integration.",
"shutter_alignment": "instantaneous",
"frame_rate_binding": "not_applicable"
},
"output": {
"master": "linear_exr",
"presentation": "png",
"color_transform": "project_declared",
"exposure_policy": "manual_locked"
},
"seed": 21026,
"required_capabilities": [
"CAP-001",
"CAP-002",
"CAP-003",
"CAP-004",
"CAP-005",
"CAP-006",
"CAP-025",
"CAP-026",
"CAP-027",
"CAP-057",
"CAP-058",
"CAP-059",
"CAP-065",
"CAP-073",
"CAP-074",
"CAP-081",
"CAP-083",
"CAP-085"
],
"budget_policy": "Resolve memory and time against the selected executor; never switch devices or downgrade silently.",
"baseline_compatibility": "Future authoring target; resolve every field against the current executor before use.",
"validation": [
"Measure convergence in dark regolith and bright metal or glass regions.",
"Compare raw and finished results; preserve labels, fibers and thin wires.",
"Report elapsed time, peak memory and incomplete quality targets."
],
"provenance_id": "ORIGINAL-2026-10-CATALOG",
"seed_policy": "Base seed; bind a versioned frame/pixel/sample/time-stratum stream policy and record it in the render manifest."
},
{
"id": "Q-005",
"name": "LED Optical Study",
"status": "specified_unbound",
"goal": "optics",
"resolution": {
"width": 2560,
"height": 1440
},
"integrator_intent": "path_tracing",
"sampling": {
"minimum_samples": 128,
"maximum_samples": 4096,
"relative_error_target": 0.01,
"error_estimator": "Versioned relative standard error of scene-linear luminance with a documented dark-signal floor; adapter must bind an estimator before use.",
"maximum_samples_action": "Finish with an unconverged flag when the target is not met.",
"scope": "Estimated Monte Carlo uncertainty for the bound estimator, not total optical error, finite-depth bias, rare-path coverage or temporal error.",
"stopping_rule_binding": "Require a versioned pixel/tile aggregation rule and dark-signal floor before execution."
},
"transport": {
"maximum_depth": 20,
"maximum_transmission_depth": 16,
"russian_roulette_after_depth": 3,
"roulette_policy": "Compensated stochastic termination; finite depth is a declared truncation."
},
"denoising": {
"enabled": false,
"preserve_raw": true,
"temporal_validation_required": false
},
"motion": {
"enabled": false,
"shutter_fraction": 0.0,
"temporal_samples": 0,
"requires_approved_bake": false,
"sample_budget_policy": "No time integration.",
"shutter_alignment": "instantaneous",
"frame_rate_binding": "not_applicable"
},
"output": {
"master": "linear_exr",
"presentation": "png",
"color_transform": "project_declared",
"exposure_policy": "manual_locked"
},
"seed": 21026,
"required_capabilities": [
"CAP-001",
"CAP-002",
"CAP-003",
"CAP-004",
"CAP-005",
"CAP-006",
"CAP-025",
"CAP-026",
"CAP-027",
"CAP-057",
"CAP-058",
"CAP-059",
"CAP-073",
"CAP-074",
"CAP-081",
"CAP-083",
"CAP-085"
],
"budget_policy": "Resolve memory and time against the selected executor; never switch devices or downgrade silently.",
"baseline_compatibility": "Future authoring target; resolve every field against the current executor before use.",
"validation": [
"Measure convergence in dark regolith and bright metal or glass regions.",
"Compare raw and finished results; preserve labels, fibers and thin wires.",
"Report elapsed time, peak memory and incomplete quality targets."
],
"provenance_id": "ORIGINAL-2026-10-CATALOG",
"seed_policy": "Base seed; bind a versioned frame/pixel/sample/time-stratum stream policy and record it in the render manifest."
},
{
"id": "Q-006",
"name": "Lunar Hero Master",
"status": "specified_unbound",
"goal": "lunar",
"resolution": {
"width": 3840,
"height": 2160
},
"integrator_intent": "path_tracing",
"sampling": {
"minimum_samples": 256,
"maximum_samples": 8192,
"relative_error_target": 0.005,
"error_estimator": "Versioned relative standard error of scene-linear luminance with a documented dark-signal floor; adapter must bind an estimator before use.",
"maximum_samples_action": "Finish with an unconverged flag when the target is not met.",
"scope": "Estimated Monte Carlo uncertainty for the bound estimator, not total optical error, finite-depth bias, rare-path coverage or temporal error.",
"stopping_rule_binding": "Require a versioned pixel/tile aggregation rule and dark-signal floor before execution."
},
"transport": {
"maximum_depth": 24,
"maximum_transmission_depth": 20,
"russian_roulette_after_depth": 3,
"roulette_policy": "Compensated stochastic termination; finite depth is a declared truncation."
},
"denoising": {
"enabled": false,
"preserve_raw": true,
"temporal_validation_required": false
},
"motion": {
"enabled": false,
"shutter_fraction": 0.0,
"temporal_samples": 0,
"requires_approved_bake": false,
"sample_budget_policy": "No time integration.",
"shutter_alignment": "instantaneous",
"frame_rate_binding": "not_applicable"
},
"output": {
"master": "linear_exr",
"presentation": "png",
"color_transform": "project_declared",
"exposure_policy": "manual_locked"
},
"seed": 21026,
"required_capabilities": [
"CAP-001",
"CAP-002",
"CAP-003",
"CAP-004",
"CAP-005",
"CAP-006",
"CAP-025",
"CAP-026",
"CAP-027",
"CAP-057",
"CAP-058",
"CAP-059",
"CAP-073",
"CAP-074",
"CAP-081",
"CAP-083",
"CAP-085",
"CAP-105"
],
"budget_policy": "Resolve memory and time against the selected executor; never switch devices or downgrade silently.",
"baseline_compatibility": "Future authoring target; resolve every field against the current executor before use.",
"validation": [
"Measure convergence in dark regolith and bright metal or glass regions.",
"Compare raw and finished results; preserve labels, fibers and thin wires.",
"Report elapsed time, peak memory and incomplete quality targets."
],
"provenance_id": "ORIGINAL-2026-10-CATALOG",
"seed_policy": "Base seed; bind a versioned frame/pixel/sample/time-stratum stream policy and record it in the render manifest."
},
{
"id": "Q-007",
"name": "Lunar Sample Macro",
"status": "specified_unbound",
"goal": "lunar",
"resolution": {
"width": 3072,
"height": 3072
},
"integrator_intent": "path_tracing",
"sampling": {
"minimum_samples": 256,
"maximum_samples": 8192,
"relative_error_target": 0.005,
"error_estimator": "Versioned relative standard error of scene-linear luminance with a documented dark-signal floor; adapter must bind an estimator before use.",
"maximum_samples_action": "Finish with an unconverged flag when the target is not met.",
"scope": "Estimated Monte Carlo uncertainty for the bound estimator, not total optical error, finite-depth bias, rare-path coverage or temporal error.",
"stopping_rule_binding": "Require a versioned pixel/tile aggregation rule and dark-signal floor before execution."
},
"transport": {
"maximum_depth": 24,
"maximum_transmission_depth": 20,
"russian_roulette_after_depth": 3,
"roulette_policy": "Compensated stochastic termination; finite depth is a declared truncation."
},
"denoising": {
"enabled": false,
"preserve_raw": true,
"temporal_validation_required": false
},
"motion": {
"enabled": false,
"shutter_fraction": 0.0,
"temporal_samples": 0,
"requires_approved_bake": false,
"sample_budget_policy": "No time integration.",
"shutter_alignment": "instantaneous",
"frame_rate_binding": "not_applicable"
},
"output": {
"master": "linear_exr",
"presentation": "png",
"color_transform": "project_declared",
"exposure_policy": "manual_locked"
},
"seed": 21026,
"required_capabilities": [
"CAP-001",
"CAP-002",
"CAP-003",
"CAP-004",
"CAP-005",
"CAP-006",
"CAP-025",
"CAP-026",
"CAP-027",
"CAP-057",
"CAP-058",
"CAP-059",
"CAP-073",
"CAP-074",
"CAP-081",
"CAP-083",
"CAP-085",
"CAP-105"
],
"budget_policy": "Resolve memory and time against the selected executor; never switch devices or downgrade silently.",
"baseline_compatibility": "Future authoring target; resolve every field against the current executor before use.",
"validation": [
"Measure convergence in dark regolith and bright metal or glass regions.",
"Compare raw and finished results; preserve labels, fibers and thin wires.",
"Report elapsed time, peak memory and incomplete quality targets."
],
"provenance_id": "ORIGINAL-2026-10-CATALOG",
"seed_policy": "Base seed; bind a versioned frame/pixel/sample/time-stratum stream policy and record it in the render manifest."
},
{
"id": "Q-008",
"name": "Animation Preview",
"status": "specified_unbound",
"goal": "animation",
"resolution": {
"width": 1280,
"height": 720
},
"integrator_intent": "path_tracing",
"sampling": {
"minimum_samples": 16,
"maximum_samples": 256,
"relative_error_target": 0.06,
"error_estimator": "Versioned relative standard error of scene-linear luminance with a documented dark-signal floor; adapter must bind an estimator before use.",
"maximum_samples_action": "Finish with an unconverged flag when the target is not met.",
"scope": "Estimated Monte Carlo uncertainty for the bound estimator, not total optical error, finite-depth bias, rare-path coverage or temporal error.",
"stopping_rule_binding": "Require a versioned pixel/tile aggregation rule and dark-signal floor before execution."
},
"transport": {
"maximum_depth": 8,
"maximum_transmission_depth": 6,
"russian_roulette_after_depth": 3,
"roulette_policy": "Compensated stochastic termination; finite depth is a declared truncation."
},
"denoising": {
"enabled": false,
"preserve_raw": true,
"temporal_validation_required": true
},
"motion": {
"enabled": true,
"shutter_fraction": 0.5,
"temporal_samples": 8,
"requires_approved_bake": true,
"sample_budget_policy": "One shutter-time sample per camera path, stratified over temporal_samples intervals; temporal_samples does not multiply the spatial sample budget.",
"shutter_alignment": "centered_on_frame_time",
"frame_rate_binding": "positive_finite_shot_frame_rate_hz_required"
},
"output": {
"master": "linear_exr",
"presentation": "png",
"color_transform": "project_declared",
"exposure_policy": "manual_locked"
},
"seed": 21026,
"required_capabilities": [
"CAP-001",
"CAP-002",
"CAP-003",
"CAP-004",
"CAP-005",
"CAP-006",
"CAP-053",
"CAP-057",
"CAP-058",
"CAP-059",
"CAP-073",
"CAP-074",
"CAP-078",
"CAP-081",
"CAP-083",
"CAP-085",
"CAP-111",
"CAP-112",
"CAP-119"
],
"budget_policy": "Resolve memory and time against the selected executor; never switch devices or downgrade silently.",
"baseline_compatibility": "Future authoring target; resolve every field against the current executor before use.",
"validation": [
"Measure convergence in dark regolith and bright metal or glass regions.",
"Compare raw and finished results; preserve labels, fibers and thin wires.",
"Report elapsed time, peak memory and incomplete quality targets."
],
"provenance_id": "ORIGINAL-2026-10-CATALOG",
"seed_policy": "Base seed; bind a versioned frame/pixel/sample/time-stratum stream policy and record it in the render manifest."
},
{
"id": "Q-009",
"name": "Animation Final",
"status": "specified_unbound",
"goal": "animation",
"resolution": {
"width": 3840,
"height": 2160
},
"integrator_intent": "path_tracing",
"sampling": {
"minimum_samples": 128,
"maximum_samples": 4096,
"relative_error_target": 0.012,
"error_estimator": "Versioned relative standard error of scene-linear luminance with a documented dark-signal floor; adapter must bind an estimator before use.",
"maximum_samples_action": "Finish with an unconverged flag when the target is not met.",
"scope": "Estimated Monte Carlo uncertainty for the bound estimator, not total optical error, finite-depth bias, rare-path coverage or temporal error.",
"stopping_rule_binding": "Require a versioned pixel/tile aggregation rule and dark-signal floor before execution."
},
"transport": {
"maximum_depth": 16,
"maximum_transmission_depth": 12,
"russian_roulette_after_depth": 3,
"roulette_policy": "Compensated stochastic termination; finite depth is a declared truncation."
},
"denoising": {
"enabled": false,
"preserve_raw": true,
"temporal_validation_required": true
},
"motion": {
"enabled": true,
"shutter_fraction": 0.5,
"temporal_samples": 32,
"requires_approved_bake": true,
"sample_budget_policy": "One shutter-time sample per camera path, stratified over temporal_samples intervals; temporal_samples does not multiply the spatial sample budget.",
"shutter_alignment": "centered_on_frame_time",
"frame_rate_binding": "positive_finite_shot_frame_rate_hz_required"
},
"output": {
"master": "linear_exr",
"presentation": "png",
"color_transform": "project_declared",
"exposure_policy": "manual_locked"
},
"seed": 21026,
"required_capabilities": [
"CAP-001",
"CAP-002",
"CAP-003",
"CAP-004",
"CAP-005",
"CAP-006",
"CAP-053",
"CAP-057",
"CAP-058",
"CAP-059",
"CAP-073",
"CAP-074",
"CAP-078",
"CAP-081",
"CAP-083",
"CAP-085",
"CAP-105",
"CAP-111",
"CAP-112",
"CAP-119"
],
"budget_policy": "Resolve memory and time against the selected executor; never switch devices or downgrade silently.",
"baseline_compatibility": "Future authoring target; resolve every field against the current executor before use.",
"validation": [
"Measure convergence in dark regolith and bright metal or glass regions.",
"Compare raw and finished results; preserve labels, fibers and thin wires.",
"Report elapsed time, peak memory and incomplete quality targets."
],
"provenance_id": "ORIGINAL-2026-10-CATALOG",
"seed_policy": "Base seed; bind a versioned frame/pixel/sample/time-stratum stream policy and record it in the render manifest."
},
{
"id": "Q-010",
"name": "Reference Convergence",
"status": "specified_unbound",
"goal": "reference",
"resolution": {
"width": 2048,
"height": 2048
},
"integrator_intent": "path_tracing",
"sampling": {
"minimum_samples": 1024,
"maximum_samples": 16384,
"relative_error_target": 0.002,
"error_estimator": "Versioned relative standard error of scene-linear luminance with a documented dark-signal floor; adapter must bind an estimator before use.",
"maximum_samples_action": "Finish with an unconverged flag when the target is not met.",
"scope": "Estimated Monte Carlo uncertainty for the bound estimator, not total optical error, finite-depth bias, rare-path coverage or temporal error.",
"stopping_rule_binding": "Require a versioned pixel/tile aggregation rule and dark-signal floor before execution."
},
"transport": {
"maximum_depth": 32,
"maximum_transmission_depth": 24,
"russian_roulette_after_depth": 3,
"roulette_policy": "Compensated stochastic termination; finite depth is a declared truncation."
},
"denoising": {
"enabled": false,
"preserve_raw": true,
"temporal_validation_required": false
},
"motion": {
"enabled": false,
"shutter_fraction": 0.0,
"temporal_samples": 0,
"requires_approved_bake": false,
"sample_budget_policy": "No time integration.",
"shutter_alignment": "instantaneous",
"frame_rate_binding": "not_applicable"
},
"output": {
"master": "linear_exr",
"presentation": "png",
"color_transform": "project_declared",
"exposure_policy": "manual_locked"
},
"seed": 21026,
"required_capabilities": [
"CAP-001",
"CAP-002",
"CAP-003",
"CAP-004",
"CAP-005",
"CAP-006",
"CAP-025",
"CAP-026",
"CAP-027",
"CAP-057",
"CAP-058",
"CAP-059",
"CAP-073",
"CAP-074",
"CAP-081",
"CAP-083",
"CAP-085"
],
"budget_policy": "Resolve memory and time against the selected executor; never switch devices or downgrade silently.",
"baseline_compatibility": "Future authoring target; resolve every field against the current executor before use.",
"validation": [
"Measure convergence in dark regolith and bright metal or glass regions.",
"Compare raw and finished results; preserve labels, fibers and thin wires.",
"Report elapsed time, peak memory and incomplete quality targets.",
"Compare multiple seeds and increased sample/depth budgets; an estimated noise threshold alone does not establish reference accuracy."
],
"provenance_id": "ORIGINAL-2026-10-CATALOG",
"seed_policy": "Base seed; bind a versioned frame/pixel/sample/time-stratum stream policy and record it in the render manifest."
}
]
}
File diff suppressed because it is too large Load Diff
-153
View File
@@ -1,153 +0,0 @@
# Physical material integration contract
This resource slice is based on `6d79d44fa403581e4508d7c9df5e163ff85085bf`.
It does not edit the parallel renderer implementation or the native document
format. The native candidate-card schema is in
[`crates/doc/src/physical_material.rs`](../../../crates/doc/src/physical_material.rs).
The personal integration branch separately implements design-owned cards and
library v2; capability checks must use the actual executing build.
## Three independent representations
1. **Extended reference profile:** the authoritative physical data with quantities,
individual source values, SI conversions, conditions, ranges, uncertainty,
directions and unresolved applicability. It is not a solver material model.
2. **Native schema-1 card:** only the supported scalar point properties and complete
admitted elastic blocks. Unsupported data remains in the extended profile and
is listed in `not_projected`; it is never silently discarded.
3. **Assigned document material:** the application's material object, its explicit
mass density, optional physical card and optional appearance. A profile link in
the render catalog is not evidence that this object has been created or assigned.
`exports/native-cards.json` is a bundle of individually valid card objects plus
projection metadata. It is not `solidcraft-library/1`, `solidcraft-library/2`, or
an application command script. Do not pass its whole envelope as a single card.
## Supported current commands
The baseline read-only API is:
```json
{"command":"material.properties.validate","params":{"card":"<individual card object>"}}
```
Replace the placeholder string with the actual JSON object. It validates
numerical/structural consistency and reports missing conditions and source review;
it does not assign, persist or authenticate the data.
The parallel personal runtime's persistence contract, inspected in its engine
source, is `material.create` / `material.edit` with an explicit `physical_card`:
```text
material.create {name, density_g_cm3, physical_card}
material.edit {material, physical_card}
```
A host must first verify that its registered command specification supports
`physical_card`; older command implementations may ignore fields they do not know.
The generated `prepare` plan names this prerequisite and is not executed here.
It does not issue `material.assign`, guess body names or create geometry.
On the persistence implementation, **editing the physical card does not silently
replace the document's mass density**. A discrepancy between card density in kg/m3
and assigned density in g/cm3 must be shown and explicitly resolved. The creation
plan uses the same selected point density in both fields. A range, missing density
or unselected formulation cannot create a falsely precise material.
For an existing body, retain its current physical material until a user explicitly
selects a compatible reference profile. Changing a visual finish must retain
physical fields, mass, joint geometry, body dimensions and material axes. Conversely,
changing an engineering card must not recolor the body or replace its PBR maps.
All document mutations must use the engine command/undo transaction, not direct
JSON mutation or a Python runtime dependency.
## Projection policy
The current native schema permits density, isotropic/orthotropic elasticity,
scalar tensile/compressive/yield/shear strengths, thermal conductivity/expansion
and specific heat. This exporter deliberately does not manufacture missing
constants to fill those fields.
- An isotropic block requires a same-source compatible E and Poisson ratio. Where
Poisson ratio is explicitly derived from the same source's E and G, the formula
and derived status are retained and identified in the native condition text.
- A single directional Young modulus is not a complete orthotropic tensor. Wood
flexural values and printed XY/Z coupon values stay directional in the extended
data; no isotropic substitute is introduced.
- Lower/upper bounds and intervals do not become native nominal point values.
They remain listed omissions. No midpoint or minimum-as-typical substitution.
- Directional thermal data is not copied into three axes. A repeated thermal
diagonal is allowed only for a profile explicitly marked as an isotropic
reference approximation, which the native state text discloses.
- Each native property retains its own temperature, moisture and strain-rate
fields. Other source conditions, including an interval or test method, are
retained in its state text. Unknown conditions are null, not assumed room values.
- Electrical data, hardness scales, radiative properties, porosity and transition
temperatures remain in the richer profile. Typed runtime support for these
quantities is separate work; preserve the extended data when designing it.
A structurally valid empty card is not a usable numerical material. The bundle
labels it `extended_data_only`; the coverage and native report separate those
eleven records from the 48 nonempty partial cards.
## Layers, compounds and analogues
`bindings.json` states whether a concept is a bulk body, layer, deposit, treated
surface, composite, textile, panel, granular material, device or assembly. Read
both `physical_role` and every reference's `relation` before offering assignment.
A constituent reference cannot be directly used as the effective mass density or
stiffness of a foam, laminate, populated PCB, honeycomb or multi-layer blanket.
Layer properties need thickness/coverage; homogenized properties need an explicit
construction and admissible model. Do not turn an unqualified comparison into a
default because it is the first reference in a list.
Lunar-like concepts use named terrestrial simulants only where stated. Bulk
packing density differs from grain density. Angle of repose is not substituted
for an internal friction angle, and terrestrial packing data does not certify
vacuum, gravity or thermal behavior on the Moon.
## Future library v2 export
The integrated runtime's `solidcraft_io::catalog` owns library serialization.
It computes entry revisions from its compact serde representation of provenance
and definition. Do not calculate an assumed equivalent Python hash or hand-write
a catalog revision: cross-language float spelling and omitted fields can differ.
The next runtime adapter should construct `ValidatedCard` through native admission,
use the runtime's catalog types, retain provenance and the extended-profile ID,
and generate the canonical revision through `Entry::new` / `content_revision`.
Tests must cover import, undo/redo, saved reopen, unchanged appearance, density
comparison and retention of unsupported extended fields. This slice performs
none of those document mutations and does not claim them tested.
## Validation boundaries
The supplied runner can invoke a trusted installed CLI only for read-only card
validation. The report identifies its binary hash and explicitly says that it
was not rebuilt by this gate. Numeric admission is not a source-license decision,
a physical experiment, a constitutive validation, a safety factor, a whole-app
build, or evidence of physics-driven rendering.
## Safe native library exporter
`tools/export_library.py --acknowledge-reference` prepares a non-executing plan
for the 46 point-density references and identifies 13 omitted profiles. Omission
is explicit: the application requires a scalar mass density, but these profiles
have no defensible scalar value. Full source profiles remain authoritative.
With `--cli` the helper checks the executable's registered command contract before
creating a new review folder or running a material mutation. It requires explicit
physical-card support, native inspection and library-v2 export. It uses a fresh
CLI process, no geometry and no existing user design. The actual application
serializes the v2 catalog and its revisions; Python does not approximate serde
canonicalization. Every card, source, condition and density is compared before
and after a separate-process cold reopen/re-export. The extended unsupported
properties remain in this package, not falsely marked as embedded in schema 1.
The helper is an offline authoring/integration tool, not an application dependency.
It never runs `material.assign` or changes a PBR appearance. Passing plan and
refusal tests does not imply that modern-runtime persistence was executed; only
a successful helper receipt establishes that separate result. Baseline read-only
native admission and complete library export are deliberately distinct gates.
-133
View File
@@ -1,133 +0,0 @@
# Sourced physical material references
Expanded data checkpoint: **2026-10-22**. Physical engineering data is maintained
separately from the authored render appearance. The original source files below
are authoritative; exported native cards are reproducible projections, not a
second independently edited dataset.
## Delivered coverage
| Collection | Current coverage |
| --- | --- |
| Named reference profiles | **59**, with **416 numerical records** covering **46 quantity types** |
| Public primary source records | **50**, with locators, retrieval date and review limitations |
| Material concept specifications | **600/600** have an explicit physical role, identification requirements and unknowns |
| Concepts with reference candidates | **306**; these can be substrate, constituent, comparison or named-grade candidates |
| Concepts still requiring a specific formulation | **294**; no unrelated numerical material is substituted |
| Authored PBR surface associations | **48/48** linked to reviewed reference candidates and explicit physical-role rules |
| Schema-1 native projections | **59 envelopes**: **48 nonempty**, **11 extended-only**, **113 native property blocks** |
| References eligible for material creation | **46** have a supported point density; other references do not acquire an invented mass density |
A surface reference association is **not identification of a manufactured part**.
A metallic-looking coating is not a metal substrate; a fibre is not a laminate;
feedstock is not an isotropic printed part; loose granular density is not grain
density. References remain explicitly named and require grade, processing and
conditions to match the intended application. No physical material is silently
assigned to a body, and no appearance edit changes its engineering data.
## Data and relationships
- [profiles.json](profiles.json): the 59 exact material identities, numerical
values/ranges, original units, SI conversion, measurement conditions and limits.
- [sources.json](sources.json): primary-source URLs, selected locators and review
records. Public availability is not a redistribution licence or authentication.
- [quantities.json](quantities.json): units, dimensions and bounds. Hardness scales,
thermal transitions, frequencies and directional measurements remain distinct.
- [bindings.json](bindings.json): all 600 concept specifications and 48 authored
surface relationships, including substrate/stack/recipe dependencies.
- [exports/native-cards.json](exports/native-cards.json): conservative native
schema-1 projections, with every excluded property identified and retained in
the extended profiles. This is not itself a native library import file.
- [exports/coverage.json](exports/coverage.json): generated coverage and exclusions.
The references cover metals and solders, engineering polymers, silicones and
resins, additive-manufacturing feedstock and specimens, woods, cork/MDF, glass and
ceramics, PCB and composite constituents, coatings and terrestrial lunar
simulants. New named references include PMMA sheet, PVC stock, Kevlar 49 fibre,
cured epoxy, UV epoxy and specific alkyd, stoving-enamel and acrylic coatings.
## Properties preserved
Mechanical records include density, Young/shear/Poisson measurements, tensile,
yield, compression, shear, flexural properties, elongation, fracture/tear data
and explicitly identified hardness scales where published. Thermal records
include conductivity, expansion, heat capacity, glass transition, melting,
decomposition and qualified service-temperature statements. Electrical records
retain frequency-specific permittivity/loss, resistivity and dielectric strength.
Optical, moisture, granular and application-thickness records retain their own
units and meaning; they are not reinterpreted as native elastic constants.
Each numerical record preserves a source, original value/unit, conversion,
qualifier, condition fields and limitations. A range is not reduced to its
midpoint; a lower-bound strength is not a typical value. Missing conditions and
uncertainties remain null/unknown. Per-property cure schedules survive native
projection. An isotropic elastic block refuses mismatched source, temperature,
moisture, rate, state or direction rather than combining unrelated constants.
Some new coating profiles contain only published temperature or film-thickness
information. Their cured density, modulus and strength remain unknown. In
particular, liquid/mixed-resin density and package weight are not cured-film
material properties. See [source review](SOURCE_REVIEW.md).
## Validate and inspect
All tools use Python 3.11+ standard-library code. From the repository root:
```sh
python -B assets/render/physical/tools/physical.py validate
python -B -m unittest discover -s assets/render/physical/tools -p 'test_*.py'
python -B assets/render/physical/tools/check_physical.py --cli /trusted/solidcraft-cli
python -B assets/render/physical/tools/physical.py resolve SURF-001
python -B assets/render/physical/tools/physical.py prepare steel-c45 --acknowledge-reference
```
The native check calls the read-only `material.properties.validate` command. It
checks numerical admission and property preservation, **not** source authenticity,
material assignment, solver correctness, persistence or library import. Evidence
and executable hashes stay in ignored `target/physical-material-review/`.
## Native library export without data loss
Generate a non-executing plan for all point-density references:
```sh
python -B assets/render/physical/tools/export_library.py --acknowledge-reference
```
With a trusted runtime that actually supports physical-card persistence and
library v2, create and verify a separate reference library:
```sh
python -B assets/render/physical/tools/export_library.py \
--acknowledge-reference --cli /trusted/card-capable/solidcraft-cli
```
Optional repeated `--profile ID` arguments restrict the selection. The exporter
first verifies the command contract, then uses a fresh CLI design with no
geometry, writes a native v2 library and design into a new `target/` directory,
checks every card/source/condition/density, asks the native library inspector to
verify content revisions, and cold-reopens/re-exports the design. It does not
manually approximate Rust's canonical revision serialization. Existing files,
live GUI sessions, user models and body assignments are untouched.
**The baseline Pegasus CLI validates cards but cannot persist them in materials;
the exporter therefore refuses it before creating anything.** Unit tests and that
refusal are not a successful end-to-end v2 export. A successful modern-runtime run
writes its own receipt; no such runtime receipt is implied by the authored data.
The complete extended dataset must accompany the narrower native library.
Electrical, hardness, ranges, directional measurements and unsupported quantities
must not be claimed to have been embedded in schema 1 merely because a profile
has a native projection.
## Integration and publication
Use [INTEGRATION.md](INTEGRATION.md) to connect the records to the separate Render
branch. `runtime_assignment` remains false until actual target selection and
application acceptance are performed. No native document migration, material-card
schema expansion, rendering shader, solver, model, appearance texture or optical
preset is changed here. Full workspace CI remains a separate integration gate.
All new curation, validators and tooling are original contributor work under
MIT OR Apache-2.0. Publisher data remains attributed to its sources; supplier
brand/grade names do not imply affiliation or certification. Selected numerical
facts are documented without bundling supplier tables, PDFs, artwork or code.
-140
View File
@@ -1,140 +0,0 @@
# Source review and interpretation limits
Research date: 2026-10-22. Exact URLs and source locators are stored in
[sources.json](sources.json); each numerical record in [profiles.json](profiles.json)
points to one of those references. Selection and normalization are original work.
Supplier publications retain their own rights and are not bundled or relicensed.
## Evidence levels
The profiles distinguish typical values, published intervals, lower/upper bounds
and explicitly calculated values. A manufacturer datasheet is evidence for the
named grade and stated test, not identification of an object from its rendering.
No record claims certified design allowables. A public URL and retrieval date are
traceability metadata, not a signed source archive or proof that the URL can never
change. Verify the relevant current datasheet and certificate before engineering use.
Missing temperature, strain rate, specimen orientation or test method is marked
unknown. Published crosshead speed is retained as speed, not guessed strain rate.
A source's plus/minus notation is preserved without inventing its confidence level.
## Conflicts and exclusions retained for review
| Source or material | Treatment |
| --- | --- |
| Sandmeyer 304/304L | Use the identified main physical/plate tables; do not average different FAQ values. Typical plate strengths are not minimum-grade specifications. |
| Elgiloy Nickel 200 | Keep the cited metric expansion value; a conflicting imperial entry is not used to create a second inconsistent constant. |
| SAC305 solder | A field with resistivity units but a conductivity label is excluded rather than silently reinterpreted. Solid alloy data does not identify flux-bearing paste. |
| PolyLite ABS | Product-page and wiki densities disagree, so density remains unresolved. The suspect modulus unit is not repaired by intuition. The retained XY/Z strengths remain directional. |
| Isola 370HR | The modulus row cites a flexural test; retain it as flexural modulus, not a complete orthotropic elastic tensor. Dielectric-strength unit entries are not silently reconciled. |
| Fused silica | A softening value is not used as a melting point; uncertain or inconsistent auxiliary optical entries were excluded. |
| Amorim TPS | Use the cited aerospace table rather than mix differing values from another brand page. Room-condition reference properties do not prove ablation performance. |
Every exclusion stays explicit in the relevant profile. Reference candidates that
would imply wrong chemistry were removed during association review: HDPE is not a
vinyl-flooring default; a glass profile is not a polymer interlayer; MDF is not an
unspecified insulation core. Wrought 6061 may only be an explicitly labelled
comparison for an unidentified casting, never a cast-alloy strength certificate.
## Materials requiring special treatment
### Wood and engineered panels
USDA clear-wood results identify species, moisture and test orientation. Clear
specimen means are not graded structural-lumber design values. Their bending
modulus is not renamed tensile modulus or combined with an invented Poisson ratio.
The specific-gravity basis is retained. For the cited 12% moisture rows, wet mass
at the stated specimen volume is derived as `SG * 1000 * (1 + 0.12)` kg/m3, with
1000 kg/m3 explicitly identified as the water-reference convention. That formula
is recorded, checked and not extrapolated to another moisture content.
Birch specimen properties are a veneer/constituent reference, not effective plywood
properties. Panel layup, glue, moisture, thickness class and density must be known.
MEDITE PREMIER >12 to 19 mm values are bounded panel specifications; the missing
board density is not invented. Cork's MDFACADE product name is not confused with
medium-density fibreboard.
### Printing and composites
Printed PLA/ABS coupon directions and reported spreads remain distinct. Feedstock
solid density is not an infilled-part envelope density. SLS PA12 is not automatically
a valid MJF material model. TPU density intervals remain intervals.
AS4 fibre data describes a constituent. AS4/8552 unidirectional laminate data
requires the stated fibre volume, cure and orientation. Neither describes a woven
twill part simply because its texture resembles carbon fibre. The woven aramid
surface remains unresolved until a particular fibre/resin/layup is selected.
### Electronics, elastomers and coatings
FR-4 dielectric values retain frequency, and expansion retains axis and thermal
regime. A solder-mask appearance is not a single FR-4 property card for the whole
board. Copper thickness, laminate construction, mask, components and interfaces
need separate treatment.
Dragon Skin 20's stress at 100% extension is not Young modulus. A reference casting
silicone is not automatically qualified for cable insulation, optical lenses or
space seals. Filled potting and IC mould compounds need their actual filler/cure
recipe; unfilled casting epoxy is only a labelled comparison.
AZ-93 thermal emittance and solar absorptance retain their coating context and
thickness condition. They are not visible RGB, material emission strength or a
bulk heat capacity. A coating's nominal application thickness is not an automatic
thickness assignment to a CAD model.
### Lunar-like resources
The four Exolith datasets are terrestrial laboratory analogues. Grain density,
packing density, void ratio, porosity and apparatus-dependent repose angle remain
separate. They do not identify a geological site, vacuum condition, internal
friction law or reduced-gravity mechanical response. Rock concepts are not assigned
loose-soil density merely to increase numeric coverage.
## Review backlog
The 294 concepts without an applicable named reference need a selected recipe,
grade or multi-material construction. The 306 with candidates still require review
of their stated relation and conditions; some references are constituents or
explicit comparisons, not effective-property defaults. No numerical completeness
percentage is derived from these association counts.
Further evidence is especially needed for actual paint/cure stacks, woven aramid
laminates, named glass/polymer optical grades, flexible-cable formulations, filled
thermal interfaces, resin systems, foams, friction materials and aerospace-qualified
processes. Temperature-dependent curves, fatigue, creep, damping, outgassing and
failure envelopes should be added as documented quantities/models, not filled with
uncited family averages.
## Added source review — 2026-10-22
Eight additional primary supplier/manufacturer references add 43 selected records,
bringing the dataset to 59 profiles, 50 source records and 416 numerical records.
All 48 authored surfaces now have an explicit reference-candidate relationship.
This does not make the 600 concepts numerically complete or identify their actual
manufacturing recipe.
PMMA sheet preserves published intervals for stiffness, Poisson ratio, heat
capacity, thermal expansion/conductivity and electrical resistivity; no midpoint
is selected for elasticity. Rigid PVC stock data does not identify plasticized
cable insulation. Kevlar 49 axial fibre data does not establish cured laminate or
meta-aramid paper/honeycomb properties; those mappings remain separate.
Sikafloor 161 HC compressive and flexural data retain the 28-day cure at 23 C;
its Shore D datum retains the 7-day cure. The manufacturer's mixed-liquid density
is not assigned to the cured resin. UV22 retains two different glass-transition
ranges, distinguishing UV-only cure from thermal post-cure. Its refractive index
is not a spectral model or certified optical transmission curve.
C71 Speedline, Stoving Enamel Hammer and Acrythane SC601 remain named coating
candidates with only the published temperature and/or coverage-basis dry-film
thickness recorded. Temperature statements without duration are upper-bound
manufacturer references, not continuous-use allowables. No cured density, modulus,
strength or film mass is inferred from paint package weights, liquid densities,
nominal coverage, visual roughness or colour. Those unknowns remain visible.
The inspected Kuhn PVC page contains blank mechanical cells and a hardness row
with inconsistent units. Neither is imported. Public reference metadata preserves
the source relationship without claiming that an openly accessible supplier page
has a permissive dataset licence. No PDF, table layout, prose, logo or image from
those publishers is included in this repository.
File diff suppressed because it is too large Load Diff
@@ -1,65 +0,0 @@
{
"schema": "solidcraft-physical-coverage/1",
"profile_count": 59,
"source_count": 50,
"property_records": 416,
"catalog_specifications": 600,
"concepts_with_reference_candidates": 306,
"concepts_needing_specific_recipe": 294,
"surface_specifications": 48,
"surfaces_with_reference_candidates": 48,
"native_property_blocks": 113,
"nonempty_native_cards": 48,
"extended_only_profiles": 11,
"profiles_with_point_density": 46,
"quantities": {
"angle_of_repose": 8,
"compressive_modulus": 2,
"compressive_strength": 14,
"decomposition_temperature": 2,
"density": 49,
"dielectric_strength": 7,
"dry_film_thickness": 4,
"electrical_conductivity_iacs": 3,
"elongation_break": 19,
"flexural_modulus": 14,
"flexural_strength": 16,
"fracture_toughness": 1,
"glass_transition": 11,
"grain_density": 4,
"hardness_brinell": 2,
"hardness_janka": 10,
"hardness_knoop": 2,
"hardness_shore_a": 2,
"hardness_shore_d": 11,
"heat_deflection": 9,
"internal_bond_strength": 1,
"loss_tangent": 9,
"mean_particle_size": 4,
"melting_temperature": 7,
"poisson_ratio": 8,
"porosity": 4,
"refractive_index": 6,
"relative_permittivity": 10,
"service_temperature": 2,
"shear_modulus": 3,
"shear_strength": 10,
"solar_absorptance": 1,
"specific_gravity": 10,
"specific_heat": 16,
"stress_at_strain": 1,
"tear_strength": 1,
"tensile_strength": 24,
"thermal_conductivity": 24,
"thermal_emittance": 1,
"thermal_expansion": 26,
"thickness_swelling": 1,
"void_ratio": 4,
"volume_resistivity": 14,
"water_absorption": 3,
"yield_strength": 10,
"young_modulus": 26
},
"runtime_assignment": false,
"numerically_complete_catalog_claim": false
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-209
View File
@@ -1,209 +0,0 @@
{
"schema": "solidcraft-physical-quantities/1",
"units": {
"density": "kg/m^3",
"young_modulus": "Pa",
"poisson_ratio": "1",
"shear_modulus": "Pa",
"tensile_strength": "Pa",
"yield_strength": "Pa",
"compressive_strength": "Pa",
"flexural_strength": "Pa",
"flexural_modulus": "Pa",
"elongation_break": "1",
"thermal_conductivity": "W/(m K)",
"thermal_expansion": "1/K",
"specific_heat": "J/(kg K)",
"glass_transition": "K",
"melting_temperature": "K",
"heat_deflection": "K",
"volume_resistivity": "ohm m",
"relative_permittivity": "1",
"loss_tangent": "1",
"dielectric_strength": "V/m",
"water_absorption": "1",
"hardness_shore_a": "1",
"hardness_shore_d": "1",
"hardness_knoop": "1",
"hardness_janka": "N",
"service_temperature": "K",
"specific_gravity": "1",
"stress_at_strain": "Pa",
"tear_strength": "N/m",
"refractive_index": "1",
"decomposition_temperature": "K",
"compressive_modulus": "Pa",
"electrical_conductivity_iacs": "1",
"fracture_toughness": "Pa sqrt(m)",
"shear_strength": "Pa",
"hardness_brinell": "1",
"grain_density": "kg/m^3",
"porosity": "1",
"void_ratio": "1",
"mean_particle_size": "m",
"angle_of_repose": "rad",
"thermal_emittance": "1",
"solar_absorptance": "1",
"dry_film_thickness": "m",
"internal_bond_strength": "Pa",
"thickness_swelling": "1"
},
"conversions": {
"kg/m^3": {
"scale": 1.0,
"offset": 0.0
},
"g/cm^3": {
"scale": 1000.0,
"offset": 0.0
},
"Pa": {
"scale": 1.0,
"offset": 0.0
},
"kPa": {
"scale": 1000.0,
"offset": 0.0
},
"MPa": {
"scale": 1000000.0,
"offset": 0.0
},
"GPa": {
"scale": 1000000000.0,
"offset": 0.0
},
"psi": {
"scale": 6894.757293168,
"offset": 0.0
},
"ksi": {
"scale": 6894757.293168,
"offset": 0.0
},
"1": {
"scale": 1.0,
"offset": 0.0
},
"%": {
"scale": 0.01,
"offset": 0.0
},
"W/(m K)": {
"scale": 1.0,
"offset": 0.0
},
"1/K": {
"scale": 1.0,
"offset": 0.0
},
"ppm/K": {
"scale": 1e-06,
"offset": 0.0
},
"1/degF": {
"scale": 1.8,
"offset": 0.0
},
"J/(kg K)": {
"scale": 1.0,
"offset": 0.0
},
"J/(g K)": {
"scale": 1000.0,
"offset": 0.0
},
"K": {
"scale": 1.0,
"offset": 0.0
},
"degC": {
"scale": 1.0,
"offset": 273.15
},
"ohm m": {
"scale": 1.0,
"offset": 0.0
},
"ohm cm": {
"scale": 0.01,
"offset": 0.0
},
"microohm cm": {
"scale": 1e-08,
"offset": 0.0
},
"ohm mm^2/m": {
"scale": 1e-06,
"offset": 0.0
},
"V/m": {
"scale": 1.0,
"offset": 0.0
},
"kV/mm": {
"scale": 1000000.0,
"offset": 0.0
},
"V/mil": {
"scale": 39370.07874015748,
"offset": 0.0
},
"N": {
"scale": 1.0,
"offset": 0.0
},
"N/m": {
"scale": 1.0,
"offset": 0.0
},
"pli": {
"scale": 175.1268352465,
"offset": 0.0
},
"lb/in^3": {
"scale": 27679.904710203,
"offset": 0.0
},
"Btu/(h ft degF)": {
"scale": 1.730734666371,
"offset": 0.0
},
"Btu/(lb degF)": {
"scale": 4186.8,
"offset": 0.0
},
"MPa sqrt(m)": {
"scale": 1000000.0,
"offset": 0.0
},
"Pa sqrt(m)": {
"scale": 1.0,
"offset": 0.0
},
"degF": {
"scale": 0.5555555555555556,
"offset": 255.3722222222222
},
"micrometre": {
"scale": 1e-06,
"offset": 0.0
},
"mil": {
"scale": 2.54e-05,
"offset": 0.0
},
"degree": {
"scale": 0.017453292519943295,
"offset": 0.0
},
"rad": {
"scale": 1.0,
"offset": 0.0
},
"m": {
"scale": 1.0,
"offset": 0.0
}
}
}
-505
View File
@@ -1,505 +0,0 @@
{
"schema": "solidcraft-physical-sources/1",
"sources": [
{
"id": "sandmeyer-304",
"title": "Sandmeyer Steel Alloy 304/304L plate",
"url": "https://www.sandmeyersteel.com/alloy-304-304l/",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Main Physical Properties and Typical Mechanical Properties (.375 in plate), not FAQ",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "alfa-6061",
"title": "Alfa Chemistry aluminium alloy 6061",
"url": "https://alloys.alfa-chemistry.com/aluminum-alloy-6061.html",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Physical properties and T6 sheet (0.020-0.500 in) typical properties",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "metalcor-ti5",
"title": "Metalcor titanium grade 5 3.7164/65",
"url": "https://www.metalcor.de/en/datenblatt/125/",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Mechanical and Physical Properties at 20 C",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "cda-c11000",
"title": "Copper Development Association C11000",
"url": "https://alloys.copper.org/alloy/C11000",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Physical properties table; property-specific temperatures",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "cda-c36000",
"title": "Copper Development Association C36000",
"url": "https://alloys.copper.org/alloy/C36000",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Physical properties table; property-specific temperatures",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "cda-c93200",
"title": "Copper Development Association C93200",
"url": "https://alloys.copper.org/alloy/C93200",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Physical properties table; property-specific temperatures",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "elgiloy-ni200",
"title": "Elgiloy Nickel 200 and 201 strip",
"url": "https://www.elgiloy.com/strip-nickel-200-and-201-alloys",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Physical properties; room-temperature modulus table",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "feinhuette-sac305",
"title": "Feinhuette SAC305 electronic solder",
"url": "https://www.feinhuette.de/en/metals/sn965ag3cu05",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Physical Data",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "ensinger-abs-stock",
"title": "Ensinger TECARAN ABS grey",
"url": "https://www.ensingerplastics.com/en/shapes/tecaran-abs-grey",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Facts and Technical details; exact grade/stock-shape values",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "ensinger-pom-c",
"title": "Ensinger TECAFORM AH natural POM-C",
"url": "https://www.ensingerplastics.com/en-gb/shapes/acetal-tecaform-ah-natural",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Facts and Technical details; exact grade/stock-shape values",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "ensinger-peek",
"title": "Ensinger TECAPEEK natural PEEK",
"url": "https://www.ensingerplastics.com/en/shapes/peek-tecapeek-natural",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Facts and Technical details; exact grade/stock-shape values",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "ensinger-pc",
"title": "Ensinger TECANAT natural PC",
"url": "https://www.ensingerplastics.com/en-gb/shapes/polycarbonate-tecanat-natural",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Facts and Technical details; exact grade/stock-shape values",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "ensinger-pa6-stock",
"title": "Ensinger TECAMID 6 natural PA6",
"url": "https://www.ensingerplastics.com/en/shapes/pa6-tecamid-6-natural",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Facts and Technical details; exact grade/stock-shape values",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "ensinger-ptfe",
"title": "Ensinger TECAFLON PTFE natural",
"url": "https://www.ensingerplastics.com/en/shapes/tecaflon-ptfe-natural",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Facts and Technical details",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "goodfellow-kapton",
"title": "Goodfellow Kapton HN PI coil",
"url": "https://www.goodfellow.com/global/kapton-hn-pi-coil-group",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Technical properties of supplied coil",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "ovako-c45",
"title": "Ovako C45 steel navigator",
"url": "https://steelnavigator.ovako.com/steel-grades/c45/",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Other properties; general grade, not an unspecified heat-treatment strength",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "ensinger-pp",
"title": "Ensinger TECAFINE PP grey",
"url": "https://www.ensingerplastics.com/en-sg/shapes/polypropylene-tecafine-pp-grey",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Technical details for stock shape",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "ensinger-hdpe",
"title": "Ensinger TECAFINE PE 300 black",
"url": "https://www.ensingerplastics.com/en-gb/shapes/polyethylene-tecafine-pe-300-black",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Technical details for stock shape",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "schott-borofloat",
"title": "SCHOTT BOROFLOAT 33 technical details",
"url": "https://www.schott.com/en-us/products/borofloat-p1000314/technical-details",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Physical, thermal, mechanical and optical tables",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "crystran-silica",
"title": "Crystran fused silica SiO2",
"url": "https://www.crystran.com/optical-materials/fused-silica-sio2/",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Optical material data table; inconsistent molecular/softening labels not imported",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "crystran-sapphire",
"title": "Crystran sapphire Al2O3",
"url": "https://www.crystran.com/optical-materials/sapphire-al2o3/",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Physical and optical constants; crystal orientation explicit",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "coorstek-ad96",
"title": "CoorsTek alumina AD-96",
"url": "https://www2.coorstek.com/en/materials/alumina/",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "AD-96 column, nominal material-property comparison",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "smoothon-ds20",
"title": "Smooth-On Dragon Skin 20",
"url": "https://www.smooth-on.com/products/dragon-skin-20/",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Technical data; 100% modulus is a finite-strain stress, not Young modulus",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "smoothon-ep690",
"title": "Smooth-On EpoxAcast 690",
"url": "https://www.smooth-on.com/products/epoxacast-690/",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Cured casting technical properties",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "polymaker-pla",
"title": "Polymaker PolyLite PLA technical sheet",
"url": "https://wiki.polymaker.com/polymaker-products/more-about-our-products/documents/technical-data-sheets/pla/polylite-tm-pla",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Thermal and XY/Z mechanical properties of specified printed test specimens",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "polymaker-abs",
"title": "Polymaker PolyLite ABS product specification",
"url": "https://shop.polymaker.com/products/polylite-abs/",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Mechanical XY/Z and thermal table; conflicting density/modulus excluded",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "polymaker-abs-wiki",
"title": "Polymaker PolyLite ABS wiki sheet",
"url": "https://wiki.polymaker.com/polymaker-products/more-about-our-products/documents/technical-data-sheets/abs-asa/polylite-tm-abs",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Density discrepancy, retained for resolution rather than averaged",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "polymaker-tpu",
"title": "Polymaker PolyFlex TPU95 technical sheet",
"url": "https://wiki.polymaker.com/polymaker-products/more-about-our-products/documents/technical-data-sheets/tpu/polyflex-tm-tpu95",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Material density range and nominal Shore hardness",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "eos-pa2200",
"title": "EOS PA 2200 PA12 reference",
"url": "https://www.eos.info/polymer-solutions/polymer-materials/biocompatible",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "PA 2200 typical part properties; not the different CarbonReduced record",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "isola-370hr",
"title": "Isola 370HR laminate and prepreg",
"url": "https://www.isola-group.com/pcb-laminates-prepreg/370hr-laminate-prepreg/",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Typical Values, revision E (2024-06); not construction-specific Dk tables",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "hexcel-as4",
"title": "Hexcel HexTow AS4 continuous fibre and laminate data",
"url": "https://www.hexcel.com/Products/Carbon-Fiber/HexTow-Continuous-Carbon-Fiber/",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "AS4 12K fibre and AS4/8552 unidirectional laminate columns",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "usda-wood-2021",
"title": "USDA Wood Handbook 2021 Chapter 5",
"url": "https://research.fs.usda.gov/download/treesearch/62244.pdf",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Table 5-3a, printed pages 5-4, 5-6 and 5-7; 12% moisture clear wood",
"license": "US federal government publication; source notice should still be reviewed",
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "amorim-md-facade",
"title": "Amorim MDFACADE decorative cork board",
"url": "https://www.amorimcorkinsulation.com/en/applications/Decorative-board-Cork-at-sight/162/",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Technical Features, additive-free decorative expanded cork",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "amorim-tps",
"title": "Amorim aerospace TPS P45/P50",
"url": "https://amorimcorksolutions.com/en-us/materials-applications/aerospace/",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Metric TPS materials table; this page is distinct from legacy brand-page values",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "std-polyester-powder",
"title": "Standard Group MT-D1036 polyester powder",
"url": "https://www.std-coatings.com/Powder/564.html",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Powder properties, colour-dependent specific-gravity range; not an Interpon product",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "az93",
"title": "AZ Technology AZ-93 inorganic coating",
"url": "https://www.aztechnology.com/product/1/az-93",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Cured AZ-93 specifications; radiative properties are not visible RGB or volumetric emission",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "az-primer",
"title": "AZ Technology MLP-300-AZ primer",
"url": "https://www.aztechnology.com/product/29/mlp-300-az",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Cured primer specifications",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "medite-premier",
"title": "MEDITE PREMIER datasheet prem-ds-en-13",
"url": "https://medite-smartply.files.svdcdn.com/production/files/MEDITE-PREMIER-Datasheet-prem-ds-en-13.pdf",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "PDF page 5, Technical Specification Sheet; >12 to 19 mm column",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "exolith-lms-1",
"title": "Exolith Lab LMS-1 mare simulant",
"url": "https://exolithsimulants.com/collections/regolith-simulants/products/lms-1-lunar-mare-simulant",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Physical Properties; terrestrial simulant reference, not an Apollo sample",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "exolith-lhs-1",
"title": "Exolith Lab LHS-1 highlands simulant",
"url": "https://exolithsimulants.com/collections/regolith-simulants/products/lhs-1-lunar-highlands-simulant",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Physical Properties; terrestrial simulant reference, not an Apollo sample",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "exolith-lms-1d",
"title": "Exolith Lab LMS-1D mare dust simulant",
"url": "https://exolithsimulants.com/collections/regolith-simulants/products/lms-1d",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Physical Properties; terrestrial simulant reference, not an Apollo sample",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "exolith-lhs-1d",
"title": "Exolith Lab LHS-1D highlands dust simulant",
"url": "https://exolithsimulants.com/collections/regolith-simulants/products/lhs-1d-lunar-highland-dust-simulant",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Physical Properties; terrestrial simulant reference, not an Apollo sample",
"license": null,
"review": "Selected numerical facts transcribed with original structure; no supplier document, image, implementation or complete table is redistributed."
},
{
"id": "goodfellow-pmma-sheet",
"title": "Goodfellow PMMA film and sheet reference",
"url": "https://www.goodfellow.com/eu/pmma-film-group",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Physical, electrical, mechanical and thermal property sections; ranges retain their endpoints.",
"license": null,
"review": "Selected numerical facts and explicit source conditions, not a copied supplier table. Publicly indexed manufacturer/supplier data were reviewed; publisher rights are not relicensed. No safety or grade certificate is implied."
},
{
"id": "goodfellow-kevlar49",
"title": "Goodfellow Kevlar 49 polyaramid fibre",
"url": "https://www.goodfellow.com/global/kevlar-49-polyaramid-fibre-group",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Physical, thermal and mechanical properties of the supplied fibre, not a cured woven laminate.",
"license": null,
"review": "Selected numerical facts and explicit source conditions, not a copied supplier table. Publicly indexed manufacturer/supplier data were reviewed; publisher rights are not relicensed. No safety or grade certificate is implied."
},
{
"id": "kuhn-pvc-stock",
"title": "Kuhn PVC stock-material reference",
"url": "https://www.kuhn-marsberg.de/en/product/PVC/",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Density, thermal conductivity and electrical properties; ambiguous hardness-unit and empty cells excluded.",
"license": null,
"review": "Selected numerical facts and explicit source conditions, not a copied supplier table. Publicly indexed manufacturer/supplier data were reviewed; publisher rights are not relicensed. No safety or grade certificate is implied."
},
{
"id": "sika-161-cured",
"title": "Sika Sikafloor 161 HC cured resin reference",
"url": "https://phl.sika.com/en/construction/flooring/industry-wet-areas/epoxy-flooring-system/sikafloor-161-hc.html",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Cured resin hardness at 7 days and strengths at 28 days, +23 C. Mixed-resin density is not cured density.",
"license": null,
"review": "Selected numerical facts and explicit source conditions, not a copied supplier table. Publicly indexed manufacturer/supplier data were reviewed; publisher rights are not relicensed. No safety or grade certificate is implied."
},
{
"id": "masterbond-uv22",
"title": "Master Bond UV22 UV-cured epoxy",
"url": "https://www.masterbond.com/tds/uv22",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Typical properties and cure-specific glass-transition ranges; no numerical outgassing result imported.",
"license": null,
"review": "Selected numerical facts and explicit source conditions, not a copied supplier table. Publicly indexed manufacturer/supplier data were reviewed; publisher rights are not relicensed. No safety or grade certificate is implied."
},
{
"id": "hmg-c71",
"title": "HMG C71 Speedline machinery enamel",
"url": "https://hmgpaint.mybigcommerce.com/c71-speedline/",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Manufacturer product description: temperature resistance up to 170 C, dependent on colour; matt, satin and gloss variants.",
"license": null,
"review": "Selected numerical facts and explicit source conditions, not a copied supplier table. Publicly indexed manufacturer/supplier data were reviewed; publisher rights are not relicensed. No safety or grade certificate is implied."
},
{
"id": "hmg-hammer-stoving",
"title": "HMG Stoving Enamel Hammer TOPSTV",
"url": "https://www.hmgpaint.com/products/all-products/topcoat/1928/stoving-enamel-hammer",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Technical properties: coverage basis at 25 micrometres DFT and temperature resistance 160 C.",
"license": null,
"review": "Selected numerical facts and explicit source conditions, not a copied supplier table. Publicly indexed manufacturer/supplier data were reviewed; publisher rights are not relicensed. No safety or grade certificate is implied."
},
{
"id": "hmg-sc601-clear",
"title": "HMG Acrythane SC601 Clearcoat",
"url": "https://hmgpaint.com/products/all-products/topcoat/1942/acrythane-sc601-clearcoat",
"accessed_on": "2026-10-22",
"kind": "public_reference",
"locator": "Two-component acrylic/isocyanate clearcoat; stated coverage basis 25 micrometres DFT. No cured density or modulus stated.",
"license": null,
"review": "Selected numerical facts and explicit source conditions, not a copied supplier table. Publicly indexed manufacturer/supplier data were reviewed; publisher rights are not relicensed. No safety or grade certificate is implied."
}
]
}
@@ -1,107 +0,0 @@
#!/usr/bin/env python3
"""Run physical-data checks and optional read-only native card admission.
The optional --cli must point to a trusted existing SolidCraft CLI executable.
It only runs material.properties.validate in fresh CLI processes; it never runs
material.create, material.assign, opens a user's document or writes a native design.
Evidence stays under ignored target/. This is not aggregate workspace CI.
"""
from __future__ import annotations
import argparse
import copy
from datetime import datetime, timezone
import hashlib
import json
from pathlib import Path
import subprocess
import sys
import time
import physical
def digest_file(path):
h=hashlib.sha256()
with path.open('rb') as f:
for block in iter(lambda:f.read(1024*1024),b''):h.update(block)
return h.hexdigest()
def snapshot():
paths=[p for p in physical.PACK.rglob('*') if p.is_file() and '__pycache__' not in p.parts]
paths += [physical.ROOT/p for p in ('docs/render-materials/materials.json','assets/render/starter/materials/manifest.json',
'crates/doc/src/physical_material.rs','crates/engine/src/cmd/material_properties.rs','ATTRIBUTION.md')]
return {p.relative_to(physical.ROOT).as_posix():digest_file(p) for p in sorted(paths)}
def invoke(cli,card):
payload=json.dumps({'card':card},ensure_ascii=True,separators=(',',':'),allow_nan=False)
physical.require(len(payload)<65536,'native candidate exceeds invocation budget')
result=subprocess.run([str(cli),'exec','material.properties.validate',payload],cwd=physical.ROOT,
capture_output=True,timeout=30)
physical.require(len(result.stdout)+len(result.stderr)<4*1024*1024,'native output exceeds budget')
if result.returncode:return result,None
return result,json.loads(result.stdout,object_pairs_hook=physical.unique,parse_constant=physical.reject_constant)
def native_check(cli,items):
physical.require(cli.is_file(),'trusted native CLI not found')
identity={'name':cli.name,'sha256':digest_file(cli),'rebuilt_by_this_gate':False}
receipts=[]
for i,item in enumerate(items):
result,value=invoke(cli,item['card'])
physical.require(result.returncode==0 and isinstance(value,dict) and value.get('valid') is True,
f"native card admission failed: {item['profile_id']}: {result.stderr.decode(errors='replace')[-500:]}")
report=value.get('validation',{})
expected=sorted(r['data']['kind'] for r in item['card']['properties'])
physical.require(sorted(report.get('property_kinds',[]))==expected,'native property kinds changed or were dropped')
receipts.append({'profile_id':item['profile_id'],'valid':True,'property_kinds':expected,
'empty_projection':not expected,'source_review_required':report.get('source_review_required'),
'missing_condition_fields':report.get('missing_condition_fields'),
'isotropic_moduli':report.get('isotropic_moduli')})
if (i+1)%12==0:print(json.dumps({'native_cards_checked':i+1,'total':len(items)}),flush=True)
template=next(x['card'] for x in items if x['profile_id']=='steel-c45')
bad=[]
x=copy.deepcopy(template);next(r for r in x['properties'] if r['data']['kind']=='elasticity')['data']['model']['poisson_ratio']=.5;bad.append(('unstable-isotropic',x))
x=copy.deepcopy(template);x['properties'][0]['source_id']='missing';bad.append(('unknown-source',x))
x=copy.deepcopy(template);x['properties'].append(copy.deepcopy(x['properties'][0]));bad.append(('duplicate-property',x))
x=copy.deepcopy(template);next(r for r in x['properties'] if r['data']['kind']=='density')['data']['kg_m3']=-1;bad.append(('negative-density',x))
refused=[]
for label,card in bad:
result,value=invoke(cli,card)
physical.require(result.returncode!=0 or isinstance(value,dict) and value.get('valid') is False,
f'hostile card was accepted: {label}')
refused.append(label)
return {'binary':identity,'admitted_card_envelopes':len(receipts),
'nonempty_cards':sum(not r['empty_projection'] for r in receipts),
'empty_cards_not_ready':sum(r['empty_projection'] for r in receipts),
'hostile_cases_refused':refused,'receipts':receipts,'persistence_tested':False,'body_assignment_tested':False}
def main():
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--cli',type=Path)
args=parser.parse_args();stamp=datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%S.%fZ')
folder=physical.ROOT/'target/physical-material-review'/stamp;folder.mkdir(parents=True,exist_ok=False)
start=time.monotonic();before=snapshot();report={'schema':'solidcraft-physical-check/1','status':'fail','source_inputs':before,
'python':sys.version.split()[0],'full_workspace_ci':'not_run_by_this_gate','runtime_assignment':False}
exit_code=1
try:
result=subprocess.run([sys.executable,'-B','-m','unittest','discover','-s','assets/render/physical/tools','-p','test_*.py'],
cwd=physical.ROOT,capture_output=True,timeout=120)
(folder/'python-tests.stdout.txt').write_bytes(result.stdout);(folder/'python-tests.stderr.txt').write_bytes(result.stderr)
physical.require(result.returncode==0,'physical Python regressions failed')
report['python_tests']=result.stderr.decode(errors='replace')[-1000:]
report['coverage']=physical.validate()
bundle=physical.read(physical.PACK/'exports/native-cards.json')
report['native']=native_check(args.cli,bundle['items']) if args.cli else {'status':'not_requested'}
after=snapshot();physical.require(before==after,'physical/source inputs changed during validation')
report['source_inputs_unchanged']=True;report['status']='pass';exit_code=0
except (OSError,ValueError,TypeError,KeyError,subprocess.SubprocessError) as error:
report['error']=str(error)
report['seconds']=round(time.monotonic()-start,3)
(folder/'report.json').write_bytes(physical.encoded(report))
print(json.dumps({'status':report['status'],'report':str((folder/'report.json').relative_to(physical.ROOT)),
'native_requested':bool(args.cli),'runtime_assignment':False,'error':report.get('error')},indent=2))
return exit_code
if __name__=='__main__':raise SystemExit(main())
@@ -1,178 +0,0 @@
#!/usr/bin/env python3
"""Export chosen reference materials through a trusted card-capable SolidCraft CLI.
A fresh command-line session creates no geometry or body assignments. The native
application owns library-v2 serialization and content revisions. All output stays
in a new review directory under target/. Older scalar-only executables refuse
before any material is created. Public reference data is not design certification.
"""
from __future__ import annotations
import argparse
from datetime import datetime, timezone
import hashlib
import json
from pathlib import Path
import subprocess
import sys
import tempfile
import physical as p
MAX_OUTPUT = 16 * 1024 * 1024
PACK_ID = "tangency.physical-references"
SOURCE_FILES = ("profiles.json", "sources.json", "bindings.json", "quantities.json",
"exports/native-cards.json", "tools/physical.py", "tools/export_library.py")
def input_pins() -> dict[str, str]:
return {name: p.sha(p.PACK/name) for name in SOURCE_FILES}
def selection(ids: list[str] | None, acknowledge: bool) -> dict:
p.require(acknowledge, "Explicit --acknowledge-reference is required")
before = input_pins()
p.validate()
profiles, sources, _ = p.load()
selected, omitted = [], []
names = sorted(profiles) if ids is None else ids
p.require(isinstance(names, list) and 0 < len(names) <= 256, "Select 1..256 profiles")
p.require(all(isinstance(name, str) for name in names) and len(names) == len(set(names)), "Duplicate or invalid selected profile")
for id in names:
p.require(id in profiles, f"Unknown profile: {id}")
native = p.native_card(profiles[id], sources)
density = next((row for row in native['card']['properties'] if row['data']['kind'] == 'density'), None)
if density is None:
omitted.append({'profile_id': id, 'reason': 'No supported point density; no nominal midpoint or unrelated substrate is substituted.'})
continue
selected.append({'profile_id': id, 'material_form': profiles[id]['material_form'],
'command': p.prepare(id, True)['commands'][0],
'extended_property_ids': [row['id'] for row in native['not_projected']]})
p.require(selected, "No selected reference has a supported point density")
p.require(before == input_pins(), "Source revision changed during plan preparation")
return {'schema': 'solidcraft-physical-library-plan/1', 'executed': False,
'reference_selection_required': True, 'assigns_bodies': False,
'id': PACK_ID, 'input_sha256': before, 'materials': selected, 'omitted': omitted,
'preserve_extended_profiles': True,
'required_contract': 'material.create physical_card, material.inspect, solidcraft-library/2'}
def registry_check(registry: object) -> None:
p.require(isinstance(registry, list) and len(registry) <= 4096, "Invalid native command registry")
p.require(all(isinstance(row, dict) and isinstance(row.get('id'), str) for row in registry), "Malformed native command record")
commands = {row['id']: row for row in registry}
p.require(len(commands) == len(registry), "Duplicate command ID")
for name in ('file.new', 'material.create', 'material.inspect', 'library.export', 'library.inspect'):
p.require(name in commands, f"Native command unavailable: {name}")
p.require(all(isinstance(commands[name].get('params'), str) for name in ('material.create', 'library.export')), 'Malformed native parameter documentation')
p.require('physical_card' in commands['material.create'].get('params', ''),
"This executable cannot preserve physical cards; scalar material creation is refused")
p.require('solidcraft-library/2' in commands['library.export'].get('params', ''),
"Native library-v2 export is required; scalar export would lose engineering data")
def invoke(cli: Path, arguments: list[str]) -> object:
# Capture into temporary files, then enforce the JSON byte budget before reading.
with tempfile.TemporaryFile() as out, tempfile.TemporaryFile() as err:
result = subprocess.run([str(cli), *arguments], cwd=p.ROOT, stdout=out, stderr=err, timeout=180)
p.require(out.tell() <= MAX_OUTPUT and err.tell() <= MAX_OUTPUT, "CLI output exceeds the diagnostic budget")
err.seek(0)
p.require(result.returncode == 0, 'Native command failed: ' + err.read(2048).decode(errors='replace'))
out.seek(0)
return json.load(out, object_pairs_hook=p.unique, parse_constant=p.reject_constant)
def verify_catalog(catalog: object, plan: dict) -> None:
p.require(isinstance(catalog, dict) and catalog.get('format') == 'solidcraft-library/2', "Native export did not produce library v2")
p.require(catalog.get('id') == PACK_ID, "Wrong native library identity")
entries = catalog.get('entries')
p.require(isinstance(entries, list) and len(entries) == len(plan['materials']), "Missing or extra native materials")
expected = {row['command']['params']['name']: row['command']['params'] for row in plan['materials']}
found = set()
for entry in entries:
p.require(isinstance(entry, dict), "Invalid native entry")
definition = entry.get('definition', {})
p.require(isinstance(definition, dict) and definition.get('kind') == 'material', "Unexpected appearance entry")
value = definition.get('value', {})
p.require(isinstance(value, dict), "Invalid material definition")
name = value.get('name')
p.require(name in expected and name not in found, "Unknown or duplicate exported material")
found.add(name)
reference = expected[name]
p.require(value.get('density_g_cm3') == reference['density_g_cm3'], "Assigned reference density changed")
p.require(value.get('physical_card') == reference['physical_card'], "A physical card, source or condition was changed or dropped")
p.require(value.get('appearance') is None, "Physical export must not assign a visual appearance")
revision = entry.get('revision', '')
p.require(isinstance(revision, str) and len(revision) == 64 and all(c in '0123456789abcdef' for c in revision), "Invalid native revision")
# Revisions are subsequently verified by the real library.inspect command,
# not a parallel Python approximation of serde's ordering/float formatting.
def library_command(path: Path) -> dict:
return {'command': 'library.export', 'params': {
'path': str(path), 'id': PACK_ID, 'name': 'Physical references - select grade and conditions',
'format': 'solidcraft-library/2', 'overwrite': False,
'provenance': {'license': 'MIT OR Apache-2.0', 'author': 'SolidCraft contributors',
'source_url': 'https://github.com/0xlillith/solidcraft/tree/assets/material-physical-properties/assets/render/physical'}}}
def export(cli: Path, plan: dict) -> dict:
cli = cli.resolve(strict=True)
p.require(cli.is_file(), "Trusted CLI executable not found")
p.require(plan.get("input_sha256") == input_pins(), "Stale physical reference plan")
registry_check(invoke(cli, ['commands']))
# No live app, existing document, source profile, output file or library is touched.
stamp = datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%S.%fZ')
folder = p.ROOT / 'target/physical-library-exports' / stamp
p.require(not folder.exists(), "Review output already exists")
folder.mkdir(parents=True, exist_ok=False)
output, cold = folder/'physical-reference-library.json', folder/'cold-reopened-library.json'
design = folder/'physical-reference-library-design.json'
script = folder/'create.json'
commands = [{'command': 'file.new', 'params': {'name': 'Physical reference library'}}]
commands += [row['command'] for row in plan['materials']]
commands.append(library_command(output))
script.write_bytes(p.encoded({'commands': commands}))
before = input_pins()
p.require(before == plan['input_sha256'], 'Source changed after native preflight')
invoke(cli, ['run', str(script), '--results', '--save', str(design)])
catalog = p.read(output)
verify_catalog(catalog, plan)
inspected = invoke(cli, ['exec', 'library.inspect', json.dumps({'path': str(output)})])
p.require(isinstance(inspected, dict) and inspected.get('catalog') == catalog, "Native library inspection disagrees with its export")
# New process, cold document load and native re-export must preserve every card.
reopen = folder/'reopen.json'
reopen.write_bytes(p.encoded({'commands': [library_command(cold)]}))
invoke(cli, ['run', str(reopen), '--in', str(design), '--results'])
restored = p.read(cold)
verify_catalog(restored, plan)
p.require(restored == catalog, "Cold reopen changed the native material library")
p.require(before == input_pins(), "Source revision changed during library export")
receipt = {'schema': 'solidcraft-physical-library-export-receipt/1', 'status': 'pass',
'cli_sha256': p.sha(cli), 'input_sha256': before, 'library_sha256': p.sha(output),
'material_count': len(plan['materials']), 'omitted': plan['omitted'],
'native_inspection': True, 'cold_reopen': True, 'body_assignment': False,
'engineering_certification': False, 'extended_data_embedded': False,
'library': str(output.relative_to(p.ROOT))}
(folder/'receipt.json').write_bytes(p.encoded(receipt))
return receipt
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--profile', action='append', help='Exact reference ID; default selects all point-density references')
parser.add_argument('--acknowledge-reference', action='store_true')
parser.add_argument('--cli', type=Path, help='Trusted card-capable executable; absent means a non-executing JSON plan only')
args = parser.parse_args()
try:
plan = selection(args.profile, args.acknowledge_reference)
result = export(args.cli, plan) if args.cli else plan
print(json.dumps(result, indent=2, ensure_ascii=True, allow_nan=False))
return 0
except (OSError, ValueError, KeyError, TypeError, subprocess.SubprocessError) as error:
print(json.dumps({'status': 'fail', 'error': str(error)}), file=sys.stderr)
return 1
if __name__ == '__main__':
raise SystemExit(main())
-348
View File
@@ -1,348 +0,0 @@
#!/usr/bin/env python3
"""Validate physical references and prepare non-executing schema-1 material cards.
JSON masters retain provenance, directional data, limits and unsupported quantities.
No CAD document is opened, no body is assigned and no missing constant is invented.
"""
from __future__ import annotations
import argparse
from collections import Counter
import hashlib
import json
import math
from pathlib import Path
import re
import sys
ROOT = Path(__file__).resolve().parents[4]
PACK = ROOT / 'assets/render/physical'
MAX_BYTES = 8 * 1024 * 1024
ID = re.compile(r'[a-z][a-z0-9-]{0,63}\Z')
CORE = {'density','elasticity','tensile_strength','yield_strength','compressive_strength','shear_strength','thermal_conductivity','thermal_expansion','specific_heat'}
PRESSURE = {'young_modulus','shear_modulus','tensile_strength','yield_strength','compressive_strength','shear_strength','flexural_strength','flexural_modulus','compressive_modulus','stress_at_strain','internal_bond_strength'}
TEMPERATURE = {'glass_transition','melting_temperature','heat_deflection','service_temperature','decomposition_temperature'}
DIMENSIONLESS = {'poisson_ratio','elongation_break','relative_permittivity','loss_tangent','water_absorption','hardness_shore_a','hardness_shore_d','hardness_brinell','hardness_knoop','specific_gravity','electrical_conductivity_iacs','refractive_index','porosity','void_ratio','thermal_emittance','solar_absorptance','thickness_swelling'}
CONVERSIONS = {
'kg/m^3':('density',1.,0.), 'g/cm^3':('density',1000.,0.), 'lb/in^3':('density',27679.904710203,0.),
'Pa':('pressure',1.,0.), 'kPa':('pressure',1e3,0.), 'MPa':('pressure',1e6,0.), 'GPa':('pressure',1e9,0.),
'psi':('pressure',6894.757293168,0.), 'ksi':('pressure',6894757.293168,0.),
'1':('ratio',1.,0.), '%':('ratio',.01,0.), 'W/(m K)':('conductivity',1.,0.),
'Btu/(h ft degF)':('conductivity',1.730734666371,0.), '1/K':('expansion',1.,0.),
'ppm/K':('expansion',1e-6,0.), '1/degF':('expansion',1.8,0.),
'J/(kg K)':('heat',1.,0.), 'J/(g K)':('heat',1000.,0.), 'Btu/(lb degF)':('heat',4186.8,0.),
'K':('temperature',1.,0.), 'degC':('temperature',1.,273.15), 'degF':('temperature',5/9,255.3722222222222),
'ohm m':('resistivity',1.,0.), 'ohm cm':('resistivity',.01,0.), 'microohm cm':('resistivity',1e-8,0.), 'ohm mm^2/m':('resistivity',1e-6,0.),
'V/m':('field',1.,0.), 'kV/mm':('field',1e6,0.), 'V/mil':('field',1/.0000254,0.),
'N':('force',1.,0.), 'N/m':('tear',1.,0.), 'pli':('tear',175.1268352465,0.),
'Pa sqrt(m)':('toughness',1.,0.), 'MPa sqrt(m)':('toughness',1e6,0.),
'm':('length',1.,0.), 'micrometre':('length',1e-6,0.), 'mil':('length',.0000254,0.),
'rad':('angle',1.,0.), 'degree':('angle',math.pi/180,0.)}
QUANTITY = {q:('pressure','Pa') for q in PRESSURE}
QUANTITY.update({q:('temperature','K') for q in TEMPERATURE})
QUANTITY.update({q:('ratio','1') for q in DIMENSIONLESS})
QUANTITY.update({'density':('density','kg/m^3'),'grain_density':('density','kg/m^3'),
'thermal_conductivity':('conductivity','W/(m K)'), 'thermal_expansion':('expansion','1/K'),
'specific_heat':('heat','J/(kg K)'), 'volume_resistivity':('resistivity','ohm m'),
'dielectric_strength':('field','V/m'), 'hardness_janka':('force','N'), 'tear_strength':('tear','N/m'),
'fracture_toughness':('toughness','Pa sqrt(m)'), 'mean_particle_size':('length','m'),
'dry_film_thickness':('length','m'), 'angle_of_repose':('angle','rad')})
class Invalid(ValueError):
"""A physical reference violates the reviewed contract."""
def require(ok, message):
if not ok: raise Invalid(message)
def unique(pairs):
out = {}
for key, value in pairs:
require(key not in out, f'duplicate JSON field: {key}')
out[key] = value
return out
def reject_constant(value):
raise Invalid(f'non-finite JSON constant: {value}')
def read(path):
require(path.is_file() and not path.is_symlink(), f'expected regular input: {path.name}')
require(path.stat().st_size <= MAX_BYTES, f'oversized input: {path.name}')
raw = path.read_bytes();require(len(raw) <= MAX_BYTES, 'input grew beyond byte budget')
data = json.loads(raw, object_pairs_hook=unique, parse_constant=reject_constant)
require(isinstance(data, dict), 'expected JSON object')
return data
def text(value, label, limit=2048):
require(isinstance(value,str) and 0 < len(value.encode('utf-8')) <= limit and not any(ord(c)<32 for c in value), f'invalid {label}')
def number(value, low=-1e30, high=1e30):
require(type(value) in (int,float) and low <= value <= high and math.isfinite(value), 'invalid bounded number')
return float(value)
def close(a,b):
return math.isclose(a,b,rel_tol=2e-11,abs_tol=1e-14*max(abs(b),1e-20))
def sha(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
def encoded(value):
return (json.dumps(value,ensure_ascii=True,indent=2,allow_nan=False)+'\n').encode()
def check_conditions(c):
base={'temperature_k','temperature_interval_k','moisture_dry_basis_kg_kg','strain_rate_per_s','state','direction','test_method'}
numeric={'crosshead_speed_mm_min','frequency_hz','specimen_thickness_mm','engineering_strain','heating_rate_k_min',
'test_stress_pa','wavelength_nm','minimum_film_thickness_m','immersion_hours','specimen_mass_kg'}
strings={'environment','thermal_regime','density_basis','excitation'}
require(isinstance(c,dict) and base<=set(c)<=base|numeric|strings|{'lower_thickness_exclusive','thickness_range_mm'},'unknown or missing condition field')
for key in numeric & set(c): number(c[key],0,1e18)
for key in strings & set(c): text(c[key],key,256)
if 'lower_thickness_exclusive' in c: require(type(c['lower_thickness_exclusive']) is bool,'thickness endpoint flag must be Boolean')
if 'thickness_range_mm' in c:
bounds=c['thickness_range_mm']
require(isinstance(bounds,list) and len(bounds)==2 and 0<number(bounds[0])<number(bounds[1]),'invalid specimen thickness range')
if c['temperature_k'] is not None: number(c['temperature_k'],1e-20,20000)
def check_property(p, sources):
require(isinstance(p,dict),'property must be an object')
required={'id','quantity','value','unit','qualifier','source_id','source_locator','original','conditions','note','uncertainty'}
require(required <= set(p) <= required|{'derivation'},'unknown or missing property fields')
text(p['id'],'property ID',128)
q=p['quantity'];require(q in QUANTITY and p['unit']==QUANTITY[q][1],'unknown quantity or wrong SI unit')
require(p['source_id'] in sources,'unresolved property source');text(p['source_locator'],'source locator')
require(p['qualifier'] in {'typical','reported_range','lower_bound','upper_bound','derived'},'invalid qualifier')
orig=p['original'];require(isinstance(orig,dict) and set(orig)=={'value','unit','scale_to_si','offset_to_si'},'invalid original quantity')
require(orig['unit'] in CONVERSIONS,'unknown source unit')
dim,scale,offset=CONVERSIONS[orig['unit']]
require(dim==QUANTITY[q][0],'source quantity has incompatible dimensions')
require(number(orig['scale_to_si'])==scale and number(orig['offset_to_si'])==offset,'conversion recipe altered')
value,raw=p['value'],orig['value'];is_range=isinstance(value,list)
require(is_range==isinstance(raw,list),'range/scalar mismatch')
if is_range:
require(len(value)==len(raw)==2 and p['qualifier']=='reported_range','range needs two endpoints and range qualifier')
require(number(value[0])<=number(value[1]),'reversed range')
else: require(p['qualifier']!='reported_range','scalar cannot claim range')
for v,r in zip(value if is_range else [value],raw if is_range else [raw]):
v,r=number(v),number(r);require(close(v,r*scale+offset),'SI value does not reproduce cited original')
if q not in {'thermal_expansion','poisson_ratio'}: require(v>=0,'negative physical value')
if q in TEMPERATURE|{'density','grain_density','young_modulus','shear_modulus','specific_heat','thermal_conductivity','relative_permittivity','refractive_index'}:require(v>0,'positive physical quantity required')
if q in {'porosity','thermal_emittance','solar_absorptance'}:require(v<=1,'fraction above one')
if q.startswith('hardness_shore_'):require(v<=100,'Shore hardness above 100')
c=p['conditions'];check_conditions(c)
for key in ['temperature_k','moisture_dry_basis_kg_kg','strain_rate_per_s']:
require(key in c,'condition field must be explicit even when unknown')
if c[key] is not None:number(c[key],0,1e12)
if c.get('temperature_interval_k') is not None:
interval=c['temperature_interval_k'];require(isinstance(interval,list) and len(interval)==2,'temperature interval shape')
require(0<number(interval[0])<=number(interval[1])<=20000,'temperature interval bounds')
for key in ('state','direction','test_method'):text(c.get(key),key,1024)
if p['uncertainty'] is not None:
u=p['uncertainty'];require(isinstance(u,dict) and set(u)=={'reported_plus_minus','interpretation'},'invalid uncertainty')
number(u['reported_plus_minus'],0);text(u['interpretation'],'uncertainty interpretation')
require((p['qualifier']=='derived')==('derivation' in p),'derived value needs a calculation receipt')
def check_derivation(record, properties):
d=record['derivation'];require(isinstance(d,dict),'invalid derivation')
require(all(key in properties for key in d.get('input_property_ids',[])),'missing derivation input')
if d.get('formula')=='young_modulus/(2*shear_modulus)-1':
require(record['quantity']=='poisson_ratio','wrong modulus-derived quantity')
e,g=properties['young_modulus'],properties['shear_modulus']
require(e['source_id']==g['source_id']==record['source_id'],'mixed-source elastic derivation')
expected=number(e['value'])/(2*number(g['value'],1e-30))-1
require(-1<expected<.5 and close(record['value'],expected),'incorrect derived Poisson ratio')
elif d.get('formula')=='specific_gravity*1000*(1+moisture_dry_basis_kg_kg)':
require(record['quantity']=='density','wrong moisture-derived quantity')
moisture=number(d.get('moisture_dry_basis_kg_kg'),0,100)
require(d.get('water_reference_density_kg_m3')==1000 and record['conditions']['moisture_dry_basis_kg_kg']==moisture,'moisture-density conditions mismatch')
expected=number(properties['specific_gravity']['value'])*1000*(1+moisture)
require(close(record['value'],expected),'incorrect moisture-derived density')
else:raise Invalid('unreviewed derivation formula')
def validate_data(data, source_data, binding, registry, root=ROOT):
require(data.get('schema')=='solidcraft-physical-reference-profiles/1' and data.get('runtime_assignment') is False,'unknown profile schema or false runtime claim')
require(source_data.get('schema')=='solidcraft-physical-sources/1','unknown source schema')
require(binding.get('schema')=='solidcraft-physical-bindings/1','unknown binding schema')
require(registry.get('units')=={q:u for q,(_,u) in QUANTITY.items()},'quantity registry changed without validator review')
require(registry.get('conversions')=={k:{'scale':v[1],'offset':v[2]} for k,v in CONVERSIONS.items()},'unit conversion registry drift')
require(isinstance(source_data.get('sources'),list) and 1<=len(source_data['sources'])<=256,'source count')
sources={}
for s in source_data['sources']:
require(isinstance(s,dict) and isinstance(s.get('id'),str) and ID.fullmatch(s['id']) is not None and s['id'] not in sources,'invalid/duplicate source')
text(s.get('title'),'source title',256);text(s.get('url'),'source URL')
require(s['url'].startswith('https://') and s.get('kind')=='public_reference','source must identify public HTTPS reference')
require(s.get('license') is None or isinstance(s['license'],str),'invalid source rights metadata')
sources[s['id']]=s
profiles={};require(isinstance(data.get('profiles'),list) and 1<=len(data['profiles'])<=256,'profile count')
for p in data['profiles']:
require(isinstance(p,dict) and isinstance(p.get('id'),str) and ID.fullmatch(p['id']) is not None and p['id'] not in profiles,'invalid/duplicate profile')
require(p.get('family') in {'wood','polymer','glass','metal','other'},'unknown native material family')
require(p.get('source_id') in sources and p.get('certified_design_allowables') is False,'invalid provenance or false certification')
require(type(p.get('isotropic_reference_model')) is bool,'isotropic policy must be Boolean')
text(p.get('designation'),'designation',256);text(p.get('reference_state'),'state',800)
allowed={'id','designation','family','material_form','reference_state','identity_status','isotropic_reference_model','source_id','properties','limitations','certified_design_allowables','quarantined_source_ids'}
require(set(p)<=allowed,'unknown profile field')
require(all(x in sources for x in p.get('quarantined_source_ids',[])),'unresolved quarantined source')
require(isinstance(p.get('properties'),list) and len(p['properties'])<=128,'property list bounds')
seen={}
for r in p['properties']:
check_property(r,sources);require(r['id'] not in seen,'duplicate property ID');seen[r['id']]=r
for r in seen.values():
if r['qualifier']=='derived':check_derivation(r,seen)
profiles[p['id']]=p
expected={'catalog':('docs/render-materials/materials.json',600),'surfaces':('assets/render/starter/materials/manifest.json',48)}
authorities={}
for key,(path,count) in expected.items():
spec=binding.get('inputs',{}).get(key,{})
require(spec.get('path')==path and type(spec.get('count')) is int and spec['count']==count,'authority path/count mismatch')
require(spec.get('sha256')==sha(root/path),'catalog changed: explicit binding review required')
authorities[key]=read(root/path)['items'];require(len(authorities[key])==count,'authority count mismatch')
concepts={c['id']:c for c in authorities['catalog']}
require(len(binding.get('catalog',[]))==600 and len(binding.get('surfaces',[]))==48,'all concepts/surfaces need physical specifications')
checked={}
for b in binding['catalog']:
id=b.get('catalog_id');require(id in concepts and id not in checked and b.get('name')==concepts[id]['name'],'invalid/duplicate concept binding')
require(b.get('auto_assign_body') is False and b.get('preserve_existing_material') is True and b.get('unknown_is_zero') is False,'appearance must not overwrite host physics')
require(b.get('physical_role') in binding.get('role_definitions',{}),'unknown physical role')
require(isinstance(b.get('required_definition_fields'),list) and len(b['required_definition_fields'])>0,'physical requirements cannot be empty')
refs=b.get('references');require(isinstance(refs,list) and len(refs)<=16,'reference list bounds');pairs=set()
for ref in refs:
require(ref.get('profile_id') in profiles,'unresolved physical reference');text(ref.get('relation'),'reference relationship',256)
pair=(ref['profile_id'],ref['relation']);require(pair not in pairs,'duplicate reference');pairs.add(pair)
require(b.get('status')==('reference_selection_required' if refs else 'specific_recipe_required'),'status conflicts with references')
checked[id]=b
surface_ids=set();originals={s['id']:s for s in authorities['surfaces']}
for s in binding['surfaces']:
id=s.get('surface_id');require(id in originals and id not in surface_ids,'invalid surface ID');original=originals[id]
require(s.get('catalog_ids')==original['catalog_ids']==s.get('physical_binding_ids'),'surface physical association drift')
require(s.get('name')==original['name'] and s.get('auto_assign_body') is False and s.get('preserve_existing_material') is True,'surface scope/name mismatch')
first=checked[original['catalog_ids'][0]]
require(s.get('status')==first['status'] and s.get('physical_role')==first['physical_role'],'surface status/role drift');surface_ids.add(id)
return profiles,sources
def load():
data,sources,binding,registry=[read(PACK/name) for name in ('profiles.json','sources.json','bindings.json','quantities.json')]
profiles,source_map=validate_data(data,sources,binding,registry)
return profiles,source_map,binding
def native_condition(p,r):
c=r['conditions'];extra={k:v for k,v in c.items() if k not in {'state','temperature_k','moisture_dry_basis_kg_kg','strain_rate_per_s'} and v is not None}
state=c['state']+' | '+r['qualifier']+' reference; source review required; not design allowables.'
if extra:state+=' | '+json.dumps(extra,sort_keys=True,separators=(',',':'))
if r['uncertainty'] is not None:state+=' | Reported uncertainty retained in extended profile.'
if r['qualifier']=='derived':state+=' | Derived value; formula retained in extended profile.'
require(len(state.encode())<=1024,'native condition overflow')
return {k:c[k] for k in ('temperature_k','moisture_dry_basis_kg_kg','strain_rate_per_s')}|{'state':state}
def native_card(p,sources):
selected=[];used=set();properties=p['properties'];by_q={}
for r in properties:by_q.setdefault(r['quantity'],[]).append(r)
def point(q):
rows=by_q.get(q,[])
if len(rows)!=1:return None
r=rows[0]
return r if type(r['value']) in (int,float) and r['qualifier'] in {'typical','derived'} and r['conditions']['direction'] in {'not_reported','isotropic'} else None
def add(r,data,extra=()):
selected.append({'data':data,'source_id':r['source_id'],'conditions':native_condition(p,r)})
used.add(r['id']);used.update(extra)
rho=point('density')
if rho:add(rho,{'kind':'density','kg_m3':rho['value']})
cp=point('specific_heat')
if cp:add(cp,{'kind':'specific_heat','j_kg_k':cp['value']})
for q in ('tensile_strength','yield_strength','compressive_strength','shear_strength'):
r=point(q)
if r:add(r,{'kind':q,'pa':r['value']})
if p['isotropic_reference_model']:
e,nu=point('young_modulus'),point('poisson_ratio')
if e and nu:
keys=('temperature_k','temperature_interval_k','moisture_dry_basis_kg_kg','strain_rate_per_s','state','direction','environment','thermal_regime','specimen_thickness_mm','heating_rate_k_min')
require(e['source_id']==nu['source_id'] and all(e['conditions'].get(k)==nu['conditions'].get(k) for k in keys),'mixed elastic source/conditions')
require(-1<number(nu['value'])<.5,'unstable isotropic Poisson ratio')
add(e,{'kind':'elasticity','model':{'symmetry':'isotropic','young_modulus_pa':e['value'],'poisson_ratio':nu['value']}},[nu['id']])
selected[-1]['conditions']['state']+=' | Isotropic reference model; not validated part behavior.'
if nu['qualifier']=='derived':selected[-1]['conditions']['state']+=' Poisson ratio derived from same-source E and G, not independently measured.'
for q,field in [('thermal_conductivity','w_m_k'),('thermal_expansion','per_k')]:
r=point(q)
if r:
add(r,{'kind':q,field:[r['value']]*3,'axes':'material123'})
selected[-1]['conditions']['state']+=' | Isotropic reference approximation, not measured directional tensor.'
selected.sort(key=lambda r:r['data']['kind']);source_ids=sorted({r['source_id'] for r in selected} or {p['source_id']})
card={'schema_version':1,'designation':p['designation'],'family':p['family'],
'sources':[{'id':id,'title':sources[id]['title'],'kind':'public_reference','license':sources[id]['license'],'reference':sources[id]['url']} for id in source_ids],
'properties':selected}
return {'profile_id':p['id'],'card':card,
'native_projection_status':'partial_native_card' if selected else 'extended_data_only',
'exported_property_ids':sorted(used),
'not_projected':[{'id':r['id'],'quantity':r['quantity'],'reason':'Retained in extended profile: unsupported native quantity, range/bound, directional measurement or incomplete constitutive model.'} for r in properties if r['id'] not in used],
'missing_native_kinds':sorted(CORE-{r['data']['kind'] for r in selected}),
'reference_selection_required':True,'runtime_assignment':False}
def make_exports():
profiles,sources,bindings=load();cards=[native_card(p,sources) for p in profiles.values()]
pin={name:sha(PACK/name) for name in ('sources.json','profiles.json','quantities.json','bindings.json')}
data={'schema':'solidcraft-physical-card-bundle/1','native_card_schema':1,'native_library_format':False,
'source_sha256':pin,'generator_sha256':sha(Path(__file__)),'runtime_assignment':False,'items':cards}
coverage={'schema':'solidcraft-physical-coverage/1','profile_count':len(profiles),'source_count':len(sources),
'property_records':sum(len(p['properties']) for p in profiles.values()),'catalog_specifications':len(bindings['catalog']),
'concepts_with_reference_candidates':sum(bool(c['references']) for c in bindings['catalog']),
'concepts_needing_specific_recipe':sum(not c['references'] for c in bindings['catalog']),
'surface_specifications':len(bindings['surfaces']),
'surfaces_with_reference_candidates':sum(s['status']=='reference_selection_required' for s in bindings['surfaces']),
'native_property_blocks':sum(len(c['card']['properties']) for c in cards),
'nonempty_native_cards':sum(bool(c['card']['properties']) for c in cards),
'extended_only_profiles':sum(not c['card']['properties'] for c in cards),
'profiles_with_point_density':sum(any(r['data']['kind']=='density' for r in c['card']['properties']) for c in cards),
'quantities':dict(sorted(Counter(r['quantity'] for p in profiles.values() for r in p['properties']).items())),
'runtime_assignment':False,'numerically_complete_catalog_claim':False}
return {'native-cards.json':data,'coverage.json':coverage}
def build():
outputs=make_exports();folder=PACK/'exports';folder.mkdir(exist_ok=True)
for name,data in outputs.items():
path=folder/name;require(not path.is_symlink(),'refusing symlink export');path.write_bytes(encoded(data))
return outputs['coverage.json']
def validate():
outputs=make_exports()
for name,data in outputs.items():
path=PACK/'exports'/name
require(path.is_file() and not path.is_symlink() and path.read_bytes()==encoded(data),f'stale export: {name}; review source changes before rebuilding')
return {'status':'pass',**outputs['coverage.json']}
def resolve(id):
profiles,_,binding=load()
if id.startswith('SURF-'):
row=next((s for s in binding['surfaces'] if s['surface_id']==id),None)
require(row is not None,'unknown surface ID');id=row['catalog_ids'][0]
row=next((c for c in binding['catalog'] if c['catalog_id']==id),None)
require(row is not None,'unknown catalog ID')
return {'binding':row,'profiles':[profiles[r['profile_id']] for r in row['references']],'runtime_assignment':False}
def prepare(id,acknowledge):
require(acknowledge,'--acknowledge-reference is required; these are not certified design allowables')
profiles,sources,_=load();require(id in profiles,'unknown reference profile')
p=profiles[id];result=native_card(p,sources)
densities=[v for v in result['card']['properties'] if v['data']['kind']=='density']
require(len(densities)==1,'no single supported density; select a reviewed point value/grade before material creation')
name='REF '+p['designation'];require(len(name.encode())<=128,'material name too long')
return {'schema':'solidcraft-physical-command-plan/1',
'requires':['material.create must explicitly support physical_card; refuse older command contracts',
'review grade, form, temperature, moisture, process and density basis before execution',
'body assignment is a separate explicit operation; preserve existing appearances'],
'executed':False,'assigns_bodies':False,'profile_id':id,
'commands':[{'command':'material.create','params':{'name':name,'density_g_cm3':densities[0]['data']['kg_m3']/1000,'physical_card':result['card']}}],
'extended_profile':'assets/render/physical/profiles.json#'+id,'limitations':p['limitations'],
'omitted_native_properties':result['not_projected']}
def main():
parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('command',choices=['build','validate','resolve','prepare']);parser.add_argument('id',nargs='?')
parser.add_argument('--acknowledge-reference',action='store_true');args=parser.parse_args()
try:
if args.command=='build':result=build()
elif args.command=='validate':result=validate()
elif args.command=='resolve':result=resolve(args.id or '')
else:result=prepare(args.id or '',args.acknowledge_reference)
print(json.dumps(result,ensure_ascii=True,indent=2,allow_nan=False));return 0
except (ValueError,OSError,TypeError,KeyError,AttributeError,RecursionError) as error:
print(json.dumps({'status':'fail','error':str(error)}),file=sys.stderr);return 1
if __name__=='__main__':
raise SystemExit(main())
@@ -1,121 +0,0 @@
"""Regression checks for explicit, no-loss physical-library export planning."""
import copy
from pathlib import Path
import tempfile
import unittest
from unittest.mock import patch
import export_library as e
import physical as p
def modern_registry():
rows = [{'id': name, 'params': ''} for name in ('file.new', 'material.create', 'material.inspect', 'library.export', 'library.inspect')]
rows[1]['params'] = 'physical_card: schema 1'
rows[3]['params'] = 'format solidcraft-library/2'
return rows
def projected_catalog(plan):
entries = []
for i, row in enumerate(plan['materials']):
params = row['command']['params']
entries.append({'id': e.PACK_ID + '/material-' + str(i), 'revision': 'a'*64,
'definition': {'kind': 'material', 'value': copy.deepcopy(params)}})
return {'format': 'solidcraft-library/2', 'id': e.PACK_ID, 'entries': entries}
class ExportLibraryTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.plan = e.selection(None, True)
def test_plan_covers_every_point_density_without_part_assignment(self):
self.assertEqual(len(self.plan['materials']), 46)
self.assertEqual(len(self.plan['omitted']), 13)
self.assertFalse(self.plan['assigns_bodies'])
self.assertFalse(self.plan['executed'])
self.assertTrue(self.plan['preserve_extended_profiles'])
ids = {x['profile_id'] for x in self.plan['materials']}
omitted = {x['profile_id'] for x in self.plan['omitted']}
self.assertFalse(ids & omitted)
self.assertEqual(len(ids | omitted), 59)
for row in self.plan['materials']:
self.assertEqual(row['command']['command'], 'material.create')
self.assertEqual(set(row['command']['params']), {'name', 'density_g_cm3', 'physical_card'})
def test_acknowledgement_unknown_duplicate_and_empty_selections_refuse(self):
for ids, acknowledge in [(None, False), ([], True), (['unknown'], True), (['steel-c45']*2, True)]:
with self.subTest(ids=ids), self.assertRaises(p.Invalid):
e.selection(ids, acknowledge)
def test_ranges_and_coatings_without_density_are_not_assigned(self):
omitted = {x['profile_id'] for x in self.plan['omitted']}
self.assertTrue({'c71-enamel', 'hmg-stoving-hammer', 'hmg-sc601-clear', 'uv22-coating'} <= omitted)
with self.assertRaises(p.Invalid):
e.selection(['c71-enamel'], True)
plan = e.selection(['pmma-goodfellow-sheet'], True)
params = plan['materials'][0]['command']['params']
self.assertAlmostEqual(params['density_g_cm3'], 1.19)
self.assertNotIn('elasticity', {r['data']['kind'] for r in params['physical_card']['properties']})
def test_old_scalar_registry_refuses_and_modern_contract_is_recognized(self):
e.registry_check(modern_registry())
for field, replacement in [(1, 'density and colour only'), (3, 'scalar library/1')]:
rows = modern_registry()
rows[field]['params'] = replacement
with self.assertRaises(p.Invalid):
e.registry_check(rows)
for value in [None, {}, [None], modern_registry()+[modern_registry()[0]]]:
with self.subTest(value=value), self.assertRaises(p.Invalid):
e.registry_check(value)
def test_catalog_retains_every_native_value_source_and_condition(self):
catalog = projected_catalog(self.plan)
e.verify_catalog(catalog, self.plan)
for mutation in ('drop_card', 'density', 'appearance', 'source', 'state', 'revision', 'duplicate', 'missing'):
value = copy.deepcopy(catalog)
material = value['entries'][0]['definition']['value']
if mutation == 'drop_card':material.pop('physical_card')
elif mutation == 'density':material['density_g_cm3'] *= 2
elif mutation == 'appearance':material['appearance'] = {'name': 'unrequested optical change'}
elif mutation == 'source':material['physical_card']['sources'][0]['reference'] = 'changed'
elif mutation == 'state':material['physical_card']['properties'][0]['conditions']['state'] = 'wrong condition'
elif mutation == 'revision':value['entries'][0]['revision'] = 'not a revision'
elif mutation == 'duplicate':value['entries'][1] = copy.deepcopy(value['entries'][0])
elif mutation == 'missing':value['entries'].pop()
with self.subTest(mutation=mutation), self.assertRaises(p.Invalid):
e.verify_catalog(value, self.plan)
def test_legacy_refusal_does_not_create_output_or_run_mutations(self):
rows = modern_registry()
rows[1]['params'] = 'no card persistence'
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
cli = root/'trusted-cli-fixture'
cli.write_bytes(b'unit test marker; not executable')
with patch.object(p, 'ROOT', root), patch.object(e, 'invoke', return_value=rows) as invoke:
with self.assertRaises(p.Invalid):e.export(cli, self.plan)
invoke.assert_called_once_with(cli.resolve(), ['commands'])
self.assertFalse((root/'target').exists())
def test_stale_plan_refuses_before_executable_invocation(self):
plan = copy.deepcopy(self.plan)
plan['input_sha256']['profiles.json'] = '0'*64
with tempfile.TemporaryDirectory() as temporary:
cli = Path(temporary)/'fixture'
cli.write_bytes(b'fixture')
with patch.object(e, 'invoke') as invoke:
with self.assertRaises(p.Invalid):e.export(cli, plan)
invoke.assert_not_called()
def test_library_command_has_no_overwrite_and_no_body_targets(self):
command = e.library_command(Path('review.json'))
self.assertEqual(command['command'], 'library.export')
self.assertEqual(command['params']['format'], 'solidcraft-library/2')
self.assertIs(command['params']['overwrite'], False)
self.assertNotIn('targets', command['params'])
if __name__ == '__main__':
unittest.main()
@@ -1,277 +0,0 @@
"""Regression tests for reference data, physical scope and loss-aware projection."""
import copy
import json
import math
from pathlib import Path
import tempfile
import unittest
import physical as p
class PhysicalTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.data,cls.sources,cls.bindings,cls.registry=[p.read(p.PACK/name) for name in ('profiles.json','sources.json','bindings.json','quantities.json')]
cls.profiles,cls.source_map=p.validate_data(cls.data,cls.sources,cls.bindings,cls.registry)
def mutated(self,which,change):
args=copy.deepcopy([self.data,self.sources,self.bindings,self.registry]);change(args[which])
with self.assertRaises((p.Invalid,KeyError,TypeError)):p.validate_data(*args)
def row(self,id,quantity):
return next(x for x in self.profiles[id]['properties'] if x['quantity']==quantity)
def test_all_profiles_and_associations_validate(self):
self.assertGreaterEqual(len(self.profiles),51)
self.assertEqual(len(self.bindings['catalog']),600)
self.assertEqual(len(self.bindings['surfaces']),48)
def test_duplicate_json_fields_and_nonfinite_literals_refuse(self):
with self.assertRaises(p.Invalid):json.loads('{"density":1,"density":2}',object_pairs_hook=p.unique)
for value in ('NaN','Infinity','-Infinity'):
with self.assertRaises(p.Invalid):json.loads('{"value":'+value+'}',parse_constant=p.reject_constant)
def test_numeric_boolean_text_infinity_and_huge_integers_refuse(self):
for v in (True,False,'1',float('nan'),float('inf'),10**400,-10**400):
with self.subTest(v=repr(v)[:20]),self.assertRaises(p.Invalid):p.number(v)
def test_units_and_source_arithmetic_are_checked(self):
row=copy.deepcopy(self.row('aluminium-6061-t6','young_modulus'))
for change in (lambda x:x.update(unit='MPa'),lambda x:x.update(value=x['value']/1000),
lambda x:x['original'].update(scale_to_si=1),lambda x:x['original'].update(unit='degC')):
bad=copy.deepcopy(row);change(bad)
with self.assertRaises(p.Invalid):p.check_property(bad,self.source_map)
def test_temperature_conversions_are_affine_not_scalar(self):
r=self.row('epoxy-690','heat_deflection')
self.assertAlmostEqual(r['value'],(115-32)*5/9+273.15,places=9)
self.assertIsNone(r['conditions']['temperature_k'])
self.assertAlmostEqual(self.row('pom-c','glass_transition')['value'],213.15)
def test_electrical_resistivity_and_breakdown_have_distinct_units(self):
self.assertAlmostEqual(self.row('steel-304-plate','volume_resistivity')['value'],74e-8)
self.assertAlmostEqual(self.row('ptfe','dielectric_strength')['value'],80e6)
self.assertAlmostEqual(self.row('silicone-ds20','dielectric_strength')['value'],350/.0000254,delta=1e-4)
def test_ranges_never_become_a_midpoint(self):
result=p.native_card(self.profiles['tpu95-feedstock'],self.source_map)
self.assertFalse(any(r['data']['kind']=='density' for r in result['card']['properties']))
with self.assertRaises(p.Invalid):p.prepare('tpu95-feedstock',True)
def test_reversed_range_and_wrong_range_qualifier_refuse(self):
r=copy.deepcopy(self.row('tpu95-feedstock','density'));r['value'].reverse()
with self.assertRaises(p.Invalid):p.check_property(r,self.source_map)
r=copy.deepcopy(self.row('tpu95-feedstock','density'));r['qualifier']='typical'
with self.assertRaises(p.Invalid):p.check_property(r,self.source_map)
def test_limits_are_not_native_nominal_strengths(self):
result=p.native_card(self.profiles['titanium-grade5'],self.source_map)
kinds={r['data']['kind'] for r in result['card']['properties']}
self.assertNotIn('yield_strength',kinds);self.assertNotIn('tensile_strength',kinds)
def test_missing_poisson_ratio_does_not_invent_elasticity(self):
for id in ('steel-304-plate','peek','pa6-stock','epoxy-690'):
result=p.native_card(self.profiles[id],self.source_map)
self.assertNotIn('elasticity',{r['data']['kind'] for r in result['card']['properties']})
def test_native_steel_c45_analytic_elastic_constants(self):
result=p.native_card(self.profiles['steel-c45'],self.source_map)
model=next(r['data']['model'] for r in result['card']['properties'] if r['data']['kind']=='elasticity')
self.assertEqual(model['young_modulus_pa'],210e9)
self.assertAlmostEqual(model['young_modulus_pa']/(2*(1+model['poisson_ratio'])),80.76923076923077e9)
def test_derived_poisson_is_not_presented_as_measured(self):
result=p.native_card(self.profiles['c11000'],self.source_map)
elastic=next(r for r in result['card']['properties'] if r['data']['kind']=='elasticity')
self.assertIn('derived',elastic['conditions']['state'])
self.assertAlmostEqual(elastic['data']['model']['poisson_ratio'],17000/(2*6400)-1)
def test_derived_value_without_receipt_refuses(self):
r=copy.deepcopy(self.row('c11000','poisson_ratio'));del r['derivation']
with self.assertRaises(p.Invalid):p.check_property(r,self.source_map)
def test_wood_moisture_density_not_dry_mass_specific_gravity(self):
r=self.row('wood-white-oak','density')
self.assertAlmostEqual(r['value'],761.6)
self.assertNotEqual(r['value'],680)
self.assertEqual(r['conditions']['moisture_dry_basis_kg_kg'],.12)
def test_wood_flexure_not_isotropic_young_modulus(self):
result=p.native_card(self.profiles['wood-white-oak'],self.source_map)
self.assertNotIn('elasticity',{r['data']['kind'] for r in result['card']['properties']})
self.assertNotIn('compressive_strength',{r['data']['kind'] for r in result['card']['properties']})
self.assertIn('flexural_modulus',{r['quantity'] for r in result['not_projected']})
def test_print_directions_and_uncertainty_are_preserved(self):
profile=self.profiles['pla-printed']
strengths=[x for x in profile['properties'] if x['quantity']=='tensile_strength']
self.assertEqual({x['conditions']['direction'] for x in strengths},{'XY','Z'})
self.assertTrue(all(x['uncertainty'] is not None for x in strengths))
self.assertNotIn('tensile_strength',{r['data']['kind'] for r in p.native_card(profile,self.source_map)['card']['properties']})
def test_finite_strain_stress_never_becomes_elastic_modulus(self):
profile=self.profiles['silicone-ds20']
self.assertEqual(self.row('silicone-ds20','stress_at_strain')['conditions']['engineering_strain'],1)
self.assertNotIn('young_modulus',{r['quantity'] for r in profile['properties']})
def test_source_conflicts_stay_quarantined(self):
profile=self.profiles['abs-printed']
self.assertTrue(profile['quarantined_source_ids'])
self.assertNotIn('density',{r['quantity'] for r in profile['properties']})
with self.assertRaises(p.Invalid):p.prepare('abs-printed',True)
def test_fr4_frequency_and_cte_regimes_stay_distinct(self):
profile=self.profiles['fr4-370hr']
eps=[r for r in profile['properties'] if r['quantity']=='relative_permittivity']
self.assertEqual(len({r['conditions']['frequency_hz'] for r in eps}),5)
z=[r for r in profile['properties'] if r['quantity']=='thermal_expansion' and r['conditions']['direction']=='Z']
self.assertEqual({r['conditions']['thermal_regime'] for r in z},{'pre-Tg','post-Tg'})
self.assertNotIn('elasticity',{r['data']['kind'] for r in p.native_card(profile,self.source_map)['card']['properties']})
def test_silica_and_sapphire_are_not_the_same_material_model(self):
self.assertTrue(self.profiles['fused-silica']['isotropic_reference_model'])
self.assertFalse(self.profiles['sapphire']['isotropic_reference_model'])
self.assertNotIn('thermal_expansion',{r['data']['kind'] for r in p.native_card(self.profiles['sapphire'],self.source_map)['card']['properties']})
def test_references_do_not_relicense_supplier_publications(self):
self.assertIsNone(self.source_map['isola-370hr']['license'])
self.assertIsNone(self.source_map['schott-borofloat']['license'])
def test_simulant_bulk_and_grain_density_remain_distinct(self):
self.assertEqual(self.row('lms-1','density')['value'],1560)
self.assertEqual(self.row('lms-1','grain_density')['value'],2920)
self.assertIn('NOT measured lunar soil',self.profiles['lms-1']['reference_state'])
def test_coating_preserves_substrate_and_has_no_invented_density(self):
result=p.resolve('SURF-045')
self.assertEqual(result['binding']['physical_role'],'layer')
self.assertTrue(result['binding']['preserve_existing_material'])
self.assertFalse(result['binding']['auto_assign_body'])
with self.assertRaises(p.Invalid):p.prepare('az93-coating',True)
def test_no_preparation_without_explicit_reference_acknowledgement(self):
with self.assertRaises(p.Invalid):p.prepare('steel-c45',False)
result=p.prepare('steel-c45',True)
self.assertFalse(result['executed']);self.assertFalse(result['assigns_bodies'])
self.assertEqual(result['commands'][0]['params']['density_g_cm3'],7.8)
self.assertEqual(result['commands'][0]['command'],'material.create')
self.assertNotIn('appearance',result['commands'][0]['params'])
def test_unresolved_ids_fail_not_fallback_to_generic_material(self):
for id in ('MAT-999','SURF-999','__proto__',''):
with self.assertRaises(p.Invalid):p.resolve(id)
def test_false_ready_certification_and_zero_unknown_claims_refuse(self):
self.mutated(0,lambda d:d.update(runtime_assignment=True))
self.mutated(0,lambda d:d['profiles'][0].update(certified_design_allowables=True))
self.mutated(2,lambda d:d['catalog'][0].update(unknown_is_zero=True))
self.mutated(2,lambda d:d['catalog'][0].update(auto_assign_body=True))
def test_duplicate_profiles_sources_properties_refuse(self):
self.mutated(0,lambda d:d['profiles'].append(d['profiles'][0]))
self.mutated(1,lambda d:d['sources'].append(d['sources'][0]))
self.mutated(0,lambda d:d['profiles'][0]['properties'].append(d['profiles'][0]['properties'][0]))
def test_catalog_surface_drift_and_missing_records_refuse(self):
self.mutated(2,lambda d:d['catalog'].pop())
self.mutated(2,lambda d:d['surfaces'].pop())
self.mutated(2,lambda d:d['surfaces'][0].update(physical_binding_ids=['MAT-003']))
self.mutated(2,lambda d:d['inputs']['catalog'].update(sha256='0'*64))
self.mutated(2,lambda d:d['inputs']['catalog'].update(path='../../secret'))
def test_undefined_property_source_and_profile_refuse(self):
self.mutated(0,lambda d:d['profiles'][0]['properties'][0].update(source_id='absent'))
self.mutated(2,lambda d:d['catalog'][0]['references'][0].update(profile_id='absent'))
def test_nonfinite_frequency_and_unknown_conditions_refuse(self):
r=copy.deepcopy(self.row('fr4-370hr','relative_permittivity'))
r['conditions']['frequency_hz']=float('nan')
with self.assertRaises(p.Invalid):p.check_property(r,self.source_map)
r=copy.deepcopy(self.row('fr4-370hr','relative_permittivity'));r['conditions']['temperature']=20
with self.assertRaises(p.Invalid):p.check_property(r,self.source_map)
def test_negative_and_impossible_fraction_refuse(self):
for id,q,value in [('steel-c45','density',-1),('lms-1','porosity',2)]:
r=copy.deepcopy(self.row(id,q));r['value']=value
r['original']={'value':value,'unit':r['unit'],'scale_to_si':1.,'offset_to_si':0.}
with self.assertRaises(p.Invalid):p.check_property(r,self.source_map)
def test_native_projection_has_bounded_unique_kinds(self):
for profile in self.profiles.values():
card=p.native_card(profile,self.source_map)['card']
self.assertLessEqual(len(card['properties']),16)
kinds=[r['data']['kind'] for r in card['properties']]
self.assertEqual(len(kinds),len(set(kinds)))
self.assertTrue(all(len(r['conditions']['state'].encode())<=1024 for r in card['properties']))
def test_authority_bytes_and_exports_are_deterministic(self):
first=p.make_exports();second=p.make_exports()
self.assertEqual(p.encoded(first),p.encoded(second))
self.assertFalse(first['coverage.json']['numerically_complete_catalog_claim'])
def test_bounded_input_and_array_top_level_refuse(self):
with tempfile.TemporaryDirectory() as folder:
path=Path(folder)/'input.json';path.write_text('[]')
with self.assertRaises(p.Invalid):p.read(path)
with path.open('wb') as stream:stream.truncate(p.MAX_BYTES+1)
with self.assertRaises(p.Invalid):p.read(path)
def test_empty_native_projection_is_explicit_not_reported_ready(self):
for id in ('fr4-370hr','az93-coating','mdf-premier-18'):
result=p.native_card(self.profiles[id],self.source_map)
self.assertEqual(result['native_projection_status'],'extended_data_only')
self.assertFalse(result['card']['properties'])
self.assertTrue(result['not_projected'])
def test_wrong_chemistry_is_not_a_convenient_density_fallback(self):
by_id={row['catalog_id']:row for row in self.bindings['catalog']}
for id,wrong in [('MAT-145','hdpe'),('MAT-248','borofloat-33'),('MAT-439','mdf-premier-18')]:
self.assertNotIn(wrong,{r['profile_id'] for r in by_id[id]['references']})
self.assertNotIn('c11000',{r['profile_id'] for r in by_id['MAT-398']['references']})
def test_no_reference_profile_is_duplicated_with_two_roles(self):
for row in self.bindings['catalog']:
ids=[r['profile_id'] for r in row['references']]
self.assertEqual(len(ids),len(set(ids)),row['catalog_id'])
def test_incomplete_coverage_is_not_reported_numerically_complete(self):
c=p.make_exports()['coverage.json']
self.assertEqual(c['nonempty_native_cards']+c['extended_only_profiles'],c['profile_count'])
self.assertEqual(c['concepts_with_reference_candidates']+c['concepts_needing_specific_recipe'],600)
self.assertFalse(c['numerically_complete_catalog_claim'])
self.assertEqual(c['surfaces_with_reference_candidates'],48)
def test_cured_resin_state_survives_native_projection(self):
result=p.native_card(self.profiles['sikafloor161-cured'],self.source_map)
row=next(r for r in result['card']['properties'] if r['data']['kind']=='compressive_strength')
self.assertIn('28 days',row['conditions']['state'])
self.assertEqual(row['conditions']['temperature_k'],296.15)
self.assertNotIn('density',{r['data']['kind'] for r in result['card']['properties']})
def test_distinct_elastic_conditions_cannot_be_mixed(self):
for field,value in [('state','other temper'),('moisture_dry_basis_kg_kg',.1),('strain_rate_per_s',1.0)]:
profile=copy.deepcopy(self.profiles['steel-c45'])
nu=next(r for r in profile['properties'] if r['quantity']=='poisson_ratio')
nu['conditions'][field]=value
with self.subTest(field=field),self.assertRaises(p.Invalid):
p.native_card(profile,self.source_map)
def test_new_reference_ranges_and_fibre_axes_are_not_flattened(self):
pmma=p.native_card(self.profiles['pmma-goodfellow-sheet'],self.source_map)
self.assertNotIn('elasticity',{r['data']['kind'] for r in pmma['card']['properties']})
fibre=p.native_card(self.profiles['kevlar49-fibre'],self.source_map)
self.assertNotIn('elasticity',{r['data']['kind'] for r in fibre['card']['properties']})
self.assertNotIn('tensile_strength',{r['data']['kind'] for r in fibre['card']['properties']})
self.assertNotIn('thermal_expansion',{r['data']['kind'] for r in fibre['card']['properties']})
def test_named_coating_candidates_do_not_invent_bulk_density(self):
for id in ('c71-enamel','hmg-stoving-hammer','hmg-sc601-clear','uv22-coating'):
profile=self.profiles[id]
self.assertNotIn('density',{r['quantity'] for r in profile['properties']})
with self.assertRaises(p.Invalid):p.prepare(id,True)
def test_para_aramid_not_assigned_as_meta_aramid_honeycomb(self):
row=next(r for r in self.bindings['catalog'] if r['catalog_id']=='MAT-482')
self.assertNotIn('kevlar49-fibre',{r['profile_id'] for r in row['references']})
if __name__=='__main__':unittest.main()
-40
View File
@@ -1,40 +0,0 @@
# Raspberry Pi 5 visual reconstruction
The electronics work uses original SolidCraft geometry to replace the source board's generic port and GPIO envelopes with recognizable packages, connectors and contacts. It is a dimension-informed visual model for close inspection and rendering. It is not a complete electrical BOM, netlist, PCB fabrication file or manufacturer-certified enclosure model.
## Reference geometry
The [Raspberry Pi 5 mechanical drawing](https://datasheets.raspberrypi.com/rpi5/raspberry-pi-5-mechanical-drawing.pdf) gives an approximately **85 × 56 mm** board outline, **58 × 49 mm** mounting-hole spacing, **3.5 mm** offsets to the nearby edges and **2.7 mm** mounting-hole diameter. The manufacturer explicitly describes its dimensions as approximate and leaves some components out of the drawing. These are visual reference dimensions, not manufacturing tolerances.
The existing SELENE PCB uses a **1.6 mm** modeled thickness. Its final placement must be read from the evaluated source: the source timeline contains later translations, so the early sketch plane is not the assembled board elevation. The component generator preserves the original mounting position and documents its exact world-coordinate origin.
The [official GPIO documentation](https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#gpio-and-the-40-pin-header) establishes a **2.54 mm** pitch. The visual header therefore uses two rows of 20 individually modeled pins, with a separate dark carrier and visible solder/pad details.
## Component identity and connector inventory
The manufacturer's [Pi 5 introduction](https://www.raspberrypi.com/news/introducing-raspberry-pi-5/) identifies the BCM2712 application processor, LPDDR4X memory, RP1 I/O controller, DA9091 power-management IC, CYW43455 wireless chip and BCM54213 Ethernet PHY. The PHY is visibly rotated 45 degrees. These identities guide named geometry rather than copied chip artwork or logos.
The [current official board documentation](https://www.raspberrypi.com/documentation/computers/sbcs.html#raspberry-pi-5-ports) provides the connector inventory:
| Interface | Visual modeling target |
|---|---|
| USB-A | Two stacked USB 2.0 ports with black tongues, two USB 3.0 ports with blue tongues; hollow metal shells and visible contacts. |
| Ethernet | RJ45 cavity, eight contact representations and two LED windows. |
| GPIO | 40 pins in two rows of 20. |
| Display output | Two micro-HDMI connector shells and contact representations. |
| Power | USB-C shell and tongue. |
| Camera/display | Two 22-contact MIPI FFC connectors at 0.5 mm pitch. |
| PCIe | One 16-contact FFC connector at 0.5 mm pitch. |
| Auxiliary headers | Four-pin fan, three-pin UART, two-pin RTC and four-pin PoE. |
| Storage | Underside microSD cage and contact representations. |
| Board details | Power button, status LED, shields, crystals, inductors, representative passives and plated holes. |
The [manufacturer's PCIe connector specification](https://datasheets.raspberrypi.com/pcie/pcie-connector-standard.pdf) independently confirms the 16-contact, 0.5 mm-pitch interface. Detailed solder terminations, internal contact spring shapes and production-specific component populations remain simplified.
## Rendering and assembly contract
A separate component groups the new electronics and attaches rigidly to the original avionics assembly. Ports have openings, tongues and contacts; separate materials distinguish solder mask, package epoxy, ceramic, connector polymer, shields, heat spreaders, solder and gold-colored plating. Appearance values are illustrative PBR controls. A bulk Brass material on a gold-colored contact is a density proxy, not a claim that the contact is solid gold.
Exact body counts, removed placeholder names, generator hash and native validation results belong in the generated build evidence. No count or completed validation result is inferred from this reference document. Before shipping, check the evaluated component position, mounting-hole and header spacing, representative connector cavities, the rigid attachment and a close-up native render.
All added geometry and documentation are original. No Raspberry Pi trademark artwork, manufacturer CAD, PCB texture or photograph is distributed with the model.
-137
View File
@@ -1,137 +0,0 @@
# SELENE mechanism design and verification contract
The improved native source already contains assembly structure, solar-actuator loops, wheel links and a tour. The functional derivative builds on those mechanisms through SolidCraft commands. This document distinguishes inspected source facts from reconstruction targets and the numeric checks needed to accept the result. It does not certify an unexecuted generator or replace native validation evidence.
## Source assembly
Read-only inspection of the pinned source in [SOURCE.md](SOURCE.md) confirms 49 joints, nine motion links and the 120-step `SELENE tour`. Joint IDs below belong to that source and must be resolved again if a derivative deletes and recreates joints.
| Source mechanism | Native source identity | Relation |
|---|---|---|
| Wheel train | `Wheel axis -1:-90` through `Wheel axis 1:90`, IDs 1–6 | Five links, ratio +1 between neighboring wheel joints. |
| Solar wings | Port/starboard tilt, IDs 9–10 | Mirrored rotation, ratio −1. |
| Solar actuators | Four sets of root pin, stroke and wing pin, IDs 18–29 | Each rod follows a closed loop; slider limits −6 to +0.4 mm. |
| Sensor and dish yaw | Turret ID 7, dish ID 16 | Source demonstrator gear, radii 24:36, ratio −2/3. |
| Moving demonstration track | `Track slide`, ID 48 | Source rack coupling to wheel ID 1, nominal radius 38 mm, reversed. |
| Vial cap | `Vial cap thread`, ID 49 | Cylindrical joint; 1.5 mm axial travel per full turn; slide limits 0–6 mm. |
The source turret elevation is saved at −12 degrees. New bearing geometry and motion links must be constructed in a deliberately restored common neutral pose; mixing that saved pose with zero-angle link offsets can rotate new parts unexpectedly.
## Reconstruction targets
The individual component scripts and the executed derivative are the authority for implementation. The following bounds are deliberately narrower than the source's broad demonstration ranges. They define an inspection envelope to be checked, not a proof of global collision freedom.
| Driver | Intended limits, degrees | Purpose |
|---|---:|---|
| Sensor azimuth | −135 to +135 | Independent pointing with a bounded cable envelope. |
| Sensor elevation | −25 to +45 | Controlled sensor-head tilt. |
| Dish azimuth | −120 to +120 | Independent dish pointing. |
| Dish elevation | −35 to +40 | Controlled dish tilt. |
| Arm azimuth | −45 to +45 | Keep a conservative showcase sweep. |
| Shoulder | −35 to +15 | Restrict travel around the modeled clevis. |
| Elbow | −30 to +55 | Articulate the rebuilt link stack. |
| Wrist and tool tilt | −45 to +45 each | Bound the local tool envelope. |
| Port solar wing | 0 to +30 | Remain within the inherited actuator stroke. |
| Starboard solar wing | −30 to 0 | Mirror the port driver. |
| Gripper jaw driver | −15 to +18 | Move two independently mounted jaws with opposite signs. |
| Suspension arm | −4 to +1.5 | Short-travel closed-loop damper demonstration. |
Remove the source's illustrative sensor-to-dish gear link so either assembly can point without moving the other. Transfer wheel shafts, flanges, hubs and retaining hardware to the parts they physically rotate with. The track remains an optional visualization helper. A 40 mm nominal coupling may match the grouser envelope, but it does not solve discrete grouser contact, slip or rover traction.
The gripper requires separate jaw components, concentric pivot sleeves, opposite revolute motion and contact pads. A revolute joint by itself is insufficient if the jaw is still part of the fixed gripper body. The geometric envelope needs a sweep check independently of the linked angle values.
## Rolling-element bearings
The bearing reconstruction separates the outer race, inner race, cage and each ball. The outer race follows its housing; the inner race follows the shaft. The cage revolves around the shaft axis, and each ball spins relative to the cage while its center orbits with it. Grooved races and spaced cage bridges provide a recognizable section and visible rolling elements.
Let **R** be pitch radius, **r** ball radius, and **θ** the shaft angle relative to the housing. For radial contact and an ideal no-slip bearing with a fixed outer ring:
```text
cage angle relative to housing = 0.5 × (1 − r/R) × θ
ball spin relative to cage = −0.5 × (R/r − r/R) × θ
ball spin relative to housing = −(R/r) × cage angle
```
The sign assumes all revolute axes use the same positive direction. The ball's CAD joint is relative to the cage; substituting an absolute spin rate there produces the wrong motion. These relations follow the cage and rolling-element frequencies in [Schaeffler, An Overview of Bearing Vibration Analysis, section 3](https://www.schaeffler.com/remotemedien/media/_shared_media/08_media_library/01_publications/schaeffler_2/technicalpaper_1/download_1/vibration_analysis_en_en.pdf), specialized to zero contact angle.
The design uses visual clearance between ideal raceway surfaces and balls. It does not solve Hertzian contact, lubrication, cage slip, preload, bearing life or load ratings. The equations drive a kinematic demonstration; no ball-to-race contact force is inferred.
For every bearing, validate a neutral pose, an interior nonzero shaft angle and its return to neutral. Compare both cage angle and relative ball spin with the equations. Also compare a ball's center of mass with its expected orbit; angle labels alone cannot prove that the component frame is correct. The spherical appearance of a ball makes spin visually ambiguous, so the joint readback is part of the evidence.
## Plain yaw supports
[yaw_supports.py](tools/yaw_supports.py) replaces the three source stacks of solid yaw disks with plain journal and thrust geometry. The fixed housing, rotating disk, existing component ownership and joint frames remain in place. Each housing receives a flanged brass bushing; its rotating disk gains an integral hollow steel journal. A stationary retaining pin captures the rotor inside a recess below the unchanged mast or clevis mounting plane.
| Support | Fixed housing / rotor diameter, mm | Steel journal diameter, mm | Brass bore diameter, mm | Retaining pin diameter, mm |
|---|---:|---:|---:|---:|
| Arm | 42 / 36 | 12 | 12.2 | 5 |
| Dish | 20 / 18 | 6 | 6.2 | 2.4 |
| Sensor turret | 28 / 26 | 8 | 8.2 | 3.6 |
The flanged thrust seats are 0.3 mm thick, with nominal seated contact against the rotating disks. Journal radial clearance is 0.1 mm; the retaining pins clear the hollow journals by 0.15 mm radially. Recessed heads provide 0.1 mm axial retention play. Four local reliefs also remove the dish housing's approximately 21.33 mm³ overlap with each original mounting head. This stage adds six bodies and no components or joints. Retainer threads use nominal cylindrical seats; fit classes, preload, friction, wear and structural capacity require separate engineering work.
The official 0.18.2 reduced native fixture reproduced the actual support and neighboring mast/clevis envelopes. Its 101 commands produced 20 bodies and 67 features without timeline errors or warnings. All 44 scoped interference checks, six source-range endpoint drives, nine analytic volume checks and twelve seating/separation measurements passed; each rebuilt journal, bushing and retainer is one connected shell. The largest volume discrepancy was 0.0156%, within the stated 0.02% numerical tolerance. Native section and exterior views were inspected. This fixture evidence does not replace final integration validation; the derivative's narrower pointing limits above remain unchanged.
## Closed-loop checks
### Solar actuators
For each actuator, rotate the tip point **T** about the wing hinge **H** and compute:
```text
stroke(θ) = length(T(θ) − R) − length(T(0) − R)
```
Here **R** is the fixed root pin. The source port hinge is at `(0, −140, 158)`, the root at `(x, −91, 134)`, and the tip at `(x, −180.40678269379407, 169.70686616300375)`, with `x = −62` or `+62`. Positive port tilt rotates about +X. At **20 degrees**, the expected stroke is approximately **−2.8662098186 mm**. The starboard wing has mirrored geometry and a negative drive angle. Verify all four sliders, not only one.
The [SolidCraft mechanism reference](https://github.com/tangency-labs/solidcraft/blob/ac1768e08b83ed33212459951f5b50aa7a57cddc/skills/solidcraft-mechanisms/reference.md) documents this closed-loop pattern, `flip: true` for co-directed frames and comparison with independent length calculations. Do not key the root pins, sliders or wing pins in a study; they are followers.
### Suspension dampers
The short-travel reconstruction gives each drive carrier its own hinge and splits each damper into barrel and rod. With the chosen neutral frame, the root and tip separation is 22 mm. Its analytic stroke relation is:
```text
stroke(θ) = hypot(35 cos θ − 20 sin θ − 35,
−35 sin θ − 20 cos θ − 2) − 22
```
Angles in this formula are radians. Expected strokes are **−2.4462732115 mm at −4 degrees** and **+0.9155981856 mm at +1.5 degrees**. Validate every closed loop against its actual reconstructed world points before using these numbers. Solid coil springs and rigid motor wires do not deform; hide them in moving inspection profiles unless a later animation model replaces them.
The suspension generator retains the original `Damper piston …` and `Suspension bearing …` body labels to avoid costly late rebuilds of the source history. The piston label also names the new rod component; the pivot ring functions as a brass plain bushing and the carrier ring as a fixed spacer. Those retained labels do not alter the geometry or joint behavior.
### Screw cap
The expected axial displacement is `1.5 × angle_degrees / 360` mm. A 360-degree drive gives 1.5 mm; 1,440 degrees gives 6 mm. The angular and axial limits should describe the same four-turn travel. This is an ideal screw coupling; modeled thread surface contact and sealing compression require separate work.
## Native acceptance
Use the model's own saved studies and exact joint names. Save the final derivative with all independent drivers at the agreed neutral pose. A study should return its drivers to neutral so opening another study starts predictably.
The pinned authoring executor is the official v0.18.2 CLI described in [SOURCE.md](SOURCE.md), producing format 28 for compatibility with the personal render branch. Once the master is saved and its bytes are fixed, run the independent validator from the repository root:
```sh
PYTHONDONTWRITEBYTECODE=1 python3 assets/render/selene/tools/validate_native.py \
--cli /path/to/solidcraft-cli \
--model assets/render/selene/models/SELENE-functional.solidcraft \
--out target/selene/validation-final \
--interference-pairs assets/render/selene/motion/interference-pairs.json
```
Use a new output directory for every run. The default suite performs 25 sampled driven cases and a final neutral check. Before each case it reads all independent joint coordinates, resets those that are nonzero, and verifies every primary coordinate is neutral. It compares actual native joint readbacks and world-coordinate centers of mass, retaining requests, responses, hashes, tolerances and failures. It drives an in-memory copy and never saves test poses to the master. The input model and pinned original must have identical hashes before and after validation.
Run full-model native processes serially. The validator confines its native subprocess to two Rayon workers and sets one glibc allocation arena with 64 KiB mmap and trim thresholds. These resource settings are recorded in the report alongside the unchanged geometric tolerances.
Optional `--interference-pairs path/to/pairs.json` accepts a JSON array of exact body-name pairs. The committed [selection](motion/interference-pairs.json) requests seven neutral-pose checks for opposing arm links, the tool wrist, gripper jaws and PCB connector clearances. Each pair receives its own bounded native interference check. The result records whether conservative broad-phase exclusion or exact Boolean intersection resolved it. Missing bodies, skipped or unchecked pairs, truncation and budget exhaustion remain failures or incomplete checks. A request to run the suite is not a passing result; report the returned status and evidence path explicitly.
The minimum evidence for each accepted mechanism is:
- A native timeline with no unreviewed errors or geometry warnings.
- A grounded assembly chain and a clean joint graph; no unexplained floating mechanism, loop conflict or coupling error.
- Actual movement of the intended occurrence, with positions or centers of mass checked before and after.
- Numeric follower agreement at selected interior and endpoint values, with native degrees/mm distinguished from persisted radians/mm.
- Scoped interference checks between parts that must clear each other. Timeouts and unchecked pairs remain incomplete checks, never passes.
- A native view or render showing the actual geometry. A diagram or an inferred body count cannot substitute for this review.
Broad all-body pair checks are expensive and can confuse deliberate mating contact with penetration. Check the relevant opposing link plates, clevis/race seats, shaft/bore and gripper contacts explicitly. A few passing samples do not prove clearance across every possible combination of joint angles.
Lunar gravity, loads, motor torque, thermal behavior, flexible wiring, suspension spring dynamics, granular terrain and autonomous sample capture remain separate simulation and animation work.
-89
View File
@@ -1,89 +0,0 @@
# SELENE functional showcase
This package develops the **author-supplied improved SELENE** into an editable native model for close-up rendering and subsequent mechanism animation. The input is SychPL's `SELENE-showcase.solidcraft`, matched byte for byte to his published file. The original is retained unchanged; the enhanced derivative is [models/SELENE-functional.solidcraft](models/SELENE-functional.solidcraft).
Open the derivative in the personal SolidCraft render branch. The model contains the geometry, component ownership, joints, motion links, physical-material proxies, appearances, browser groups, saved views and inspection studies. Python is only a development tool for reproducing the changes; opening and using the design does not require Python.
![SELENE functional showcase in native Design shading](exports/SELENE-overview.png)
*Actual native Design preview of the neutral master. Close-ups and the product-render executor's current limits are documented in [RENDER.md](RENDER.md).*
## What changed
| Area | Native implementation |
|---|---|
| Rolling-element bearings | Ten open radial bearings at the arm and sensor head. Each has grooved steel races, eight individually represented balls and a brass cage. The inner race follows the shaft, the outer race follows the housing, and linked revolutes drive cage orbit and relative ball spin. |
| Arm joints | Revised plate spacing, bored seats, stepped clamp hubs, shafts, spacers, locking screws and a machined wrist tongue. Rigid decorative hydraulic cylinders that would detach during movement are removed. |
| Gripper | Two separate moving jaw components, concentric brass plain sleeves, opposite revolute motion and elastomer contact pads. Its commanded range is −15° to +18° at the driver. |
| Suspension | Six hinged trailing carriers with reinforcing gussets. Separate hollow damper barrels and rods form six closed revolute–slider–revolute loops. Local support reliefs clear the moving eyes and arms over the bounded travel. |
| Drivetrain | Wheel shafts, hubs, flanges and retaining screws belong to the rotating wheels. Wheel joints attach to the moving carriers. The optional demonstration track uses a 40 mm nominal rolling radius. |
| Yaw supports and pointing | Three solid-disk placeholders become journal supports with flanged brass bushings, hollow rotating journals and recessed axial retaining pins. Sensor and dish pointing are independent; the illustrative gear coupling is removed. |
| Raspberry Pi 5 | A board with rounded corners, mounting holes, plated pads and vias, plus 491 new bodies for packages, shields, passives, headers, connector housings, contacts, solder details and mounting tabs. Hollow ports replace solid envelopes. |
| Materials and appearance | Retained orange/charcoal identity with distinct metals, polymer, elastomer, FR4, silicon and optical surfaces. Opaque bodies carry PBR values; physical density proxies are explicitly separate from optical properties. |
| Sample laboratory | Each glass vial is one hollow native solid with an integral bottom, retaining its original external dimensions. The existing screw-driven cap retains 1.5 mm travel per revolution. The source rock is grounded as demonstration scenery. |
| Inspection | Body groups by function, ten named views with render setup records, seven focused motion studies, and an adapted copy of the author's original tour. |
The native build and independent acceptance run have separate evidence. See [VALIDATION.md](VALIDATION.md) for the final hash, actual counts, executor, completed checks and remaining limitations. [MECHANISMS.md](MECHANISMS.md) gives the joint conventions and independent equations; [ELECTRONICS.md](ELECTRONICS.md) explains the board reconstruction and its fidelity boundaries.
## Using the model
The file opens in the neutral pose: all independent drivers are at zero, including sensor elevation. Saved views are named `SELENE - Whole rover`, `Electronics`, `Shoulder bearing`, `Elbow bearing`, `Gripper`, `Sensor head`, `Sample laboratory`, `Drivetrain`, `Suspension` and `Solar cells`, each with the same `SELENE - ` prefix.
Use the seven inspection studies for drivetrain, solar deployment, arm articulation, gripper, independent sensor pointing, sample cap and suspension. They return their independent drivers to neutral. The inherited tour is named **`SELENE - Legacy author tour (adapted limits)`**; it preserves the author's keyed motion while resolving recreated wheel joints and clamping the selected limits. A keyed study is an authoring resource, not evidence that every frame of every combined pose has been cleared.
For suspension motion, hide the six rigid spring solids and twelve motor leads. They remain useful neutral-pose render details but do not deform. The `motion` visibility profile in [tools/presentation.py](tools/presentation.py) performs this selection and also hides the optional track and source flexible-hose display geometry. Export or save inspection poses to a separate file; keep the master neutral.
The source cap has an ideal screw relation. The six complete vials remain in the sample laboratory assembly. Independent grasp, release, free flight and capture are future work; the current cap motion does not implement those events.
## Reproduce the native derivative
Use Python 3.11+ and the **official SolidCraft v0.18.2 CLI** identified in [SOURCE.md](SOURCE.md). That executor writes `solidcraft/28`, readable by the personal render branch's format-29 reader. The newer official v0.19.0 writes format 30 and is unsuitable as a drop-in authoring replacement for that branch. No format marker is edited to bypass compatibility.
From the repository root:
```sh
PYTHONDONTWRITEBYTECODE=1 python3 assets/render/selene/tools/build.py \
--cli /path/to/solidcraft-cli \
--work target/selene/rebuild \
--output target/selene/SELENE-functional.solidcraft
```
The builder checks the pinned original, evaluates it, then uses only native commands. Stages run in fresh processes so the large model's undo history remains bounded. Each stage retains its command script, full native results, timing trace and saved checkpoint under the selected work directory. Every checkpoint must have a clean feature timeline, unique live body names and a clean joint graph. The build history records hashes of the source, executor, recipes, results and checkpoints.
Resume an interrupted build by repeating the command with `--resume`. The builder rejects changed recipes, executables, results or checkpoints. `--until` stops at a named stage for inspection. Inspect failures in their logs; use a separate work directory when changing an already completed stage.
Run independent acceptance on the saved derivative:
```sh
PYTHONDONTWRITEBYTECODE=1 python3 assets/render/selene/tools/validate_native.py \
--cli /path/to/solidcraft-cli \
--model target/selene/SELENE-functional.solidcraft \
--interference-pairs assets/render/selene/motion/interference-pairs.json \
--out target/selene/acceptance
```
This drives an in-memory copy and compares native joint readbacks and occurrence centers of mass with independent calculations. It never saves test poses over the master. Optional exact-name interference pairs are bounded and report unchecked pairs as incomplete. Detailed execution evidence stays in `target/`; production native files and English documentation are the shipped resources.
## Render preparation and the wider catalog
The full-detail [native 4K studio render](RENDER.md#full-detail-4k-path-traced-studio-image-2026-10-22) now provides a path-traced showcase of the unchanged functional master. It preserves all original visible geometry, uses explicit glass and authored materials, and ships with its exact [request/provenance manifest](scenes/studio-hero.json). The Design previews remain distinct geometry-inspection resources.
The ten saved camera/setup records and the model's body appearances are concrete native resources. [RENDER.md](RENDER.md) documents the actual image workflow and any executor limits. Transparent viewport appearances alone do not establish physical refraction, and density does not supply roughness, friction, electrical behavior or optical transmission.
The existing [48-appearance pack](materials/appearance-pack.json), [33 semantic roles](scenes/roles.json), [ten stage specifications](scenes/stages.json), [12 shots](scenes/shots.json), [UI flow](ui/demo-flow.json) and [future sample dynamics sequence](motion/sample-sequence.json) remain a broader authoring specification. The source is now pinned; those resources still need their individual geometry bindings and renderer admission. The catalog's `runtime_ready: false` status is intentional. No lunar terrain, dust dynamics, calibrated optical library or throw-and-catch simulation is implied by the native model's completion.
```sh
python3 docs/render-materials/validate_catalog.py
python3 assets/render/selene/tools/validate_prepared.py
cargo xtask ci
```
The Python checks validate prepared data and the source contract. They do not replace native geometry, mechanism or visual checks. Repository-wide gate status is recorded separately in the validation report.
## Provenance and scope
[SOURCE.md](SOURCE.md) and [source-lock.json](source-lock.json) pin the exact source bytes and author history. The original rover is credited to 0xlillith, the improved showcase assembly to SychPL, and the emblem to SolidCraft contributors. Ubuntu Light lettering retains its [Ubuntu Font Licence notice](licenses/UFL.txt). The source branch has no separate file-specific license declaration for the showcase additions; that provenance distinction is preserved.
New geometry, procedural scripts and documentation are original contributions under MIT OR Apache-2.0. Exact non-Markdown asset rows are in [ATTRIBUTION.md](../../../ATTRIBUTION.md). Manufacturer CAD, PCB artwork, logos and netlists are not embedded.
The model demonstrates geometric detail and ideal kinematics. It is not a manufacturing release, a complete electrical design, a load-rated bearing selection or a physics simulation. Joint limits are a documented inspection envelope; sampled checks do not certify every simultaneous arm, solar and suspension configuration.
-124
View File
@@ -1,124 +0,0 @@
# SELENE native rendering and reproducible studio showcase
## Full-detail 4K path-traced studio image (2026-10-22)
![SELENE native studio render](exports/SELENE-studio-4k.png)
The selected [3840 x 2400 PNG](exports/SELENE-studio-4k.png) is a completed native SolidCraft GPU-path render of the latest functional master, not a Design screenshot, a generated illustration, a painted geometry replacement or an upscaled preview. The exact request, original resource hashes, editable source and executable identities are retained in [studio-hero.json](scenes/studio-hero.json).
| Property | Verified value |
|---|---|
| Editable authority | `models/SELENE-functional.solidcraft`, original bytes unchanged |
| Functional source commit | `cf8e651022deee528b26c303fde58bf3eb8a22a5` |
| Native model SHA-256 | `3bd2bfa1c6336394c4a4208d30b52c80d06658a3bd7ea57cc93b774411d1bd68` |
| Evaluated native bodies | 2,116; zero timeline errors or warnings |
| Original visible subject | 2,094 bodies; 2,144,099 triangles |
| Separate render-only stage | One passive ground rectangle; 2 triangles |
| Optical bodies | 12 explicitly selected closed dielectric bodies; topology and medium-overlap checks retained |
| Authored surface detail | 48 exact body bindings across six original surface families |
| Output | 3840 x 2400, RGBA8 PNG, sRGB |
| Sampling | 1,024 samples per pixel; maximum path depth 16; seed 21026 |
| Presentation | ACES-fitted tone mapping and edge-aware a-trous denoising |
| Lighting | Original RIG-002 rectangular emitters, explicit 1/16 creative radiance gain, original HDR-002 fill at intensity 0.12 |
| Camera | Perspective fit to all captured body bounding boxes; 8% frame margin |
| Native executable source | `895704a2fdc96dabb9a2afb2e548a8896e4b1e79` |
| Native executable SHA-256 | `3421d538c571647c1f24fd74d63487c4ba4ab597980cfeea111a01c0db94e699` |
| Completed PNG SHA-256 | `40a492e7979137d0f4027300da3d8af7ca1008c1aab2c309259e65d309a17316` |
| PNG length | 2,388,114 bytes; chunk CRCs, bounded scanlines and final-frame hash verified |
The image uses the original saved visibility and neutral pose. There is no mesh simplification, geometry removal, hidden resolution downgrade, engineering-material reassignment or software-device fallback. The native job's logical admission estimate was 2,814,770,732 bytes; this is not a peak-RSS or GPU-memory measurement. Hardware-only policy and Vulkan were enforced on Pegasus; the hardware survey identifies its AMD Radeon 8060S.
### Reproduction
Build the native CLI once, then reuse the loaded model and device:
```sh
cargo build --locked --profile ci -p solidcraft-cli --bin solidcraft-cli
python -i tools/render-library/selene_studio.py --cli /path/to/solidcraft-cli --out /new/output/directory
```
In the resulting interactive studio:
```python
from selene_recipe import hero_request
request = hero_request(studio, width=3840, height=2400, samples=1024)
receipt = studio.render("selene-studio-4k", request, timeout=7200)
studio.close()
```
The helper checks the pinned master and binary, public-command preparation, resource hashes, actual executor, exact requested dimensions and exported frame bytes. The frame is not automatically declared visually accepted. The original camera-fit/publication tools and seven independent Python tests are under `tools/render-library/`; provisional images, native-pixel crops and execution logs stay outside the shipped resource collection.
### Quality and scope
The complete image and four unscaled detail crops were visually reviewed: subject and gripper framing, mechanical edges, electronic connectors, glass and highlights remain readable. Residual Monte Carlo grain is visible at native pixel scale in some vial/lens regions; the current denoiser deliberately does not reconstruct those transmission paths. This is a high-quality native showcase, not proof of zero residual noise or a completed maximum-quality renderer roadmap.
RGB absorption and material recipes are illustrative authored optical choices, not measured glass spectra or engineering properties. The explicit rig gain is a creative lighting adjustment, not an unchanged photometric calibration. Normal maps alter shading, not CAD geometry. LED diffusers are transmitted surfaces, not validated volumetric/mesh emitters. The static image does not establish collision-free motion, contact physics, lunar dust or dynamics.
Native renderer tests passed 457/457. The hardware GPU suite passed 95 tests with three explicitly ignored tests, and the official 12-crate library/web WASM check passed. Whole-workspace type checking, strict lint, resource/persistence command checks and camera/publication tests have their own evidence. Interactive GUI acceptance and whole-project CI are not inferred from this image. The focused Render-workspace harness also passed 58 tests with no failures; its click-stability repair and remaining whole-project acceptance scope are recorded in the batch ledger and PR #7.
## Historical Design previews and earlier CPU-product evidence
The sections below preserve the earlier `10ada1b4` CPU-product/Design observations. Their resource ceilings describe that legacy raster executor, **not** the current native GPU path or the explicit larger CPU raytrace profiles. The whole-master GPU limitation described in that historical workflow is now resolved by the completed image above.
The enhanced native master is the editable authority. Its ten named camera and render-setup records make close inspections reproducible. Body appearances distinguish metals, painted structure, elastomer, FR4, packages, contacts and optical surfaces. The orange and charcoal identity is retained.
## Reusable Design previews
The package ships three previews generated directly by SolidCraft from the final neutral master: a whole-rover overview, the Raspberry Pi assembly and an exposed elbow bearing. They use native Design shading. They are intended for the model's README and showcase preparation; they are not photographs or path-traced optical renders. No raster image generator or painted replacement geometry is involved.
| Preview | Resource | Purpose |
|---|---|---|
| Whole rover | [SELENE-overview.png](exports/SELENE-overview.png) | Overall form, component placement and retained visual identity. |
| Electronics | [SELENE-electronics.png](exports/SELENE-electronics.png) | Board, mounting, packages and connector mouths, viewed from the +X side. |
| Open bearing | [SELENE-bearing.png](exports/SELENE-bearing.png) | Grooved races, individual balls and the brass cage. |
Regenerate them in a fresh output directory:
```sh
PYTHONDONTWRITEBYTECODE=1 python3 assets/render/selene/tools/export_views.py \
--cli /path/to/solidcraft-cli \
--model assets/render/selene/models/SELENE-functional.solidcraft \
--out target/selene/previews
```
The exporter opens the entire native model once, changes only transient body visibility and cameras, and requests three native MCP screenshots. It saves no changes to the master and verifies its SHA-256 afterward. The output report records the actual capture metadata, image hashes, dimensions, cameras and executor hash. Its default size is 1400 × 1000 pixels.
The body selector for electronics retains the PCB, all `Pi5 ` componentry and the `Pi M2.5` mounting hardware. The bearing selector isolates `Bearing Elbow port`. The full overview hides the optional track and source flexible-hose display geometry. None of these visibility changes delete geometry.
## Actual product-render checks
The personal fork at `10ada1b45a4eb285ce752ac72f9aaa968ff8b5c4` was compiled and exercised using its native `cpu-product` executor. The binary reports version 0.18.2 and writes native format 29. Its binary SHA-256 was `2a5485f7c7832d29b6340988837316e247b94eeeb9d4f7333ee616cea38c6826`.
Two bounded opaque PBR images passed native preflight and rendering:
| Actual subject | Visible bodies | Triangles | Admitted job estimate | Output |
|---|---:|---:|---:|---|
| Fresh single arm-bearing fixture generated by `bearings.py` | 20 | 93,654 | 43,594,876 bytes | 768 × 576 PNG, 51,705 bytes |
| Pi isolated from the actual integrated stage-03 model | 504 | 103,004 | 50,848,960 bytes | 768 × 576 PNG, 42,351 bytes |
Both returned no preflight warnings and were visually inspected. The Pi geometry was taken from the integrated rover checkpoint, not a replacement board illustration. The single-bearing image was a reduced fixture of the production geometry; it was not a rendering of the whole rover. Their raw logs, transient native files and review images belong under ignored `target/selene/render-review/`. The final model and reusable Design previews have separate hashes in [VALIDATION.md](VALIDATION.md).
That historical `cpu-product` raster path uses three fixed directional studio lights. Pure metals look dark away from their highlights because it does not provide an ambient or HDRI contribution in this path. This behavior does not mean the material assignments were lost. Use the Design previews to inspect geometry, and inspect PBR behavior with the product executor's actual lighting limits in mind.
## Historical whole-model CPU-product resource limit
At this fork revision the CPU product-image path admits at most **262,144 triangles**, **128 MiB of source geometry**, **384 MiB per job estimate** and **128 million projected pixel tests**. A single arm ball has 10,224 reference-mesh triangles; the 64 arm balls alone account for 654,336 triangles, before the head bearings or rover are included. The full-detail model therefore exceeds this CPU product triangle cap.
Reducing output dimensions reduces image buffers, not reference-mesh triangle count. Changing viewport display quality does not lower the meshes used by this product renderer. Do not bypass the admission check or silently remove mechanical detail from the master. Use the whole native Design preview, isolate a component for current CPU PBR work, or author a separately documented reduced-visibility render scene. A future whole-rover render executor must admit the complete scene or explicitly support a documented mesh-detail policy.
CAD evaluation is additional to the render-job estimate. Opening the integrated stage-03 model during these checks took about 102 seconds and peaked near 3.76 GiB RSS. Later assemblies are larger. Run the native builder, validation and full-model image export **sequentially**. The provided tools constrain native worker threads and use an explicit allocator policy; detailed evidence records resource failures and recovered attempts rather than treating them as passes.
## Material and optical boundaries
- Opaque PBR values are authored visual parameters, not measured BRDF data. Painted orange and charcoal surfaces retain dielectric/coated appearances; exposed races, balls, contacts and shafts have separate metal responses.
- Engineering-material density is a nominal proxy. It does not establish the complete component mass, electrical specification, thermal performance or contact friction.
- Each glass vial is one hollow native solid with an integral bottom. This supplies coherent geometry for later closed-medium admission.
- Viewport opacity is not physical refraction. Camera lenses, diffusers and vials require explicit optical descriptors, lighting and medium validation; the current studio recipe above supplies and validates them without rewriting the native master.
- No lunar terrain, illumination ephemeris, dust simulation, HDRI or manufacturer PCB artwork is bundled in the native derivative.
## Preparing animation shots
Use the seven focused studies to inspect the actual joint chains. Hide the six rigid suspension coils and twelve motor leads while the suspension moves; these neutral render details do not deform. The `motion` profile in `presentation.py` selects that view. Removed arm hydraulic display cylinders do not reappear during motion.
Keep the final master neutral and export motion/render copies separately. The adapted author tour and focused studies are kinematic authoring resources. A complete animated shot still needs its selected combined path checked for collisions, a timing choice, flexible-detail treatment and any physical contact model it intends to claim. Throwing and catching a free vial remains future dynamics work.
-70
View File
@@ -1,70 +0,0 @@
# SELENE native source and lineage
The owner supplied **`SELENE-showcase.solidcraft`**, the improved mechanism demonstration published by **SychPL (Mateusz Sych)**. Its bytes match the file added to the author's `media/selene-showcase` branch on 2026-10-21. The unchanged source is retained at [source/SELENE-showcase.solidcraft](source/SELENE-showcase.solidcraft); [source-lock.json](source-lock.json) pins its identity and records the review.
The functional showcase is a new derivative of this file. The older `Rover-v7.solidcraft` remains a historical reference, not the modeling input.
## Exact identity
| Field | Verified value |
|---|---|
| Author repository | `SychPL/solidcraft` |
| Source branch | `media/selene-showcase` |
| Commit | `32ec1720447e3fcc6bb6cc53a63fb5b39cfa21b9` |
| Published timestamp | `2026-10-21T20:00:01Z` |
| Repository path | `SELENE-showcase.solidcraft` |
| Git blob SHA-1 | `a06fee2c8a839f5efccc75ae6de4e68f951cb266` |
| SHA-256 | `1b0df486f30da157db27a33e0b5ed67f13d527d4dfdc6c334717a1df4e5b8677` |
| Length | 314,452 bytes |
| Native container | ZIP with `document.json` and `container.json` |
| Native source format | `solidcraft/23` |
| Units | Millimeters; Z up. Stored angles are radians; joint commands accept degrees. |
The [source commit](https://github.com/SychPL/solidcraft/commit/32ec1720447e3fcc6bb6cc53a63fb5b39cfa21b9) identifies the design as Rover-v7 with closed-loop actuators, links, gears and the SELENE tour study. Both the Git blob hash and SHA-256 were computed independently from the supplied attachment on 2026-10-22.
Read-only container inspection found **3,378 features, 47 component definitions, 47 occurrences, 49 joints, nine motion links and one motion study**. These are source-document counts. They do not establish that every feature evaluates, that every body survives the timeline, or that the assembly is collision-free. Native evaluation and derivative checks are separate evidence.
## Following the original clue
The owner's [exact pinned mechanism reference](https://github.com/tangency-labs/solidcraft/blob/ac1768e08b83ed33212459951f5b50aa7a57cddc/skills/solidcraft-mechanisms/reference.md) describes the SELENE solar-actuator loops, wheel and solar motion links, grounded modules and visibility treatment. It also specifies command units, joint-origin conventions and validation methods. Those descriptions led to the author's showcase history.
| Evidence | What it establishes |
|---|---|
| [Native source at `32ec1720`](https://github.com/SychPL/solidcraft/blob/32ec1720447e3fcc6bb6cc53a63fb5b39cfa21b9/SELENE-showcase.solidcraft) | The exact improved design supplied by the owner. |
| [Parent media commit `df2e7423`](https://github.com/SychPL/solidcraft/commit/df2e74230e6659409d65501b5d9396b55c6503d3) | Three earlier GIF demonstrations, with credit to 0xlillith and Apache-2.0/MIT for the original model. |
| [Author's showcase comment on PR 1540](https://github.com/tangency-labs/solidcraft/pull/1540#issuecomment-6205857321) | The combined demonstration and the motivation for its added mechanisms. |
| [Motion export PR 1680](https://github.com/tangency-labs/solidcraft/pull/1680) | The exact showcase filename and tour/export examples. |
| [Original rover repository](https://github.com/0xlillith/solidcraft-rover/tree/b32437915372d32b71419dc8f5143fe22ff1e869) | The owner's original Rover-v7 model and its attribution. |
The source lock preserves the earlier bounded public search as historical evidence. That search had inspected the media branch before the native file was added. Its former `awaiting_native_source` outcome is superseded by the supplied file and the new author commit; it is not a current blocker.
## Authorship and notices
The [original rover attribution](https://github.com/0xlillith/solidcraft-rover/blob/b32437915372d32b71419dc8f5143fe22ff1e869/ATTRIBUTION.md) offers the original model under **MIT OR Apache-2.0** and credits the SolidCraft emblem to SolidCraft contributors. The original modeled lettering follows **Ubuntu Light** outlines by Canonical Ltd., under the **Ubuntu Font Licence 1.0**. Its exact notice is retained in [licenses/UFL.txt](licenses/UFL.txt). No font binary is added.
SychPL is credited for the improved assembly, helper geometry and showcase study.
On 2026-10-23T22:13:37Z, [SychPL explicitly licensed their additions](https://github.com/tangency-labs/solidcraft/pull/5494#issuecomment-6375788360)
under **MIT OR Apache-2.0**, at the recipient's option. The grant expressly covers
redistribution and derived files, including `SELENE-functional.solidcraft`.
The author media branch itself has no separate licence file; the linked public
author declaration supplies the previously missing permission. The original
rover and Ubuntu Font Licence notices above remain applicable and unchanged.
New procedural geometry, English documentation and command generators in this derivative are original contributions. Raspberry Pi references inform dimensions and component identity; manufacturer CAD, logos, PCB artwork, electrical netlists and photographs are not embedded. No commercial CAD assets or GPL geometry implementations are imported.
## Authoring runtime and format compatibility
The reconstruction workflow selects the official **SolidCraft v0.18.2 Linux x86_64 CLI**. Its [published release](https://github.com/tangency-labs/solidcraft/releases/tag/v0.18.2) identifies source commit `4927214976ff20758fde35af03bd6b2d6cf73e18`. The release archive SHA-256 is `9d0c922ac72204f34ad22ca7a171be78016ab002e304ace3c07d34d187d830ff`; the extracted CLI SHA-256 is `e50fe78292d569c36052b24f46f1732bc4e43d1ecdac6a64092aa1cee3b97086`. The executor is an external development dependency and is not bundled as a model asset.
This runtime writes **`solidcraft/28`**, which the personal render branch's format-29 reader can migrate. The unchanged source remains in format 23. The newer official v0.19.0 writes format 30, beyond that render branch's reader, so its isolated component fixtures remain development evidence rather than the authoritative final build. Final acceptance must name the actual executor and final model hash; success in a different runtime does not replace that run. No native format marker is rewritten by these tools.
## Reproducibility boundaries
- Keep the pinned source unchanged. Author geometry, appearances, components, joints and studies through SolidCraft commands into a separately named derivative.
- Record the executor version and binary hash with each native build. A newer native format is not backwards compatible merely because geometry looks similar. Do not edit a file's format marker to bypass this restriction.
- Bind exact evaluated body names and component identities. Feature declarations alone are not a body inventory.
- Verify joint movement and followers against independent calculations. A successful command without actual movement is not a successful mechanism test.
- Preserve execution failures and incomplete checks explicitly. Do not carry validation claims from a lost intermediate checkpoint into a newly rebuilt model.
- A native model suitable for inspection and render preparation is not a certified manufacturing design, an electrical design, or a physically simulated lunar rover.
The prepared appearance catalog, scene specifications and future dynamics sequence have their own evidence status. Admitting this source does not automatically bind or validate those resources.
-147
View File
@@ -1,147 +0,0 @@
# SELENE native validation
This report distinguishes evaluation of the complete saved rover, independent checks of its motion, reduced geometry fixtures and actual image generation. A clean feature timeline alone does not establish mechanical clearance, and a reduced fixture does not establish a collision-free whole rover.
## Native artifact and executors
The saved derivative is [models/SELENE-functional.solidcraft](models/SELENE-functional.solidcraft), **439,883 bytes**, native format **`solidcraft/28`**. Its SHA-256 is:
```text
3bd2bfa1c6336394c4a4208d30b52c80d06658a3bd7ea57cc93b774411d1bd68
```
The unchanged source is `source/SELENE-showcase.solidcraft`, 314,452 bytes, SHA-256 `1b0df486f30da157db27a33e0b5ed67f13d527d4dfdc6c334717a1df4e5b8677`. It is byte-identical to SychPL's published improved showcase file; [SOURCE.md](SOURCE.md) pins its author commit and license evidence. Native evaluation of that source found 1,418 bodies and no feature errors or warnings. Its container has 3,378 history features, 47 components excluding root, 47 occurrences, 49 joints, nine motion links and one motion study.
The authoring and final acceptance executor is the official SolidCraft v0.18.2 Linux CLI, SHA-256 `e50fe78292d569c36052b24f46f1732bc4e43d1ecdac6a64092aa1cee3b97086`. It writes `solidcraft/28`. The personal render branch's format-29 reader accepts that format. The native container is only read for inspection and selector construction; all CAD changes are made through SolidCraft commands.
The personal fork at `10ada1b45a4eb285ce752ac72f9aaa968ff8b5c4` was also compiled successfully and used for bounded product-render checks. Its CLI SHA-256 is `2a5485f7c7832d29b6340988837316e247b94eeeb9d4f7333ee616cea38c6826`. This separately built executable is not substituted into the authoring checkpoint chain.
## Complete-model build and motion acceptance
The full native build passed **17 saved stages and 2,318 native commands**, including the explicit inventory and graph inspections. Its final timeline has **zero errors and zero warnings**; all live body names are unique. The final native joint graph reports no errors, conflicts or floating occurrences.
| Native inventory | Supplied source | Functional derivative |
|---|---:|---:|
| Evaluated bodies | 1,418 | 2,116 |
| Components excluding root | 47 | 172 |
| Occurrences | 47 | 172 |
| History features | 3,378 | 5,484 |
| Joints | 49 | 180 |
| Motion links | 9 | 99 |
| Motion studies | 1 | 8 |
The final joint inventory contains 135 revolutes, 11 sliders, 33 rigid joints and one cylindrical joint. The saved document also contains **121 browser groups, ten named views and ten render setup records**. The build history and final native inventory are in `target/selene/native-build/`.
Independent final-master acceptance **passed all 33 cases and 880 numerical comparisons** in a fresh official v0.18.2 process. The run completed on 2026-10-22, from 08:40:24 to 08:53:15 UTC, with exit code 0. It exercised 26 kinematic/neutral cases and seven selected neutral-pose interference pairs. Both the master and original source remained byte-for-byte unchanged. All 19 independent drivers were neutral on open and after the final reset; the tested graph inspections reported no errors, conflicts or floating occurrences.
| Kinematic case family | Cases | Accepted exercise |
|---|---:|---|
| Drivetrain | 1 | Six wheels follow the driver; a 90° turn gives −62.831853 mm demonstration-track travel at the 40 mm nominal radius. Independent wheel and track world-position checks pass. |
| Solar deployment | 1 | The selected 20° deployment closes all four actuator loops and agrees with the independently calculated strokes. |
| Independent pointing | 2 | Sensor and dish pointing are driven independently, with native follower and world-position checks. |
| Gripper | 2 | Driver endpoints −15° and +18°, opposite follower values and jaw world positions. |
| Sample cap | 2 | 360° and 1440° rotation produce 1.5 mm and 6 mm axial travel respectively. |
| Suspension | 12 | Each of six carriers is driven to both −4° and +1.5°. Closed-loop strokes and carrier world centers of mass match independent calculations. |
| Rolling-element bearings | 5 | Shoulder, elbow, wrist, tool tilt and sensor elevation drivers exercise all ten bearing cages and 80 ball-spin relations, with one orbital witness per bearing. |
| Neutral reset | 1 | All 19 independent driver coordinates return to zero. |
| **Kinematic total** | **26** | **All passed.** |
The 880 numerical comparisons include repeated neutral-coordinate readbacks between cases; they are not 880 distinct poses. Numerical agreement is a solver check, not a manufacturing tolerance claim.
| Numerical quantity | Comparisons | Maximum absolute error | Acceptance tolerance |
|---|---:|---:|---:|
| Joint angles | 756 | 7.106 × 10⁻¹⁵° | 10⁻⁶° |
| Linear joint coordinates | 19 | 3.777 × 10⁻¹¹ mm | 10⁻⁵ mm |
| Suspension strokes, included above | 12 | 3.927 × 10⁻¹² mm | 10⁻⁵ mm |
| World center-of-mass coordinates | 105 | 4.443 × 10⁻⁵ mm | 0.002 mm |
| Bearing orbit coordinates, included above | 30 | 1.318 × 10⁻⁵ mm | 0.002 mm |
The largest linear error was a solar actuator stroke; the largest world-coordinate error was the dish yaw mount's center-of-mass height.
| Selected neutral-pose pair | Completed method | Result |
|---|---|---|
| `Shoulder link -9` / `Elbow link -9` | Exact solid intersection | Clear |
| `Elbow link -9` / `Distal wrist link -9` | Exact solid intersection | Clear |
| `Tool wrist machined tongue` / `Tool tilt joint shaft` | Exact solid intersection | Clear |
| `Gripper jaw -1` / `Gripper jaw 1` | Conservative broad-phase separation | Clear |
| `Raspberry Pi 5 PCB` / `Pi5 USB2 mounting tab 1` | Exact solid intersection | Clear |
| `Raspberry Pi 5 PCB` / `Pi5 USB3 mounting tab 2` | Exact solid intersection | Clear |
| `Raspberry Pi 5 PCB` / `Pi5 GPIO pin 40` | Exact solid intersection | Clear |
These are **six exact intersection checks and one broad-phase clearance**, all with zero reported overlaps, unchecked or skipped pairs, no truncation and no exhausted budget. They are selected checks at neutral, not an exhaustive swept collision analysis.
The complete local machine-readable report is `target/selene/validation-final/validation.json`, schema `1.0.0`, 228,492 bytes, SHA-256 `0ccbf279f27885f6a4646a691a03915ca9ae22ae2f6bfef9574b856392474130`. It records every case, comparison, drive and interference result. The validator's SHA-256 is `b032962210e86a516193bd549d9137dcff93dbff4a3e038ca9a08de3493e1aac`. The repository's resource rules keep raw execution reports in ignored `target/`; the shipped validator and committed case selection reproduce them.
The reproducible builder saves every completed stage, validates complete native command and timing traces, rejects timeline errors or warnings, requires unique live body names and checks the native joint graph. Resume verifies source, executable, recipe, result and checkpoint hashes. The independent validator opens the final saved file in a new process and checks requested values, follower values and world-coordinate measurements against separate calculations. It does not save its test poses over the master.
The selected interference pairs in [motion/interference-pairs.json](motion/interference-pairs.json) are deliberately bounded. Any timeout, unchecked pair or failed command is incomplete evidence, not a clearance pass. They do not exhaust all body pairs or all simultaneous configurations.
## Reduced-fixture evidence
These checks were completed before integrating the corresponding production commands. Their scope remains local even when the same geometry is present in the full rover.
| Subject | Actual evidence | Result and scope |
|---|---|---|
| Rolling-element kinematics | An arm bearing and a sensor-head bearing each driven by 30°; all eight relative ball spins and one orbital position checked in each case. | Arm cage 12.679558011° and relative ball spin −94.643843725°; head cage 13.360248447° and relative ball spin −135.576157538°. Maximum world orbital error 1.474 × 10⁻¹⁰ mm. Ideal no-slip relations, without contact-force simulation. |
| Bearing clearances | Eleven core arm/bearing pairs and four head-retention pairs: 14 completed exact Boolean checks and one bounding-box exclusion. Core fixture: 288 bodies, 736 features, 117 components, 115 joints and 90 links. | No overlap reported and no pair unresolved after retries. Both fixture inventories have zero feature errors/warnings. These official v0.19.0 fixtures precede the production arm set-screw tips' final 0.05 mm extension; the named local pairs do not establish all-body or all-pose clearance. |
| Raspberry Pi 5 | Official v0.19.0 fixture: 748 commands, 492 bodies and 1,202 features. Two analytic volumes, all 40 GPIO centers, six exact Boolean pairs and a separate broad-phase check of 26 major package envelopes. | PCB volume error 0.000514094% against a 0.01% tolerance; maximum GPIO center error 1.422 × 10⁻¹⁴ mm. No reported overlaps, unchecked pairs or timeline errors/warnings. Component layout remains a visual reconstruction. |
| Gripper | Native checks at −15°, 0° and +18°, plus a 661-point analytic sweep at 0.05° and a continuous vertex/stationary-point separation bound. | Minimum jaw-to-jaw gap 5.735342 mm; minimum pad-to-pad gap 6.360460 mm. The local jaw envelopes remain in opposite half-planes over the continuous driver interval. Pin/sleeve radial clearance is 0.05 mm. This is a local gripper proof. |
| Suspension | Retained-source-label fixture: 71 commands, 15 bodies, 43 features; both −4° and +1.5° endpoints; 12 pair-pose checks over eight unique pairs, including six exact Boolean intersections. | No timeline errors/warnings or reported overlaps; no unchecked pairs. Maximum slider-equation error 3.927 × 10⁻¹² mm and world center-of-mass error 1.973 × 10⁻⁹ mm. |
| Yaw journals | Official v0.18.2 fixture: 101 commands, 20 bodies, 67 features; six support-only rotation cases, 44 pair-pose checks over 26 unique pairs, including 29 exact Boolean intersections; nine volume checks and 12 seating/distance measurements. | All cases passed; no timeline errors/warnings, unchecked pairs or reported overlaps. Maximum analytic volume error 0.0155823%, within the 0.02% tolerance. |
| Integral glass vials | Official v0.18.2 fixture: 55 commands, six cups, one component and 49 features. The 22-command production subrecipe matches the validated recipe exactly. | Maximum analytic volume error 0.0080213%, COM error 0.000108088 mm and bounding-box error 0.000155825 mm. Each cup has one kernel shell; names, ownership, Glass material and opacity 0.30 are preserved. |
The yaw fixture exposed an existing overlap of approximately 21.33 mm³ between each of four dish mounting screw heads and their stationary support. Four local reliefs remove those overlaps. The support-only fixture exercises arm/turret ±160° and dish ±150° to inspect the axisymmetric interfaces; the complete rover retains its narrower documented joint limits. Those local rotations do not expand the rover's accepted motion envelope.
The journal supports have nominal 0.1 mm radial journal clearance, 0.15 mm radial pin clearance and 0.1 mm axial retention play. Display-mesh distance measurements of the tiny retainer gaps were 0.0644–0.0826 mm. These are mesh approximations; the nominal analytic dimensions and completed solid-interference results are recorded separately.
The vial fixture also exported 57,264 native STL triangles. After coordinate welding at 10⁻⁶ mm, the mesh has six connected components and zero boundary, nonmanifold or inconsistent-winding edges, and zero degenerate triangles. Each vessel retains its 11.6 mm outside diameter and 27 mm height, with a 0.8 mm wall and 1 mm integral floor. Its fixture SHA-256 is `76f0cb4e25e7beb2f04a1ccd84619144441f9d580129f0325c29ef8d774ef2a6`; detailed evidence is in `target/selene/vial-repair/validation-report.json`. Mesh closure does not establish physical dielectric renderer support.
Two initial bearing checks exhausted their eight-second budgets. With 30-second budgets, the shoulder-clevis/outer-race check completed in 12.122 seconds and the ball/inner-race check in 10.382 seconds, both clear. The original timeout reports remain alongside the successful retries. Their initial zero-overlap counts were not accepted as passes.
The electronics and core bearing fixtures use official v0.19.0, format 30; they are development fixtures, not the distributed master. Their production commands were subsequently replayed in the complete rover with v0.18.2. The retained-label suspension fixture's SHA-256 is `1da6c92ba9ff2c421dc5b475f9971b458d3d43bbb048147df0cdeb70fefb9a11`; the yaw fixture's is `65ba6b033e301bd6cc4c91caa31d2ea67c69064d970355ff0597609adc62efcd`. The unchanged electronics generator is `67a69a7e613ad59dcf474dc6ecd9d9fb8439aa193e2e2ee458c282b65186fd9d`; its fixture is `578d9ba7b00738552ef9201c57489520fd86275c7fcfa23818adcdaed07aae04`.
Local report locations in a development checkout are `target/selene/bearings/compact-phase-report.json`, `target/selene/bearings/review-gripper-presentation.json`, `target/selene/electronics/validation-report.json`, `target/selene/suspension/retained-labels/validation-report.json` and `target/selene/yaw-support-audit/validation-report.json`. Detailed generated logs, fixture files and diagnostic images stay outside the production asset package.
## Image evidence
The compiled personal fork reopened the complete final master and generated three actual **1400 × 1000 native Design PNGs**. All three were visually reviewed and are shipped under `exports/`. The overview shows the neutral rover, the electronics view exposes the connector mouths and populated board, and the bearing view isolates its races, balls and cage. The capture opened all 2,116 bodies with a clean timeline and left the master hash unchanged.
| Shipped preview | Bytes | SHA-256 |
|---|---:|---|
| [SELENE-overview.png](exports/SELENE-overview.png) | 423,686 | `44d317ca9b0f77c3ac5d43409f0b6b281b9d6724d58a3c8edd8ca2db19a0bc63` |
| [SELENE-electronics.png](exports/SELENE-electronics.png) | 225,933 | `cd8af0700ce6a4cc0aa00bdfa5d246e4b3ed491847795636c65d9a8ead45991e` |
| [SELENE-bearing.png](exports/SELENE-bearing.png) | 284,094 | `ed3a3da01946019c9c380bf8515013eca3a05c7bf5161ff4bb98ee36eb5ef92b` |
The native capture metadata identifies the CPU rasterizer and its expected warning: surface-analysis colors, highlights and legends are not drawn. These previews do not claim physical PBR or transmission. Cameras, executor hash and capture metadata are recorded in `target/selene/final-previews/export-report.json`; [tools/export_views.py](tools/export_views.py) reproduces the three selections without saving transient visibility or camera changes over the master.
Two actual `cpu-product` renders from the compiled personal fork passed preflight and image generation with no warnings: a single bearing fixture (20 visible bodies, 93,654 triangles) and the Pi isolated from the integrated rover checkpoint (504 visible bodies, 103,004 triangles). Both outputs were 768 × 576 PNGs and were visually inspected. The bearing is a reduced fixture; the electronics image uses actual integrated geometry. See [RENDER.md](RENDER.md) for exact resource estimates, rendering scope and limitations.
The complete rover exceeds this revision's 262,144-triangle CPU product admission limit. The 64 arm-bearing balls alone contain 654,336 reference-mesh triangles. That code path has not produced a whole-rover PBR image. Native Design previews and bounded product renders are different evidence.
## Repository checks
The catalog and prepared-resource validators passed with the source status `pinned_native`. The wider scene catalog intentionally remains `runtime_ready: false`: the native model's completion does not bind every proposed stage, material role or future dynamics event.
The required repository-wide `cargo xtask ci` command was attempted with Rust 1.95.0 and the required socket permissions. Formatting, asset attribution, icon checks (17 tests), license checks, generated lists and dependency-layer checks (17 crates) passed. CI then stopped at a pre-existing `clippy::nonminimal_bool` diagnostic in `crates/geom/src/fill_general.rs:166`, under `-D warnings`. That application source is unchanged by this work. Later workspace tests and WebAssembly checks were not reached. The whole repository gate is therefore **not green**; the successful native CLI build and asset checks do not replace it.
The final `cargo xtask ci` attempt, after adding the shipped master and all three previews, reproduced that same baseline Clippy failure. Its exact output is retained locally in `target/selene/ci-final.log`. Its asset and icon gates passed. The catalog and prepared-resource validators also passed with zero errors, and the final whitespace check was clean. No application Rust source is changed by this package.
## Resource failures and recovered attempts
The working environment has an 8 GiB memory limit. Three earlier native attempts were killed for exceeding it: one overlapped a second cold full-model open; two involved late renames of original suspension bodies. Late original-body renames forced a long history rebuild while prior evaluated state remained resident. The production recipe retains those original labels and splits suspension geometry into smaller saved stages. Geometry, ownership and joint intent remain explicit.
Full-model native processes now run sequentially. The provided tools set two native worker threads and an allocator policy of `MALLOC_ARENA_MAX=1`, `MALLOC_MMAP_THRESHOLD_=65536` and `MALLOC_TRIM_THRESHOLD_=65536`. These are process-memory settings, not geometry simplifications. Failed attempts and their logs are retained under `target/selene/failed-attempts/`; they are not counted as successful validation.
Strict container inspection also rejected one intermediate suspension checkpoint because the inherited tour still referenced the original wheel joint ID after wheel reparenting. The builder now refreshes that study by exact joint names before saving the suspension checkpoint. The corrected stage completed with 172 components and 180 joints, and the rejected file remains diagnostic evidence only.
The first presentation attempt failed at the original touching vial-wall/bottom union and saved no presentation checkpoint. The validated replacement removes each split pair and creates an envelope with a blind bore, retaining the original wall name, external dimensions, component and appearance. Its base coordinates account for the source's already baked `Move9` translation. The failed union log remains under `target/selene/failed-attempts/vial-touching-union/`.
## Remaining physical and animation scope
The rover is an editable geometric and kinematic showcase. The rolling bearings use ideal linked revolutes; the suspension uses rigid links and a closed pin–slider–pin loop. Neither provides contact forces, spring dynamics, damping, backlash, stress, fatigue or a rated load capacity. Fasteners have nominal seats rather than fully modeled thread engagement.
The six rigid spring solids and twelve motor leads remain neutral-pose render details and must be hidden for suspension movement. The motion visibility profile handles this. Flexible hose display geometry and rigid decorative arm hydraulic cylinders are excluded from the relevant moving presentation.
The Pi is an original visual reconstruction based on cited public mechanical dimensions. It is not manufacturer CAD, a PCB netlist, exact component artwork or an electrical design. Opaque PBR parameters are authored appearances; nominal material density does not establish measured mass, thermal behavior or friction. Glass viewport opacity is not a physical transmission model.
The adapted legacy tour and seven focused studies are animation authoring resources. Their presence does not certify every frame, every combined pose, obstacle clearance or a future throw-and-catch sequence. Those checks belong to the specific animation shot and any physical simulator chosen later.
-96
View File
@@ -1,96 +0,0 @@
-------------------------------
UBUNTU FONT LICENCE Version 1.0
-------------------------------
PREAMBLE
This licence allows the licensed fonts to be used, studied, modified and
redistributed freely. The fonts, including any derivative works, can be
bundled, embedded, and redistributed provided the terms of this licence
are met. The fonts and derivatives, however, cannot be released under
any other licence. The requirement for fonts to remain under this
licence does not require any document created using the fonts or their
derivatives to be published under this licence, as long as the primary
purpose of the document is not to be a vehicle for the distribution of
the fonts.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this licence and clearly marked as such. This may
include source files, build scripts and documentation.
"Original Version" refers to the collection of Font Software components
as received under this licence.
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to
a new environment.
"Copyright Holder(s)" refers to all individuals and companies who have a
copyright ownership of the Font Software.
"Substantially Changed" refers to Modified Versions which can be easily
identified as dissimilar to the Font Software by users of the Font
Software comparing the Original Version with the Modified Version.
To "Propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification and with or without charging
a redistribution fee), making available to the public, and in some
countries other activities as well.
PERMISSION & CONDITIONS
This licence does not grant any rights under trademark law and all such
rights are reserved.
Permission is hereby granted, free of charge, to any person obtaining a
copy of the Font Software, to propagate the Font Software, subject to
the below conditions:
1) Each copy of the Font Software must contain the above copyright
notice and this licence. These can be included either as stand-alone
text files, human-readable headers or in the appropriate machine-
readable metadata fields within text or binary files as long as those
fields can be easily viewed by the user.
2) The font name complies with the following:
(a) The Original Version must retain its name, unmodified.
(b) Modified Versions which are Substantially Changed must be renamed to
avoid use of the name of the Original Version or similar names entirely.
(c) Modified Versions which are not Substantially Changed must be
renamed to both (i) retain the name of the Original Version and (ii) add
additional naming elements to distinguish the Modified Version from the
Original Version. The name of such Modified Versions must be the name of
the Original Version, with "derivative X" where X represents the name of
the new work, appended to that name.
3) The name(s) of the Copyright Holder(s) and any contributor to the
Font Software shall not be used to promote, endorse or advertise any
Modified Version, except (i) as required by this licence, (ii) to
acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with
their explicit written permission.
4) The Font Software, modified or unmodified, in part or in whole, must
be distributed entirely under this licence, and must not be distributed
under any other licence. The requirement for fonts to remain under this
licence does not affect any document created using the Font Software,
except any version of the Font Software extracted from a document
created using the Font Software may only be distributed under this
licence.
TERMINATION
This licence becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER
DEALINGS IN THE FONT SOFTWARE.
File diff suppressed because it is too large Load Diff
@@ -1,151 +0,0 @@
# SELENE original appearance pack
Forty-eight original appearance recipes are prepared for the current opaque PBR command path. Every recipe has a directly copyable, model-independent baseline and a separate advanced target specification. The pack contains no measured material dataset, external artwork, engineering material properties or runtime implementation.
## Files and status
| File | Purpose |
| --- | --- |
| `appearance-pack.json` | Editable authority for 48 recipes, their opaque projections and future target requirements. This is authoring data, not a native portable library file. |
| `appearance-swatches.json` | Prepared command script with 48 labeled box swatches and one neutral support base. It uses the existing `file.new`, `solid.box` and `appearance.assign` command shapes. |
| `appearance-pack.md` | Contract, use, limitations and recipe index. |
The authoring step checked JSON structure, ID/name uniqueness, catalog and capability references, numeric admission bounds, exact runtime parameter keys, and every swatch assignment against the recipe authority. **No SolidCraft runtime was executed and no rendered image was visually reviewed during authoring.** The script is a prepared input whose execution and render admission must be verified on the selected build.
## Runtime projection contract
Copy `items[].baseline_projection.runtime_parameters`, then add an existing target selection before calling `appearance.assign`. The projection includes `appearance`, `color`, `opacity: 1` and exactly three PBR fields: `metallic`, `roughness` and `dielectric_ior`. Targets are deliberately absent because the improved SELENE source model must be pinned and inspected before binding.
```json
{
"command": "appearance.assign",
"params": {
"bodies": [
"Existing target body"
],
"appearance": "SELENE-AP-001 Neutral matte enclosure",
"color": "#717882",
"opacity": 1.0,
"pbr": {
"metallic": 0.0,
"roughness": 0.68,
"dielectric_ior": 1.5
}
}
}
```
The body name in this example is a placeholder, not a ready-to-run model binding. The current command also accepts face points and components. An occurrence name supplied as a component target resolves to its component definition; do not assume an independent occurrence override. Face overrides take precedence over broader appearances. Assigning a look replaces it: omitting `pbr` later restores the scalar response instead of retaining these fields.
Current bounds are metallic 0 to 1, perceptual roughness 0.045 to 1, relative dielectric IOR 1 to 3, and opacity exactly 1 for PBR. Colors are sRGB8 hex values decoded by the existing renderer. All values here are original illustrative authoring choices. The common IOR control of 1.5 is not a measured value; it controls reflection of the nonmetal fraction and does not enable transmission. The metal recipes use metallic 1 and the dielectric recipes use metallic 0; no mechanical or electrical properties follow from either choice.
The pack JSON is **not** `solidcraft-library/1` and cannot be passed directly to `library.inspect` or `library.apply`. That scalar library format cannot preserve PBR. Use the prepared script, an explicit appearance overlay, or a native format-21 design. A future catalog importer needs its own versioned admission contract.
## Prepared swatch script
The 48 swatches are 12 x 12 x 8 mm boxes in 8 columns and 6 rows with an 18 mm pitch. Their lower corners run from x = -69 mm to 57 mm and y = -51 mm to 39 mm, starting at z = 1 mm. A 144 x 108 x 1 mm base sits below them. IDs ascend along x within each row, then along y. Body names carry the recipe ID and English name; there is no text geometry, font, decal or external asset.
The script contains 99 commands: one new design, 49 boxes, and 49 appearance assignments. Flat boxes intentionally bound tessellation demand; they are an inspection grid, not a complete optical validation target. A curved reference, an angular light sweep, and close-up geometry remain necessary for validating highlights, anisotropy and layered targets when those functions exist.
Prepared commands for a built native CLI, run from the repository root:
```sh
mkdir -p target/selene-render
solidcraft-cli run assets/render/selene/materials/appearance-swatches.json --save target/selene-render/appearance-swatches.solidcraft --results --json-out target/selene-render/appearance-swatches-results.json
solidcraft-cli render target/selene-render/appearance-swatches.solidcraft --out target/selene-render/appearance-swatches.png --width 768 --height 576
```
These commands have been prepared, not executed. The render CLI automatically fits a camera when none is supplied. Existing output is protected unless `--overwrite` is explicitly provided. Keep local render evidence under ignored `target/`; this pack does not bundle unreviewed preview exports.
To style a pinned SELENE design, prepare a separate overlay containing only `appearance.assign` commands with verified target names, then run:
```sh
solidcraft-cli run target/selene-render/verified-appearance-overlay.json --in path/to/pinned-selene.solidcraft --save target/selene-render/selene-styled.solidcraft --results
```
Both input paths above are placeholders until the model and overlay are prepared. The overlay must contain no `file.new`, geometry edits, physical-material edits, joint edits or pose changes. Native save is the preservation path. `timeline.export_script` currently omits appearance overrides and refuses component/joint assemblies; it is not an appearance or articulated-model round-trip mechanism. A multi-command script stops at the first error and is not a whole-script transaction, so verify targets before applying it to the review copy.
## What the baseline can show
The current Render consumer supports opaque direct-lit responses under three fixed neutral directional lights, with the existing preview tone mapping and PNG export. It does not implement the textured, layered, transmissive or emissive targets described here. Current consumers have no cast shadows, environment illumination, indirect bounces, surface coating, anisotropy, absorption or transmission. Saved nondefault environment/exposure intent is not executable by this baseline. These limits are documented in [`docs/render-opaque-pbr.md`](../../../../docs/render-opaque-pbr.md) and [`docs/render-jobs.md`](../../../../docs/render-jobs.md).
Advanced targets remain separate authoring data. Their numeric controls are illustrative texture or geometry settings in millimetres, radians or explicitly named dimensionless units. They are not manufacturing recommendations or measurements. Optical spectra, refractive data, layer thicknesses and scattering coefficients remain unspecified where the recipe lists missing inputs. CAP references link to requirements in [`capabilities.json`](../../../../docs/render-materials/capabilities.json), not to a claim of implementation.
## SELENE-specific fidelity boundaries
- The sample vial, optical window and diffuser recipes are explicitly opaque inspection surrogates (AP-029, AP-030 and AP-039). They do not show glass, refraction or light transmission. Transparent containers need closed wall geometry and correct interior/exterior medium handling before the advanced target can be represented.
- The unpowered LED face (AP-038) emits no light. Active LEDs require a separate emission model; spectrally excited phosphor conversion additionally requires excitation, conversion and emission data. Ordinary RGB emission must not be labelled fluorescence.
- The black and silver anodized recipes (AP-015 and AP-016) are simplified outer-response surrogates. Actual anodizing combines a dielectric or absorbing oxide with a metal substrate; a single metalness value is not that layer model.
- Blanket folds (AP-018 and AP-019), honeycomb walls (AP-040), mesh-wheel openings (AP-044), serrations and threads require geometry. Color or roughness alone cannot provide their silhouettes, empty space or self-shadowing.
- Lunar terrain and deposits (AP-041 to AP-043) are granular surfaces in vacuum. The dust-patch baseline belongs on separate deposit geometry or a deliberately bounded face. Applying it to an entire rover body does not reproduce sparse dust coverage. No terrestrial fog, wetness or aerial perspective is implied.
- Appearance assignments do not define density, stiffness, friction, thermal emissivity, electrical performance, leak tightness, cleanliness, ESD behavior or flight readiness. They must leave the improved model geometry, articulation, joint limits and motion data intact.
- The automotive rubber sidewall (AP-048) is a representative terrestrial material target. Do not substitute it for the SELENE open metal wheel (AP-044) merely because both are wheel-related.
## Recipe index
All entries use opacity 1 and the illustrative IOR control 1.5. M is metallic fraction; R is perceptual roughness. The catalog link is semantic traceability to the 600-recipe planning catalog, not an imported texture or measured asset.
| ID | Original recipe | Catalog source | sRGB color | M | R | Baseline role |
| --- | --- | --- | --- | ---: | ---: | --- |
| SELENE-AP-001 | Neutral matte enclosure | MAT-031 | `#717882` | 0 | 0.68 | opaque surface |
| SELENE-AP-002 | White thermal-coating preview | MAT-491 | `#d9dde0` | 0 | 0.58 | opaque surface |
| SELENE-AP-003 | Matte optical baffle | MAT-468 | `#141619` | 0 | 0.94 | opaque surface |
| SELENE-AP-004 | Ceramic gripper contact pad | MAT-574 | `#d9d7cf` | 0 | 0.32 | opaque surface |
| SELENE-AP-005 | Machined aluminium wheel rim | MAT-561 | `#b9c0c7` | 1 | 0.29 | opaque surface |
| SELENE-AP-006 | Serrated metal gripper jaw | MAT-573 | `#a6adb5` | 1 | 0.40 | opaque surface |
| SELENE-AP-007 | Linear-brushed stainless housing | MAT-002 | `#b8bdc2` | 1 | 0.34 | opaque surface |
| SELENE-AP-008 | Polished stainless sample cup | MAT-593 | `#c9ced3` | 1 | 0.16 | opaque surface |
| SELENE-AP-009 | Titanium wrist finish | MAT-572 | `#8b9098` | 1 | 0.43 | opaque surface |
| SELENE-AP-010 | Exposed copper conductor | MAT-164 | `#b97850` | 1 | 0.27 | opaque surface |
| SELENE-AP-011 | Machined brass printer nozzle | MAT-391 | `#b4a06b` | 1 | 0.30 | opaque surface |
| SELENE-AP-012 | Solidified solder joint | MAT-165 | `#bec3c6` | 1 | 0.25 | opaque surface |
| SELENE-AP-013 | Metal sampling scoop | MAT-575 | `#9ba4ad` | 1 | 0.44 | opaque surface |
| SELENE-AP-014 | Gold-finished contact pad | MAT-163 | `#bf9c49` | 1 | 0.21 | opaque surface |
| SELENE-AP-015 | Black anodized chassis surrogate | MAT-567 | `#252a30` | 0 | 0.56 | opaque surface |
| SELENE-AP-016 | Silver anodized cover surrogate | MAT-015 | `#abb3ba` | 1 | 0.39 | opaque surface |
| SELENE-AP-017 | Composite arm tube surrogate | MAT-571 | `#252a2e` | 0 | 0.42 | opaque surface |
| SELENE-AP-018 | Aluminized blanket metal face | MAT-501 | `#c3c8cd` | 1 | 0.18 | opaque surface |
| SELENE-AP-019 | Polyimide-facing blanket surrogate | MAT-502 | `#80602e` | 0 | 0.30 | opaque surface |
| SELENE-AP-020 | Solar-cell opaque preview | MAT-511 | `#172b43` | 0 | 0.24 | opaque surface |
| SELENE-AP-021 | Silicone cable jacket | MAT-333 | `#484d56` | 0 | 0.77 | opaque surface |
| SELENE-AP-022 | Polyimide harness wrap | MAT-332 | `#9d713d` | 0 | 0.46 | opaque surface |
| SELENE-AP-023 | Fluoropolymer sealing ring | MAT-598 | `#dbddd7` | 0 | 0.49 | opaque surface |
| SELENE-AP-024 | Elastomer seal candidate | MAT-599 | `#26292b` | 0 | 0.86 | opaque surface |
| SELENE-AP-025 | Opaque sample cassette body | MAT-595 | `#424a54` | 0 | 0.54 | opaque surface |
| SELENE-AP-026 | Textured ABS instrument shell | MAT-042 | `#59616b` | 0 | 0.64 | opaque surface |
| SELENE-AP-027 | Powder-printed polyamide preview | MAT-175 | `#abb0b1` | 0 | 0.87 | opaque surface |
| SELENE-AP-028 | FDM top-skin preview | MAT-171, MAT-361 | `#aab5bd` | 0 | 0.46 | opaque surface |
| SELENE-AP-029 | Sample vial opaque inspection surrogate | MAT-582 | `#91a2ae` | 0 | 0.35 | opaque inspection surrogate |
| SELENE-AP-030 | Window opaque inspection surrogate | MAT-583 | `#537286` | 0 | 0.24 | opaque inspection surrogate |
| SELENE-AP-031 | Matte ceramic sample insert | MAT-594 | `#c9c5bb` | 0 | 0.59 | opaque surface |
| SELENE-AP-032 | Threaded metal closure cap | MAT-596 | `#b6bcc3` | 1 | 0.31 | opaque surface |
| SELENE-AP-033 | Graphite thermal-sheet preview | MAT-349 | `#35383d` | 0 | 0.91 | opaque surface |
| SELENE-AP-034 | Solder-mask board face | MAT-161 | `#32624b` | 0 | 0.42 | opaque surface |
| SELENE-AP-035 | Exposed glass-epoxy board edge | MAT-162 | `#9b9270` | 0 | 0.77 | opaque surface |
| SELENE-AP-036 | Molded epoxy component body | MAT-166 | `#252727` | 0 | 0.80 | opaque surface |
| SELENE-AP-037 | Battery-can metal preview | MAT-341 | `#b3bbc3` | 1 | 0.35 | opaque surface |
| SELENE-AP-038 | Unpowered LED phosphor face | MAT-441 | `#d6bd64` | 0 | 0.65 | opaque surface |
| SELENE-AP-039 | Diffuser opaque inspection surrogate | MAT-451 | `#dadcdb` | 0 | 0.72 | opaque inspection surrogate |
| SELENE-AP-040 | Aluminium honeycomb cut edge | MAT-481 | `#a5adb6` | 1 | 0.40 | opaque surface |
| SELENE-AP-041 | Mare-style regolith surface | MAT-531 | `#5e5c58` | 0 | 0.96 | opaque surface |
| SELENE-AP-042 | Highland-style regolith surface | MAT-546 | `#8b8881` | 0 | 0.96 | opaque surface |
| SELENE-AP-043 | Tool-transferred dust patch | MAT-559 | `#7b766d` | 0 | 0.98 | opaque deposit geometry response |
| SELENE-AP-044 | Woven-metal wheel mesh | MAT-565 | `#999fa7` | 1 | 0.38 | opaque surface |
| SELENE-AP-045 | Oiled oak workshop surface | MAT-111 | `#a17a51` | 0 | 0.56 | opaque surface |
| SELENE-AP-046 | Varnished walnut joinery | MAT-113 | `#6d4d37` | 0 | 0.30 | opaque surface |
| SELENE-AP-047 | Automotive smooth clearcoat surrogate | MAT-034, MAT-204 | `#4e697e` | 0 | 0.21 | opaque surface |
| SELENE-AP-048 | Embossed tyre-sidewall preview | MAT-237 | `#282a2c` | 0 | 0.83 | opaque surface |
## Source and contract references
- [`appearance.assign` implementation](../../../../crates/engine/src/cmd/inspect.rs): named custom colors, body/face/component targeting and exact PBR block admission.
- [Persistable PBR parameters](../../../../crates/doc/src/pbr_appearance.rs): appearance-owned optical values and sRGB decoding.
- [Opaque product surface contract](../../../../docs/render-opaque-pbr.md): current consumers, limits, native preservation and unsupported functions.
- [Original opaque studio example](../../../../examples/render-opaque-studio.json): existing `solid.box` and appearance command shapes used for this prepared script.
- [CLI render workflow](../../../../apps/solidcraft-cli/src/render.rs) and [product-image jobs](../../../../docs/render-jobs.md): bounded PNG admission, preflight/start/wait/export and overwrite behavior.
- [Script executor](../../../../crates/engine/src/lib.rs) and [timeline export](../../../../crates/engine/src/cmd/script.rs): replay semantics and export omissions.
- [Scalar catalog contract](../../../../crates/io/src/catalog.rs): version-1 scalar-only fields and PBR export refusal.
## Original provenance
Author: SolidCraft contributors. License: MIT OR Apache-2.0. The two JSON files are original clean-room resource data and analytic command inputs. No commercial material library, copied shader, third-party artwork, restricted CAD extraction or generated image texture is bundled. Their exact attribution rows belong in the repository root `ATTRIBUTION.md`; Markdown documentation is exempt from the non-Markdown asset gate.
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More