Compare commits

...

132 Commits

Author SHA1 Message Date
bherbruck 409ab1ed45 Release 0.26.0 (zip 9.1.3) 2026-10-28 19:41:38 -04:00
dependabot[bot] 00df48dfe4 Bump zip from 9.0.4 to 9.0.5 (#8572)
Bumps [zip](https://github.com/zip-rs/zip2) from 9.0.4 to 9.0.5.
- [Release notes](https://github.com/zip-rs/zip2/releases)
- [Changelog](https://github.com/zip-rs/zip2/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zip-rs/zip2/compare/v9.0.4...v9.0.5)

---
updated-dependencies:
- dependency-name: zip
  dependency-version: 9.0.5
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-28 19:41:29 -04:00
bherbruck 5e03fe2f4b Release 0.26.0 2026-10-28 19:32:17 -04:00
bherbruck 0656819123 Release 0.26.0: fold point-clouds into format 43 (#8579)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 19:32:15 -04:00
github-actions[bot] 926d753a40 Regenerate generated files and fold pending files [skip ci] 2026-10-28 23:29:43 +00:00
github-actions[bot] c03dccabb6 Regenerate generated files and fold pending files [skip ci] 2026-10-28 23:20:35 +00:00
bherbruck 776653dd7c Release 0.26.0 (#8574)
Fold the pending design format change (linked-body-properties) into format 42.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 19:17:37 -04:00
semmelrinde 9b8be3b8a5 Romanian: translate the 121 messages the catalog still lacks (batch 4) (#8494)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:23:47 +00:00
semmelrinde d755b70e25 Swedish: translate the 121 messages the catalog still lacks (batch 4) (#8497)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:23:33 +00:00
semmelrinde 0c4673a89d Russian: translate the 125 messages the catalog still lacks (batch 4) (#8495)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:23:29 +00:00
semmelrinde 1b000c92f3 Hungarian: translate the 121 messages the catalog still lacks (batch 4) (#8489)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:23:28 +00:00
semmelrinde eb8cfdf8cc Indonesian: translate the 125 messages the catalog still lacks (batch 4) (#8490)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:23:25 +00:00
semmelrinde 2da2c85f27 Norwegian Bokmål: translate the 121 messages the catalog still lacks (batch 4) (#8493)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:23:02 +00:00
semmelrinde 359a7253e9 Ukrainian: translate the 121 messages the catalog still lacks (batch 4) (#8500)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:22:18 +00:00
semmelrinde 64bbfe9ed3 Turkish: translate the 121 messages the catalog still lacks (batch 4) (#8499)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:22:10 +00:00
YAEemf e6dc105134 Point clouds: insert PLY, XYZ and PTS scans as a modelling reference (#5151) (#5381)
* Point clouds: insert PLY, XYZ and PTS scans as a modelling reference (#5151)

A point cloud is kept in the design as a reference, drawn as depth-tested
dots and never a body.

- io: a PLY reader (ASCII, binary little/big endian; x, y, z of any numeric
  type, optional colours, other elements skipped; counts checked against the
  file) and XYZ/PTS text, thinned evenly past max_points. PLY meshes with
  faces can also come in through file.insert_mesh.
- doc: Document.point_clouds (shared f32 points relative to an f64 origin,
  colours, units, rigid placement, size and colour), stored as a binary
  container part; format change in pending/point-clouds.rs.
- engine: pointcloud.insert / edit / delete / list; doc.open opens a cloud as
  a new design; selection.delete takes point_clouds; view bounds include them.
- gpu: an instanced point-sprite pipeline (16 bytes a point).
- ui: Browser Point Clouds folder with eyes, menu and an Edit Point Cloud
  card; drop, Open, Insert and file dialogs take .ply/.xyz/.pts; a CPU
  viewport fallback; messages in en, en-GB, ja, zh, de and ko.

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

* Point clouds: colour by the file, one colour or height, from the UI (#5151)

The Edit Point Cloud card gets Color By (File Colors, Single Color,
Height). Height is a blue-to-red ramp over design Z, between the cloud's
own two heights or, by default, a range worked out from the points (all
but the lowest and highest 2 %, from an even sample). pointcloud.insert
and pointcloud.edit take color_by and height_range; pointcloud.list
reports them. The colour of each point is worked out in one place
(PointCloud::shade) for the GPU and CPU viewports.

Also: the card's dropdowns compare the picked index (a dropdown's
response doesn't report the pick, so File Units did not change either).

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

* Point clouds: integrate format registration and viewport caches (#5151)

* Point clouds: satisfy current UI audits and document comparisons (#5151)

Route model RGB data through the theme conversion boundary, provide a compact ribbon label, and include point clouds in the new document comparison. Gate the Unix-only CLI-link helper and test so Windows all-target checks remain buildable after the rebase.

* Point clouds: isolate translations for current main (#5151)

Move the existing 74 messages unchanged into one pending locale file and attribute it through a pending row, following the current repository workflow. Repository checks, 50 tooling tests, final workspace Clippy, wasm and generated-catalog equality checks passed after the rebase.

* Point clouds: fit the rebased code to main's GpuTarget and Frame

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

* Compare point clouds when reusing placement-independent results

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

* Point clouds: complete command help and required translations (#5151)

* Point clouds: size the save cap from the raised limit after the rebase

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
2026-10-28 21:17:02 +00:00
CamWight 866029aaa2 Engine: one sketch store for the sketch commands (W38.0, #5425) (#6336)
* Engine: one sketch store for the sketch commands (W38.0, #5425, no behaviour change)

The sketch commands found and kept the sketch they work on in about forty places, each its own
way. They now go through one module, cmd::sketch_store, so a drawing session (W38a/b) can put
sheet and view sketches behind the same calls without the commands changing again.

- target: the command's `sketch` param or the sketch being edited (target_sketch, moved).
- active: the sketch being edited, if any. The thirteen commands that need one keep their own
  refusal text; unifying the three texts is a separate, visible change.
- read and write: the only calls from the sketch commands to Document::sketch and sketch_mut.
- update: change a sketch on a copy of the document and keep both, the edit the text and link
  commands made by hand (sketch.text, sketch.edit_text, the projection, intersection and
  include links, sketch.fit_curves_to_section, sketch.break_link). As before, those paths
  neither solve the sketch nor run its checks.
- checked: the checks an edited sketch passes (the periodic splines), shared by edit_solved and
  sketch.offset.break.

edit and edit_solved stay in sketch.rs and load and keep their sketch through read and write.
Imported text, snap points and the face projections keep their own shape (a text that can't be
linked is skipped, snap points copy the document only when one is needed) through read and
write. The reads and writes that are about the design's own sketches stay as they are, each on
a line marked `// design-only:` (Redefine Sketch Plane, a joint's curve, a motion trace's wire,
and the session's live drag, parameter edits and `created` diff).

A source scan, each_sketch_command_goes_through_the_store, fails when a command reads or writes
a sketch, or asks for the active one, anywhere but the store or a marked design-only line in the
files that work on the design's sketches. Every existing sketch test is unchanged.

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

* Engine: the sketch store's scan also catches the active sketch taken as a value (#5425, review of #6336)

The scan only caught `active_sketch.ok_or`, so main's `let Some(id) = s.active_sketch else …`
could have come back unseen (0xlillith's review). It now also flags the value-read forms:
`) = s.active_sketch` (let-else and if let), `=> s.active_sketch` (a match arm),
`s.active_sketch.and_then(` and `s.active_sketch.map(`. State checks (`is_some`, `is_none`,
`== Some(`), assignments and snapshot copies stay allowed.

The wider scan found five commands that hand-rolled the store's `target` rule (a `sketch` param or
the sketch being edited). They now call `sketch_store::target`: sketch.inspect, sketch.glyphs,
sketch.export_dxf, inspect.curvature_comb and inspect.minimum_radius (their `curves` form).
Their refusals become target's:
- no `sketch` and none being edited: "no sketch is being edited" (was "no such sketch (and none
  is active)", or "no such sketch" for sketch.glyphs);
- an unknown name or id: "no sketch `X`" (an unknown id used to reach "that sketch is not
  evaluated");
- a feature that is not a sketch: "`X` is not a sketch";
- a `sketch` that is neither an id nor a name (true, a list, an object): refused,
  where it used to fall back to the sketch being edited. A null `sketch` still means none given
  (the scenario harness sends one): target now reads it that way for every sketch command
  (target_reads_a_null_sketch_as_none_given).
No test, doc or schema text named the old wording; the params docs ("sketch?: id|name (default
active)") still hold.

The two remaining value reads, the default sketch for a feature (features.rs) and the sketch a
measurement prefers (measure_sel.rs), go through `sketch_store::active`, with no change.

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

* Engine: a number in `sketch` is an exact sketch id, never a name (#5425, review of #6336)

`sketch_store::target` turned a JSON number into text and looked it up with
`Document::find_feature`, which falls back to a name when no feature has that id. So
`{"sketch": 999999}` could pick a sketch named "999999" whose id is something else, and export
or inspect it. sketch.export_dxf, sketch.glyphs and the two curvature inspects read an exact id
before they moved onto the store; the other sketch commands (the old `target_sketch`) and
sketch.inspect read a number as text.

One rule now, for every command that takes `sketch`: a number is an exact id (`as_u64`, then
the feature with that id); text is an id written out or a name, as before. A number that isn't
a whole number of 0 or more (negative, fractional) is refused with "`sketch` must be an id or a
name"; an id no feature has gives "no sketch `N`", as before. Nothing in the tests, docs,
scenarios or examples names a sketch with digits, so nothing relied on the old fallback.

Test (sketch_store): a sketch named "999999" with another id. sketch.export_dxf, sketch.glyphs
(with another sketch being edited, for its enabled check), inspect.curvature_comb and
inspect.minimum_radius (with a real curve id) refuse `{"sketch": 999999}`, find the sketch as
`"999999"` and by its own id, and refuse -1 and 1.5.

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

* Engine: MidPoint finds its sketch through the store too (#5425, after #5011)

#5011 added a caller of the old `target_sketch` (MidPoint's `line_stays`, which swaps a point
picked after its line) after this branch's base. It now reads the sketch through
`sketch_store::target` and `sketch_store::read`, as the other sketch commands do, so a number
there is an exact id too.

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

* Engine: standard parts fix their sketch's entities through the store (#5425, after #6376)

#6376 (a standard part's sketches are fully defined) reads the sketch being drawn straight from
the document's features, after this branch's base. `fix_all` now takes it from
`sketch_store::active` and `sketch_store::read`, as the store's scan requires, with the same
result.

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

* Engine: Toggle Diameter and Delete on a profile find their sketch through the store (#5425)

Two sketch commands landed on main after the store: sketch.toggle_diameter (#7729) still
called the removed sketch::target_sketch, and Delete on a picked profile (#7107) read the
sketch straight from the document. Both now go through sketch_store (target and read), and
the store's source scan passes again.

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

* Sketch dimensioned: use the sketch store's target after the rebase

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

* Sketch dimensioned: read the sketch through the store

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
2026-10-28 20:56:05 +00:00
Nathan Dane 85a60c1af6 Render output: Output editor, size presets, Check list, Copy, progress pill and result viewer (#6281) (#6433)
* Render output: size presets, Check list, Copy, progress pill, R and Esc, and the result viewer

* Complete Output GPU help translations and format choices

* Remove Output size labels already present in catalogs

* fix: compile the legacy finishing controls only for tests
2026-10-28 20:55:55 +00:00
semmelrinde 683535a18f Evaluation: a pattern of another component's body follows its own component when that moves (#8467)
A pattern of bodies copies them about an axis in its component's frame,
so its copies of a root body depend on where that component is placed.
placement_digest never counted a pattern's bodies, so moving the
component kept the cached copies. Read each body through placed_body,
as Combine does.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 20:49:55 +00:00
Logan Night d67c8e6231 Sketch text: Arabic and Hebrew are ordered right to left and joined (#8237) (#8324)
* claim: #8237

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

* Sketch text: right-to-left lines are ordered and shaped (#8237)

A line with Arabic or Hebrew letters is split into display-order runs by the
Unicode bidirectional algorithm (unicode-bidi) and each run is shaped with the
font's own rules (harfrust), so letters join and marks sit where the font puts
them. Lines without right-to-left characters keep the glyph-by-glyph layout.

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

* Sketch text: a line break inside a right-to-left line keeps the text after it (#8237)

The bidirectional algorithm ends a paragraph at a line break; every
paragraph is shaped now, not only the first, so measuring such a line
counts all of it.

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

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 20:31:02 +00:00
semmelrinde 2841210fb2 Sketch Dimension: choose horizontal or vertical from the two points' box (#7295) (#7355)
* claim: #7295

* Sketch Dimension: choose horizontal or vertical from the two points' box (#7295)

Two points or a slanted line got the aligned distance whenever the text
was between the perpendiculars through them, so straight above the middle
of nearly level points was aligned and horizontal needed a thin wedge.
Now the text position relative to the box the two span picks the type:
inside it or in its outer corners aligned, above or below horizontal,
beside it vertical. This is the rule midpoint pairs already used, so both
share auto_orientation().

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

* Sketch Dimension: aligned across the line, horizontal or vertical by the pull past an end

Review on #7355: the aligned length must stay the default, and the box
rule shrank its zone to a thin strip on a shallow line. Now `auto` is
the distance between the two points everywhere across from them
(between the perpendiculars through them, and a tenth of their distance
past), and only a clear pull past an end changes it: up or down from
that end gives the horizontal distance, sideways the vertical one. The
two meet on the diagonals through the end, so both zones stay wide on a
shallow or steep line. Midpoint pairs keep main's box rule.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 20:27:44 +00:00
github-actions[bot] 170f2555ce Regenerate generated files and fold pending files [skip ci] 2026-10-28 22:30:25 +00:00
raykholo 6b523d6839 Theme drops: build for the web, and import there too (#8226) (#8534)
DroppedFile::bytes exists only on native; the web build stopped at
take_theme_drops. Native keeps reading the file there. On the web, the
shell already reads every drop asynchronously before the app sees it: a
drop over Preferences' Import tile now arrives as a theme and goes to
prefs::import_theme_bytes, instead of being opened as a design.
2026-10-28 18:25:26 -04:00
github-actions[bot] 06c9479a3f Regenerate generated files and fold pending files [skip ci] 2026-10-28 22:22:05 +00:00
Frank d7ef5280c5 Ctrl+W closes the design shown, not the browser tab (#5366) (#5466)
Ctrl+W (Cmd+W) closes the active design, asking first when it is
unsaved; closing the last one shows the start page. In the web app a
capture-phase keydown listener keeps the browser from closing the tab
while a design is shown; on the start page the browser closes the tab.
Ctrl+W joins the keys the app reserves, and the tab menu's Close shows it.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 20:15:33 +00:00
froglemonade2 f595f38e1c Define a named parameter inline from any value input (#4071) (#7341)
* claim: #4071

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

* Define a named parameter inline from any value input (#4071)

Any value input, typed or sent as a command's expression parameter, takes
`name = expression` (or `#name = expression`): the design gets the user
parameter `name` with that expression, and the input stores `name`, in the
command's one undo step.

- doc: the lexer skips a definition's `name =`, so until the design stores
  it the text evaluates as its expression and commands read and check it
  like any value. `Document::define_inline` then turns every stored
  definition (feature inputs and placements, dimension parameters, sketch
  pattern and text values, joint and coupling values, sheet metal and
  plastic rules, canvas planes) into a user parameter, the unit following
  how the input reads a bare number. A taken, reserved or empty name, two
  different values for one name, and a definition in a parameter's own
  expression or a configuration cell are refused; all or nothing.
- engine: `Session::execute` runs it after any command whose parameters
  hold definition-shaped text; the reply has `defined` (the first name,
  as `sketch.dimension` already gives) and `definitions`. A refusal fails
  the command, which leaves the design as it was.
- `parameters.change` with `new = expression` renames the parameter it
  changes (a dimension's or feature input's own, references following)
  and sets it; when the value is just `new`, it sets `new`. `changes`
  refuses definitions. `expr.evaluate` reports `defines`.
- UI: lengths keep the name when the design unit is applied; sketch text
  sends a definition as typed and previews only its value; the box corner
  and offset Distance 2 use the value; fields whose command keeps no
  expression (sketch scale, move, chamfer) refuse a definition with the
  way to name it. The dimension editor says which parameter it named.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 20:14:36 +00:00
Colin Faulkingham a3ee8957f4 Sketch: conic curves take their own id prefix, not the constraints' k (#6173) (#8407)
* claim: #6173

* Sketch: conic curves take the id prefix n, not the constraints' k (#6173)
2026-10-28 20:12:48 +00:00
semmelrinde be2168f70d Snap Fit: the Base Height hint in the user's language (#8451)
The hint "at the face" had no catalog message, so it stayed English in
every language. Add it in English and the 26 other catalogs, and check
that every plastic dialog hint has a message.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 20:12:42 +00:00
Daniel 93066ab60c Select Other: a still hold opens the list while Orbit, Pan or Zoom is lit (#8437) (#8438)
* Select Other: a still hold opens the list while Orbit, Pan or Zoom is lit

With a mode lit in the navigation bar (or Space held), holding the left button still on overlapping geometry opened nothing, and the release then selected the top item as a click. The viewport treats such a mode as owning left drags: a plain click still selects, and every other check on the mode sits on a drag. The hold was the one place a press that had not moved counted as navigation.

The hold is now cancelled only by a navigation that has started (the drag past the click distance), so it behaves as it does with no mode lit. The hint and the user guide already describe the hold without that condition. A preset's modifier binding (Maya Alt, Alias Shift+Alt, HFSS Alt) and Ctrl+left orbit still cancel it at the press, as before: holding such a chord is a gesture to navigate.

Tests: holding still with each mode lit opens the list, picks from it and a click still selects; a drag under each mode still moves the view and opens nothing; Maya's Alt chord held still does not open the list (it fails without the preset check).

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

* Select Other: the press that opened the list does not move the view

egui calls a press a drag once it has lasted 0.8 s. With Orbit, Pan or Zoom lit, the press that opened Select Other at 0.5 s then became an orbit, pan or zoom, and moving the pointer to a row turned the view under the open list. While the list is open, a primary press no longer takes the lit mode.

Test: a press held past 0.8 s that then moves keeps the list open and the view still, for each mode.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 20:07:33 +00:00
Colin Faulkingham 90ef106e5f Preferences: the File view entry and the search field carry the window's name (#8318) (#8432)
* claim: #8318

* Preferences: the File view entry and the search field carry the window's name (#8318)

The File view's entry that opens Preferences said "Settings", and the window's
search field said "Search settings" / "No matching settings", while the window
title, the File menu and the help texts around the app say "Preferences".  The
strings that name the window or point to it now use "Preferences"; ids unchanged.

Translations of these ids followed the English source and keep their wording
until the language teams catch up.

Tests: home::tests::the_entry_that_opens_preferences_carries_the_windows_name
and prefs_search::tests::the_search_field_names_the_window_it_searches fail on
main ("Settings" vs "Preferences") and pass here; the prefs_search:: and home::
suites pass, and the full solidcraft-ui-egui lib suite passed in `cargo xtask ci`.
2026-10-28 20:06:24 +00:00
froglemonade2 02ac56d12f Feature windows choose Join, New Body or Cut from what the material touches (#6901) (#6903)
* claim: #6901

* Feature windows choose Join, New Body or Cut from what the material touches (#6901)

Extrude, Revolve, Loft, Sweep, Coil and the Box, Cylinder, Sphere and Torus choose
their operation by running the Join on a copy of the design off the window thread
and counting the pieces: Join when the material makes one body with what it
touches, New Body when it would be a separate piece, Cut when it lies inside a
body (Revolve and the four primitives; Extrude keeps its own Cut). The choice
changes only when a check is in, a pick in the Operation row (the shown option
included) stops it for good, and OK waits for a running check. Hidden bodies take
no part: with some hidden, the shown ones become the feature's targets.

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

* Operation row: a pick in Extrude stops its own choice at once; ja/zh strings, gallery entry

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

* Operation check: contact test first, mesh volumes for Cut, OK takes the evaluated Join

Material touching no body is a New Body without running the Join (the contact
test costs 0-240 ms where the Join costs seconds on a large body). Inside is told
from display-mesh volumes instead of exact ones (8 s on a 400-hole plate). The
check evaluates the Join through Session::ahead, and OK of the same commands
takes it (Session::prime) rather than computing it again.

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

* Preference: Choose the operation automatically (on by default)

Preferences > Design gets "Choose the operation automatically" (auto_operation,
on by default). Off, feature windows don't choose Join, New Body or Cut from
what their material touches: each keeps the operation it opens with (Extrude
its own Join or Cut), no check runs and OK doesn't wait for one, as before

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

* Arabic, Hebrew, Vietnamese and Turkish strings for the operation choice and its preference

Arabic, Hebrew and Vietnamese are complete catalogs now, and Turkish has a full
catalog, so this change's three strings get them too.

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

* Operation check: a closed window's check stops; the icon audit doesn't choose

A window's operation check kept running after the window closed, a Join on
the design for nothing, and the test harness waited for it after every step:
the icon audit, which opens every window, took ~30 min instead of ~1. The
check now runs under a Progress, and closing the window (or one that doesn't
choose) abandons it: its evaluation stops at the next feature and nothing
waits for it. The icon audit turns auto_operation off, as it only looks at
icons (75 s again).

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

* Box placement test: the window keeps its New Body

The test #6248 added places a box that overlaps Base and expects a second body;
with #6901 the window would join or cut it. The test is about where the box
goes, so it turns auto_operation off.

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

* Join pieces: read the target lists leniently after the rebase

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
2026-10-28 19:57:27 +00:00
Lars Jansen 261d00fbb2 History and browser: a sketch's icon in the colour of its state (#7245) (#7250)
* claim: #7245

* feat(ui): a sketch's icon in History and the browser takes the colour of its solve state (#7245)
2026-10-28 19:40:45 +00:00
raykholo e2935aff95 Stand-up offer: a note only when the part seems to lie on its back (#7717) (#7718)
* claim: #7717

* Stand-up offer: a note only when the part seems to lie on its back (#7717)

* Stand-up note: an unclear shape still gets the note

* Stand-up note: a Don't ask again button, as the dialog has

* Control protocol: the stand-up settings in the sorted ui.prefs list (#7717)

* Stand-up note: the control import test asks as before (#7717)

The test checks the caller's `up`, so it sets the setting to Ask and every
file from another program still gets the offer.
2026-10-28 19:40:25 +00:00
sascha2101 a95f30f186 Sketch: Center Point Arc follows the cursor clockwise or counter-clockwise (#6645) (#6664)
* claim: #6645

* Sketch: Center Point Arc follows the cursor clockwise or counter-clockwise (#6645)

The centre-point arc always ran counter-clockwise from its start; a
clockwise arc needed a typed negative sweep. The tool now tracks the
signed turn the cursor makes round the centre after the start is placed,
and its sign sets the direction of the preview, the live Sweep box and
the created arc.

sketch.arc.center_point takes `clockwise` with a clicked end: the arc is
stored counter-clockwise from the end and reversed, as a negative sweep
is, so the start keeps its radius and `<id>.start`.

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

* Sketch: keep the old counter-clockwise centre-point arc behind a preference (#6645)

Preferences > Sketch gets "Center point arc follows the cursor's
direction" (center_arc_follows_cursor, on). Off, the arc runs
counter-clockwise from the start whichever way the cursor went, as it
always did. Its strings are a pending locale file (en, de).

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

* Sketch: Japanese and Chinese strings for the centre-point arc preference (#6645)

ja and zh are complete catalogs, so the new preference's label and tip
need them too.

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

* Sketch: Arabic, Hebrew and Vietnamese strings for the centre-point arc preference (#6645)

Arabic, Hebrew and Vietnamese became complete catalogs on main, so the new
preference needs them too.

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

---------

Co-authored-by: sascha2101 <221609842+sascha2101@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 19:32:13 +00:00
semmelrinde 429c2b44b5 Russian: translate the Render workspace's 733 messages (batch 2) (#8251)
* Russian: translate the Render workspace's 706 messages (batch 2)

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

* Russian: the 27 Render messages main gained since (the Appearances panel)

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 19:06:50 +00:00
github-actions[bot] 28b8da3126 Regenerate generated files and fold pending files [skip ci] 2026-10-28 21:55:57 +00:00
github-actions[bot] 9ddbcac775 Regenerate generated files and fold pending files [skip ci] 2026-10-28 21:46:59 +00:00
semmelrinde 9628e60ca0 Sketch: Reverse Spline Direction (#8224) (#8414)
A new command, sketch.spline.reverse, runs a spline the other way: its
point list is reversed in place, so its last point becomes its first and
the curve keeps its path (chord-length fits, uniform clamped knots and
closed fits read the same both ways). Points and constraints stay. A
projected spline and one an offset uses or makes are refused. The
sketch's right-click menu offers it for one selected spline.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:56:34 +00:00
Logan Night bcdee6e0bb Sketch on a face: a round's edges that end a hair apart still close (#8284) (#8386)
* claim: #8284

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

* Sketch on a face: a round's edges that end a hair apart still close (#8284)

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

* Sketch on a face: test both ends of a rounded loft end to end (#8284)

A square lofted to a circle with its side edges rounded, as in the
report: a sketch on either end face takes the whole face. Without the
fix the circular end fails with "face projection has a branched
boundary". Also drops a redundant term from the join tolerance.

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

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 18:56:20 +00:00
Colin Faulkingham c2e69ed724 Timeline: the feature menu's suppress item follows the reader's language (#8301) (#8383)
* claim: #8301

* Timeline: the feature menu's suppress item follows the reader's language (#8301)

Suppress Features / Unsuppress Features were raw literals. Menu::label()
translates a label only when a catalog entry carries it, and none did, so the
item stayed English in every language, the Japanese UI included.

The two labels are now the messages timeline.suppress_features and
timeline.unsuppress_features in a new assets/locales/pending file: the English
wording is unchanged, and the complete catalogs (ar, he, ja, vi, zh) plus de and
ko carry the translations.

cargo test --profile ci -p solidcraft-ui-egui --lib -- timeline::tests::the_feature_menu_suppress_item_follows_the_language
fails on the claim commit (the Japanese menu paints the English literal) and
passes with the change.
2026-10-28 18:55:37 +00:00
semmelrinde 94d23338fe Render: the Appearances panel and the shots strip in 15 languages (#8417)
The Appearances panel's 27 messages for Dutch, Hungarian, Galician,
Malay and Norwegian Bokmål, which their merged Render batches missed,
and the shots strip's 7 newer messages for those and ten more.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:54:59 +00:00
Jasper van Loenen 728100c26d UI: Fix settings autoscroll bug (#8388)
* Fix issue with settings window automatically scrolling to selected item, preventing scrolling by user

* Fix issue with settings window automatically scrolling to selected item, preventing scrolling by user
2026-10-28 18:54:23 +00:00
Daniel b470aebe80 Toolbar: Sketch Create drop-down rows share the menu's width (#8409)
In the Sketch > Create drop-down a plain row such as Point was drawn
200 pt wide, the minimum width its labels fit in, while a family row such
as Slot filled the menu. The menu was as wide as the one-line pin hint in
its footer, so the plain rows' hover background and pin stopped short of
the right edge.

The Create menu now sets its width from its rows before drawing them, as
the other panel drop-downs do, and passes that width to the plain rows
instead of recomputing it over a subset. Family rows fill the same width
and the footer hint wraps inside it. The overflow menu's plain rows take
the width of all its commands in the same way. A family's submenu still
sizes its variants to their own labels.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:53:32 +00:00
MarkusRe 05f62b09e0 Browser menus: V runs the open menu's Show/Hide on the row it was opened for (#8377)
Most browser menus show V beside Show/Hide, but the V key always ran
toggle_visibility, which acts on the selection. A right-click on a body or
sketch row also selects it, so the two agreed there; a right-click on the
Bodies, Sketches or Origin folder, a group, the component or an origin plane
does not, so V did nothing, or flipped a body selected earlier, and left the
menu open.

With a menu open, V now runs that menu's row whose shortcut is V, through
run_item as a click does, and closes the menu (context_menu::press_shortcut).
A disabled row takes the key and does nothing. With no menu open, or a menu
without such a row, V still acts on the selection (#4128, #4409: V stays
selection-based).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:23:26 +00:00
bherbruck 5d1c33fc82 Docs: the macOS app is notarized, and the update check names tangency-labs/solidcraft (#8402)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:15:03 +00:00
bherbruck d26de76097 Updates: leave the update to the launcher that manages this copy (#8403)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:14:57 +00:00
austin62jake 65784cae2f Save As offers the open file's own name, not the name stored in the design (#8329) (#8354)
* claim: #8329

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

* Save As offers the open file's own name (#8329)

Save As, Save As JSON and Save a Copy proposed the design's name, the one
stored inside the file. Copies of a file under other names keep the
stored name, so "Sample Plate-bug.solidcraft" was offered
"Sample Plate.sc", the same as its sibling, and one replaced the other.

Session::save_as_name() offers the stem of the open file unless the
design was renamed since it was opened or last saved, and the design's
name otherwise (a new design, or one renamed in the Browser).

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-28 18:11:06 +00:00
semmelrinde 003d388b78 Sketch: an arc slot is drawn without a redundant Equal on its caps (#8355) (#8395)
Each cap is tangent to both concentric sides, so its radius is already
half the gap between them and the two caps are equal. The Equal the arc
slot added between them was always redundant; it is no longer added.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:09:47 +00:00
Jasper van Loenen 562d066c0e Fix endless redraw of settings window if setting has empty row, caused by storing setting in row of previous setting (#8399) 2026-10-28 18:07:52 +00:00
github-actions[bot] eaf6ef741c Regenerate generated files and fold pending files [skip ci] 2026-10-28 21:14:05 +00:00
github-actions[bot] 3026cca31a Regenerate generated files and fold pending files [skip ci] 2026-10-28 21:06:56 +00:00
semmelrinde f0095a1ae6 Ribbon: a two-line button label stays inside the button's highlight (#8391) (#8405)
A button is 60 points tall and its label starts 32 points down, so two
lines of the label font (31 points) ran 3 points past it. Two-line
labels now take the font size plus 2 points per line and start higher
when they would still run past the button, leaving 2 points below.
One-line labels are unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:07:43 +00:00
semmelrinde aca93d1557 Swedish: translate the 203 messages main gained since batch 1 (batch 3) (#8387)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:07:40 +00:00
N3MI-DG 6fffd745fd Insert Part: the Length list opens on the default length, with no "Default" item (#8020) (#8022)
* claim: #8020

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

* Insert Part: the Length list opens on the default length (#8020)

The list carried an item called "Default" above the standard lengths. It
never said which length it stood for, and one of the lengths below it
always was that length, so the list offered the same length twice under
two names. Change Part Size has always listed concrete lengths.

The item is gone and the list opens on the length the part would be given
anyway. The default cannot be worked out in the dialog, because it is not
one rule: a cap screw or bolt takes the first standard length at or above
2.5 d, a set screw the first at or above d, a dowel pin the first at or
above 4 d. parts.library now reports default_length beside each size's
table values, taken from the shape the engine would build, and the dialog
selects it; the test checks that report against what an insert without a
length is actually built to, for every size of every family.

Leaving the length alone behaves as "Default" did: a new size resolves
the list again, so M6 to M12 moves the selection from 20 to 30. A length
chosen by hand stays until the size changes.

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

* Insert Part: the labels test skips default_length too

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
2026-10-28 17:58:03 +00:00
Tyler Gregorcyk 4a7a8f31f8 Sphere, Torus and Cone dialogs: place them by their centre or base (#5638) (#8328)
* claim: #5638

* Sphere, Torus and Cone dialogs: place them by their centre or base (#5638)

The Box and Cylinder dialogs already had typed X, Y and Z placement
fields; the Sphere, Torus and Cone dialogs now do too. Sphere and Torus
show Placement: Center and take a picked point like the Box; Cone shows
Placement: Base Center like the Cylinder. The fields take expressions,
and Edit Feature shows and keeps the stored centre or base.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:39:31 +00:00
Daniel a1a801fb31 Feature dialog: draw the busy spinner in the footer row (#8362)
With the dialog buttons at the bottom, the spinner shown while the preview
computes or OK applies was painted on its own after the footer: at the
card's left edge and one control height up from its bottom. The footer
insets its row by 12 pt at the sides and 10 pt above and below and centres
the buttons, so the spinner sat outside that inset and about 10 pt below
the buttons' centre line.

Panel::feature now draws the spinner through the footer's left slot, so it
shares the row's inset and vertical centre, in the space the footer already
leaves for it before the status text. The rect is published as
dialog:footer:spinner, and a panel test checks it lies in the footer row,
at its left inset and on the OK button's centre line. The spinner in the
title row (buttons at the top) is unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:32:56 +00:00
semmelrinde 2a6e7105cc Italian: translate the 159 messages main gained since batch 1 (batch 3) (#8361)
* Italian: translate the 132 messages main gained since batch 1 (batch 3)

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

* Italian: the Appearances panel's 27 Render messages

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:32:56 +00:00
semmelrinde 035e065b27 Russian: translate the 247 messages main gained since batch 1 (batch 3) (#8363)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:32:21 +00:00
semmelrinde 67bda4dfd1 Malay: translate the 199 messages main gained since batch 1 (batch 3) (#8368)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:32:17 +00:00
semmelrinde aba05eb951 Dutch: translate the 199 messages main gained since batch 1 (batch 3) (#8366)
* Dutch: translate the 199 messages main gained since batch 1 (batch 3)

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

* Dutch: Spoed for a thread's pitch, as the catalog's Pitch (review)

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:31:27 +00:00
semmelrinde b8bae7222a Japanese: translate the 29 Render entries that still held English (#8357)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:31:27 +00:00
semmelrinde 32eea80c98 Czech: translate the 203 messages main gained since batch 1 (batch 3) (#8376)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:31:21 +00:00
semmelrinde fa19a49db9 Indonesian: translate the 247 messages main gained since batch 1 (batch 3) (#8364)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:31:20 +00:00
github-actions[bot] 6ef3d71e02 Regenerate generated files and fold pending files [skip ci] 2026-10-28 20:37:10 +00:00
semmelrinde bd6e8ef567 French: translate the 149 messages main gained since batch 1, Render included (batch 3) (#8379)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:30:29 +00:00
semmelrinde 5df84bbaf5 Turkish: translate the 340 messages main gained since batch 1, Render included (batch 3) (#8380)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:30:08 +00:00
github-actions[bot] eaea3e9087 Regenerate generated files and fold pending files [skip ci] 2026-10-28 20:04:18 +00:00
github-actions[bot] c03a43c88c Regenerate generated files and fold pending files [skip ci] 2026-10-28 19:54:52 +00:00
semmelrinde b32448f713 Ukrainian: translate the 340 messages main gained since batch 1, Render included (batch 3) (#8381)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:29:47 +00:00
semmelrinde f3a23d1c75 Galician: translate the 199 messages main gained since batch 1 (batch 3) (#8367)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:29:34 +00:00
semmelrinde 2dc406ae86 Norwegian Bokmål: translate the 198 messages main gained since batch 1 (batch 3) (#8375)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:24:56 +00:00
Nathan Dane 293ed5fab7 Render inspector: Appearance editor with the look chain, scope, mixed values and Advanced (#6281) (#6432)
* Render inspector: selection-following appearance panel with the look chain, Applies to and edit controls

* Render appearance: Glass, Clear coat, Maps, Saved optics and Reflections sections

* Update Render navigation tests for dedicated editors

* Exercise Focus keyboard protection through the Library search field
2026-10-28 17:23:16 +00:00
semmelrinde d2456921ca French: translate the 295 messages the catalog was missing (#8012)
* French: translate the 205 messages the catalog was missing

The French catalog lacked 205 of the English messages, so parts of
Preferences, the hole presets, the slicer messages, the Render
workspace and the Assistant showed in English. They are translated in
one pending file, with the catalog's terms (Esquisse, Fonction,
Bossage, Appui, Clip, Prise de vue) and its non-breaking space before
: ; ? and !.

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

* Translate the 90 messages main added since (the navigation bar's)

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:22:21 +00:00
semmelrinde 93d1e09bf7 Hungarian: translate the 203 messages main gained since batch 1 (batch 3) (#8373)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:09:02 +00:00
semmelrinde dddae33285 Romanian: translate the 203 messages main gained since batch 1 (batch 3) (#8374)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:08:54 +00:00
semmelrinde 1eec6d90b7 Sketch text: Bold in a variable font sets its weight axis (#8211) (#8331)
A variable font (one file for every weight, as most web fonts ship) has no face marked bold,
so Bold failed with "has no bold face". Without a bold face, a face whose wght axis reaches past
semibold is now set to weight 700. ttf-parser gets its variable-fonts feature for that.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:08:27 +00:00
semmelrinde 405217c3e5 Czech: translate the Render workspace's 584 messages (batch 2) (#8323)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 16:58:48 +00:00
austin62jake 86342b8c5c DXF import: an elliptical arc comes in as exact conic arcs, not a fitted spline (#8155) (#8241)
* claim: #8155

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

* DXF import: an elliptical arc comes in as exact conic arcs (#8155)

A partial ELLIPSE (how Fusion, and SolidCraft's own DXF export, write an
elliptical arc) was read as a degree-3 spline fitted through 16 samples,
which strays from the ellipse between them: 0.84 mm on a 300-degree arc
of a 200 x 40 mm ellipse. It is now read as exact rational-quadratic
conic pieces of at most a quarter turn each, which the sketch keeps as
conics. A conic arc written by SolidCraft's DXF export now reads back as
the same conic.

The arc's sweep is taken with rem_euclid instead of a loop that added a
turn at a time, which ran for billions of turns on a start angle of 1e11.

sketch.drawing_bounds and the width/height sizing of sketch.insert_dxf
measure a conic by points along it, not by its apex, which can lie
outside the curve.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-28 16:56:17 +00:00
Colin Faulkingham 95ba99d710 Move/Copy: a triad drag ends where the button is released (#8265) (#8309)
* claim: #8265

* Move/Copy: a triad drag ends where the button was released (#8265)

A frame carries all the input queued since the last one, so the release
that ends a drag and further pointer movement — the hand keeps moving as
the button goes up, or input is queued during a busy frame — arrive
together, and the frame's latest pointer position is already past the
release. The drag's final change was read from it, so the value, the
preview and the command both jumped to wherever the mouse had got to;
pressing Enter right after the release closed the window first, which is
why that worked around it. Rotate and occurrence moves share the triad.

The final change now comes from the release event's own position, which
the raw input carries (egui's processed pointer events drop it). The
regression test sends a release at 20 mm and a move to 30 mm in one frame:
30 mm before, 20 mm after.

Fixes #8265.
2026-10-28 16:53:41 +00:00
raykholo dd2e44bb12 Raise the largest file SolidCraft opens with a setting or launch flag (#4086) (#7565)
* claim: #4086

* Raise the largest file SolidCraft opens with a setting or launch flag (#4086)

* Translate the file size limit strings

* Size limits: design files are .sc now, .solidcraft still read
2026-10-28 16:52:29 +00:00
github-actions[bot] 506f79c092 Regenerate generated files and fold pending files [skip ci] 2026-10-28 19:23:10 +00:00
semmelrinde 2b7159f363 Romanian: translate the Render workspace's 733 messages (batch 2) (#8343)
* Romanian: translate the Render workspace's 733 messages (batch 2)

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

* Romanian: the Render tree's switch hint is a verb

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 16:41:23 +00:00
semmelrinde 895264c4cd Swedish: translate the Render workspace's 733 messages (batch 2) (#8344)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 16:41:17 +00:00
github-actions[bot] 48ba6ff3c6 Regenerate generated files and fold pending files [skip ci] 2026-10-28 18:54:51 +00:00
Homero Thompson f103468dba Params: a list parameter refuses a wrong type instead of losing entries (#6651) (#6683)
* claim: #6651

* test(#6651): a list parameter of the wrong type is refused

A list parameter such as a cut's `targets` or an export's `bodies` loses entries without an error: a value of the wrong type reads as an empty list, which most commands take to mean "every body". The regression test drives the engine the way a user does: two bodies, a box cut that crosses both and is meant for one of them, and a `targets` of the wrong type. The command must refuse it and leave the design as it was.

* fix(#6651): a list parameter refuses a wrong type instead of losing entries

A list parameter of the wrong type was read as an empty list, and an empty
list means "every body" to most commands: a cut meant for one body cut every
body it touched and the command reported success.

A wrong type is now an error that names the parameter, the offending entry
index and the value it got. A list longer than 10000 entries is an error
instead of being cut short. `null` stays absent, as the schema contract says,
and a plain string is still a list of one. The 80 call sites now propagate the
error instead of losing entries; the commands that take feature keys, which
may be named or numbered, read them with their own reader.
2026-10-28 16:25:53 +00:00
Lars Jansen dca332201f Status bar: the sketch's state, what is being edited and the design's units (#7243) (#7248)
* claim: #7243

* feat(ui): status bar with the sketch's state, what is being edited and the design's units (#7243)
2026-10-28 16:25:32 +00:00
bherbruck 37a586dd61 Release 0.25.1 2026-10-27 20:58:30 -04:00
bherbruck a79f40a711 Release 0.25.0 2026-10-27 18:05:39 -04:00
bherbruck b5728f284b Release 0.24.1 2026-10-27 08:34:27 -04:00
bherbruck 5965439a15 Release 0.24.0 2026-10-26 19:23:21 -04:00
bherbruck 586a358ed4 Release 0.23.1 2026-10-26 07:07:21 -04:00
bherbruck 7770f8982d Release 0.23.0 2026-10-25 18:18:39 -04:00
bherbruck 72ad6383ef Release 0.22.1 2026-10-25 10:38:39 -04:00
bherbruck 0537623a75 Release 0.22.0 2026-10-24 18:53:43 -04:00
bherbruck 65d288227e Release 0.21.0 (rc.2's code, version only)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 20:51:08 -04:00
bherbruck fedb3bcdb1 Release 0.21.0-rc.2 2026-10-23 17:14:38 -04:00
bherbruck 097b8b7550 Release 0.21.0-rc.1
The tree is main at f120285c9 exactly; the previous release commit is kept as a parent so release only moves forward.
2026-10-23 13:48:05 -04:00
bherbruck af7ca3a8a2 Release 0.21.0-rc.1
The tree is main at b31fddb98 exactly; the previous release commit is kept as a parent so release only moves forward.
2026-10-23 13:37:43 -04:00
bherbruck 27ddb725c7 Release 0.21.0-rc.1
The tree is main at 2bf3d4e11 exactly; the previous release commit is kept as a parent so release only moves forward.
2026-10-23 12:10:02 -04:00
bherbruck a06bdf5cfc Release 0.20.0
The tree is main at 030187452 exactly; the previous release commit is kept as a parent so release only moves forward.
2026-10-22 18:34:40 -04:00
bherbruck 33fdcdb3a0 Release 0.19.0
The tree is main at 5899e5b6c exactly; the previous release commit is kept as a parent so release only moves forward.
2026-10-21 21:13:33 -04:00
bherbruck c07ab5f8f9 Release 0.18.4
The tree is main at 728f753b8 exactly; the previous release commit is kept as a parent so release only moves forward.
2026-10-21 12:26:34 -04:00
bherbruck fea32f8aac Release 0.18.3
The tree is main at 034aa60d4 exactly; the previous release commit is kept as a parent so release only moves forward.
2026-10-21 08:46:37 -04:00
bherbruck 4927214976 Release 0.18.2
The tree is main at 57bcc4083 exactly; the previous release commit is kept as a parent so release only moves forward.
2026-10-20 22:09:18 -04:00
bherbruck b621819c0c Release 0.18.1
The tree is main at 522ae7755 exactly; the previous release commit is kept as a parent so release only moves forward.
2026-10-20 17:39:11 -04:00
bherbruck ec552a5300 Release 0.18.0
The tree is main at c3151fb7f exactly; the previous release commit is kept as a parent so release only moves forward.
2026-10-20 16:28:59 -04:00
bherbruck e0cc740cc3 Release 0.17.0
The tree is main at 511faedcf exactly; the 0.16.x release commits are kept as a parent so release only moves forward.
2026-10-20 11:41:48 -04:00
bherbruck 5de48e916d Release 0.16.2 2026-10-19 00:39:30 +00:00
bherbruck 80af7bbe3e Release 0.16.2
Version 0.16.2: 0.16.1 with the rigid orbit fix (#2753). Same design format (27).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 00:38:53 +00:00
bherbruck 191e983423 Fix orbit about the picked point (regression in 0.16.0) (#2753)
Orbiting about the point under the cursor (or the origin) turned the camera
about its target and then panned to put the pivot back. The pan works at the
target's depth with the parallel-view scale, so in a perspective view the
pivot slid off its pixel and the model swung about the camera.

The camera now turns rigidly about the pivot: Camera::orbit_about keeps the
pivot's coordinates in the camera's frame and rebuilds the target from the
new orientation, so the pivot stays exactly on its pixel in both projections.
The Shift snap spring and view.navigate's pivot orbit use the same.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit e88779218)
2026-10-19 00:38:52 +00:00
bherbruck 2b9445b049 Release 0.16.1 2026-10-18 20:16:21 +00:00
bherbruck 7ddf48ca16 Release 0.16.1
Version 0.16.1: 0.16.0 with the orbit fix (reverts #2142). Same design format (27).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 20:12:04 +00:00
bherbruck d2f6848377 Revert #2142: orbit about the picked point like 0.15.0
#2142 made orbiting about the point under the cursor turn the camera in place instead of turning the part about that point. Restore the 0.15.0 behaviour.

This reverts commit cac4bccea8.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 20:12:00 +00:00
bherbruck 0b6d6fecd8 Release 0.16.0 2026-10-18 18:16:11 +00:00
bherbruck fa691707c2 Release 0.15.0 2026-10-17 13:54:24 +00:00
bherbruck b9a55c74e2 Release 0.15.0
Version 0.15.0; the pending design-format changes are folded into format 26.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 13:53:44 +00:00
bherbruck 2bb9c81069 Release 0.14.0 2026-10-16 09:19:14 +00:00
bherbruck cedb51b8ea Release 0.14.0
Version 0.14.0; the pending design-format changes are folded into format 25.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 09:19:10 +00:00
bherbruck 66d13059dd Release 0.13.1 2026-10-15 21:56:43 +00:00
bherbruck 800ea40c0d Release 0.13.1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 21:56:35 +00:00
bherbruck 20442eecd3 Agent chat on Windows: start npm agents through their .cmd shim
npm puts a Unix shell script with the bare name (codex-acp,
claude-code-acp, gemini) beside the .cmd shim. The lookup tried the bare
name first, so every npm-installed agent failed with "not a valid Win32
application (os error 193)". Windows now only considers .exe/.cmd/.bat,
and a command saved with the bare path runs the shim beside it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 60ea8c69d2)
2026-10-15 21:51:01 +00:00
bherbruck 22d1cd7032 Release 0.13.0 2026-10-15 20:34:13 +00:00
bherbruck 6a0dc18655 Fix the Windows 32-bit and macOS release builds
On 32-bit Windows SetWindowLongPtrW is SetWindowLongW, which takes and
returns i32, so the window-procedure hook didn't compile for i686.
The macOS app bundle now signs the bundled solidcraft-cli before the
bundle itself; codesign refuses a bundle with an unsigned helper.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 94e76cd97e)
2026-10-15 20:34:12 +00:00
bherbruck e006dbf1af Release 0.13.0
The tree is the 0.13.0 version commit (8241a750fa) on main exactly; the previous release is kept as a parent so release only moves forward.
2026-10-15 19:59:50 +00:00
bherbruck 8241a750fa Release 0.13.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 19:59:42 +00:00
bherbruck 41779cf7bb Release 0.12.0: release branch follows main again after the 0.11.1 patch
The tree is main at c36a6f9f exactly; the 0.11.1 patch commit is kept as a parent so release only moves forward.
2026-10-15 13:32:01 +00:00
bherbruck b01b9689c8 Release 0.11.1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:41:41 +00:00
bherbruck 4426da6f5d Windows: start Direct3D 12 first, and Vulkan only without a D3D12 GPU (#1190)
* Windows: start Direct3D 12 first, and Vulkan only without a D3D12 GPU

wgpu started Vulkan, D3D12 and OpenGL together and preferred Vulkan, so
every Windows start loaded all installed Vulkan drivers. An old Intel
Vulkan driver (igvk64.dll) crashed 0.10.1 at startup inside the driver.

The app now probes D3D12, then Vulkan, then OpenGL, one backend at a
time, and starts only the first with a hardware adapter (WARP when none
has one). WGPU_BACKEND still wins; graphics.json's "backend" sets the
order by hand; the software renderer (safe mode, the retry) uses D3D12
alone. A new Windows CI job starts the real app under several settings.

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

* CI: a test filter YAML parses

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

* CI: the startup smoke test writes graphics.json where the app reads it

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

* CI: run the WARP shader tests one at a time

WARP's shader JIT (d3d10warp.dll) crashed twice in 124 runs of the gpu
tests with four test threads, with a null read inside its own optimizer
while several devices compiled compute pipelines at once. Serial runs
did not crash.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 95ea05e7c8)
2026-10-14 20:41:37 +00:00
bherbruck 5ead82e627 Release 0.11.0: release branch follows main again after the 0.10.1 patch
The tree is main at cccbe125 exactly; c29b0d2f (0.10.1) is kept as a parent so release only moves forward.
2026-10-14 20:07:41 +00:00
bherbruck c29b0d2f13 Release 0.10.1 2026-10-14 14:41:19 +00:00
bherbruck 480fd8d517 Fix the startup crash on D3D12 with FXC: CPU fallback for the tessellation pipeline, FXC-friendly shader (#1098) (#1099)
* GPU tessellation: a pipeline the device cannot build falls back to CPU meshing instead of crashing (#1098)

Build the tessellation compute pipeline inside error scopes; TessGpu::new returns an error,
and the viewport logs it and meshes bodies on the CPU. A Windows CI job builds the pipeline
with D3D12's FXC on the WARP adapter.

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

* Tessellation shader: basis functions in one-dimensional local arrays, so D3D12's FXC compiles it (#1098)

FXC has to unroll loops that write an array inside a struct (Basis.n0/n1) or a 2-D array
(ndu) by a counter, and cannot with run-time trip counts (X3511). A2.2 basis functions in a
local 1-D array returned whole; derivatives from the degree p-1 functions; the pole step
offsets computed rather than indexed.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 14:41:16 +00:00
bherbruck 4c09fa5355 Hovered and selected faces light solidly again over GPU-drawn bodies (#1064)
Since the model's mesh went back to the reference mesher (#940), the GPU viewport drew
opaque bodies from their display plans while hover and selection lit faces with the
reference mesh's triangles. Two triangulations of the same face at equal depth fought, so
a hovered face shimmered in wedges instead of lighting whole.

Faces lit over a body drawn from its plan now use the plan's own triangles
(ModelBody::plan_mesh, evaluated once on the CPU), and the highlight pass gets a small
depth bias toward the eye so f32 rounding between the GPU and CPU evaluations of the same
triangles cannot speckle it either.

Test: every face of a box with a pocket and a round boss, hovered and rendered offscreen
through the viewport's real pipelines on the GPU path, lights the same pixels as on the
CPU path (skipped without a GPU adapter).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 12:56:47 +00:00
262 changed files with 22764 additions and 1372 deletions
+1
View File
@@ -602,6 +602,7 @@ Original SolidCraft drawings, Open for owner review; no runtime assignments.
| assets/locales/sr.json | Tarik Sijercic (tsijercic1) | Original Serbian (ekavian, Cyrillic) translation of SolidCraft's English catalog; no commercial CAD text used | MIT OR Apache-2.0 |
| assets/icons/ui/design/svg/model/finish_edit.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
| `assets/locales/pending/sketch-tracking-guides.json` | SolidCraft contributors | Original alignment-guide preference labels and help text | MIT OR Apache-2.0 |
| assets/locales/pending/point-clouds.json | SolidCraft contributors | Original point-cloud UI strings and translations | MIT OR Apache-2.0 |
## Code adapted from sibling projects
Generated
+27 -21
View File
@@ -1692,9 +1692,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"r-efi 6.0.0",
"rand_core 0.10.1",
"wasm-bindgen",
]
[[package]]
@@ -4208,7 +4210,7 @@ dependencies = [
[[package]]
name = "solidcraft"
version = "0.25.1"
version = "0.26.0"
dependencies = [
"block2 0.6.2",
"eframe",
@@ -4243,11 +4245,11 @@ dependencies = [
[[package]]
name = "solidcraft-3dx-client"
version = "0.25.1"
version = "0.26.0"
[[package]]
name = "solidcraft-cli"
version = "0.25.1"
version = "0.26.0"
dependencies = [
"image",
"log",
@@ -4259,7 +4261,7 @@ dependencies = [
[[package]]
name = "solidcraft-derive"
version = "0.25.1"
version = "0.26.0"
dependencies = [
"proc-macro2",
"quote",
@@ -4268,7 +4270,7 @@ dependencies = [
[[package]]
name = "solidcraft-doc"
version = "0.25.1"
version = "0.26.0"
dependencies = [
"log",
"serde",
@@ -4285,7 +4287,7 @@ dependencies = [
[[package]]
name = "solidcraft-engine"
version = "0.25.1"
version = "0.26.0"
dependencies = [
"image",
"log",
@@ -4306,7 +4308,7 @@ dependencies = [
[[package]]
name = "solidcraft-geom"
version = "0.25.1"
version = "0.26.0"
dependencies = [
"serde",
"serde_json",
@@ -4315,7 +4317,7 @@ dependencies = [
[[package]]
name = "solidcraft-gpu"
version = "0.25.1"
version = "0.26.0"
dependencies = [
"image",
"log",
@@ -4335,7 +4337,7 @@ dependencies = [
[[package]]
name = "solidcraft-io"
version = "0.25.1"
version = "0.26.0"
dependencies = [
"cadmpeg-core",
"cadmpeg-ir",
@@ -4359,7 +4361,7 @@ dependencies = [
[[package]]
name = "solidcraft-kernel"
version = "0.25.1"
version = "0.26.0"
dependencies = [
"log",
"proptest",
@@ -4379,7 +4381,7 @@ dependencies = [
[[package]]
name = "solidcraft-mcp"
version = "0.25.1"
version = "0.26.0"
dependencies = [
"serde_json",
"solidcraft-engine",
@@ -4388,7 +4390,7 @@ dependencies = [
[[package]]
name = "solidcraft-render"
version = "0.25.1"
version = "0.26.0"
dependencies = [
"image",
"moxcms",
@@ -4402,25 +4404,27 @@ dependencies = [
[[package]]
name = "solidcraft-sketch"
version = "0.25.1"
version = "0.26.0"
dependencies = [
"harfrust",
"serde",
"serde_json",
"solidcraft-geom",
"thiserror 2.0.21",
"ttf-parser",
"unicode-bidi",
]
[[package]]
name = "solidcraft-space-mouse"
version = "0.25.1"
version = "0.26.0"
dependencies = [
"serde",
]
[[package]]
name = "solidcraft-ui-egui"
version = "0.25.1"
version = "0.26.0"
dependencies = [
"arboard",
"egui",
@@ -4450,7 +4454,7 @@ dependencies = [
[[package]]
name = "solidcraft-web"
version = "0.25.1"
version = "0.26.0"
dependencies = [
"eframe",
"egui",
@@ -4919,9 +4923,9 @@ dependencies = [
[[package]]
name = "typed-path"
version = "0.12.3"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e"
checksum = "9ce898823d8cd0f028662d2f349694b1af0a5cc9be83f1d213a00d3147445e1e"
[[package]]
name = "typeid"
@@ -5995,7 +5999,7 @@ checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7"
[[package]]
name = "xtask"
version = "0.25.1"
version = "0.26.0"
dependencies = [
"proc-macro2",
"serde",
@@ -6293,16 +6297,18 @@ dependencies = [
[[package]]
name = "zip"
version = "9.0.4"
version = "9.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a954114b47a54df49f83d2722814e86b23d4bb1b54a1b90fb7d92b41619ddc22"
checksum = "1156b39d142a34b999c2310a50455e0cb3760bf7efad0122f796d813bee26ccb"
dependencies = [
"crc32fast",
"flate2",
"getrandom 0.4.3",
"indexmap",
"memchr",
"nix 0.31.3",
"typed-path",
"windows-sys 0.61.2",
"zopfli",
]
+2 -2
View File
@@ -5,7 +5,7 @@ members = ["crates/*", "apps/*", "xtask"]
exclude = ["vendor"]
[workspace.package]
version = "0.25.1"
version = "0.26.0"
edition = "2024"
license = "MIT OR Apache-2.0"
rust-version = "1.95"
@@ -80,7 +80,7 @@ cadmpeg-ir = "=0.6.3"
cadmpeg-registry = { version = "=0.6.3", default-features = false, features = ["fcstd", "rhino", "step"] }
# fonts (sketch text outlines; MIT OR Apache-2.0, pure Rust)
ttf-parser = { version = "0.25", default-features = false, features = ["std"] }
ttf-parser = { version = "0.25", default-features = false, features = ["std", "variable-fonts"] }
# Ends a process group with the system call (MIT; already in the tree through zip).
nix = { version = "0.31", default-features = false, features = ["signal"] }
+3 -3
View File
@@ -58,9 +58,9 @@
- **Web:** [try it in your browser][web-app], no install.
ARM64 and 32-bit builds, portable zip, .deb, .rpm, Flatpak and the other packages are on the
[downloads page](docs/downloads.md). The downloads aren't signed yet: on Windows choose
**More info**, then **Run anyway** in SmartScreen; on macOS right-click SolidCraft and choose **Open**
the first time. On SolidCraft 0.11 to 0.14, download once by hand; see
[downloads page](docs/downloads.md). The macOS app is signed with a Developer ID and notarized by
Apple. The Windows downloads aren't signed yet: choose **More info**, then **Run anyway** in
SmartScreen. On SolidCraft 0.11 to 0.14, download once by hand; see
[updating](https://docs.solidcraft.io/guide/updates.html#updating-from-011-to-014).
## What works
+694
View File
@@ -80,3 +80,697 @@ permanently.
- Discord: <https://discord.gg/FtU4NQNVDH>
- Releases: <https://github.com/tangency-labs/solidcraft/releases>
- Contributing: [CONTRIBUTING.md](CONTRIBUTING.md)
Status as of **2026-10-14**. SolidCraft supports parametric modelling (sketch →
features → parametric timeline) as an open, pure-Rust, agent-drivable application.
## At a glance
| Question | Answer |
|---|---|
| Where are we? | **M0 and M13 done; M1, M2, M5–M7, M9, M10, M14 in progress.** Sketch with solver and the full SKETCH create/modify set; extrude, revolve (two sides, symmetric, to object), fillet/chamfer (variable radius, chord length, a radius per edge, corner patches), holes, threads (cosmetic and modelled), patterns, mirror, shell (inside/outside/both), draft, loft, sweep, coil, split body and split face, delete face, construction planes, axes and points; persistent naming of faces and edges; parameters and configurations; components, joints, motion studies, exploded views; sheet metal and a plastic enclosure subset; STEP/IGES/3MF/STL/OBJ/DXF; FreeCAD/Rhino import; autosave and crash recovery; desktop and browser (wasm) app, CLI, MCP server. |
| Features | Every registered command by area — [docs/features.md](docs/features.md) (`cargo xtask features`); registry breadth does not establish workflow or geometric depth |
| Reference tests | [Current case count and results](docs/oracle.md): public cases in `tests/oracle/` with analytic or FreeCAD-computed expected values, checked by the reference-test job in [CI](.github/workflows/ci.yml). |
| Tests | Solver, profiles, kernel booleans/blends/measures, expressions, timeline, persistent naming, file formats and migrations, assemblies, sheet metal, engine end-to-end, hostile-input fuzz over every command and headless UI scenarios. `cargo xtask ci` reports the current passing counts. |
| Gates | `cargo xtask ci`: fmt, clippy -D warnings, tests, asset attribution, layering, wasm32 build — green |
| Time to a useful alpha (M0–M6) | ≈ **82 agent hours** remain (sum of the M1–M6 estimates below) |
Optional GPU viewport ambient occlusion is available in View settings and Graphics preferences,
with persistent settings, Performance/Quality graphics presets (Quality combines AO, Fine tessellation and adaptive 2× supersampling; manual combinations show Custom), and the
`view.ambient_occlusion` and `view.graphics_preset` commands. See [display settings](docs/ambient-occlusion.md).
## Scope (owner decision, 2026-10-07)
The issue batch adds preservation of blind pockets in through cuts, associative projected
offsets and plastic face placements, XZ (+Y normal) with a v6 migration preserving older
designs, interactive angle placement, file-picker sketch imports, Home/recent controls,
toolbar overflow, and the remaining component-frame and assembly WIP work. The enclosure
acceptance scenarios now include board resizes; STEP reopening is automated. See [WIP reconciliation](docs/wip-reconciliation.md).
Face-based Extrude Join (including outward Press Pull) now defaults to the picked body,
so edge-touching assembly neighbours stay separate regardless of body order (#226).
In scope, in priority order:
1. SOLID and SKETCH (now).
2. Parameters / functional constraints (done: see `parameters.*`, `expr.evaluate`; unit-aware,
cycles reported with their path, rename follows references, deletes list their users):
user parameters with units and expressions (+-*/, ^,
parentheses, sin/cos/sqrt/min/max/floor/ceil/round, unit conversion), model parameters
d1, d2… for every dimension and feature input, references between parameters with a
dependency graph and cycle detection, re-evaluation on change, comments, favourites,
CSV/JSON import and export.
3. Components and assemblies with joints (M9).
4. SHEET METAL: flange, contour flange, bend, unfold/refold, flat pattern with DXF export, sheet
metal rules (thickness, K-factor, bend radius) (M10).
5. A small PLASTIC subset for enclosures, after sheet metal: Boss, Rib, Web, Lip/Groove, Snap Fit.
Deferred until further notice: MESH, FORM (T-splines), SURFACE beyond what solids need (scope proposed in #772), PCB / 3D
PCB / Package, Render, Animation, Simulation, Manufacture/CAM, Drawings.
## Milestones
| # | Milestone | Status | Hours left |
|---|---|---|---|
| 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, PLY/XYZ point clouds as a modelling reference and PLY meshes (done — [docs/point-clouds.md](docs/point-clouds.md), #5151) | 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 |
| 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 |
| 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. 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 |
M6 navigation preferences include **Touchpad**: Shift + pointer movement pans, Alt/Option +
pointer movement orbits without a mouse button, and vertical scrolling zooms. Other presets
retain their saved two-finger scroll preference; camera motion is available through
`view.navigate` and modifier-aware `ui.move` automation.
With the **Fusion** preset and two-finger pan enabled, two fingers pan and Shift + two fingers
orbit freely, with pinch zoom and mouse bindings unchanged. The shared navigation handler
applies the same gestures to model and Render views and blocks orbit on flat pages.
UI redesign (#2016): the File/start view (#2027) uses shared light/dark navigation,
action tiles, recent rows and sample cards, with recovery review and Save As/Export
entry points. Document loading, sample caches and recovery safety retain their
existing command paths.
M11 precursor: File → Export Current View as DXF exports shown placed bodies and sketches from the current
camera as a 2D millimetre drawing, with hidden edges omitted or dashed. Vector occlusion splits
partly hidden edges and includes curved silhouettes. Repeated part meshes and triangle
hierarchies are shared; bounded desktop preparation and occlusion use up to eight workers.
Curves are approximated; drawing sheets,
annotations and exact projected curves remain deferred. See [view DXF](docs/view-dxf.md).
Drawing foundations: [RFC #860](https://github.com/tangency-labs/solidcraft/issues/860)
and the [foundation ledger](docs/drawing-foundations.md) track the bounded implementation
packages. The first geometry increment defines validated section records in a plane-local
frame; the ledger separates that contract from section producers and the Drawing workspace.
Desktop view DXF export runs in the file-operation worker with five-stage progress and
cooperative cancellation. Curve tolerance (0.0001–1 mm, default 0.01 mm) is remembered alongside
the hidden-edge setting. Cancelled exports preserve the model and the previous destination;
completed drawings publish atomically. The browser still exports synchronously.
## Interactive modelling
- Modelled threads preview their cut before OK using the background worker on desktop.
Browser users apply the cut with OK to avoid blocking on every edit.
- Thread resolves a suggested metric size when a cylindrical face is picked. Diameter and
pitch selectors expose the existing metric designation parameter; custom designations stay
editable, and Automatic size returns to the face's suggestion. Cosmetic/modelled and
full/partial-length options remain available. This does not add tolerance classes or
left-hand threads.
- New Body extrusion and face-offset previews show completed intermediate geometry while a
drag continues, then catch up to the exact final value. Changes to the selected geometry,
operation or feature still discard obsolete results. Distance arrows and their value boxes
follow the current frame's input.
- Large fine modelled threads use pitch-based helix resolution and tighter faceted
reconstruction fallbacks, preserving thin crests and closed shells on large shafts.
## Physical material data
- Physical material card validation (#923/#922) provides typed SI mechanical and
thermal data, per-property sources/conditions and isotropic/orthotropic stability
checks through read-only `material.properties.validate`. It does not assign or
persist properties, provide a solver or enable rendering; see
[the contract and staged follow-ups](docs/physical-material-cards.md).
- Directional elasticity (#994) queries the validated card in material-local axes
through read-only `material.properties.directional`, with source/condition review.
It does not assign body frames, predict structural behavior or enable a solver.
## Library foundations (#785, #786, #787)
- Local scalar libraries (#846) inspect/export bounded portable catalogs and copy exact
selected appearance/material revisions into existing design definitions in one undo step.
Appearance offers local loading/search/export; physical materials use existing commands.
This slice is independent of the render stack and does not change the document format.
See [the contract and limits](docs/book/src/dev/local-libraries.md).
- Proposed phases cover offline bundled/project/user packs, physical materials and
appearances, editable wood mapping, and parameterized component/assembly families.
Stable identity/revision, copied design definitions, provenance and resource limits
apply from the first implementation. Existing material, appearance and standard-part
commands remain the application paths. See [the proposal](docs/book/src/dev/libraries.md)
for source/license review and acceptance gates. This is planning only: pack loading,
texture rendering and expanded component families are not implemented by this entry.
## Render foundation
- Original starter appearances (#1722): 34 opaque recipes through existing commands, with six explicitly unavailable glass recipes and preserved legacy looks; see [the contract](docs/render-starter-appearances.md).
- Bounded linear film accumulation (#1696) supplies caller-admitted mean/variance storage and complete-pass views; actual progressive consumers remain separate. See [the film contract](docs/render-film.md).
- Definition-local visual mapping (#1214) supplies a bounded numerical frame, physical repeat coordinates and a deterministic one-time grain-axis suggestion. It adds no saved record or texture consumer; see [the mapping contract](docs/render-visual-mapping.md).
- Explicit texture normalization (#1218) produces completed float RGBA with
role-aware linear color/data semantics, bounded matrix/TRC ICC conversion and
opt-in EXIF orientation. It retains high-precision raw samples and records lossy
gamut clipping only when explicitly requested. It is not a renderer/resource
consumer; see [the supported policy and limits](docs/render-texture-normalization.md).
- Opt-in opaque PBR (#1046) connects persisted surface parameters to the integrated
product viewport and CPU PNG, using a fixed direct-light studio. Geometry and mass
stay unchanged. HDRI, transmission and textures remain separate gates.
See [the consumer and limitations](docs/render-opaque-pbr.md).
- Combined integration is tracked in #842, with independent scalar libraries in
#863 and richer PBR/material-card transport in #1086. See [phase boundaries and validation gates](docs/render-integration.md).
Render/library consumers use the merged asynchronous picker (#1127). Material-card
UI (#1094), texture mapping and HDRI lighting remain explicit follow-ups.
- R4a adds bounded CPU product-image jobs, cancellation and atomic PNG export through
engine commands, the Render panel, CLI and persistent MCP. Immutable captures and
results stay outside persistence/undo; native workers and cooperative browser tasks
share the same work protocol. See [jobs, limits and export](docs/render-jobs.md).
- R3 adds an overlay-free product scene and the integrated Render workspace, with
runtime GPU host capabilities, per-document drafts and named camera/background
controls. Design keeps its camera and drawing policy. Raster transparency remains
approximate; PBR and environments are later slices. See
[workspace and scene contracts](docs/render-workspace.md).
- R2 adds bounded named render setups in document format 21 and the read/save
`render.settings.get/set` commands. Proven render-only changes preserve geometry
and prepared world data through undo/redo and checkpoint restore. This slice does
not add a Render workspace, PBR, environments or image jobs; see
[settings and refresh policy](docs/render-settings.md).
- Bounded PNG/JPEG texture admission (#900) validates in-memory sources and returns
completed RGBA8 pixels with explicit color/data roles. It is an input utility only:
ICC/16-bit normalization, local wood mapping, resource persistence and the renderer
consumer remain separate work. See [limits and licensed-file probes](docs/render-texture-input.md).
- The initial opaque surface numerical contract for #1004 provides bounded linear
color/metallic/roughness/IOR admission and an original tested direct-light BRDF.
The numerical module alone advertises no capabilities; the installed consumer above
provides rendering. See
[the model, evidence and remaining consumer gates](docs/render-surface.md).
- A bounded cooperative Radiance RGBE input utility is available in L1 render;
it does not yet enable HDRI/PBR rendering or a document import command. See
[the input contract](docs/render-hdr-input.md) and #813 for integration work.
- R1 adds renderer-independent backend/profile metadata, strict request resolution and
bounded image/scratch admission checks, exposed by the read-only `render.backends`
command. It describes the current CPU raster snapshot path without evaluating the model.
The legacy Design snapshot route remains unchanged. Later slices above provide the
workspace/jobs and opaque PBR; HDRI and optional engines remain future work. See [the contract](docs/render-backends.md)
and [the phased proposal (#620)](https://github.com/bherbruck/solidcraft/issues/620).
## 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.
- Sketch Create groups drawing variants under Line, Rectangle, Circle, Arc, Slot,
Polygon and Spline. Each open part remembers its last interactive choice per family
across sketch sessions and document-tab switches; closing the part resets these UI choices.
- Inscribed, circumscribed and edge polygon tools preview their actual outline before
placement, including orientation and the selected side count.
- Construction (X) preserves the active sketch drawing tool, picked anchors and inline
dimension values, so line type can change while a shape is in progress.
- Planar sketch construction previews and hover feedback keep the same dashed pattern as
committed geometry; centerlines retain their dash-dot pattern.
- Point has a default Sketch CREATE toolbar button alongside the existing drawing tools.
Saved custom layouts are preserved; Reset Toolbar restores the default Point button.
- Construction planes cannot take an origin-plane name (XY, XZ, YZ, Top, Front, Right). Plane
arguments reject such a name when an older design's construction plane already has it. Use
the other alias, the construction feature's numeric ID or an explicit plane; existing
stored references keep their saved meaning. Section Analysis uses the same check.
- Sketch text can be edited from a letter interior or outline's context menu or the Edit Text command.
Interior selection requires every profile boundary to belong to the same text source;
the editor highlights the whole source without replacing profile selections.
Content, baseline X/Y, height and rotation update the original text source in one undo step;
cancelling or rejecting an invalid value preserves the sketch. Position and height fields
use the document units captured when editing starts; generated outlines remain associated
with their text source, and untouched inputs preserve their saved internal values.
Dragging a text outline moves the entire source in one undo step; Escape restores the
gesture. Move/Copy on a selected text outline provides unit-aware X/Y distances and rotation
about its baseline; copies keep an independent editable text source.
- Repeating the initial point of a planar multi-point sketch drawing tool is neutral: the tool
waits for a distinct point without a degenerate-shape error or history entry. Typed input
and programmatic commands retain their validation.
- Circular-pattern and angled-plane axes reject explicitly supplied malformed or expression
origins instead of silently rotating about zero. Numeric origins remain in millimetres;
associative axis expressions and construction-axis references remain unsupported.
- Single-line and rectangle-side dimensioning supports selecting a line or its two endpoints.
Type a value and press Enter to commit one undo step; rejected values remain editable and
cancellation leaves the sketch unchanged. Fit keeps geometry clear of the Sketch Palette
and navigation controls, including enlarged displays and nested pending placements.
- 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.
Component/canvas distance controls, sketch cursor coordinates, flat-pattern dimensions
and pocket depths, interference volumes and parts-library lengths also use design units.
- Curvature and Polygon constraints accept complete preselected operands. Fix/Unfix
consumes complete point/curve selections consistently; mixed or ambiguous selections
retain the picking workflow. Rejected constraints preserve selection and undo history.
- Contextual error reporting: a **Create Issue** action beside feature errors opens a
reviewable GitHub issue draft with the error, operation settings, build diagnostics and
useful reproduction context. The draft is editable, with optional capture of the actual
viewport and copy/save controls. Opening GitHub does not submit the issue. Help also exposes
reporting for unexpected results that did not produce an error.
- The header exposes **Report a bug**, including an accessible compact control on
narrow windows. It opens the same editable report and screenshot review without
changing the active model or a pending dimension.
- Crash reports: every panic (in a command, in the kernel, anywhere else) and every unclean
exit (abort, stack overflow, kill: found at the next start through a locked per-run command
journal) leaves a local report with the backtrace, version and commit, the command and the
design that reproduce it (`solidcraft-cli crashes replay <id>`). Repeats are counted, not
rewritten; at most 20 reports, 50 MB and 90 days. The app shows them after a crash (Help >
Crash Reports…) with a reviewed issue draft; agents read them through
`diagnostics.crash_reports`, the MCP `crash_reports` tool or the CLI. Nothing is sent
anywhere. See [crash reports](docs/crash-reports.md).
- Agent chat (off by default, desktop): a side panel that drives a coding agent the user
already has (Claude Code through its ACP adapter, Codex, Gemini CLI, OpenCode, Grok Build, …) over the
Agent Client Protocol, under the user's own login; SolidCraft stores no keys. The agent works
through SolidCraft's MCP tools on a gated endpoint of its own: file actions wait for Allow or
Deny, crash reports are off limits, 24 changes per message, one undo step per message, and
the agent's whole process group ends with the panel or the app. Docks left or right,
minimises to a strip. Grok Build discovery includes `~/.grok/bin` and reuses the user's
Grok login. See [agent chat](docs/agent-chat.md).
- Command scripts from designs (#886): `timeline.export_script` (CLI `solidcraft-cli script`)
writes a design's history as a script that `eval` and `run` replay into the same design —
user parameters, units, sheet metal and plastic rules and materials as their own commands,
then each feature through `timeline.add` with its ids, names and dimension parameters kept,
so every feature kind replays exactly and a failing feature fails the same way. Trimming the
script after a step gives the design at that step. Appearances, folders, views and
configurations are listed as left out; components and joints are not supported yet.
## 0.2.0 release batch
- Includes the new axis navigation, adaptive sketch drawing/picking, origin-corner repairs,
durable supported profile/face references, endpoint dimensioning and contextual reporting.
- Smooth ruled-wall display joins are hidden only within the supported geometric certificate;
authored splits and actual creases remain protected. Wireframe retains physical edges.
- Paste New uses a bounded current-geometry comparison for supported convex line/circle
prisms with separated circular holes; unsupported shapes retain the existing strict check.
- Common-bore Union retries check additional boundary curves before recutting the bore,
preventing the reproduced thin-web loss. Unknown additional trims refuse; inherited
common-cylinder trims retain their existing policy, so this is not a general Boolean proof.
- Exact full-path assembly joint editing remains separate work and is not part of this batch.
- Stable 0.2.0 is published: all release payload checksums, native cold reopening, deployed
web app and both Flatpak architectures were verified. The replacement README demo uses
the original electronics enclosure story and animated pointer in the actual English
0.2.0 UI, at 4× playback. Publication follows [the release workflow](docs/releasing.md).
## 0.2.1 critical fixes
- Separated multi-profile Join/Cut extrusions into one solid target batch their tools using
conservative construction bounds, avoiding a Boolean rebuild for every profile. Live
extrusion previews build only their tools; confirmation validates the final Boolean.
- Coplanar selected body faces share one linked sketch and one extrusion. Automatic Join/Cut
keeps the evaluator's participant scope, including parts of an empty imported component. Desktop OK/Enter
computes on a scratch session in the background, with atomic acceptance and cancellation.
Planar section stitching projects lines/arcs analytically and removes duplicate endpoints.
- Imported rotated face groups can check all full start-face seams in one guarded
construction. Separated tools may batch a Join when each meets the same single body;
nearby uninvolved bodies retain the original contact decisions. Regression comparisons
cover all four extrusion operations.
- Inward multi-face cuts retain every eligible target and subtract separated tools together.
Safe interior classification handles roundoff at shared imported line/arc endpoints.
Straight extrusion feedback caches existing face/profile triangles and updates the display
directly, including a newly opened STEP's single identity component placement; other
extents, moved or repeated components and curved sketch profiles keep the Engine preview.
- The extrusion fixes retain tracked Boolean history and the current `solidcraft` packages.
Confirmation and tool previews use main's shared background calculation. Main's collapsed
blend-wall refusal and its tests are retained, including smaller valid radii and mirrored parts.
- Axis view and navigation controls stay anchored to the viewport when feature dialogs or
the command palette open, at normal and enlarged display scales. Fit still accounts for
dialog coverage; covered controls do not receive clicks through dialogs.
- Eligible planar faces survive fillet, chamfer and shell operations with exact source
certificates. Sketches placed directly on the enclosure wall and inner floor follow
width and wall changes, including after the USB cut and cold reopening.
- Remove artificial straight-wall and rounded-corner subdivisions left by shell and
extrusion cuts. Identical generated wall segments span adjacent slabs as one physical
face, with exact full-height supports and boundary stitching. Preserve authored face
splits and face appearances. Permanent regressions cover material, STEP, reopening and
unaffected bodies across the extrusion end plane.
- Feature dialogs use the available screen height for their fields, with OK and Cancel
outside the scrolling region, including enlarged display scales.
- Design format 13 records shell-face attachments. Older designs remain readable;
newly saved designs require 0.2.1 or later. Ambiguous or unsupported faces still refuse
attachment rather than guessing a plane.
- Saved projections from a removed strip retain their geometry and report the lost
reference. Explicitly reselect the wall to repair that projection.
## Workspace layouts (#2029)
- General preferences save Floating or Compact using the universal UI containers.
Compact joins the workspace selector and ribbon, docks the content-hugging Browser
flush beneath them with a square join, and opens tool sheets below the compass.
Sheets remain draggable, rounding all corners away from their right dock; footer
backgrounds follow the same corners. A separate preference hides ribbon icon labels
for a shorter ribbon in either layout.
Assistant keeps the outermost edge and History keeps its chosen dock. Switching
retains the active tool, camera, selection and design; system windows and menus
keep their existing behavior.
## View navigation
- Navigation has an opt-in Free orbit preference for mouse and 3D-mouse rotation through
both poles with roll. It defaults off, preserving existing preset snapping and pole limits.
The SolidWorks face menu offers Normal to Selection (`view.normal_to`), shared with Look At
and Ctrl+8, including repeat-to-flip, preserving scale and projection.
- Desktop Ctrl/Cmd +/- and 0 change or reset the saved UI scale. The scale slider keeps
its layout stable during a drag and applies the selected scale on release.
- OpenSCAD navigation preset: left drag orbits, right/middle drag pans, Shift+right/middle
drag zooms, and the wheel zooms. Available in the shared desktop/web Navigation preferences.
- Compact clickable signed X/Y/Z axis tips, smooth drag-to-orbit, and Shift-orbit snapping
to 26 standard and diagonal views. Pointer motion accumulates independently of a lightly
damped spring, with a boundary margin to prevent flicker. Z-up/Y-up and the nearest square
top/bottom heading are preserved. Diagonals include 45-degree edge and isometric corner views.
## Known limitations
- Scheduled and manual CI scan locked dependency advisories, bans and sources with
cargo-deny. Vulnerabilities fail the scan; unmaintained crates warn. Local vendored
patches require separate upstream review because advisory databases cannot version-match
their path dependencies. Pull requests retain the existing licence checks.
- Associative shell-face sketches require a unique certified planar source. Nested shell
attachments and ambiguous coincident sources remain unsupported.
- G2 fillets: exact quintic blends of straight edges between planar faces, with one planar
end face at each end, including sloped ends (#2708). The UI and `solid.fillet` expose
`curvature` (alias `g2`); the size is an equivalent circular setback. Curved faces/edges,
multi-face ends and corner junctions remain unsupported.
- Fillets and chamfers: constant, variable-radius and chord-length fillets; a radius per edge,
rounds next to earlier rounds of another radius, and smooth corner patches where three rounds
meet (a sphere for equal radii); perpendicular corner fillets after a closed ring of earlier
rounds preserve the ring's radii and the enclosed planar face (#1557);
mitres between separate rounds of the same radius keep their endpoint tangent, so a later
fillet can select an upright and its adjoining bottom mitre as one chain (#2422);
straight edges (convex or concave) between planar faces with perpendicular
planar end faces (equal-distance chamfers also meet sloped planar end faces, including inner
basin rims within the rim width); convex fillets also meet sloped planar ends and reflex
corners around pocket rims, with unequal wall slopes and extensions confined to the face;
connected equal-distance chamfers also trim each other across unequal sloped rim walls;
curved chamfer chains with straight planar ends can terminate across two existing planar
bevel faces, splitting their shared seam; picks on the same tangent chain are applied once (#1303);
whole smooth loops of a planar face, picked whole or by one edge (tangent
chain), including circles and walls at any angle (boss bases and tops, bores, pocket floors,
plate outlines), loops with sharp corners between straight or mixed arc/line edges (straight
blends meet in mitres; perpendicular curved walls use offset intersections, with D-rim removal
checked within 0.1% of analytic offset slices), runs of
neighbouring edges along a planar face's loop (two top edges of a box: mitred, ending square;
with the edge between them too, the corner becomes a sphere octant),
rounded corners rounded again (sphere octants, tori at concave corners); every edge of a
convex planar body or of an extruded part; fillets inside patterns; closed chains of curved
edges between any two smooth sides (a branch pipe on a main pipe), by a rolling ball (the
blend a rational B-spline through exact arcs), closed or ending at planar faces square to the
edges, where straight or curved cap edges are trimmed to the contacts (the straight edge
between two overlapping extruded arcs); knife edges, where the two faces have opposite
normals (a wall that runs out tangent into a face: the ball that fits there); an edge that
runs out along an end face tangent to one side (the arc top edge of that wall), cut where the
blend meets it; rounds that end in an earlier round on curved faces, in any order (the rounds
of two edges at a vertex meet in a crease, as on a box), picked together or one after the
other, except a round after a round of the edge between the wall and a flat face that it
would run over; the three edges at a vertex of any smooth faces, of one radius, end in a
piece of sphere. A limiting blend that collapses a side wall into coincident boundary
curves is refused as too large rather than returning an unmeshable face (#1113).
- Booleans: prisms along one direction (plates with holes, slots, patterns of them, equal
sections stacked end to end) are combined exactly in 2D. Qualified axial cuts preserve
annular holes with overrun cutters, including inward Press Pull after a joined extrusion;
curved wall qualification rejects false transverse axes instead of filling holes.
Bodies touching along one whole face
(a part and its mirror image) are stitched; coincident planar faces are pushed apart when
their neighbours are planes or cylinders. Other coincident curved configurations can still
fail. Bounded containment classification preserves fully internal cavities and nested
material islands independently of shell order, with STEP round-trip and material-membership
regressions. Near-contact curved shells use the general boolean path rather than a
containment shortcut. Spheres are built from six pole-free patches.
- Mass properties come from fine tessellation (curved faces within ~1e-4 relative); triangles
that cut through curved faces (trimmed faces after booleans) are split onto the surface first.
- STEP import (our own reader, crates/kernel/src/step_in) reads solids (B-rep with analytic and B-spline geometry), units,
product names, colours and assemblies; analytic offsets on planes, cylinders, cones, spheres
and regular tori are read exactly (signed/nested distances, units and face sense). Freeform
offset surfaces and pcurve-only edges are not read yet,
and assembly components are flattened into bodies. Closed periodic faces (torus, B-spline
bands) are split in two for meshing; measure() uses a finer tolerance for small radii.
- Persistent naming follows the kernel's exact face history (docs/book/src/dev/naming.md):
references resolve by name first, follow splits and merges, and warn when the entity split,
merged or vanished, or was found by geometry only (operations that report no history yet).
New planar sketch attachments use exact box, extrusion cap/side and immutable imported-face
source certificates with checked target and author occurrence paths. A missing or changed
source fails explicitly; Redefine Sketch Plane previews reattachment or a fixed plane while
retaining current geometry and dimension identities. Unsupported modified faces need an
explicit fixed plane. Legacy attachments retain their original resolver behavior.
- Modelled threads are cut as a faceted body (curved faces within 1/200 of the pitch); Extrude
and Revolve To Object on a curved face cut the tool back to the target's surface.
- Explicit sketch breaks preserve exact ellipses, fit/control splines (including high degrees)
and conics; analytic trim/break also sees free-form cutters. Free-form curves cut at a tangent
touch (no clean crossing) enter that profile as polylines.
Sweeps along 3D sketch curves follow their polyline (smooth walls when the path is smooth).
A sweep with a twist, an end scale or a parallel or direction orientation (#771) follows its
path that way too, even along sketch lines and arcs: B-spline walls through stations, ruled
walls through denser stations where the path has corners or the profile has holes.
Guide rails (one or two, #771) do the same: each station meets every rail once, and the rail
turns the profile and scales it (or stretches it across to the rail) against the start; two
rails carry it between them. Rails along a path with sharp corners are refused for now.
- Extend intersects exact ellipses and rational/high-degree splines without moving the line or
circular support. Associative free-form offsets have a whole-span 0.002 mm normal-distance
certificate and refine within bounded node limits while retaining existing IDs.
At the node limit, geometric certificate subdivisions accommodate unequal spline parameter
speeds without changing stored nodes or relaxing the distance tolerance (#971).
Associated offset fit nodes stay hidden from display, picking and snapping; dragging the
editable source updates the derived boundary live. Break Association restores point editing.
Singular, self-intersecting and ambiguous curves refuse atomically; mixed junctions and nonsmooth
closed natural-spline seams remain limitations. New explicitly closed fit splines and closed
offset outputs use periodic C2 interpolation, supporting repeated certified offsets; older
natural splines are preserved. Clicking the starting fit point deliberately closes a new loop.
Newly captured adaptive profile references with complete source coverage retain their outer
and hole identities through span-count changes, including parameter edits after extrusion.
Partial, intersected or ambiguous boundaries do not qualify for these whole-source references.
Legacy curve-span multisets remain exact and unchanged; already-lost selections may need
reselection. Format 12 stores the new bounded whole-boundary references.
Projected analytic boundaries follow nested loop identities through resizing, including
stationary holes and rounded corners. Supported convex, tangent line-and-arc boundaries
can lose or regain corner arcs during an inset while preserving reserved curve identities
and linked profile selections. Format 12 saves those associations; compatible older closed
offsets upgrade structurally without changing their curve IDs. Breaking an association
freezes its current boundary and materializes downstream profile references. Child offsets
of a boundary with retired arcs still require breaking its association first; mixed or
nonconvex junctions and retained references to disappearing individual arcs remain limited.
- Extrude and Revolve editors retain original direction, extent expressions, explicit targets,
axes, From/To inputs and All-profile intent. No-op edits preserve downstream dimension names
and selected occurrence frames; cancel and successful edits preserve one-step undo history.
Placed feature edits preview through the edited timeline entry, and cylindrical Emboss guides
use the sketch's actual target, author or uniquely placed third-component frame.
Revolve rotators follow the actual signed axis and support symmetric and two-sided extents;
dragging a To Object extent converts its derived value to an explicit angle. Legacy reversed-axis
symmetric geometry is preserved through format 11's explicit legacy convention. New revolves
use the sketch-normal direction for each selected profile, including To Object and curved
target trimming, independently of the selected axis sign.
- Cylindrical emboss/deboss wraps bounded orthogonal line profiles, including holes, concave
outlines and nested islands, onto complete exterior cylinders or coaxial sleeves with exact
radial walls. New features infer a unique eligible cylinder when the face field is empty;
ambiguous targets require an explicit face. Saved target
and author occurrence paths retain the chosen placements; missing/split faces refuse rather
than retarget. General free-form profiles, seams, stepped cylinders and other curved surfaces
remain unsupported. STEP round-trip and independent radial/volume checks cover this subset.
- Shell and offset faces work on bodies of planes, cylinders, cones, spheres and tori (convex or not:
filleted boxes, bosses, domes, countersinks; corners where four or more planes meet, such as a
floor falling to a drain, as long as the corner's edges are all convex or all concave); draft on convex planar bodies and on walls (planes, cylinders → cones) between caps; lofts through three or more sections are smooth (B-spline sides through every section), two
sections ruled. Circular-meridian barrels retain curved inner walls through either axial
opening (#2421). Solid lofts preserve holes through sections with matching hole counts
(#2435), including inner walls and caps with openings; a hole cannot appear or disappear
between sections, and sections with holes cannot collapse to a point.
- Sketch corner tools: fillet and all three chamfer modes pick a connected corner even when
its endpoint overlaps the fixed origin or a loose point. Ordinary drawing still snaps to
the origin; clicked UI regressions verify geometry, closed profiles and undo.
- Sketch display: drawing, picking, highlighting, tool previews and profile shading share
adaptive projected curve boundaries. Fit, zoom, display scale and placed sketch frames
invalidate the display cache without changing CAD geometry or timeline history. Work
budgets and clipped paths prevent unbounded sampling or invented edges across the eye plane.
The Sketch Palette scrolls its options on short windows and enlarged displays while
keeping constraint status and Finish Sketch visible.
- Face projection: closed boundaries have a deterministic outer-first order. Refresh matches
existing geometry before assigning positions, retaining saved curve IDs and constraints
across enumeration changes and resize, including concentric circles. Ambiguous identities
retain the last geometry and report a lost reference; permutation matching is bounded.
- Project accepts complete supported preselection in one undo step. Visible external sketch
curves and points retain their source sketch identity; model geometry, axes and planes use
the existing linked projection commands. Rejection preserves the document and selection;
mixed selections retain ordinary tool-first picking.
- Inline sketch dimensions: Tab and Shift+Tab cycle within the active shape's value boxes,
selecting visited values for replacement. Expression completion retains forward-Tab
priority; real keyboard regressions cover rectangles, lines and single-value circles.
Typed lengths, radii, line angles, signed arc sweeps and slot lengths persist as constraints
and expressions through save/reopen. Shape creation and its dimensions form one undo step;
rejected values keep the editable field focused.
- Repeated components: Browser selection, visibility, properties, copy and Move/Copy use
checked occurrence paths. Active nested occurrences author planar sketches and solids in
their selected frame. Pending moves compose through parents; dialog cancellation restores
its selected move. Source materials and body/face appearances follow transformed instances.
Shared nested definitions remain shared. Deep Paste New copies reachable definition graphs,
independent feature/input histories, styles and internal motion records, with bounded geometry
checks and explicit external-link notices. Paste New selects the copy while preserving the active
authoring context. Imported periodic STEP seams survive placement and reexport with shared
topology and appearances intact. Recorded body transfers preserve historical Join/Cut membership,
material, placements and machining data through edits, copying, undo and reopen. New 3D wires
record their coordinate frame; legacy wires migrate from known provenance without changing
coordinates. Broken legacy links with missing provenance can require explicit refresh or relink.
See [occurrence workflows](docs/occurrence-workflows.md).
- Toolbar drops on panel labels, gaps and margins append commands with insertion feedback;
button drops keep insert-before ordering.
- Parameters and Configurations have distinct actions in the SOLID toolbar's CONFIGURE panel.
Parameters opens the user/model variable editor; Configurations opens the configuration table.
Configure remains available in the panel dropdown.
- Parameter creation supports Enter with validation and continued keyboard entry. Shared
expression autocomplete supports Up/Down selection, Enter/Tab acceptance and Escape dismissal;
accepting a suggestion does not submit a parameter or trigger viewport shortcuts.
- Movable window title bars follow the app's dark/light header palette. Radial right-click
menus are opt-in in Preferences > Navigation; ordinary context menus remain the default.
- Desktop distribution: running Windows windows and executable resources use the app icon;
packaging validates icon resources. Nix installs launchers, MIME data and generated icons,
with explicit launch apps and Vulkan-loader/driver discovery. Flatpak feed instructions
register the required runtime source; release checks exercise desktop modelling on X11/Wayland.
- OpenGL rendering clamps effective anti-aliasing to 1x across UI pipelines, textures and CAD
callbacks to prevent black frames. Other backends retain the requested sample count, and
stored preferences remain unchanged.
- Configuration rules: ordered typed conditions can drive parameters and suppressions or
restrict values. Preview explains effective values without modifying the document; applying
rules validates rows atomically. Length expressions retain their authoring units across
document-unit changes. The UI supports structured All/Any/Not conditions and rule ordering.
Component styles and certified semantic face styles are available; unsupported lineage refuses
atomically, and stale certified face paint cannot jump to another surface. See [configurations](docs/configurations.md).
- Sheet-metal material: inherited holes, crossing edge notches, overlapping cuts and retained
islands share the same boundaries in flat preview, DXF and unfolded solids. Detached material
pieces survive unfold/refold as one logical sheet group, including split flanged sheets.
Cut-outs made in the flat pattern cross bends at any angle or curved: their edges fold to
helices and curves on the bend, their walls square to the sheet. Free-form bases use bounded
sampling.
Parallel prismatic partial-depth cuts retain pocket side and depth through transfer and
unfold/refold, with separate pocket contours in preview and DXF. Planar Intersect preserves
exact holes and retained islands, including the union of multiple selected profiles, and
records complementary depth layers as pockets while retaining the stock gauge. Unsupported
tapered or bend-crossing machining refuses atomically instead of restoring material later.
Sketches attach to a sheet's flat faces (base and flanges, either side) by panel, in the
flat pattern's coordinates, and follow it through rebuilds and unfold/refold.
A flange on another flange's far edge (a return) follows that flange's closed end and keeps
clear of the flange beside it, and the returns of one feature are mitred toward each other
with the rule's gap where their flanges meet at a corner; returns of separate features, and
flanges on returns, are not closed toward each other.
- Rolling-ball fillet accuracy: independent offset-cylinder tee references check surface
distance, actual trimmed tessellation, supporting-cylinder contacts and tangency at two
scales. The measured trimmed-part error is below 0.0001 mm; this is an analytic geometry
check. Under-resolved fits return an error.
- Contact driving checks the trajectory between clear endpoints, including linked joints,
playback and sparse motion exports. Checks use bounded tessellation samples, not exact
continuous collision detection; local thin walls and small details remain limitations.
See [contact-set limits and recovery](docs/contact-sets.md).
## Assembly dragging
The design's first part is grounded when it is made, and a left-button drag repositions a free part (`occurrence.drag`: one undo step, exact occurrence paths including repeated jointed definitions, any floating-chain member carries the chain, grounded parts and joints hold where present, Escape cancels).
## 3D sketch bridges
3D sketch drawing snaps to points on other visible sketch planes without flattening them onto
the active plane. Bridge endpoints capture their source positions; associative endpoint links,
construction-plane selection of 3D wires and loft guide rails remain separate work.
Feature dialog header chevrons collapse and restore the form without discarding selections
or edited values, including the Appearance panel.
## Construction on 3D paths
Plane Along Path has a viewport path selector, fractional position, live preview and timeline
editing. Construction planes and points resolve drawn 3D wires without applying their sketch
plane a second time. Curved paths use sampled segments; multi-curve chains, absolute distance
and reference-object placement remain separate work, as do loft rails and centerlines.
## Design properties (#1015)
- Designs carry project, part number, revision, author, organization, description and licence
in an optional `metadata` object (File → Design Properties…, `document.metadata.*`,
`inspect_design`). STEP and IGES headers and 3MF model metadata take the fields their format
has a place for. Other tools keep their own data under a reverse-DNS key, kept as it is
through open, edit and save and never exported. See [design properties](docs/book/src/guide/files.md#design-properties).
Per-component properties (a part number per component) can follow with the same type.
## Document-opening performance
- Desktop STEP import prepares consecutive independent solids in bounded batches with up
to eight workers, retaining serial traversal, result order and persistent source identities.
The web path remains serial. Original multi-solid fixtures show 64–81% lower complete
import times; single-solid assembly parts are not scheduled across nodes. See
[measurements and limits](docs/perf.md#parallel-step-body-preparation-2026-10-12).
- Design containers (#303, phase one) separate and deduplicate STEP, raw images and
lossless binary mesh arrays while keeping plain JSON as a supported Save As/export
format. Generated-mesh storage measurements reduce serialization and decoding work;
a seekable reader defers original blobs until after provisional display.
See [container format](docs/design-container.md) and [performance measurements](docs/perf.md).
- Warm saves with explicit `cache: true` can include a versioned native B-rep cache through SolidCraft-owned topology
and geometry records, with a separate truck adapter and bounded binary reader. Exact
face-source certificates and operation history survive codec round trips. A provisional
picture can be generated before original blobs are read; editing and measurement wait
for authoritative reconstruction. Cached metadata binds preview and reconstruction to
the same document despite concurrent file writes. Default saves omit this cache and never force cold
evaluation or meshing; explicit `preview: true` remains a separate warm-mesh option.
Keys, resource budgets and document/revision guards reject incompatible, hostile or
stale results. A second modelling kernel still requires its own tested adapter.
- Warm saves with explicit `cache: true` can also carry a bounded PNG thumbnail from already cached meshes.
Home reads it without rebuilding the design when its local picture is missing;
mismatched or malformed thumbnails fall back to normal generation. The image is
kernel-independent and saving never forces cold meshing to produce it.
- Cold rebuild profiling separates parsing, geometry, placement, display meshing and
thumbnails. Straight-line closest-point projection and exact curved-surface evaluation
reuse reduce repeated work without relaxing geometry checks. Three alternating optimized
benchmark runs reduce median rebuild time by 26% on the public sensor enclosure and 22%
on a local case assembly; shape measurements and topology match the original build.
See [performance measurements](docs/perf.md). Further cold Boolean and import costs remain.
- Prepared assembly geometry survives selection and view changes while real geometry and
placement changes invalidate it. A Pi assembly edge-selection probe drops from 9.24 s to
0.57 s; initial opening is unchanged. Actual Windows desktop profiling measures 15.87 s
median opening, with 82% spent rebuilding geometry; halving full opening remains open.
## Kernel
- Successful Boolean retries report their strategy and numerical adjustments as
feature warnings and structured inspection data. Diagnostics survive cached
evaluation, undo/redo and cold reopening; the robustness runner counts retry
events by strategy and reports any bounded-log truncation. Existing geometry
acceptance checks remain in force.
- Cylinders of one radius whose axes cross (a second bore across a bore of the same size, a pipe
tee of equal pipes) touch at two points, where the mesh intersection cannot trace the curves
and a boolean cannot tell the four arms of the crossing apart. When the ordinary boolean fails
on such a pair, both faces are split along the line of their cylinder through the touching
points (the solids are unchanged), the boolean is taken of those, and its history is composed
back over the operands as given (#788). The result keeps the split lines as edges of the bore
walls. A crossing the ordinary route fails on and the split faces do not help (a radius of 17.5
in a 45 mm block with a tool of 80 mm) is still refused.
- Boolean intersection-curve construction refuses point-only, collapsed and non-finite
polylines without panicking. Direct constructor regressions retain valid open endpoints
and closed spherical intersections; the original interactive trigger remains unidentified.
- Shell of a stepped, joined planar body no longer creates horizontal subdivisions across
continuous walls. Fresh axis-aligned Cut walls are stitched using their exact construction
support and shared topology, preserving real steps and intentional boundaries. The local
reported case returns from 86 to 49 faces with unchanged material and bounds; regressions
cover stepped stock, STEP round trips and saved projections from removed pieces.
truck (Apache-2.0) behind our own `kernel` crate, with our own blend operation and boolean
retry/validation; see `plan/adr/0001-geometry-kernel.md` (local) — summary in README.
+35 -7
View File
@@ -23,6 +23,7 @@
//! solidcraft-cli icon --out F.png|F.ico [--size N] the app icon (packaging; drawn, not a file)
//!
//! --debug (with any command) SolidCraft's debug log on stderr (or RUST_LOG=LEVEL)
//! --max-file-size SIZE (with any command) read STEP and design files up to SIZE (2GB, 1500MB; at most 16GB)
//! ```
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
#![forbid(unsafe_code)]
@@ -99,7 +100,7 @@ const USAGE_BENCH_EDIT: &str = " solidcraft-cli bench-edit <script.json|recipe.
/// `save-stress`'s lines in the usage: the file saved over and over.
const USAGE_SAVE_STRESS: &str = " solidcraft-cli save-stress <path> <rounds>\n";
/// The flags every command takes, at the end of the top-level usage.
const USAGE_DEBUG: &str = " --debug, with any command: what SolidCraft logs (why a body has no display plan, why a save left its\n model cache out), on stderr; RUST_LOG=error|warn|info|debug|trace sets one level for every crate\n";
const USAGE_DEBUG: &str = " --debug, with any command: what SolidCraft logs (why a body has no display plan, why a save left its\n model cache out), on stderr; RUST_LOG=error|warn|info|debug|trace sets one level for every crate\n --max-file-size SIZE, with any command: read STEP and import files and design files up to SIZE for this run\n (for example 2GB or 1500MB; at most 16GB; the defaults are 512 MB and 256 MB, and a smaller SIZE keeps them)\n";
/// The commands the CLI takes, in the order the top-level usage lists them.
const COMMANDS: &[Command] = &[
@@ -131,6 +132,10 @@ const COMMANDS: &[Command] = &[
fn main() -> ExitCode {
let mut args: Vec<String> = std::env::args().skip(1).collect();
logger::init(&mut args);
if let Err(e) = max_file_size(&mut args) {
eprintln!("error: {e}");
return ExitCode::FAILURE;
}
let r = match args.first().map(String::as_str) {
// Asked-for help is not an error: the command's usage on stdout, exit 0. It is read
// before the command runs, so `--help` never starts a run (a fuzz run, a server).
@@ -205,6 +210,28 @@ fn main() -> ExitCode {
}
}
/// Take `--max-file-size SIZE` (or `--max-file-size=SIZE`), which any command takes, out of the
/// arguments and raise the file size limits for this run (#4086). A bad size is an error.
fn max_file_size(args: &mut Vec<String>) -> Result<(), String> {
let mut size = None;
let mut i = 0;
while let Some(arg) = args.get(i) {
if arg == "--max-file-size" {
let value = args.get(i + 1).cloned().ok_or("--max-file-size needs a size, for example --max-file-size 2GB")?;
args.drain(i..i + 2);
size = Some(value);
} else if let Some(value) = arg.strip_prefix("--max-file-size=") {
size = Some(value.to_string());
args.remove(i);
} else {
i += 1;
}
}
let Some(size) = size else { return Ok(()) };
let bytes = solidcraft_engine::io::limits::parse_flag(&size).map_err(|e| format!("--max-file-size: {e}"))?;
solidcraft_engine::io::limits::set_override(Some(bytes))
}
fn pretty(v: &Value) -> String {
serde_json::to_string_pretty(v).unwrap_or_default()
}
@@ -241,11 +268,12 @@ fn wants_help(args: &[String]) -> bool {
pub fn read_json(path: &str) -> Result<Value, String> {
// Through the engine's file access, which refuses anything that is not a regular file and
// reads no more than the cap: a character device or a FIFO would otherwise never end.
let s = solidcraft_engine::io::vfs::read_to_string(path, solidcraft_engine::io::MAX_DESIGN_BYTES).map_err(|e| format!("{path}: {e}"))?;
let s = solidcraft_engine::io::vfs::read_to_string(path, solidcraft_engine::io::limits::max_design_bytes())
.map_err(|e| format!("{path}: {}", solidcraft_engine::io::limits::explain(solidcraft_engine::io::limits::Kind::Design, e)))?;
serde_json::from_str(&s).map_err(|e| format!("{path}: {e}"))
}
/// A session from a design file, a STEP/3MF/STL file, a command script, or an oracle recipe.
/// A session from a design file, a STEP/3MF/STL file, a point cloud, a command script, or an oracle recipe.
pub fn load(path: &str) -> Result<Session, String> {
load_checked(path, solidcraft_engine::schema::ParamCheck::Off)
}
@@ -255,11 +283,11 @@ pub fn load(path: &str) -> Result<Session, String> {
fn load_checked(path: &str, check: solidcraft_engine::schema::ParamCheck) -> Result<Session, String> {
let mut s = Session::default();
s.param_check = check;
if solidcraft_engine::io::is_step_path(path) || solidcraft_engine::io::is_mesh_path(path) {
if solidcraft_engine::io::is_step_path(path) || solidcraft_engine::io::is_mesh_path(path) || solidcraft_engine::io::is_point_cloud_path(path) {
s.execute("doc.open", &json!({"path": path})).map_err(|e| e.to_string())?;
return Ok(s);
}
let bytes = solidcraft_engine::io::vfs::read(path, solidcraft_engine::io::MAX_DESIGN_BYTES).map_err(|e| format!("{path}: {e}"))?;
let bytes = solidcraft_engine::io::limits::read(path, solidcraft_engine::io::limits::Kind::Design).map_err(|e| format!("{path}: {e}"))?;
if solidcraft_engine::io::is_design_container(&bytes) {
let doc = solidcraft_engine::io::read_design(&bytes).map_err(|e| format!("{path}: {e}"))?;
let mut session = Session::opened_at(doc, None, Some(path));
@@ -820,8 +848,8 @@ pub(crate) fn cmd_bench_open(args: &[String]) -> Result<(), String> {
let path = args.first().ok_or("usage: solidcraft-cli bench-open <design.sc | part.step> [--parse-only]")?;
let t = std::time::Instant::now();
let step = solidcraft_engine::io::is_step_path(path);
let limit = if step { solidcraft_engine::io::MAX_STEP_BYTES } else { solidcraft_engine::io::MAX_DESIGN_BYTES };
let bytes = solidcraft_engine::io::vfs::read(path, limit).map_err(|e| format!("{path}: {e}"))?;
let kind = if step { solidcraft_engine::io::limits::Kind::Step } else { solidcraft_engine::io::limits::Kind::Design };
let bytes = solidcraft_engine::io::limits::read(path, kind).map_err(|e| format!("{path}: {e}"))?;
let read_ms = t.elapsed().as_secs_f64() * 1000.0;
println!("{}", json!({"step": "read", "ms": read_ms, "bytes": bytes.len()}));
let t = std::time::Instant::now();
+31
View File
@@ -157,3 +157,34 @@ fn render_help_keeps_the_registered_gpu_request_controls() {
}
}
}
/// `--max-file-size` (#4086), with any command: the usage names it, a bad size stops the run
/// with the reason, and a design over the default limit opens once the flag raises it.
#[test]
fn max_file_size_raises_the_limit_and_refuses_bad_sizes() {
let output = Command::new(env!("CARGO_BIN_EXE_solidcraft-cli")).arg("--help").output().unwrap();
assert!(String::from_utf8_lossy(&output.stdout).contains("--max-file-size"));
for size in ["17GB", "2", "lots"] {
let output = Command::new(env!("CARGO_BIN_EXE_solidcraft-cli")).args(["--max-file-size", size, "exec", "file.new"]).output().unwrap();
assert!(!output.status.success(), "{size} was accepted");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(stderr.starts_with("error: --max-file-size"), "{size}: {stderr}");
}
let output = Command::new(env!("CARGO_BIN_EXE_solidcraft-cli")).args(["exec", "file.new", "--max-file-size=2GB"]).output().unwrap();
assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr));
// A sparse design file just over the default 256 MB: refused unread, then read with the flag
// (and refused for what it holds, which is no design).
let dir = std::env::temp_dir().join(format!("solidcraft-cli-4086-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("big.solidcraft");
std::fs::File::create(&path).unwrap().set_len((256 << 20) + 1).unwrap();
let path = path.to_string_lossy().to_string();
let output = Command::new(env!("CARGO_BIN_EXE_solidcraft-cli")).args(["eval", &path]).output().unwrap();
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success() && stderr.contains("too large") && stderr.contains("--max-file-size"), "{stderr}");
let output = Command::new(env!("CARGO_BIN_EXE_solidcraft-cli")).args(["--max-file-size", "1GB", "eval", &path]).output().unwrap();
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success() && !stderr.contains("too large"), "{stderr}");
let _ = std::fs::remove_dir_all(&dir);
}
+6 -2
View File
@@ -40,6 +40,8 @@ pub enum Intent {
Open,
/// Add its bodies to the open design (STEP, 3MF, STL).
Insert,
/// Import it as a theme: it was dropped on Preferences' Import tile (#8226).
Theme,
}
/// A stored design, as the file panel lists it.
@@ -615,13 +617,15 @@ fn choose_file(accept: &str) -> Result<js_sys::Promise, String> {
}
/// Read files dropped on the page.
pub fn take_drops(files: &Rc<Files>, raw: &mut egui::RawInput) {
/// `theme`: the pointer is over Preferences' Import tile, so the files are themes.
pub fn take_drops(files: &Rc<Files>, raw: &mut egui::RawInput, theme: bool) {
let intent = if theme { Intent::Theme } else { Intent::Insert };
for f in std::mem::take(&mut raw.dropped_files) {
let files = files.clone();
spawn_local(async move {
let name = f.path().file_name().map(|n| n.to_string_lossy().to_string()).unwrap_or_else(|| "dropped".into());
match f.bytes_async().await {
Ok(b) => files.arrive(name, b, Intent::Insert),
Ok(b) => files.arrive(name, b, intent),
Err(e) => files.say(format!("{name}: {e}"), true),
}
});
+47 -2
View File
@@ -112,6 +112,13 @@ fn check_download_size(current: usize, incoming: u64) -> Result<(), String> {
Ok(())
}
/// Ctrl+W (⌘W, without Shift or Alt) while a design is shown closes it in the app, so the
/// browser must not close the tab (#5366). On the start page the browser closes the tab.
#[cfg(any(target_arch = "wasm32", test))]
fn closes_design(key: &str, command: bool, shift: bool, alt: bool, design_shown: bool) -> bool {
design_shown && command && !shift && !alt && key.eq_ignore_ascii_case("w")
}
#[cfg(target_arch = "wasm32")]
mod web {
use std::rc::Rc;
@@ -163,6 +170,23 @@ mod web {
thread_local! {
/// An open design has unsaved changes: leaving the page asks first.
static UNSAVED: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
/// A design is shown (not the start page): Ctrl+W closes it, not the browser tab.
static DESIGN_SHOWN: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
}
/// Ctrl+W (⌘W) closes the design shown, as on the desktop; on the start page it is left to
/// the browser, which closes the tab (#5366). Browsers that keep Ctrl+W to themselves still
/// close the tab, after asking when a design is unsaved ([`ask_before_unload`]).
fn keep_ctrl_w_for_the_design(window: &web_sys::Window) {
let on_key = wasm_bindgen::closure::Closure::<dyn FnMut(web_sys::KeyboardEvent)>::new(|e: web_sys::KeyboardEvent| {
let command = e.ctrl_key() || e.meta_key();
if crate::closes_design(&e.key(), command, e.shift_key(), e.alt_key(), DESIGN_SHOWN.with(std::cell::Cell::get)) {
e.prevent_default();
}
});
let _ = window.add_event_listener_with_callback_and_bool("keydown", on_key.as_ref().unchecked_ref(), true);
// The listener lives as long as the page.
on_key.forget();
}
/// The browser's "leave site? changes you made may not be saved" prompt while a design has
@@ -271,6 +295,10 @@ mod web {
!companion
});
for (name, bytes, intent) in incoming {
if intent == Intent::Theme {
solidcraft_ui_egui::prefs::import_theme_bytes(&mut self.app, bytes);
continue;
}
let design = solidcraft_engine::io::is_design_path(&name);
let path = if design && intent == Intent::Open {
// A design from the computer is kept in the browser (a copy), so Save works.
@@ -287,7 +315,7 @@ mod web {
};
match intent {
Intent::Open => self.app.open_path(&path),
Intent::Insert => self.app.drop_path(&path),
Intent::Insert | Intent::Theme => self.app.drop_path(&path),
}
self.files.panel.set(false);
}
@@ -625,6 +653,7 @@ mod web {
self.space_mouse.logic(&mut self.app, ctx);
self.files_tick(ctx);
UNSAVED.with(|u| u.set(solidcraft_ui_egui::documents::any_dirty(&self.app)));
DESIGN_SHOWN.with(|d| d.set(solidcraft_ui_egui::toolbar::design_shown(&self.app)));
}
fn raw_input_hook(&mut self, ctx: &egui::Context, raw: &mut egui::RawInput) {
let now = raw.time.unwrap_or_else(|| ctx.input(|i| i.time));
@@ -632,7 +661,8 @@ mod web {
ctx.request_repaint_after(delay);
}
if self.launch.persists() {
files::take_drops(&self.files, raw);
let theme = solidcraft_ui_egui::prefs::theme_drop_target(&self.app, ctx);
files::take_drops(&self.files, raw, theme);
} else {
raw.dropped_files.clear();
}
@@ -724,6 +754,7 @@ mod web {
ask_before_unload(&window);
}
leave_tab_switching_to_browser(&window);
keep_ctrl_w_for_the_design(&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 {
log::error!("missing <canvas id=\"{CANVAS_ID}\">");
@@ -956,4 +987,18 @@ mod tests {
assert!(!browser_switches_tabs("Tab", false));
assert!(!browser_switches_tabs("s", true));
}
#[test]
fn ctrl_w_closes_the_design_not_the_tab() {
// Ctrl+W and ⌘W (either case of the key) close the design shown.
assert!(closes_design("w", true, false, false, true));
assert!(closes_design("W", true, false, false, true));
// On the start page the browser closes the tab.
assert!(!closes_design("w", true, false, false, false));
// Ctrl+Shift+W (close the window), plain W and other keys stay the browser's or the app's.
assert!(!closes_design("W", true, true, false, true));
assert!(!closes_design("w", true, false, true, true));
assert!(!closes_design("w", false, false, false, true));
assert!(!closes_design("s", true, false, false, true));
}
}
+12 -1
View File
@@ -761,12 +761,15 @@ fn main() -> eframe::Result {
let mut sample = false;
// `--theme-file PATH`: follow that theme file for this run without saving it (#7573).
let mut theme_file: Option<String> = None;
let mut max_file_size = None;
let mut args = std::env::args().skip(1);
while let Some(a) = args.next() {
match a.as_str() {
"--control" => control_port = args.next().and_then(|p| p.parse().ok()),
"--sample" => sample = true,
"--theme-file" => theme_file = args.next(),
"--max-file-size" => max_file_size = Some(args.next()),
s if s.starts_with("--max-file-size=") => max_file_size = Some(s.strip_prefix("--max-file-size=").map(str::to_string)),
"--safe-mode" => safe_mode = true,
"--version" | "-V" => {
println!("solidcraft {}", env!("CARGO_PKG_VERSION"));
@@ -787,13 +790,21 @@ fn main() -> eframe::Result {
}
"--help" | "-h" => {
println!(
"usage: solidcraft [--control PORT] [--sample] [--theme-file THEME.json] [--crash-log] [--safe-mode] [design.sc | part.step]"
"usage: solidcraft [--control PORT] [--sample] [--theme-file THEME.json] [--crash-log] [--safe-mode] [--max-file-size SIZE] [design.sc | part.step]"
);
return Ok(());
}
_ => files.push(a),
}
}
// `--max-file-size 2GB`: the largest files this run reads, over Preferences, never saved (#4086).
if let Some(value) = max_file_size {
let applied = startup::max_file_size(value.as_deref()).and_then(|bytes| solidcraft_engine::io::limits::set_override(Some(bytes)));
if let Err(error) = applied {
eprintln!("SolidCraft: {error}");
return Err(eframe::Error::AppCreation(std::io::Error::other(error).into()));
}
}
// 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 mut notes: Vec<String> = Vec::new();
+18
View File
@@ -302,6 +302,14 @@ pub fn system_title_bar(windows: bool, backends: eframe::wgpu::Backends) -> bool
windows && backends.contains(Backends::GL) && !backends.intersects(Backends::DX12 | Backends::VULKAN)
}
/// The `--max-file-size` value (#4086): a size such as `2GB` or `1500MB`, at most 16 GB. It raises
/// the largest STEP/import and design file this run reads, over the saved preference, without
/// saving anything. A missing or bad value stops the start with this error.
pub fn max_file_size(value: Option<&str>) -> Result<u64, String> {
let value = value.ok_or("--max-file-size needs a size, for example --max-file-size 2GB")?;
solidcraft_engine::io::limits::parse_flag(value).map_err(|e| format!("--max-file-size: {e}"))
}
/// 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());
@@ -324,6 +332,16 @@ mod tests {
["Integrated", "Discrete small", "Discrete large"].into_iter().map(|name| AdapterChoice { name: name.into(), compatible: true }).collect()
}
#[test]
fn max_file_size_parses_sizes_and_refuses_bad_ones() {
assert_eq!(max_file_size(Some("2GB")), Ok(2 << 30));
assert_eq!(max_file_size(Some("1500MB")), Ok(1500 << 20));
for bad in [None, Some(""), Some("2"), Some("17GB"), Some("lots"), Some("-1GB")] {
let error = max_file_size(bad).unwrap_err();
assert!(error.contains("--max-file-size"), "{bad:?}: {error}");
}
}
#[test]
fn adapter_names_environment_override_and_missing_card_fallback() {
let cards = cards();
+169
View File
@@ -181,6 +181,8 @@
"Designs you open or save appear here.": "تظهر هنا التصاميم التي تفتحها أو تحفظها.",
"Determined by To Object. Choose Angle to edit the angular extent.": "يحدده خيار «إلى كائن». اختر «زاوية» لتعديل الامتداد الزاوي.",
"Diameter": "القطر",
"Did this assembly open the wrong way up?": "هل فُتحت هذه المجموعة مقلوبة الاتجاه؟",
"Did this part open the wrong way up?": "هل فُتحت هذه القطعة مقلوبة الاتجاه؟",
"Dimension new shapes": "إضافة أبعاد للأشكال الجديدة",
"Direction": "الاتجاه",
"Discard": "تجاهل",
@@ -593,6 +595,7 @@
"Square": "مربع",
"Stand it up": "إيقافه قائمًا",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "إيقافه قائمًا: ربع دورة حول X، كميزة تحريك واحدة يمكنك التراجع عنها.",
"Stand this assembly up?": "إيقاف هذه المجموعة قائمة؟",
"Stand this part up?": "إيقاف هذه القطعة قائمة؟",
"Start angle": "زاوية البداية",
"Start bulge": "انتفاخ البداية",
@@ -760,6 +763,7 @@
"appearance.pbr_opaque": "PBR معتم",
"appearance.roughness": "الخشونة",
"appearance.surface_response": "استجابة السطح",
"at the face": "عند الوجه",
"bom.group_children": "الأجزاء الفرعية في صف الجزء الأصلي",
"browser.body_name": "جسم {number}",
"browser.calibrate_title": "معايرة: {name}",
@@ -768,6 +772,7 @@
"browser.capture_one": "تم تحريك مكوّن دون تثبيت موضعه.",
"browser.component_name": "مكوّن {number}",
"browser.edit_canvas_title": "تعديل اللوحة: {name}",
"browser.edit_point_cloud_title": "تحرير سحابة النقاط: {name}",
"browser.move_title": "تحريك: {name}",
"browser.parameters": "المعاملات ({count})",
"browser.precision": "الدقة: {precision}",
@@ -995,6 +1000,10 @@
"cmd.plastic_rest": "مسند",
"cmd.plastic_rules": "قائمة قواعد البلاستيك",
"cmd.plastic_snap_fit": "وصلة كبس",
"cmd.pointcloud_delete": "حذف سحابة النقاط",
"cmd.pointcloud_edit": "تحرير سحابة النقاط",
"cmd.pointcloud_insert": "إدراج سحابة نقاط",
"cmd.pointcloud_list": "سحب النقاط",
"cmd.render_aging_describe": "تقادم السطح بالطبقات",
"cmd.render_backends": "محركات التصيير",
"cmd.render_batch": "تصيير كل اللقطات",
@@ -1017,15 +1026,20 @@
"cmd.render_library_surface_prepare": "تجهيز خرائط سطح مُعدّة",
"cmd.render_library_surface_unbind": "إزالة ربط السطح",
"cmd.render_library_targets": "البحث عن أهداف مكتبة التصيير",
"cmd.render_lights_add": "إضافة ضوء تصيير",
"cmd.render_lights_solo": "عزل الضوء",
"cmd.render_optics_bind": "حفظ الربط البصري",
"cmd.render_optics_unbind": "إزالة الربط البصري",
"cmd.render_output_size": "ضبط حجم مخرجات التصيير",
"cmd.render_preflight": "فحص طلب التصيير",
"cmd.render_preset_apply": "تطبيق استوديو أصلي",
"cmd.render_preset_resolve": "مراجعة استوديو أصلي",
"cmd.render_quality_prepare": "مراجعة ميزانية عيّنات ثابتة",
"cmd.render_result_close": "إغلاق صورة التصيير",
"cmd.render_result_copy": "نسخ صورة التصيير",
"cmd.render_result_pin": "الاحتفاظ بنتيجة التصيير المعروضة",
"cmd.render_result_regrade": "إعادة تدريج النسخة الخطية الأصلية",
"cmd.render_result_view": "عرض صورة التصيير",
"cmd.render_scene": "فحص مشهد التصيير",
"cmd.render_settings_apply": "تطبيق كاميرا التصيير",
"cmd.render_settings_capture_hdr": "حفظ إعدادات التصيير مع HDR",
@@ -1128,6 +1142,7 @@
"cmd.sketch_spline_control_point_5": "منحنى شريحي بنقاط تحكم (الدرجة 5)",
"cmd.sketch_spline_fit_point": "منحنى شريحي بنقاط ملاءمة",
"cmd.sketch_spline_insert_point": "إضافة نقطة إلى المنحنى الشريحي",
"cmd.sketch_spline_reverse": "عكس اتجاه المنحنى الشريحي",
"cmd.sketch_spun_profile": "مقطع دوراني",
"cmd.sketch_toggle_diameter": "تبديل نصف القطر / القطر",
"cmd.sketch_toggle_driven": "تبديل البُعد المُشتق",
@@ -1255,6 +1270,7 @@
"dialog.dimension_insert_hint": "انقر على بُعد قائد آخر لإدراج اسمه",
"dialog.dimension_name": "البُعد: {name}",
"dialog.edit_title": "تعديل {title}",
"dialog.no_shown_body": "لا يوجد جسم ظاهر لتغييره: أظهر جسمًا، أو اختر «جسم جديد»",
"dialog.select": "↖ تحديد",
"dialog.selected_count": "↖ {count} محدد",
"dialog.selection_guidance": "حدد مُدخلًا لتغييره. انقر على العناصر المحددة لإزالتها؛ Ctrl/Cmd مع النقر يبدّل تحديدها أيضًا. راجع المعاينة ثم اضغط موافق.",
@@ -1325,6 +1341,7 @@
"help.mit": "رخصة MIT",
"help.notice": "NOTICE",
"help.parameters.change": "يفتح جدول القيم المسماة والتعبيرات التي تتحكم في أبعاد التصميم. استخدمه لتغيير مقاس في مكان واحد فتتبعه كل الميزات التي تستخدمه.",
"help.pointcloud.insert": "أدرج مسحًا بصيغة PLY أو XYZ أو PTS كسحابة نقاط بجانب النموذج. استخدمه كمرجع عند النمذجة انطلاقًا من بيانات القياس.",
"help.solid.align": "يحرّك الأجسام المحددة بحيث تلتقي نقطة أو وجه أو مستوى منها بنقطة أو وجه أو مستوى في مكان آخر. استخدمه لوضع قطعة ملاصقة لأخرى دون قياس المسافة.",
"help.solid.chamfer": "يقطع الحواف المحددة بشطفة مستوية بالمسافة التي تحددها. استخدمه لكسر الحواف الحادة أو لتسهيل إدخال مسمار أو برغي.",
"help.solid.combine": "يضم الأجسام، أو يقتطعها من جسم هدف، أو يُبقي الحجم المشترك بينها فقط، ويمكنه الاحتفاظ بأجسام الأداة. استخدمه لدمج القطع، أو لقطع شكل بشكل آخر، أو لمعرفة موضع تداخل جسمين.",
@@ -1350,6 +1367,8 @@
"help.third_party": "رخص الأطراف الثالثة",
"help.timeline.compute_all": "يعيد بناء كل ميزة في السجل من البداية. استخدمه لتحديث النموذج ولمعرفة الميزات التي تفشل.",
"help.timeline.delete": "يحذف الميزات المحددة من السجل، وحذف رسمٍ يحذف معه الميزات المبنية عليه. استخدمه لإزالة خطوة لم تعد مطلوبة.",
"history.sketch_under_constrained": "قيود ناقصة ({count} درجات حرية)",
"history.sketch_under_constrained_one": "قيود ناقصة ({count} درجة حرية)",
"hole_presets.confirm_delete": "تأكيد الحذف",
"hole_presets.delete": "حذف",
"hole_presets.delete_prompt": "هل تحذف هذا الإعداد المسبق الشخصي؟ الثقوب الموجودة لا تتغير.",
@@ -1717,6 +1736,8 @@
"prefs.anti_aliasing_note": "ينعّم الحواف المسننة.",
"prefs.applies_on_restart": "يُطبَّق عند إعادة التشغيل",
"prefs.assistant": "المساعد",
"prefs.auto_operation": "اختيار العملية تلقائيًا",
"prefs.auto_operation_note": "تختار نوافذ الميزات «ضم» أو «جسم جديد» أو «قص» بحسب ما تلامسه المادة الجديدة، إلى أن تختار عملية بنفسك. عند الإيقاف: تحتفظ بالعملية التي تُفتح بها.",
"prefs.axes": "المحاور",
"prefs.axis": "المحور",
"prefs.bodies_folders": "مجلدات الأجسام",
@@ -1798,6 +1819,13 @@
"prefs.logo": "الشعار",
"prefs.logo_accent": "لون التمييز",
"prefs.logo_theme": "ألوان السمة",
"prefs.max_file_size": "أكبر ملف يمكن فتحه",
"prefs.max_file_size_design": "ملفات التصميم",
"prefs.max_file_size_flag": "بدأ هذا التشغيل بالخيار --max-file-size {size}، ويسري حتى إغلاق SolidCraft. تسري هذه الإعدادات عند تشغيله دون هذا الخيار.",
"prefs.max_file_size_note": "القيم الافتراضية: 512 MB و256 MB؛ الحد الأقصى 16 GB. يتطلب فتح ملف ذاكرة تعادل حجمه عدة مرات. قد يحتاج التصميم الذي يحتفظ باستيراد كبير إلى حد أعلى لملف التصميم لحفظه وإعادة فتحه.",
"prefs.max_file_size_step": "ملفات STEP والاستيراد",
"prefs.max_file_size_web": "يفتح تطبيق الويب ملفات STEP والاستيراد حتى 512 MB والتصاميم حتى 256 MB؛ لا يتسع تبويب المتصفح لملفات أكبر. يمكن رفع هذه الحدود في تطبيق سطح المكتب.",
"prefs.megabytes": "MB",
"prefs.minutes": "دقائق",
"prefs.minutes_suffix": " دقيقة",
"prefs.mouse": "الفأرة ولوحة اللمس",
@@ -1846,6 +1874,8 @@
"prefs.sketch_selection": "الرسم والتحديد",
"prefs.sketch_selection_heading": "الرسم والتحديد",
"prefs.sketch_selection_note": "كيف تلتقط الرسومات وكيف تحدد النقرات.",
"prefs.sketch_state_icons": "أيقونات الرسومات بلون حالتها",
"prefs.sketch_state_icons_note": "في السجل والمستعرض، تأخذ أيقونة الرسم لون حالته (قيود ناقصة، مقيّد بالكامل أو متعارض) ويذكر تلميحه الحالة. عند الإيقاف: لكل الرسومات الأيقونة نفسها.",
"prefs.sketch_tab_always": "إبقاء تبويب الرسم ظاهرًا",
"prefs.sketch_tab_always_note": "يبقى تبويب الرسم في الشريط بعد تبويب النموذج مباشرة حتى عندما لا يكون أي رسم مفتوحًا. عندها تبدأ أدوات الرسم فيه رسمًا على المستوي أو الوجه المحدد، أو على المستوي الذي تختاره بعد ذلك، وتكون الأداة جاهزة فيه.",
"prefs.sketch_tracking_guides": "أدلة محاذاة المراكز ونقاط المنتصف",
@@ -1855,6 +1885,13 @@
"prefs.source": "المصدر",
"prefs.speed": "السرعة",
"prefs.stand_up_note": "بعد فتح ملف STEP أو IGES ومحوره Z للأعلى، يُعرض تدويره ربع دورة حول X.",
"prefs.stand_up_unknown": "للملفات من برامج غير معروفة",
"prefs.stand_up_unknown.ask": "السؤال في مربع حوار",
"prefs.stand_up_unknown.note": "عرض ملاحظة",
"prefs.stand_up_unknown.off": "عدم العرض",
"prefs.stand_up_unknown_note": "طريقة العرض عندما لا يذكر رأس الملف برنامجًا معروفًا بحفظ المحور Y للأعلى. تظهر الملاحظة فقط عندما يبدو أن الملف ملقى على ظهره؛ أما «السؤال في مربع حوار» فيسأل عن كل ملف كهذا، كما كان سابقًا.",
"prefs.status_bar": "شريط الحالة",
"prefs.status_bar_note": "شريط رفيع على الحافة السفلية للنافذة يعرض ما يجري تحريره، وحالة الرسم المفتوح بلونها، ووحدات التصميم؛ النقر على الوحدات يغيّرها. عندها لا تظهر حالة الرسم في زاوية العرض.",
"prefs.status_done": "تم",
"prefs.status_error": "خطأ",
"prefs.status_selected": "محدد",
@@ -1937,6 +1974,45 @@
"render.aging_upward": "ترسّب على الأسطح المتجهة للأعلى",
"render.all_controls_help": "فحص مكثف لكل عناصر التحكم الحالية. صفحات الأقسام توفر تحريرًا مركّزًا دون تغيير الطلب الأساسي.",
"render.all_shots_label": "كل اللقطات",
"render.appearance_editor.absorption": "الامتصاص",
"render.appearance_editor.base_colour": "اللون الأساسي",
"render.appearance_editor.body_reason": "الربط البصري يستهدف جسمًا كاملًا واحدًا. اختر «جسم» لتحرير هذه الضوابط.",
"render.appearance_editor.boundary": "الحدود",
"render.appearance_editor.box": "مربع",
"render.appearance_editor.choose_image": "اختر صورة…",
"render.appearance_editor.closed": "جسم مصمت مغلق",
"render.appearance_editor.coat": "طبقة شفافة",
"render.appearance_editor.cpu_reason": "يتطلب تتبّع الأشعة على المعالج.",
"render.appearance_editor.glass": "الزجاج والامتصاص",
"render.appearance_editor.glass_enable": "استخدم زجاجًا فيزيائيًا على هذا الجسم",
"render.appearance_editor.glass_note": "يستخدم الزجاج المغلق المسافة الهندسية. لا يمكن أن يشارك جسمًا مع طبقة شفافة أو خرائط.",
"render.appearance_editor.gpu_reason": "يتطلب تتبّع المسار على بطاقة الرسومات.",
"render.appearance_editor.ior": "معامل الانكسار",
"render.appearance_editor.maps": "الخرائط",
"render.appearance_editor.normal": "العادي",
"render.appearance_editor.normal_strength": "قوة العادي",
"render.appearance_editor.origin": "الأصل",
"render.appearance_editor.recipe": "الوصفة",
"render.appearance_editor.reflection_sampling": "أخذ العينات لكل الأجسام العاكسة",
"render.appearance_editor.reflections": "الانعكاسات",
"render.appearance_editor.remove": "إزالة المحفوظ",
"render.appearance_editor.remove_note": "أزل الخصائص البصرية المحفوظة من هذه اللقطة. تبقى الهندسة والمظاهر المعيّنة.",
"render.appearance_editor.remove_title": "إزالة الخصائص البصرية المحفوظة؟",
"render.appearance_editor.replace": "استبدال الخصائص البصرية المحفوظة لهذا الجسم",
"render.appearance_editor.revalidate": "إعادة التحقق من هذا الجسم",
"render.appearance_editor.save": "حفظ هذه الخصائص البصرية",
"render.appearance_editor.save_reason": "يتطلب حفظ هذه الخصائص البصرية زجاجًا فيزيائيًا على جسم حالي في بطاقة الرسومات. تبقى الطبقة الشفافة وخرائط الصور المحلية مؤقتة.",
"render.appearance_editor.saved": "محفوظ مع هذه اللقطة",
"render.appearance_editor.saved_state": "هذه الخصائص البصرية محفوظة مع هذه اللقطة.",
"render.appearance_editor.scatter_reason": "التشتت الداخلي يتطلب حدًّا لجسم مصمت مغلق.",
"render.appearance_editor.temporary": "هذه الخصائص البصرية مؤقتة في هذا التبويب. احفظها لتبقى مع اللقطة.",
"render.appearance_editor.thickness": "السماكة",
"render.appearance_editor.thickness_reason": "السماكة تنطبق على الصفائح الرقيقة؛ الزجاج المغلق يستخدم المسافة الهندسية.",
"render.appearance_editor.thin": "صفيحة رقيقة",
"render.appearance_editor.u_repeat": "تكرار U",
"render.appearance_editor.v_repeat": "تكرار V",
"render.appearance_editor.v_up": "محور V للنسيج يشير للأعلى",
"render.appearance_editor.weight": "الوزن",
"render.appearance_physics_separate": "المظهر المرئي لا يعيّن الكثافة ولا البيانات الحرارية ولا أي خصائص هندسية أخرى.",
"render.appearances": "المظاهر",
"render.availability_native": "تركيب أصلي؛ راجعه قبل التطبيق",
@@ -1997,6 +2073,9 @@
"render.category_paint": "طلاء",
"render.category_plastic": "بلاستيك",
"render.category_wood": "خشب",
"render.check_opaque": "الشفافية: أسطح معتمة فقط؛ ألفا الخلفية مدعومة.",
"render.check_sorted_alpha": "الشفافية: تقريب ألفا مرتّب.",
"render.check_transmission": "الشفافية: نفاذية فيزيائية.",
"render.chromatic_offset": "إزاحة لونية عند الحواف",
"render.clear_stage": "إزالة المنصة المحفوظة",
"render.copy_request": "نسخ JSON",
@@ -2230,6 +2309,36 @@
"render.inner_half_angle": "نصف الزاوية الداخلية",
"render.inspect_scene": "تحديث ملخص المشهد الملتقط",
"render.inspector": "المفتش",
"render.inspector_advanced": "متقدم",
"render.inspector_all_users": "كل المستخدمين ({count})",
"render.inspector_applies_to": "ينطبق على",
"render.inspector_both_users": "كلا المستخدمين",
"render.inspector_chain": "من أين يأتي هذا المظهر",
"render.inspector_colour": "اللون",
"render.inspector_edit": "تحرير",
"render.inspector_finish": "اللمسة النهائية",
"render.inspector_from_material": "من المادة",
"render.inspector_gloss": "لمعان",
"render.inspector_material": "المادة",
"render.inspector_matte": "مطفأ",
"render.inspector_metal": "معدن",
"render.inspector_opacity": "العتامة",
"render.inspector_opaque_help": "تستخدم اللمسة النهائية والحقول البصرية PBR معتمًا (عتامة 1). العتامة الأقل تستخدم تظليلًا قياسيًا.",
"render.inspector_pick_target": "اختر هدفًا من هذا النوع لفحص مظهره.",
"render.inspector_remove": "إزالة التجاوز",
"render.inspector_replace": "استبدال…",
"render.inspector_reset": "إعادة تعيين",
"render.inspector_satin": "ساتان",
"render.inspector_save_library": "حفظ في المكتبة…",
"render.inspector_set_all": "مظاهر مختلطة. التحرير يضبط المظهر على كل الأهداف المحددة.",
"render.inspector_soon": "قريبًا",
"render.inspector_target_limit": "حدّد 10000 هدف كحد أقصى لفحص المظاهر.",
"render.inspector_textures": "الخامات",
"render.inspector_this_body": "هذا الجسم",
"render.inspector_this_component": "هذا المكوّن",
"render.inspector_this_face": "هذا الوجه",
"render.inspector_this_target": "هذا الهدف",
"render.inspector_used_by": "مستخدَم من {count}",
"render.keep_editing": "متابعة التحرير",
"render.layer_down": "نقل للأسفل",
"render.layer_enabled": "الطبقة مفعّلة",
@@ -2396,12 +2505,34 @@
"render.optics_saved": "الأهداف البصرية المحفوظة",
"render.optics_saved_help": "تبقى هوية التعريف والميزة والتواجد بالضبط بعد إعادة الفتح دون اتصال. الأهداف المفقودة أو المستبدلة أو المخفية تُرفض؛ والمواد الهندسية دون تغيير. احفظ التعديلات الرقمية أو تجاهلها قبل إزالة هدف محفوظ.",
"render.outer_half_angle": "نصف الزاوية الخارجية",
"render.output_advanced": "متقدم",
"render.output_backend_ready": "جاهز",
"render.output_check": "فحص",
"render.output_check_help": "شغّل الفحص لمراجعة هذا الطلب دون بدء التصيير.",
"render.output_check_summary": "فحص · ملاحظات: {count}",
"render.output_copy": "نسخ",
"render.output_destination": "الوجهة",
"render.output_destination_help": "اختر ملف الوجهة عند تصدير الصورة المكتملة.",
"render.output_dimensions": "الحجم",
"render.output_effects": "المؤثرات",
"render.output_export": "تصدير PNG…",
"render.output_exr_unavailable": "إعداد مخرجات EXR غير متاح هنا. تصدير EXR الخطي يتطلب صورة GPU مكتملة بنسخة رئيسية خطية محتفظ بها.",
"render.output_fit": "ملاءمة",
"render.output_format": "الصيغة",
"render.output_format_status": "PNG هو صورة العرض المكتملة بالضبط. EXR الاختياري على GPU يحتفظ بالإشعاع الخام الخطي للمشهد بصيغة FLOAT32 قبل مرشحات العرض. التصدير لا يستبدل أبدًا ذاكرة المعاينة المؤقتة أو مهمة أخرى.",
"render.output_image": "الصورة",
"render.output_largest": "الأقصى {width} × {height}",
"render.output_lock_ratio": "قفل نسبة الأبعاد",
"render.output_over_budget": "الصورة المطلوبة تتجاوز نطاق الموارد المختار. اختر حجمًا أو ملفًا متوافقًا صراحةً؛ لا يحدث تخفيض تلقائي.",
"render.output_presets_help": "الصيغة والجودة وقائمة الفحص في محرر المخرجات.",
"render.output_quality": "الجودة",
"render.output_quality_waiting": "بانتظار عقد الجودة",
"render.output_ratio_refused": "البُعد المرتبط يتجاوز حد الحجم لهذه الطريقة. يُحتفظ بالحجم السابق؛ ألغِ قفل النسبة أو اختر قيمة أصغر.",
"render.output_ready": "جاهز للتصيير",
"render.output_size_ok": "الحجم مناسب · {width} × {height} بكسل",
"render.output_swap_orientation": "تبديل العرض والارتفاع",
"render.output_tools_inspector": "عدّل الحجم والصيغة في مفتش المخرجات.",
"render.output_view_image": "عرض الصورة",
"render.page_aging": "التقادم والتلوث",
"render.page_all": "كل عناصر التحكم",
"render.page_appearance": "المظهر",
@@ -2462,10 +2593,18 @@
"render.property_unknown": "غير معروف",
"render.quality_ack": "الإقرار بإسقاط الميزانية الثابتة وما أُغفل",
"render.quality_apply": "استخدام ميزانية العيّنات الثابتة هذه",
"render.quality_balanced": "متوازن",
"render.quality_custom": "مخصص",
"render.quality_draft": "مسودة",
"render.quality_high": "عالية",
"render.quality_projection_help": "إعدادات Q الجاهزة تتضمن متطلبات مستقبلية. هذا الإجراء يُسقط فقط ميزانية PNG مدعومة بعيّنات ثابتة، ويُغفل صراحةً الإيقاف التكيّفي والإخراج الخطي الخام وحدود النفاذ المستقلة.",
"render.quality_review": "مراجعة ميزانية العيّنات",
"render.queue_pending": "لا توجد في هذه الواجهة قائمة إنتاج تلقائية ولا مُصيّر للرسوم المتحركة. تبقى الصور الثابتة المكتملة قابلة للتصدير كلٌّ على حدة.",
"render.radial_assign": "تعيين مظهر",
"render.radial_compare": "مقارنة",
"render.radial_distortion": "تشوه شعاعي",
"render.radial_fit": "ملاءمة المنتج",
"render.radial_save": "حفظ اللقطة",
"render.regrade_action": "إعادة تدريج النسخة الخام (دون إزالة تشويش)",
"render.regrade_help": "ينشئ PNG منفصلًا من النسخة الخام المكتملة المختارة باستخدام عناصر التحكم هذه في العرض. لا يتتبع أشعة، ولا يغيّر الحجم أو الكاميرا أو الأضواء الملتقطة، ولا يستبدل EXR. لا يُعاد تطبيق إزالة التشويش: النسخ الخام لا تحتفظ بأدلتها. يبقى الناتج المشتق قابلًا للتصدير بعناصر التحكم نفسها في المهمة.",
"render.regrade_label": "إعادة تدرّج",
@@ -2474,6 +2613,7 @@
"render.replace_label": "استبدال",
"render.replace_saved_surface": "استبدال ربط محفوظ موجود لهذا الهدف",
"render.restore_camera": "استعادة الكاميرا المحفوظة",
"render.result_actual": "100%",
"render.result_actual_help": "بكسل صورة واحد لكل بكسل عرض.",
"render.result_actual_size": "100%",
"render.result_capture": "المراجعة الملتقطة",
@@ -2541,6 +2681,7 @@
"render.sharpening": "زيادة حدة التفاصيل",
"render.shell_tab_appearance": "مظهر التصيير",
"render.shell_tab_camera": "كاميرا التصيير",
"render.shell_tab_lighting": "الإضاءة",
"render.shell_tab_scene": "مشهد التصيير",
"render.shot_duplicate": "تكرار اللقطة المحفوظة",
"render.shot_manager": "اللقطات المحفوظة",
@@ -2558,11 +2699,15 @@
"render.shots_save": "حفظ اللقطة",
"render.shots_update": "تحديث اللقطة",
"render.show_shots_label": "إظهار شريط اللقطات",
"render.size_4k": "4K",
"render.size_4k_uhd": "4K UHD",
"render.size_custom": "مخصص…",
"render.size_full_hd": "Full HD",
"render.size_hd": "HD",
"render.size_presets": "إعدادات الحجم المسبقة",
"render.size_presets_pending": "تصل مع محرر المخرجات",
"render.size_preview": "معاينة",
"render.size_square": "مربع",
"render.sky_ground": "الأرض · RGB خطي",
"render.sky_horizon": "الأفق · RGB خطي",
"render.sky_zenith": "السمت · RGB خطي",
@@ -2575,6 +2720,9 @@
"render.stage_output": "المخرجات",
"render.stage_panel_count": "الألواح المحفوظة",
"render.stage_preview_note": "معاينة مدمجة · ضوء مباشر",
"render.state_completed": "صورة مكتملة",
"render.state_in_progress": "صورة قيد الإنجاز",
"render.state_model": "معاينة النموذج",
"render.strip_label": "الشريط",
"render.studio_add_area": "إضافة ضوء مساحي",
"render.studio_ambient": "إشعاع البيئة الثابت",
@@ -2670,6 +2818,7 @@
"render.volume_max": "الحدود القصوى · mm العالم",
"render.volume_min": "الحدود الدنيا · mm العالم",
"render.volume_title": "حجم متجانس محدود",
"render.wait_add_light": "غير مدعوم بعد: إنشاء الأضواء المسمّاة",
"render.wait_batch": "غير مدعوم بعد: قائمة تصيير اللقطات المحفوظة",
"render.wait_compare": "غير مدعوم بعد: مقارنة الصور المحتفظ بها",
"render.wait_paint": "غير مدعوم بعد: اختيار المنتج وتلوين المظهر",
@@ -2784,6 +2933,10 @@
"sketch_text.path_hint": "منحنى من الرسم يتبعه النص: انقر هنا ثم على المنحنى",
"sketch_text.use_font_file": "استخدام ملف الخط {path}",
"solid": "مصمت",
"status.editing": "تحرير {name}",
"status.under_constrained": "قيود ناقصة ({count} درجات حرية)",
"status.under_constrained_one": "قيود ناقصة ({count} درجة حرية)",
"status.units_tip": "وحدات التصميم. انقر لتغييرها",
"the angle must be a number": "يجب أن تكون الزاوية عددًا",
"the curves must be in one sketch": "يجب أن تكون المنحنيات في رسم واحد",
"the direction must be an axis, a sketch line or a straight edge": "يجب أن يكون الاتجاه محورًا أو خط رسم أو حافة مستقيمة",
@@ -2822,8 +2975,10 @@
"timeline.move_to_group": "نقل الميزات إلى مجلد",
"timeline.rename_group": "إعادة تسمية مجلد الميزات",
"timeline.reorder_many": "إعادة ترتيب الميزات",
"timeline.suppress_features": "تعطيل الميزات",
"timeline.suppress_folders_tip": "تعطيل كل الميزات في المجلدات والمجلدات المتداخلة فيها أو إلغاء تعطيلها",
"timeline.ungroup": "إزالة مجلدات الميزات",
"timeline.unsuppress_features": "إلغاء تعطيل الميزات",
"to the face": "إلى الوجه",
"to the next face": "إلى الوجه التالي",
"toolbar.customize_note": "تبويب {tab}. حدّد أمرًا لمنحه زرًا في شريط الأدوات. يمكنك أيضًا سحب صف من القائمة المنسدلة للوحة إلى شريط الأدوات، أو سحب زر خارج شريط الأدوات لإزالته، أو استخدام الدبوس في صف القائمة المنسدلة.",
@@ -3234,6 +3389,8 @@
"ui.cancel": "إلغاء",
"ui.canvases": "اللوحات المرجعية",
"ui.center": "المركز",
"ui.center_arc_follows_cursor": "القوس بنقطة مركز يتبع اتجاه المؤشر",
"ui.center_arc_follows_cursor_tip": "يُرسم القوس بنقطة مركز باتجاه عقارب الساعة أو عكسها حسب الاتجاه الذي يدور به المؤشر حول مركزه. عند الإيقاف: دائمًا عكس اتجاه عقارب الساعة من البداية.",
"ui.centerline": "خط المركز",
"ui.chain": "سلسلة",
"ui.chamfer": "شطف",
@@ -3646,6 +3803,15 @@
"ui.plugins_skipped": "تعذّرت قراءة {count} مجلد(ات).",
"ui.plugins_version": "{version}",
"ui.point": "نقطة",
"ui.point_cloud_auto_range": "نطاق تلقائي",
"ui.point_cloud_color_by": "التلوين حسب",
"ui.point_cloud_file_colors": "ألوان الملف",
"ui.point_cloud_height_high": "أعلى ارتفاع",
"ui.point_cloud_height_low": "أدنى ارتفاع",
"ui.point_cloud_no_file_colors": "لا يحتوي الملف على ألوان",
"ui.point_cloud_single_color": "لون واحد",
"ui.point_cloud_size": "حجم النقطة",
"ui.point_cloud_units": "وحدات الملف",
"ui.point_under_the_cursor": "النقطة تحت المؤشر",
"ui.points": "النقاط",
"ui.polygon": "مضلع",
@@ -3899,11 +4065,13 @@
"update.installed": "تم تثبيت SolidCraft {version}. أعد التشغيل لاستخدامه.",
"update.installer_started": "يبدأ المثبّت عند إغلاق SolidCraft.",
"update.later": "ذكّرني لاحقًا",
"update.managed": "يتوفر تحديث: حدّث SolidCraft من داخل {manager}",
"update.no_notes": "لا توجد ملاحظات لهذا الإصدار.",
"update.package_hint": "جاءت هذه النسخة من {kind}. نزّل الحزمة الجديدة، ثم ثبّتها باستخدام:",
"update.prefs_dev": "هذا إصدار تطويري ({version}): لا يتحقق من التحديثات.",
"update.prefs_flatpak": "Flatpak يحدّث هذه النسخة ({version}): شغّل {command} أو استخدم مركز البرامج. SolidCraft لا يتحقق.",
"update.prefs_label": "التحديثات",
"update.prefs_managed": "هذا هو SolidCraft {version}، ويديره {manager}. يسأل الفحص GitHub عن أحدث إصدار ولا يرسل شيئًا آخر؛ حدّث SolidCraft من داخل {manager}.",
"update.prefs_note": "هذا SolidCraft {version} ({kind}). التحقق يسأل GitHub عن أحدث إصدار ولا يرسل أي شيء آخر.",
"update.prefs_web": "أعد تحميل الصفحة للحصول على أحدث إصدار من تطبيق الويب. إذا لم تتوفر الشبكة أو لم يكتمل تنزيل التحديث في الوقت المناسب، يُفتح الإصدار المحفوظ بدلًا منه؛ ويستمر التنزيل الجاري حتى إعادة التحميل التالية.",
"update.release_page": "فتح صفحة الإصدار",
@@ -3920,6 +4088,7 @@
"values must be finite numbers": "يجب أن تكون القيم أعدادًا منتهية",
"values must be numbers or expressions": "يجب أن تكون القيم أعدادًا أو تعابير",
"view.auto_ortho_faces": "منظوري أثناء التدوير، ومتعامد على الأوجه",
"view.render_inspector": "مفتش التصيير",
"web.files.insert_drawings_hint": "ملفات CAD أو شبكات أو DXF أو SVG إلى التصميم المفتوح",
"web.files.insert_stored_drawings_hint": "إلى التصميم المفتوح (ملفات CAD، شبكات، DXF، SVG)",
"web.open_full": "فتح في SolidCraft",
File diff suppressed because it is too large Load Diff
+9
View File
@@ -618,6 +618,7 @@
"appearance.pbr_opaque": "Uigennemsigtig PBR",
"appearance.roughness": "Ruhed",
"appearance.surface_response": "Overfladens reaktion på lys",
"at the face": "ved fladen",
"browser.body_name": "Emne{number}",
"browser.calibrate_title": "Kalibrér: {name}",
"browser.capture_many": "{count} komponenter er flyttet, men deres positioner er ikke registreret.",
@@ -923,6 +924,7 @@
"cmd.sketch_spline_control_point": "Spline med kontrolpunkter",
"cmd.sketch_spline_control_point_5": "Spline med kontrolpunkter (grad 5)",
"cmd.sketch_spline_fit_point": "Spline gennem punkter",
"cmd.sketch_spline_reverse": "Vend splinens retning",
"cmd.sketch_spun_profile": "Omdrejningsprofil",
"cmd.sketch_toggle_driven": "Skift mellem styrende og afledt mål",
"cmd.sketch_trim_path": "Trim langs bane",
@@ -1290,6 +1292,13 @@
"prefs.left_click": "Venstreklik",
"prefs.live_input": "Løbende input",
"prefs.live_preview": "Løbende forhåndsvisning",
"prefs.max_file_size": "Største fil, der kan åbnes",
"prefs.max_file_size_design": "Designfiler",
"prefs.max_file_size_flag": "Denne kørsel blev startet med --max-file-size {size}, som gælder, indtil SolidCraft lukkes. Disse indstillinger gælder ved start uden denne valgmulighed.",
"prefs.max_file_size_note": "Standard: 512 MB og 256 MB; højst 16 GB. Åbning af en fil kræver flere gange dens størrelse i hukommelse. Et design med en stor import kan kræve en højere grænse for designfiler for at kunne gemmes og åbnes igen.",
"prefs.max_file_size_step": "STEP- og importfiler",
"prefs.max_file_size_web": "Webappen åbner STEP- og importfiler op til 512 MB og design op til 256 MB: en browserfane kan ikke rumme større filer. Grænserne kan hæves i skrivebordsappen.",
"prefs.megabytes": "MB",
"prefs.minutes": "minutter",
"prefs.minutes_suffix": " min",
"prefs.mouse": "Mus og pegefelt",
+164
View File
@@ -180,6 +180,8 @@
"Designs you open or save appear here.": "Designs, die Sie öffnen oder speichern, erscheinen hier.",
"Determined by To Object. Choose Angle to edit the angular extent.": "Bestimmt durch Bis Objekt. Wählen Sie Winkel, um den Winkelbereich zu bearbeiten.",
"Diameter": "Durchmesser",
"Did this assembly open the wrong way up?": "Wurde diese Baugruppe falsch herum geöffnet?",
"Did this part open the wrong way up?": "Wurde dieses Teil falsch herum geöffnet?",
"Dimension new shapes": "Neue Formen bemaßen",
"Direction": "Richtung",
"Discard": "Verwerfen",
@@ -590,6 +592,7 @@
"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 assembly up?": "Diese Baugruppe aufrichten?",
"Stand this part up?": "Dieses Teil aufrichten?",
"Start angle": "Startwinkel",
"Start bulge": "Startausbauchung",
@@ -757,6 +760,7 @@
"appearance.pbr_opaque": "Opakes PBR",
"appearance.roughness": "Rauheit",
"appearance.surface_response": "Oberflächenverhalten",
"at the face": "an der Fläche",
"bom.group_children": "Unterteile in der Zeile ihres Teils",
"browser.body_name": "Körper {number}",
"browser.calibrate_title": "Kalibrieren: {name}",
@@ -765,6 +769,7 @@
"browser.capture_one": "Eine Komponente wurde verschoben, ihre Position ist aber nicht erfasst.",
"browser.component_name": "Komponente {number}",
"browser.edit_canvas_title": "Leinwand bearbeiten: {name}",
"browser.edit_point_cloud_title": "Punktwolke bearbeiten: {name}",
"browser.move_title": "Verschieben: {name}",
"browser.parameters": "Parameter ({count})",
"browser.precision": "Genauigkeit: {precision}",
@@ -992,6 +997,10 @@
"cmd.plastic_rest": "Auflage",
"cmd.plastic_rules": "Kunststoffregeln auflisten",
"cmd.plastic_snap_fit": "Rastverbindung",
"cmd.pointcloud_delete": "Punktwolke löschen",
"cmd.pointcloud_edit": "Punktwolke bearbeiten",
"cmd.pointcloud_insert": "Punktwolke einfügen",
"cmd.pointcloud_list": "Punktwolken",
"cmd.render_aging_describe": "Mehrschichtige Oberflächenalterung",
"cmd.render_backends": "Render-Backends",
"cmd.render_batch": "Alle Aufnahmen rendern",
@@ -1014,15 +1023,20 @@
"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_lights_add": "Licht hinzufügen",
"cmd.render_lights_solo": "Licht allein anzeigen",
"cmd.render_optics_bind": "Optische Einstellungen zuweisen",
"cmd.render_optics_unbind": "Zuweisung optischer Einstellungen entfernen",
"cmd.render_output_size": "Render-Ausgabegröße festlegen",
"cmd.render_preflight": "Renderanfrage prüfen",
"cmd.render_preset_apply": "Studio-Voreinstellung anwenden",
"cmd.render_preset_resolve": "Studio-Voreinstellung überprüfen",
"cmd.render_quality_prepare": "Festes Sampling-Budget überprüfen",
"cmd.render_result_close": "Renderbild schließen",
"cmd.render_result_copy": "Renderbild kopieren",
"cmd.render_result_pin": "Angezeigtes Render-Ergebnis behalten",
"cmd.render_result_regrade": "Lineares Master-Rendering erneut farbkorrigieren",
"cmd.render_result_view": "Renderbild anzeigen",
"cmd.render_scene": "Renderszene prüfen",
"cmd.render_settings_apply": "Renderkamera anwenden",
"cmd.render_settings_capture_hdr": "Render-Einstellungen mit HDR speichern",
@@ -1125,6 +1139,7 @@
"cmd.sketch_spline_control_point_5": "Spline mit Kontrollpunkten (Grad 5)",
"cmd.sketch_spline_fit_point": "Spline mit Fitpunkten",
"cmd.sketch_spline_insert_point": "Splinepunkt hinzufügen",
"cmd.sketch_spline_reverse": "Splinerichtung umkehren",
"cmd.sketch_spun_profile": "Gedrehtes Profil",
"cmd.sketch_toggle_diameter": "Radius / Durchmesser umschalten",
"cmd.sketch_toggle_driven": "Abhängiges Maß umschalten",
@@ -1318,6 +1333,7 @@
"help.licences": "Lizenzen",
"help.mit": "MIT-Lizenz",
"help.notice": "NOTICE",
"help.pointcloud.insert": "Fügt einen PLY-, XYZ- oder PTS-Scan als Punktwolke neben dem Modell ein. Verwenden Sie ihn als Referenz beim Modellieren anhand von Messdaten.",
"help.solid.chamfer": "Schneidet die gewählten Kanten mit einer ebenen Fase im eingestellten Abstand ab. Damit brechen Sie scharfe Kanten oder erleichtern das Einführen eines Stifts oder einer Schraube.",
"help.solid.combine": "Kombiniert Körper: vereinigt sie, zieht sie von einem Zielkörper ab oder behält nur das gemeinsame Volumen; die Werkzeugkörper können erhalten bleiben. Damit fügen Sie Teile zusammen, schneiden eine Form mit einer anderen aus oder finden, wo sich zwei Körper überschneiden.",
"help.solid.extrude": "Zieht ein Skizzenprofil über eine Strecke in die dritte Dimension und fügt dabei Material hinzu oder entfernt es. Damit machen Sie aus einer flachen Kontur einen Block, eine Erhebung oder eine Tasche.",
@@ -1331,6 +1347,8 @@
"help.solid.shell": "Höhlt einen Körper aus, lässt Wände in der eingestellten Dicke stehen und kann gewählte Flächen öffnen. Für Kästen, Deckel und andere dünnwandige Teile.",
"help.solid.sweep": "Führt ein Skizzenprofil entlang eines Pfads und erzeugt so einen Körper. Für Rohre, Schienen, Griffe und alle Teile mit gleichbleibendem Querschnitt entlang einer Kurve.",
"help.third_party": "Drittanbieterlizenzen",
"history.sketch_under_constrained": "Unterbestimmt ({count} Freiheitsgrade)",
"history.sketch_under_constrained_one": "Unterbestimmt ({count} Freiheitsgrad)",
"hole_presets.confirm_delete": "Löschen bestätigen",
"hole_presets.delete": "Löschen",
"hole_presets.delete_prompt": "Möchten Sie diese persönliche Vorlage wirklich löschen? Bereits vorhandene Bohrungen bleiben unverändert.",
@@ -1774,15 +1792,31 @@
"prefs.left": "Links",
"prefs.left_click": "Linksklick",
"prefs.linked_designs": "Verknüpfte Designs",
"prefs.max_file_size": "Maximale Dateigröße zum Öffnen",
"prefs.max_file_size_design": "Designdateien",
"prefs.max_file_size_flag": "Dieser Lauf wurde mit --max-file-size {size} gestartet. Der Wert gilt, bis SolidCraft geschlossen wird. Diese Einstellungen gelten beim Start ohne diese Option.",
"prefs.max_file_size_note": "Standard: 512 MB und 256 MB; höchstens 16 GB. Das Öffnen einer Datei benötigt ein Mehrfaches ihrer Größe im Arbeitsspeicher. Ein Design mit großem Import benötigt zum Speichern und erneuten Öffnen eventuell eine höhere Grenze für Designdateien.",
"prefs.max_file_size_step": "STEP- und Importdateien",
"prefs.max_file_size_web": "Die Web-App öffnet STEP- und Importdateien bis 512 MB und Designs bis 256 MB: Ein Browser-Tab kann keine größeren Dateien aufnehmen. In der Desktop-App lassen sich diese Grenzen erhöhen.",
"prefs.megabytes": "MB",
"prefs.minutes_suffix": " min",
"prefs.origin_plane_names": "Namen der Ursprungsebenen",
"prefs.origin_plane_names_note": "Namen in Browser, Dialogen und Listen. {top} ist XY, {front} ist XZ und {right} ist YZ, mit Z nach oben. Befehle, Skripte und gespeicherte Konstruktionen behalten XY, XZ und YZ.",
"prefs.search": "Einstellungen suchen",
"prefs.search_empty": "Keine passenden Einstellungen",
"prefs.sketch_state_icons": "Skizzensymbole in der Farbe ihres Zustands",
"prefs.sketch_state_icons_note": "Im Verlauf und im Browser nimmt das Symbol einer Skizze die Farbe ihres Zustands an (unterbestimmt, vollständig bestimmt oder im Konflikt), und ihr Tooltip nennt den Zustand. Aus: Alle Skizzen haben dasselbe Symbol.",
"prefs.sketch_tab_always": "Registerkarte Skizze immer zeigen",
"prefs.sketch_tab_always_note": "Die Registerkarte Skizze bleibt im Menüband direkt nach Modell, auch wenn keine Skizze offen ist. Ihre Zeichenwerkzeuge beginnen dann eine Skizze auf der ausgewählten Ebene oder Fläche oder auf der Ebene, die Sie als Nächstes wählen, und das Werkzeug ist darin bereit.",
"prefs.sketch_tracking_guides": "Ausrichtungshilfen für Mittelpunkte",
"prefs.sketch_tracking_guides_note": "Den Mauszeiger über einen Kreismittelpunkt oder eine Kantenmitte bewegen und dann waagerecht oder senkrecht bewegen, um eine temporäre gepunktete Hilfslinie anzuzeigen.",
"prefs.stand_up_unknown": "Für Dateien aus unbekannten Programmen",
"prefs.stand_up_unknown.ask": "In einem Dialog fragen",
"prefs.stand_up_unknown.note": "Einen Hinweis zeigen",
"prefs.stand_up_unknown.off": "Nicht anbieten",
"prefs.stand_up_unknown_note": "Wie das Aufrichten angeboten wird, wenn der Dateikopf kein Programm nennt, das bekanntlich mit Y nach oben speichert. Ein Hinweis erscheint nur, wenn die Datei auf dem Rücken zu liegen scheint; „In einem Dialog fragen“ fragt wie bisher bei jeder solchen Datei.",
"prefs.status_bar": "Statusleiste",
"prefs.status_bar_note": "Eine schmale Leiste am unteren Fensterrand zeigt, was gerade bearbeitet wird, den Zustand der offenen Skizze in seiner Farbe und die Einheiten der Konstruktion; ein Klick auf die Einheiten ändert sie. Der Zustand der Skizze steht dann nicht mehr in der Ecke der Ansicht.",
"prefs.theme_file_or_paste": "Datei oder Einfügen",
"prefs.theme_follow": "Einer Designdatei folgen",
"prefs.theme_follow_error": "Nicht angewendet: {error}. Das letzte gültige Design bleibt aktiv.",
@@ -1802,6 +1836,45 @@
"recovery.never_saved": "nie gespeichert",
"render.add_ground": "Add fitted ground to setup",
"render.all_shots_label": "Alle Aufnahmen",
"render.appearance_editor.absorption": "Absorption",
"render.appearance_editor.base_colour": "Grundfarbe",
"render.appearance_editor.body_reason": "Optische Bindungen gelten für einen ganzen Körper. Wählen Sie Körper, um diese Einstellungen zu bearbeiten.",
"render.appearance_editor.boundary": "Grenzfläche",
"render.appearance_editor.box": "Quader",
"render.appearance_editor.choose_image": "Bild wählen…",
"render.appearance_editor.closed": "Geschlossener Körper",
"render.appearance_editor.coat": "Klarlack",
"render.appearance_editor.cpu_reason": "Benötigt CPU-Strahlverfolgung.",
"render.appearance_editor.glass": "Glas und Absorption",
"render.appearance_editor.glass_enable": "Physikalisches Glas für diesen Körper verwenden",
"render.appearance_editor.glass_note": "Geschlossenes Glas verwendet geometrische Abstände. Es kann nicht mit Klarlack oder Texturen auf demselben Körper kombiniert werden.",
"render.appearance_editor.gpu_reason": "Benötigt GPU-Pfadverfolgung.",
"render.appearance_editor.ior": "IOR",
"render.appearance_editor.maps": "Texturen",
"render.appearance_editor.normal": "Normale",
"render.appearance_editor.normal_strength": "Normalenstärke",
"render.appearance_editor.origin": "Ursprung",
"render.appearance_editor.recipe": "Rezept",
"render.appearance_editor.reflection_sampling": "Abtastung für alle reflektierenden Körper",
"render.appearance_editor.reflections": "Reflexionen",
"render.appearance_editor.remove": "Gespeicherte entfernen",
"render.appearance_editor.remove_note": "Die gespeicherte Optik wird aus dieser Aufnahme entfernt. Geometrie und zugewiesene Looks bleiben erhalten.",
"render.appearance_editor.remove_title": "Gespeicherte Optik entfernen?",
"render.appearance_editor.replace": "Gespeicherte Optik dieses Körpers ersetzen",
"render.appearance_editor.revalidate": "Diesen Körper erneut prüfen",
"render.appearance_editor.save": "Diese Optik speichern",
"render.appearance_editor.save_reason": "Zum Speichern wird physikalisches Glas auf einem aktuellen GPU-Körper benötigt. Klarlack und lokale Bildtexturen bleiben vorübergehend.",
"render.appearance_editor.saved": "Mit dieser Aufnahme gespeichert",
"render.appearance_editor.saved_state": "Diese Optik ist mit dieser Aufnahme gespeichert.",
"render.appearance_editor.scatter_reason": "Innere Streuung benötigt einen geschlossenen Körper.",
"render.appearance_editor.temporary": "Diese Optik gilt vorübergehend in diesem Tab. Speichern Sie sie mit der Aufnahme.",
"render.appearance_editor.thickness": "Dicke",
"render.appearance_editor.thickness_reason": "Dicke gilt für dünne Schichten; geschlossenes Glas verwendet geometrische Abstände.",
"render.appearance_editor.thin": "Dünne Schicht",
"render.appearance_editor.u_repeat": "U-Wiederholung",
"render.appearance_editor.v_repeat": "V-Wiederholung",
"render.appearance_editor.v_up": "Textur-V zeigt nach oben",
"render.appearance_editor.weight": "Gewicht",
"render.appearances": "Erscheinungsbilder",
"render.background": "Szenenhintergrund",
"render.bloom_enable": "Darstellungs-Bloom",
@@ -1816,6 +1889,9 @@
"render.category_paint": "Lack",
"render.category_plastic": "Kunststoff",
"render.category_wood": "Holz",
"render.check_opaque": "Transparenz: nur undurchsichtige Oberflächen; Hintergrund-Alpha wird unterstützt.",
"render.check_sorted_alpha": "Transparenz: Näherung mit sortiertem Alpha.",
"render.check_transmission": "Transparenz: physikalische Transmission.",
"render.clear_stage": "Remove saved stage",
"render.cpu_budget": "CPU-Ressourcenprofil",
"render.cpu_budget_bodies": "Körperobergrenze",
@@ -1828,6 +1904,7 @@
"render.cpu_budget_studio": "Studio (große Bilder)",
"render.cpu_budget_triangles": "Dreiecksobergrenze",
"render.cpu_budget_workstation": "Workstation (komplexe Szenen)",
"render.effects_gpu_only": "Diese Effekte erfordern GPU-Pfadverfolgung. Ein Seitenwechsel schaltet den Renderer niemals automatisch um.",
"render.emitted_axis": "Abstrahlachse",
"render.environment_tiles_require_raytrace": "Umgebungen benötigen CPU- oder GPU-Raytracing. Im Ausgabeeditor wählen.",
"render.error_area_samples": "area_samples muss 1, 4, 16 oder 64 sein",
@@ -1926,6 +2003,36 @@
"render.include_rig_cards": "Include passive cards and flags",
"render.inner_half_angle": "Innerer Halbwinkel",
"render.inspector": "Inspektor",
"render.inspector_advanced": "Erweitert",
"render.inspector_all_users": "Alle {count} Nutzer",
"render.inspector_applies_to": "Anwenden auf",
"render.inspector_both_users": "Beide Nutzer",
"render.inspector_chain": "Herkunft dieses Aussehens",
"render.inspector_colour": "Farbe",
"render.inspector_edit": "Bearbeiten",
"render.inspector_finish": "Oberfläche",
"render.inspector_from_material": "Vom Material",
"render.inspector_gloss": "Glanz",
"render.inspector_material": "Material",
"render.inspector_matte": "Matt",
"render.inspector_metal": "Metall",
"render.inspector_opacity": "Deckkraft",
"render.inspector_opaque_help": "Oberfläche und optische Felder verwenden opakes PBR (Deckkraft 1). Geringere Deckkraft verwendet skalare Schattierung.",
"render.inspector_pick_target": "Ein Ziel dieses Typs wählen, um sein Aussehen zu prüfen.",
"render.inspector_remove": "Überschreibung entfernen",
"render.inspector_replace": "Ersetzen…",
"render.inspector_reset": "Zurücksetzen",
"render.inspector_satin": "Seidenmatt",
"render.inspector_save_library": "In Bibliothek speichern…",
"render.inspector_set_all": "Gemischtes Aussehen. Eine Bearbeitung weist das Aussehen allen gewählten Zielen zu.",
"render.inspector_soon": "Demnächst",
"render.inspector_target_limit": "Zum Prüfen des Aussehens höchstens 10000 Ziele auswählen.",
"render.inspector_textures": "Texturen",
"render.inspector_this_body": "Dieser Körper",
"render.inspector_this_component": "Diese Komponente",
"render.inspector_this_face": "Diese Fläche",
"render.inspector_this_target": "Dieses Ziel",
"render.inspector_used_by": "Verwendet von {count}",
"render.layer_off_suffix": " (aus)",
"render.library_appearance_available": "Verfügbares Erscheinungsbild",
"render.library_applicable": "Passende Ressourcen",
@@ -1988,8 +2095,30 @@
"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.outer_half_angle": "Äußerer Halbwinkel",
"render.output_advanced": "Erweitert",
"render.output_backend_ready": "Bereit",
"render.output_check": "Prüfen",
"render.output_check_help": "Prüfen bewertet diese Anfrage, ohne einen Renderauftrag zu starten.",
"render.output_check_summary": "Prüfen · Hinweise: {count}",
"render.output_copy": "Kopieren",
"render.output_destination": "Ziel",
"render.output_destination_help": "Die Zieldatei wird beim Exportieren des fertigen Bildes gewählt.",
"render.output_dimensions": "Größe",
"render.output_effects": "Effekte",
"render.output_export": "PNG exportieren…",
"render.output_exr_unavailable": "EXR-Ausgabeeinstellungen sind hier nicht verfügbar. Linearer EXR-Export erfordert ein fertiges GPU-Bild mit gespeichertem linearem Original.",
"render.output_fit": "Einpassen",
"render.output_format": "Format",
"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.output_image": "Bild",
"render.output_largest": "{width} × {height} max.",
"render.output_presets_help": "Format, Qualität und Prüfliste befinden sich im Ausgabeeditor.",
"render.output_quality": "Qualität",
"render.output_quality_waiting": "Qualitätsdefinition steht noch aus",
"render.output_ready": "Bereit zum Rendern",
"render.output_size_ok": "Größe OK · {width} × {height} px",
"render.output_tools_inspector": "Größe und Format im Ausgabe-Inspektor bearbeiten.",
"render.output_view_image": "Bild anzeigen",
"render.paint_label": "Aufmalen",
"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.",
@@ -2019,11 +2148,20 @@
"render.property_source": "Quelle: {id} - {locator}",
"render.property_uncertainty": "Unsicherheit: {value}",
"render.property_unknown": "unbekannt",
"render.quality_balanced": "Ausgewogen",
"render.quality_custom": "Benutzerdefiniert",
"render.quality_draft": "Entwurf",
"render.quality_high": "Hoch",
"render.radial_assign": "Erscheinungsbild zuweisen",
"render.radial_compare": "Vergleichen",
"render.radial_fit": "Produkt einpassen",
"render.radial_save": "Aufnahme speichern",
"render.regrade_label": "Neu abstimmen",
"render.reload_settings": "Gespeicherte Einstellungen laden",
"render.replace_label": "Ersetzen",
"render.replace_saved_surface": "Replace an existing saved binding for this target",
"render.restore_camera": "Gespeicherte Kamera laden",
"render.result_actual": "100%",
"render.result_actual_size": "100%",
"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",
@@ -2067,11 +2205,15 @@
"render.shots_save": "Aufnahme speichern",
"render.shots_update": "Aufnahme aktualisieren",
"render.show_shots_label": "Aufnahmenleiste einblenden",
"render.size_4k": "4K",
"render.size_4k_uhd": "4K UHD",
"render.size_custom": "Benutzerdefiniert…",
"render.size_full_hd": "Full HD",
"render.size_hd": "HD",
"render.size_presets": "Größenvorlagen",
"render.size_presets_pending": "Kommt mit dem Ausgabeeditor",
"render.size_preview": "Vorschau",
"render.size_square": "Quadrat",
"render.spot_cone": "Spotkegel",
"render.stage_frame": "Bildausschnitt",
"render.stage_light": "Licht",
@@ -2080,6 +2222,9 @@
"render.stage_output": "Ausgabe",
"render.stage_panel_count": "Saved panels",
"render.stage_preview_note": "Integrierte Vorschau · direktes Licht",
"render.state_completed": "Fertiges Bild",
"render.state_in_progress": "Bild in Bearbeitung",
"render.state_model": "Modellvorschau",
"render.strip_label": "Leiste",
"render.studio_tiles_require_gpu": "Studios benötigen GPU-Pfadverfolgung. Im Ausgabeeditor GPU wählen.",
"render.studios_environments": "Studios und Umgebungen",
@@ -2122,6 +2267,7 @@
"render.unavailable_preview": "Dieses Setup verlangt ein nicht verfügbares Vorschauprofil.",
"render.unsaved": "Ungespeicherte Vorschauänderungen",
"render.use_preview": "Integrierte Vorschau verwenden",
"render.wait_add_light": "Noch nicht unterstützt: benannte Lichtquellen erstellen",
"render.wait_batch": "Noch nicht unterstützt: Renderwarteschlange gespeicherter Aufnahmen",
"render.wait_compare": "Noch nicht unterstützt: Vergleich gespeicherter Bilder",
"render.wait_paint": "Noch nicht unterstützt: Produktauswahl und Auftragen von Erscheinungsbildern",
@@ -2224,6 +2370,10 @@
"sketch_text.path_hint": "Eine Skizzenkurve, der der Text folgt: hier klicken, dann die Kurve",
"sketch_text.use_font_file": "Schriftdatei {path} verwenden",
"solid": "Volumenkörper",
"status.editing": "{name} wird bearbeitet",
"status.under_constrained": "Unterbestimmt ({count} Freiheitsgrade)",
"status.under_constrained_one": "Unterbestimmt ({count} Freiheitsgrad)",
"status.units_tip": "Die Einheiten der Konstruktion. Zum Ändern klicken",
"the angle must be a number": "der Winkel muss eine Zahl sein",
"the curves must be in one sketch": "die Kurven müssen in einer Skizze liegen",
"the direction must be an axis, a sketch line or a straight edge": "die Richtung muss eine Achse, eine Skizzenlinie oder eine gerade Kante sein",
@@ -2253,8 +2403,10 @@
"timeline.move_to_group": "Merkmale in Ordner verschieben",
"timeline.rename_group": "Merkmalsordner umbenennen",
"timeline.reorder_many": "Merkmale neu ordnen",
"timeline.suppress_features": "Merkmale unterdrücken",
"timeline.suppress_folders_tip": "Unterdrückt alle Features in den Ordnern und ihren Unterordnern oder hebt die Unterdrückung auf",
"timeline.ungroup": "Merkmalsordner entfernen",
"timeline.unsuppress_features": "Unterdrückung der Merkmale aufheben",
"to the face": "bis zur Fläche",
"to the next face": "bis zur nächsten Fläche",
"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.",
@@ -2615,6 +2767,8 @@
"ui.cancel": "Abbrechen",
"ui.canvases": "Kanvas",
"ui.center": "Mittelpunkt",
"ui.center_arc_follows_cursor": "Mittelpunkt-Kreisbogen folgt der Richtung des Mauszeigers",
"ui.center_arc_follows_cursor_tip": "Ein Mittelpunkt-Kreisbogen läuft im oder gegen den Uhrzeigersinn, je nachdem, wie der Mauszeiger um seinen Mittelpunkt geführt wird. Aus: immer gegen den Uhrzeigersinn vom Start aus.",
"ui.centerline": "Mittellinie",
"ui.chain": "Kette",
"ui.chamfer": "Abschrägung",
@@ -2997,6 +3151,15 @@
"ui.plugins_skipped": "{count} Ordner konnte(n) nicht gelesen werden.",
"ui.plugins_version": "{version}",
"ui.point": "Punkt",
"ui.point_cloud_auto_range": "Bereich automatisch",
"ui.point_cloud_color_by": "Färben nach",
"ui.point_cloud_file_colors": "Farben der Datei",
"ui.point_cloud_height_high": "Oberste Höhe",
"ui.point_cloud_height_low": "Unterste Höhe",
"ui.point_cloud_no_file_colors": "Die Datei hat keine Farben",
"ui.point_cloud_single_color": "Einheitliche Farbe",
"ui.point_cloud_size": "Punktgröße",
"ui.point_cloud_units": "Einheit der Datei",
"ui.point_under_the_cursor": "Punkt unter dem Cursor",
"ui.points": "Punkte",
"ui.polygon": "Polygon",
@@ -3245,6 +3408,7 @@
"use a positive nominal diameter and a pitch smaller than it": "einen positiven Nenndurchmesser und eine kleinere Steigung verwenden",
"values must be finite numbers": "Werte müssen endliche Zahlen sein",
"values must be numbers or expressions": "Werte müssen Zahlen oder Ausdrücke sein",
"view.render_inspector": "Render-Inspektor",
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ auswählen · Eingabe / Tab vervollständigen · Esc schließen",
"⌘Enter continues on GitHub.": "⌘Enter geht weiter zu GitHub."
}
+14
View File
@@ -17,8 +17,18 @@
"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.",
"prefs.max_file_size": "Largest file to open",
"prefs.max_file_size_design": "Design files",
"prefs.max_file_size_flag": "This run started with --max-file-size {size}, which applies until SolidCraft closes. These settings apply when it starts without it.",
"prefs.max_file_size_note": "Defaults: 512 MB and 256 MB; at most 16 GB. Opening a file takes several times its size in memory. A design that keeps a large import may need a higher design file limit to save and reopen.",
"prefs.max_file_size_step": "STEP and import files",
"prefs.max_file_size_web": "The web app opens STEP and import files up to 512 MB and designs up to 256 MB: a browser tab can't hold larger files. The desktop app can raise these limits.",
"prefs.megabytes": "MB",
"prefs.sketch_state_icons": "Sketch icons in the colour of their state",
"prefs.sketch_state_icons_note": "In History and the browser, a sketch's icon takes the colour of its state (under constrained, fully constrained or conflicting) and its tooltip names the state. Off: every sketch has the same icon.",
"prefs.sketch_tracking_guides": "Centre and midpoint alignment guides",
"prefs.sketch_tracking_guides_note": "Hover over a circle centre or straight-edge midpoint, then move horizontally or vertically to show a temporary dotted guide.",
"prefs.status_bar_note": "A thin bar along the window's bottom edge shows what is being edited, the open sketch's state in its colour and the design's units; a click on the units changes them. The sketch's state then no longer shows in the view's corner.",
"prefs.theme_follow": "Follow a theme file",
"prefs.theme_follow_error": "Not applied: {error}. The last good theme stays on.",
"prefs.theme_follow_launch": "Followed for this run only (--theme-file); reloaded whenever another tool rewrites it.",
@@ -109,6 +119,10 @@
"ui.design_properties.organization": "Organisation",
"ui.layout_compact": "Compact",
"ui.layout_floating": "Floating",
"ui.point_cloud_color_by": "Colour By",
"ui.point_cloud_file_colors": "File Colours",
"ui.point_cloud_no_file_colors": "The file has no colours",
"ui.point_cloud_single_color": "Single Colour",
"ui.ribbon_labels": "Show text under ribbon icons",
"ui.workspace_layout": "Workspace layout",
"ui.zoom_to_cursor_tip": "The wheel zooms towards the point under the cursor (off: towards the view's centre)",
+173 -4
View File
@@ -181,6 +181,8 @@
"Designs you open or save appear here.": "Designs you open or save appear here.",
"Determined by To Object. Choose Angle to edit the angular extent.": "Determined by To Object. Choose Angle to edit the angular extent.",
"Diameter": "Diameter",
"Did this assembly open the wrong way up?": "Did this assembly open the wrong way up?",
"Did this part open the wrong way up?": "Did this part open the wrong way up?",
"Dimension new shapes": "Dimension new shapes",
"Direction": "Direction",
"Discard": "Discard",
@@ -593,6 +595,7 @@
"Square": "Square",
"Stand it up": "Stand it up",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Stand it up: a quarter turn about X, as one Move feature you can undo.",
"Stand this assembly up?": "Stand this assembly up?",
"Stand this part up?": "Stand this part up?",
"Start angle": "Start angle",
"Start bulge": "Start bulge",
@@ -720,7 +723,7 @@
"a picked edge is gone": "a picked edge is gone",
"a picked edge's body is gone": "a picked edge's body is gone",
"add a key for a joint first": "add a key for a joint first",
"ai.connected_toast": "SolidCraft is now available in {clients}. Restart them to use it (Settings > Assistant).",
"ai.connected_toast": "SolidCraft is now available in {clients}. Restart them to use it (Preferences > Assistant).",
"ai.prefs.allow_control": "Let AI agents connect (control channel / MCP)",
"ai.prefs.allow_control_note": "Opens a control channel on this computer only (127.0.0.1, port 27878 or the next free one). A program that connects must also show a secret token that only your user account can read, made fresh at every start. Takes effect the next time SolidCraft starts. Off: nothing listens unless you start SolidCraft with --control.",
"ai.prefs.availability": "Assistant controls",
@@ -760,6 +763,7 @@
"appearance.pbr_opaque": "Opaque PBR",
"appearance.roughness": "Roughness",
"appearance.surface_response": "Surface response",
"at the face": "at the face",
"bom.group_children": "Sub-parts in their part's row",
"browser.body_name": "Body{number}",
"browser.calibrate_title": "Calibrate: {name}",
@@ -768,6 +772,7 @@
"browser.capture_one": "A component was moved but its position isn't captured.",
"browser.component_name": "Component{number}",
"browser.edit_canvas_title": "Edit Canvas: {name}",
"browser.edit_point_cloud_title": "Edit Point Cloud: {name}",
"browser.move_title": "Move: {name}",
"browser.parameters": "Parameters ({count})",
"browser.precision": "Precision: {precision}",
@@ -995,6 +1000,10 @@
"cmd.plastic_rest": "Rest",
"cmd.plastic_rules": "List Plastic Rules",
"cmd.plastic_snap_fit": "Snap Fit",
"cmd.pointcloud_delete": "Delete Point Cloud",
"cmd.pointcloud_edit": "Edit Point Cloud",
"cmd.pointcloud_insert": "Insert Point Cloud",
"cmd.pointcloud_list": "Point Clouds",
"cmd.render_aging_describe": "Layered Surface Aging",
"cmd.render_backends": "Render Backends",
"cmd.render_batch": "Render All Shots",
@@ -1017,15 +1026,20 @@
"cmd.render_library_surface_prepare": "Prepare Authored Surface Maps",
"cmd.render_library_surface_unbind": "Remove Surface Binding",
"cmd.render_library_targets": "Find Render Library Targets",
"cmd.render_lights_add": "Add Light",
"cmd.render_lights_solo": "Solo Light",
"cmd.render_optics_bind": "Save Optical Binding",
"cmd.render_optics_unbind": "Remove Optical Binding",
"cmd.render_output_size": "Set Render Output Size",
"cmd.render_preflight": "Check Render Request",
"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_close": "Close Render Image",
"cmd.render_result_copy": "Copy Render Image",
"cmd.render_result_pin": "Retain Displayed Render Result",
"cmd.render_result_regrade": "Regrade Linear Master",
"cmd.render_result_view": "View Render Image",
"cmd.render_scene": "Inspect Render Scene",
"cmd.render_settings_apply": "Apply Render Camera",
"cmd.render_settings_capture_hdr": "Save Render Settings with HDR",
@@ -1128,6 +1142,7 @@
"cmd.sketch_spline_control_point_5": "Control Point Spline (Degree 5)",
"cmd.sketch_spline_fit_point": "Fit Point Spline",
"cmd.sketch_spline_insert_point": "Add Spline Point",
"cmd.sketch_spline_reverse": "Reverse Spline Direction",
"cmd.sketch_spun_profile": "Spun Profile",
"cmd.sketch_toggle_diameter": "Toggle Radius / Diameter",
"cmd.sketch_toggle_driven": "Toggle Driven Dimension",
@@ -1255,6 +1270,7 @@
"dialog.dimension_insert_hint": "Click another driving dimension to insert its name",
"dialog.dimension_name": "Dimension: {name}",
"dialog.edit_title": "EDIT {title}",
"dialog.no_shown_body": "No shown body to change: show one, or choose New Body",
"dialog.select": "↖ Select",
"dialog.selected_count": "↖ {count} selected",
"dialog.selection_guidance": "Select an input to change it. Click selected objects to remove them; Ctrl/Cmd-click also toggles them. Review the preview, then press OK.",
@@ -1325,6 +1341,7 @@
"help.mit": "MIT licence",
"help.notice": "NOTICE",
"help.parameters.change": "Opens the table of named values and expressions that drive the dimensions of the design. Use it to change a size in one place and let every feature that uses it follow.",
"help.pointcloud.insert": "Insert a PLY, XYZ or PTS scan as a point cloud alongside the model. Use it as a reference when building a model from measured data.",
"help.solid.align": "Moves the selected bodies so that a point, face or plane on them meets a point, face or plane elsewhere. Use it to place a part against another one without measuring the offset.",
"help.solid.chamfer": "Cuts the selected edges with a flat bevel of the distance you set. Use it to break sharp edges or to ease the entry of a pin or screw.",
"help.solid.combine": "Joins bodies, cuts them out of a target body, or keeps only the volume they share, and can keep the tool bodies. Use it to merge parts, to cut one shape with another or to find where two bodies overlap.",
@@ -1350,6 +1367,8 @@
"help.third_party": "Third-party licences",
"help.timeline.compute_all": "Rebuilds every feature in the timeline from the start. Use it to refresh the model and to see which features fail.",
"help.timeline.delete": "Deletes the selected features from the timeline; deleting a sketch also deletes the features built from it. Use it to remove a step that is no longer wanted.",
"history.sketch_under_constrained": "Under constrained ({count} degrees of freedom)",
"history.sketch_under_constrained_one": "Under constrained ({count} degree of freedom)",
"hole_presets.confirm_delete": "Confirm delete",
"hole_presets.delete": "Delete",
"hole_presets.delete_prompt": "Delete this personal preset? Existing holes are unchanged.",
@@ -1415,7 +1434,7 @@
"home.recovery_hint": "SolidCraft closed before these designs were saved.",
"home.report_issue": "Report an Issue",
"home.samples": "Samples",
"home.settings": "Settings",
"home.settings": "Preferences",
"home.short_date": "{day} {month}",
"home.shortcuts": "Keyboard shortcuts",
"home.start_hint": "Create a design, open a recent file, or explore a sample.",
@@ -1717,6 +1736,8 @@
"prefs.anti_aliasing_note": "Smooths jagged edges.",
"prefs.applies_on_restart": "Applies on restart",
"prefs.assistant": "Assistant",
"prefs.auto_operation": "Choose the operation automatically",
"prefs.auto_operation_note": "Feature windows pick Join, New Body or Cut from what the new material touches, until you pick one. Off: they keep the operation they open with.",
"prefs.axes": "Axes",
"prefs.axis": "Axis",
"prefs.bodies_folders": "Bodies folders",
@@ -1798,6 +1819,13 @@
"prefs.logo": "Logo",
"prefs.logo_accent": "Accent",
"prefs.logo_theme": "Theme's colors",
"prefs.max_file_size": "Largest file to open",
"prefs.max_file_size_design": "Design files",
"prefs.max_file_size_flag": "This run started with --max-file-size {size}, which applies until SolidCraft closes. These settings apply when it starts without it.",
"prefs.max_file_size_note": "Defaults: 512 MB and 256 MB; at most 16 GB. Opening a file takes several times its size in memory. A design that keeps a large import may need a higher design file limit to save and reopen.",
"prefs.max_file_size_step": "STEP and import files",
"prefs.max_file_size_web": "The web app opens STEP and import files up to 512 MB and designs up to 256 MB: a browser tab can't hold larger files. The desktop app can raise these limits.",
"prefs.megabytes": "MB",
"prefs.minutes": "minutes",
"prefs.minutes_suffix": " min",
"prefs.mouse": "Mouse and trackpad",
@@ -1829,8 +1857,8 @@
"prefs.saving": "Autosave and recovery",
"prefs.scale_note": "Also Ctrl + / Ctrl − in the window. Buttons are 28 px at 100%.",
"prefs.scale_text": "Scale and text",
"prefs.search": "Search settings",
"prefs.search_empty": "No matching settings",
"prefs.search": "Search preferences",
"prefs.search_empty": "No matching preferences",
"prefs.select": "Select",
"prefs.select_one_item": "Select one item",
"prefs.selecting": "Selecting",
@@ -1846,6 +1874,8 @@
"prefs.sketch_selection": "Sketch & selection",
"prefs.sketch_selection_heading": "Sketch and selection",
"prefs.sketch_selection_note": "How sketches snap and how clicks select.",
"prefs.sketch_state_icons": "Sketch icons in the color of their state",
"prefs.sketch_state_icons_note": "In History and the browser, a sketch's icon takes the color of its state (under constrained, fully constrained or conflicting) and its tooltip names the state. Off: every sketch has the same icon.",
"prefs.sketch_tab_always": "Keep the Sketch tab shown",
"prefs.sketch_tab_always_note": "The Sketch tab stays in the ribbon, right after Model, while no sketch is open. Its drawing tools then start a sketch on the selected plane or face, or on the plane you pick next, with the tool ready in it.",
"prefs.sketch_tracking_guides": "Center and midpoint alignment guides",
@@ -1855,6 +1885,13 @@
"prefs.source": "Source",
"prefs.speed": "Speed",
"prefs.stand_up_note": "After a STEP or IGES file opens with Z up, offer a quarter turn about X.",
"prefs.stand_up_unknown": "For files from unknown programs",
"prefs.stand_up_unknown.ask": "Ask in a dialog",
"prefs.stand_up_unknown.note": "Show a note",
"prefs.stand_up_unknown.off": "Don't offer",
"prefs.stand_up_unknown_note": "How the offer is made when the file's header names no program known to save Y up. A note is shown only when the file seems to lie on its back; Ask in a dialog asks about every such file, as before.",
"prefs.status_bar": "Status bar",
"prefs.status_bar_note": "A thin bar along the window's bottom edge shows what is being edited, the open sketch's state in its color and the design's units; a click on the units changes them. The sketch's state then no longer shows in the view's corner.",
"prefs.status_done": "Done",
"prefs.status_error": "Error",
"prefs.status_selected": "Selected",
@@ -1937,6 +1974,45 @@
"render.aging_upward": "Upward-facing deposition",
"render.all_controls_help": "Dense inspection of all current controls. Section pages provide focused editing without changing the underlying request.",
"render.all_shots_label": "All Shots",
"render.appearance_editor.absorption": "Absorption",
"render.appearance_editor.base_colour": "Base colour",
"render.appearance_editor.body_reason": "Optical bindings target one whole body. Pick Body to edit these controls.",
"render.appearance_editor.boundary": "Boundary",
"render.appearance_editor.box": "Box",
"render.appearance_editor.choose_image": "Choose image…",
"render.appearance_editor.closed": "Closed solid",
"render.appearance_editor.coat": "Clear coat",
"render.appearance_editor.cpu_reason": "Needs CPU ray tracing.",
"render.appearance_editor.glass": "Glass and absorption",
"render.appearance_editor.glass_enable": "Use physical glass on this body",
"render.appearance_editor.glass_note": "Closed glass uses geometric distance. It cannot share a body with clear coat or maps.",
"render.appearance_editor.gpu_reason": "Needs GPU path tracing.",
"render.appearance_editor.ior": "IOR",
"render.appearance_editor.maps": "Maps",
"render.appearance_editor.normal": "Normal",
"render.appearance_editor.normal_strength": "Normal strength",
"render.appearance_editor.origin": "Origin",
"render.appearance_editor.recipe": "Recipe",
"render.appearance_editor.reflection_sampling": "Sampling for all reflective bodies",
"render.appearance_editor.reflections": "Reflections",
"render.appearance_editor.remove": "Remove saved",
"render.appearance_editor.remove_note": "Remove the saved optics from this shot. Geometry and assigned looks are kept.",
"render.appearance_editor.remove_title": "Remove saved optics?",
"render.appearance_editor.replace": "Replace this body's saved optics",
"render.appearance_editor.revalidate": "Revalidate this body",
"render.appearance_editor.save": "Save these optics",
"render.appearance_editor.save_reason": "Saving these optics needs physical glass on a current GPU body. Coat and local image maps remain temporary.",
"render.appearance_editor.saved": "Saved with this shot",
"render.appearance_editor.saved_state": "These optics are saved with this shot.",
"render.appearance_editor.scatter_reason": "Interior scattering needs a closed solid boundary.",
"render.appearance_editor.temporary": "These optics are temporary on this tab. Save them to keep them with the shot.",
"render.appearance_editor.thickness": "Thickness",
"render.appearance_editor.thickness_reason": "Thickness applies to thin sheets; closed glass uses geometric distance.",
"render.appearance_editor.thin": "Thin sheet",
"render.appearance_editor.u_repeat": "U repeat",
"render.appearance_editor.v_repeat": "V repeat",
"render.appearance_editor.v_up": "Texture V points upward",
"render.appearance_editor.weight": "Weight",
"render.appearance_physics_separate": "Visual appearance does not assign density, thermal data or other engineering properties.",
"render.appearances": "Appearances",
"render.availability_native": "Native composition; review before applying",
@@ -1997,6 +2073,9 @@
"render.category_paint": "Paint",
"render.category_plastic": "Plastic",
"render.category_wood": "Wood",
"render.check_opaque": "Transparency: opaque surfaces only; background alpha is supported.",
"render.check_sorted_alpha": "Transparency: sorted alpha approximation.",
"render.check_transmission": "Transparency: physical transmission.",
"render.chromatic_offset": "Chromatic edge offset",
"render.clear_stage": "Remove saved stage",
"render.copy_request": "Copy JSON",
@@ -2230,6 +2309,36 @@
"render.inner_half_angle": "Inner half angle",
"render.inspect_scene": "Refresh captured scene summary",
"render.inspector": "Inspector",
"render.inspector_advanced": "Advanced",
"render.inspector_all_users": "All {count} users",
"render.inspector_applies_to": "Applies to",
"render.inspector_both_users": "Both users",
"render.inspector_chain": "Where this look comes from",
"render.inspector_colour": "Colour",
"render.inspector_edit": "Edit",
"render.inspector_finish": "Finish",
"render.inspector_from_material": "From Material",
"render.inspector_gloss": "Gloss",
"render.inspector_material": "Material",
"render.inspector_matte": "Matte",
"render.inspector_metal": "Metal",
"render.inspector_opacity": "Opacity",
"render.inspector_opaque_help": "Finish and optical fields use opaque PBR (opacity 1). Lower opacity uses scalar shading.",
"render.inspector_pick_target": "Pick a target of this type to inspect its appearance.",
"render.inspector_remove": "Remove override",
"render.inspector_replace": "Replace…",
"render.inspector_reset": "Reset",
"render.inspector_satin": "Satin",
"render.inspector_save_library": "Save to library…",
"render.inspector_set_all": "Mixed appearances. Editing sets the look on all selected targets.",
"render.inspector_soon": "Soon",
"render.inspector_target_limit": "Select at most 10000 targets to inspect appearances.",
"render.inspector_textures": "Textures",
"render.inspector_this_body": "This body",
"render.inspector_this_component": "This component",
"render.inspector_this_face": "This face",
"render.inspector_this_target": "This target",
"render.inspector_used_by": "Used by {count}",
"render.keep_editing": "Keep editing",
"render.layer_down": "Move down",
"render.layer_enabled": "Layer enabled",
@@ -2396,12 +2505,34 @@
"render.optics_saved": "Saved optical targets",
"render.optics_saved_help": "Exact definition, feature and occurrence identity survives offline reopen. Missing/replaced/hidden targets refuse; engineering materials are unchanged. Save or discard numeric edits before removing a saved target.",
"render.outer_half_angle": "Outer half angle",
"render.output_advanced": "Advanced",
"render.output_backend_ready": "Ready",
"render.output_check": "Check",
"render.output_check_help": "Run Check to review this request without starting a render.",
"render.output_check_summary": "Check · Notes: {count}",
"render.output_copy": "Copy",
"render.output_destination": "Destination",
"render.output_destination_help": "Choose the destination file when exporting the completed image.",
"render.output_dimensions": "Size",
"render.output_effects": "Effects",
"render.output_export": "Export PNG…",
"render.output_exr_unavailable": "EXR output setup is unavailable here. Linear EXR export requires a completed GPU image with a retained linear master.",
"render.output_fit": "Fit",
"render.output_format": "Format",
"render.output_format_status": "PNG is the exact completed display image. Optional GPU EXR retains raw scene-linear FLOAT32 radiance before display filters. Export never substitutes the preview cache or a different job.",
"render.output_image": "Image",
"render.output_largest": "{width} × {height} max",
"render.output_lock_ratio": "Lock aspect ratio",
"render.output_over_budget": "The requested image exceeds the selected resource envelope. Choose a compatible size or profile explicitly; no automatic downgrade occurs.",
"render.output_presets_help": "Format, quality and the Check list are in the Output editor.",
"render.output_quality": "Quality",
"render.output_quality_waiting": "waiting for the quality contract",
"render.output_ratio_refused": "The linked dimension exceeds this method’s size limit. The previous size is retained; unlock the ratio or choose a smaller value.",
"render.output_ready": "Ready to render",
"render.output_size_ok": "Size OK · {width} × {height} px",
"render.output_swap_orientation": "Swap width and height",
"render.output_tools_inspector": "Edit size and format in the Output inspector.",
"render.output_view_image": "View image",
"render.page_aging": "Aging & contamination",
"render.page_all": "All controls",
"render.page_appearance": "Appearance",
@@ -2462,10 +2593,18 @@
"render.property_unknown": "unknown",
"render.quality_ack": "Acknowledge the fixed-budget projection and omissions",
"render.quality_apply": "Use this fixed sampling budget",
"render.quality_balanced": "Balanced",
"render.quality_custom": "Custom",
"render.quality_draft": "Draft",
"render.quality_high": "High",
"render.quality_projection_help": "Q presets include future requirements. This action projects only a supported fixed-sample PNG budget and explicitly omits adaptive stopping, raw linear output and independent transmission limits.",
"render.quality_review": "Review sampling budget",
"render.queue_pending": "There is no automatic production queue or animation renderer in this UI. Completed still images remain separately exportable.",
"render.radial_assign": "Assign Appearance",
"render.radial_compare": "Compare",
"render.radial_distortion": "Radial distortion",
"render.radial_fit": "Fit Product",
"render.radial_save": "Save Shot",
"render.regrade_action": "Regrade raw master (no denoising)",
"render.regrade_help": "Creates a separate PNG from the selected completed raw master, using these presentation controls. Does not trace rays, change the captured size/camera/lights, or overwrite the EXR. Denoising is not reapplied: raw masters do not retain its guides. The derivative remains exportable through the same job controls.",
"render.regrade_label": "Regrade",
@@ -2474,6 +2613,7 @@
"render.replace_label": "Replace",
"render.replace_saved_surface": "Replace an existing saved binding for this target",
"render.restore_camera": "Restore Saved Camera",
"render.result_actual": "100%",
"render.result_actual_help": "One image pixel per display pixel.",
"render.result_actual_size": "100%",
"render.result_capture": "Captured revision",
@@ -2541,6 +2681,7 @@
"render.sharpening": "Detail sharpening",
"render.shell_tab_appearance": "RENDER APPEARANCE",
"render.shell_tab_camera": "RENDER CAMERA",
"render.shell_tab_lighting": "LIGHTING",
"render.shell_tab_scene": "RENDER SCENE",
"render.shot_duplicate": "Duplicate saved shot",
"render.shot_manager": "Saved shots",
@@ -2558,11 +2699,15 @@
"render.shots_save": "Save Shot",
"render.shots_update": "Update shot",
"render.show_shots_label": "Show Shots strip",
"render.size_4k": "4K",
"render.size_4k_uhd": "4K UHD",
"render.size_custom": "Custom…",
"render.size_full_hd": "Full HD",
"render.size_hd": "HD",
"render.size_presets": "Size presets",
"render.size_presets_pending": "Arrives with the Output editor",
"render.size_preview": "Preview",
"render.size_square": "Square",
"render.sky_ground": "Ground · linear RGB",
"render.sky_horizon": "Horizon · linear RGB",
"render.sky_zenith": "Zenith · linear RGB",
@@ -2575,6 +2720,9 @@
"render.stage_output": "Output",
"render.stage_panel_count": "Saved panels",
"render.stage_preview_note": "Integrated preview · direct light",
"render.state_completed": "Completed image",
"render.state_in_progress": "Image in progress",
"render.state_model": "Model Preview",
"render.strip_label": "Strip",
"render.studio_add_area": "Add area light",
"render.studio_ambient": "Constant environment radiance",
@@ -2670,6 +2818,7 @@
"render.volume_max": "Maximum bounds · world mm",
"render.volume_min": "Minimum bounds · world mm",
"render.volume_title": "Bounded homogeneous volume",
"render.wait_add_light": "not supported yet: named light creation",
"render.wait_batch": "not supported yet: the saved-shot render queue",
"render.wait_compare": "not supported yet: retained image comparison",
"render.wait_paint": "not supported yet: product picking and appearance painting",
@@ -2784,6 +2933,10 @@
"sketch_text.path_hint": "A curve of the sketch the text follows: click here, then the curve",
"sketch_text.use_font_file": "Use the font file {path}",
"solid": "solid",
"status.editing": "Editing {name}",
"status.under_constrained": "Under constrained ({count} degrees of freedom)",
"status.under_constrained_one": "Under constrained ({count} degree of freedom)",
"status.units_tip": "The design's units. Click to change them",
"the angle must be a number": "the angle must be a number",
"the curves must be in one sketch": "the curves must be in one sketch",
"the direction must be an axis, a sketch line or a straight edge": "the direction must be an axis, a sketch line or a straight edge",
@@ -2822,8 +2975,10 @@
"timeline.move_to_group": "Move Features to Folder",
"timeline.rename_group": "Rename Feature Folder",
"timeline.reorder_many": "Reorder Features",
"timeline.suppress_features": "Suppress Features",
"timeline.suppress_folders_tip": "Suppress or unsuppress every feature in the folders and their nested folders",
"timeline.ungroup": "Remove Feature Folders",
"timeline.unsuppress_features": "Unsuppress Features",
"to the face": "to the face",
"to the next face": "to the next face",
"toolbar.customize_note": "{tab} tab. Tick a command to give it a toolbar button. You can also drag a row of a panel's drop-down onto the toolbar, drag a button off the toolbar to remove it, or use the pin on a drop-down row.",
@@ -3234,6 +3389,8 @@
"ui.cancel": "Cancel",
"ui.canvases": "Canvases",
"ui.center": "Center",
"ui.center_arc_follows_cursor": "Center point arc follows the cursor's direction",
"ui.center_arc_follows_cursor_tip": "A center point arc runs clockwise or counter-clockwise the way the cursor goes round its center. Off: always counter-clockwise from the start.",
"ui.centerline": "Centerline",
"ui.chain": "Chain",
"ui.chamfer": "Chamfer",
@@ -3646,6 +3803,15 @@
"ui.plugins_skipped": "{count} folder(s) could not be read.",
"ui.plugins_version": "{version}",
"ui.point": "Point",
"ui.point_cloud_auto_range": "Automatic Range",
"ui.point_cloud_color_by": "Color By",
"ui.point_cloud_file_colors": "File Colors",
"ui.point_cloud_height_high": "Highest",
"ui.point_cloud_height_low": "Lowest",
"ui.point_cloud_no_file_colors": "The file has no colors",
"ui.point_cloud_single_color": "Single Color",
"ui.point_cloud_size": "Point Size",
"ui.point_cloud_units": "File Units",
"ui.point_under_the_cursor": "Point under the cursor",
"ui.points": "Points",
"ui.polygon": "Polygon",
@@ -3899,11 +4065,13 @@
"update.installed": "SolidCraft {version} is installed. Restart to use it.",
"update.installer_started": "The installer starts when SolidCraft closes.",
"update.later": "Remind me later",
"update.managed": "Update available: update SolidCraft in {manager}",
"update.no_notes": "This release has no notes.",
"update.package_hint": "This copy came from a {kind}. Download the new package, then install it with:",
"update.prefs_dev": "This is a development build ({version}): it doesn't check for updates.",
"update.prefs_flatpak": "Flatpak updates this copy ({version}): run {command} or use your software center. SolidCraft doesn't check.",
"update.prefs_label": "Updates",
"update.prefs_managed": "This is SolidCraft {version}, managed by {manager}. A check asks GitHub for the latest release and sends nothing else; update SolidCraft in {manager}.",
"update.prefs_note": "This is SolidCraft {version} ({kind}). A check asks GitHub for the latest release and sends nothing else.",
"update.prefs_web": "Reload the page to get the latest web app. If the network is unavailable or the update cannot finish downloading in time, the saved version opens instead; a download in progress continues for the next reload.",
"update.release_page": "Open release page",
@@ -3920,6 +4088,7 @@
"values must be finite numbers": "values must be finite numbers",
"values must be numbers or expressions": "values must be numbers or expressions",
"view.auto_ortho_faces": "Perspective while orbiting, ortho on faces",
"view.render_inspector": "Render Inspector",
"web.files.insert_drawings_hint": "CAD files, meshes, DXF or SVG into the open design",
"web.files.insert_stored_drawings_hint": "Into the open design (CAD files, meshes, DXF, SVG)",
"web.open_full": "Open in SolidCraft",
+9
View File
@@ -516,6 +516,7 @@
"appearance.pbr_opaque": "PBR opaco",
"appearance.roughness": "Rugosidad",
"appearance.surface_response": "Respuesta de la superficie",
"at the face": "en la cara",
"bom.group_children": "Subpiezas en la fila de su pieza",
"browser.body_name": "Cuerpo{number}",
"browser.calibrate_title": "Calibrar: {name}",
@@ -854,6 +855,7 @@
"cmd.sketch_spline_control_point": "Spline por puntos de control",
"cmd.sketch_spline_control_point_5": "Spline por puntos de control (grado 5)",
"cmd.sketch_spline_fit_point": "Spline por puntos de ajuste",
"cmd.sketch_spline_reverse": "Invertir dirección del spline",
"cmd.sketch_spun_profile": "Perfil de revolución",
"cmd.sketch_toggle_diameter": "Alternar radio / diámetro",
"cmd.sketch_toggle_driven": "Alternar cota de referencia",
@@ -1226,6 +1228,13 @@
"pick what to keep": "elige qué conservar",
"pick what to keep of the other surface": "elige qué conservar de la otra superficie",
"prefs.cpu": "CPU (software)",
"prefs.max_file_size": "Tamaño máximo de archivo al abrir",
"prefs.max_file_size_design": "Archivos de diseño",
"prefs.max_file_size_flag": "Esta ejecución se inició con --max-file-size {size}, que se aplica hasta cerrar SolidCraft. Estos ajustes se aplican al iniciar sin esa opción.",
"prefs.max_file_size_note": "Valores predeterminados: 512 MB y 256 MB; máximo 16 GB. Abrir un archivo requiere varias veces su tamaño en memoria. Un diseño que conserva una importación grande puede necesitar un límite mayor para guardarse y volver a abrirse.",
"prefs.max_file_size_step": "Archivos STEP y de importación",
"prefs.max_file_size_web": "La aplicación web abre archivos STEP y de importación de hasta 512 MB y diseños de hasta 256 MB: una pestaña del navegador no admite archivos mayores. La aplicación de escritorio permite aumentar estos límites.",
"prefs.megabytes": "MB",
"prefs.minutes_suffix": " min",
"prefs.theme_follow": "Seguir un archivo de tema",
"prefs.theme_follow_error": "No aplicado: {error}. Se mantiene el último tema válido.",
+461 -1
View File
File diff suppressed because it is too large Load Diff
+242
View File
@@ -1,4 +1,5 @@
{
"(active rule)": "(regra activa)",
"0 turns it off": "0 desactívao",
"1. Open GitHub draft below, then paste the report copied to your clipboard.": "1. Abre o borrador de GitHub de abaixo e pega o informe copiado no portapapeis.",
"1. Open GitHub draft below. Your title and report will be filled in.": "1. Abre o borrador de GitHub de abaixo. O título e o informe encheranse automaticamente.",
@@ -52,16 +53,20 @@
"Apply your appearance first. Exports assigned appearances and materials created in this design.": "Aplique primeiro a aparencia. Exporta as aparencias asignadas e os materiais creados neste deseño.",
"Arc length": "Lonxitude do arco",
"Area": "Área",
"As Picked": "Segundo a escolla",
"As-Built": "Tal como se construíu",
"Assign Rule": "Asignar regra",
"Attachment": "Ancoraxe",
"BLEND SURFACE": "SUPERFICIE DE UNIÓN",
"BOUNDARY SURFACE": "SUPERFICIE DE CONTORNO",
"BROWSER": "NAVEGADOR",
"Ball": "Esférica",
"Bar": "Barra",
"Base Center": "Centro da base",
"Base Height": "Altura da base",
"Bend": "Dobra",
"Bend Edges": "Arestas de dobra",
"Bend Line": "Liña de dobra",
"Bend Line Position": "Posición da liña de dobra",
"Bend Position": "Posición da dobra",
"Bend Radius": "Raio de dobra",
@@ -73,6 +78,7 @@
"Body (last if none)": "Corpo (o último se non hai ningún)",
"Body to split": "Corpo que dividir",
"Body, or faces to take out": "Corpo ou caras que quitar",
"Both": "Ambos",
"Bottom Left": "Inferior esquerda",
"Bottom Middle": "Inferior central",
"Bottom Right": "Inferior dereita",
@@ -104,6 +110,7 @@
"Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.": "Escolle un plano novo. A xeometría e as cotas do bosquexo mantéñense nas súas coordenadas bidimensionais.",
"Choose…": "Escoller…",
"Circle center": "Centro do círculo",
"Circle or sphere": "Círculo ou esfera",
"Circular": "Circular",
"Circumference": "Circunferencia",
"Clockwise": "Horario",
@@ -120,6 +127,7 @@
"Config Table": "Táboa de configs.",
"Configuration": "Configuración",
"Configuration rules": "Regras de configuración",
"Constant radius": "Raio constante",
"Contact": "Contacto",
"Contact Sets": "Conxuntos de contacto",
"Contact sets": "Conxuntos de contacto",
@@ -155,7 +163,9 @@
"Custom": "Personalizado",
"Customize Toolbar": "Personalizar a barra de ferramentas",
"Customize…": "Personalizar…",
"Cylindrical": "Cilíndrica",
"Cylindrical face": "Cara cilíndrica",
"Deboss": "Gravar en oco",
"Delete": "Eliminar",
"Delete Folders and Features…": "Eliminar cartafoles e operacións…",
"Delete Selected Features…": "Eliminar as operacións seleccionadas…",
@@ -190,6 +200,7 @@
"Duration": "Duración",
"Each new shape is also dimensioned from the sketch origin until nothing of it is left free. A shape already attached to the origin or to placed geometry gets none.": "Cada forma nova tamén se cota desde a orixe do esbozo ata que non quede nada libre. Unha forma xa unida á orixe ou a xeometría situada non recibe ningunha.",
"Each shape you draw gets all its dimensions, at the sizes you drew (typed values win). A size the sketch already sets is left out.": "Cada forma que debuxas recibe todas as súas cotas cos tamaños debuxados (os valores escritos teñen prioridade). Omítese un tamaño que o esbozo xa fixa.",
"Edge": "Aresta",
"Edges": "Arestas",
"Edit": "Editar",
"Edit Profile Sketch": "Editar o bosquexo do perfil",
@@ -198,6 +209,7 @@
"End Radius": "Raio final",
"End bulge": "Abaulamento final",
"End continuity": "Continuidade final",
"Equal Distance": "Distancia igual",
"Expand": "Expandir",
"Expected result (optional)": "Resultado esperado (opcional)",
"Explode": "Explosionar",
@@ -225,6 +237,8 @@
"Faces": "Caras",
"Faces / edges / vertices": "Caras / arestas / vértices",
"Faces to split": "Caras que dividir",
"Faces/Body": "Caras/Corpo",
"Factor must be a positive number": "O factor debe ser un número positivo",
"Fastener": "Elemento de fixación",
"Favorites": "Favoritos",
"Favourite": "Favorito",
@@ -234,18 +248,25 @@
"Filter…": "Filtro…",
"Find in Browser": "Buscar no navegador",
"Finish": "Acabado",
"First edge": "Primeira aresta",
"First plane": "Primeiro plano",
"First point": "Primeiro punto",
"Fit and G2 corners require Constant radius.": "As esquinas con Axuste e G2 requiren Raio constante.",
"Fixed Face": "Cara fixa",
"Flange Gap": "Separación da pestana",
"Flange thickness": "Espesor da pestana",
"Flanges": "Pestanas",
"Flat Pattern": "Desenvolvemento plano",
"Flat size": "Tamaño desenvolvido",
"Flat size X": "Tamaño desenvolvido X",
"Flat size Y": "Tamaño desenvolvido Y",
"Follow camera (Look At sketches)": "Seguir a cámara (Mirar cara aos bosquexos)",
"Follow the camera (Look At sketches)": "Seguir a cámara (Mirar cara aos bosquexos)",
"Font": "Tipo de letra",
"Free Move": "Movemento libre",
"From": "Desde",
"From Bodies": "Desde corpos",
"From Snap": "Axuste de orixe",
"From edges": "Arestas de orixe",
"Full Length": "Lonxitude completa",
"Full thread depth": "Profundidade total da rosca",
@@ -257,10 +278,13 @@
"Halo under sketch curves": "Halo baixo as curvas do bosquexo",
"Height": "Altura",
"Height From": "Altura desde",
"Hem Gap": "Separación do dobradillo",
"Hidden edges": "Arestas ocultas",
"History layout": "Disposición do historial",
"History names": "Nomes do historial",
"Hole (optional)": "Furado (opcional)",
"Hole Depth": "Profundidade do furado",
"Hole Diameter": "Diámetro do furado",
"Holes": "Furados",
"Holes (edges on them)": "Furados (arestas sobre eles)",
"Home view": "Vista de inicio",
@@ -283,12 +307,16 @@
"Issue title": "Título da incidencia",
"Items": "Elementos",
"JOINTS": "UNIÓNS",
"Joint 1": "Unión 1",
"Joint 2": "Unión 2",
"Joint Origin1": "Orixe da unión1",
"K Factor": "Factor K",
"Keep (points on it)": "Manter (puntos sobre ela)",
"Keep Tools": "Manter as ferramentas",
"Keep of the other surface": "Manter da outra superficie",
"Key a joint's value at this step": "Crear unha clave co valor dunha unión neste paso",
"Keys": "Claves",
"Knife Edge": "Gume de coitelo",
"L-Angle": "Perfil en L",
"LIBRARY": "BIBLIOTECA",
"LINKS": "LIGAZÓNS",
@@ -301,9 +329,11 @@
"Letter Spacing": "Espazado entre letras",
"Library": "Biblioteca",
"Library file": "Ficheiro de biblioteca",
"Library response is missing": "Falta a resposta da biblioteca",
"Limits": "Límites",
"Line Spacing": "Espazado entre liñas",
"Line Type": "Tipo de liña",
"Line on plane": "Liña no plano",
"Lines": "Liñas",
"Listing the installed fonts…": "Listando os tipos de letra instalados…",
"Load library": "Cargar biblioteca",
@@ -323,11 +353,14 @@
"Material Outside": "Material por fóra",
"Material copied. Existing body assignments are unchanged.": "Material copiado. As asignacións de corpos existentes non cambian.",
"Max": "Máx.",
"Max Thickness": "Espesor máx.",
"Maximum": "Máximo",
"Mesh body": "Corpo de malla",
"Mid Radius": "Raio medio",
"Middle double-click": "Dobre clic central",
"Min": "Mín.",
"Min Draft": "Ángulo de desmoldaxe mín.",
"Min Thickness": "Espesor mín.",
"Minimum": "Mínimo",
"Mirror Plane": "Plano de simetría",
"Mitre": "Inglete",
@@ -347,6 +380,7 @@
"Name": "Nome",
"Name on import": "Nome ao importar",
"Nearest": "Máis próximo",
"Neutral plane": "Plano neutro",
"New": "Novo",
"New Comp.": "Comp. novo",
"New Sketch": "Bosquexo novo",
@@ -357,6 +391,7 @@
"No matching appearances": "Non hai aparencias coincidentes",
"No matching physical materials.": "Ningún material físico coincide.",
"No visible bodies or sketches to export.": "Non hai corpos nin bosquexos visibles para exportar.",
"Nominal Radius": "Raio nominal",
"Nominal diameter": "Diámetro nominal",
"None": "Ningún",
"Not available yet": "Aínda non dispoñible",
@@ -385,22 +420,28 @@
"Overall length": "Lonxitude total",
"Param. Graph": "Gráf. de parám.",
"Parameter": "Parámetro",
"Part length must be greater than zero and at most 2000 mm": "A lonxitude da peza debe ser maior que cero e como máximo de 2000 mm",
"Parts": "Pezas",
"Pass through": "Atravesar",
"Path": "Traxectoria",
"Pattern": "Patrón",
"Per Edge": "Por aresta",
"Physical material definitions": "Definicións de materiais físicos",
"Pick a part size": "Escolle un tamaño de peza",
"Pick the tool bodies": "Escolle os corpos ferramenta",
"Picked": "Escollido",
"Picked point": "Punto escollido",
"Pin-slot": "Pasador en ranura",
"Pitch": "Paso",
"Pivot": "Pivote",
"Place at the origin instead": "Colocar na orixe no seu lugar",
"Planar": "Plana",
"Plastic Rules": "Regras de plástico",
"Play the joint's motion in the view": "Reproduce o movemento da unión na vista",
"Playback": "Reprodución",
"Plot": "Gráfico",
"Point on line": "Punto na liña",
"Point on plane": "Punto no plano",
"Position": "Posición",
"Position (0 to 1)": "Posición (de 0 a 1)",
"Position (G0)": "Posición (G0)",
@@ -413,6 +454,7 @@
"Profiles or curves": "Perfís ou curvas",
"Profiles/Curves": "Perfís/curvas",
"Quantity": "Cantidade",
"Quantity must be at least 2": "A cantidade debe ser polo menos 2",
"RECENT": "RECENTES",
"REFERENCE": "REFERENCIA",
"RULED SURFACE": "SUPERFICIE REGRADA",
@@ -426,6 +468,8 @@
"Rectangular": "Rectangular",
"Rectangular Tube": "Tubo rectangular",
"Reference face": "Cara de referencia",
"Relief Depth": "Profundidade do desafogo",
"Relief Width": "Largura do desafogo",
"Remove Tools": "Quitar as ferramentas",
"Remove condition": "Quitar a condición",
"Remove from Folder": "Quitar do cartafol",
@@ -454,6 +498,7 @@
"Restore this studio's tools": "Restaurar as ferramentas deste estudio",
"Result": "Resultado",
"Retake screenshot": "Repetir a captura de pantalla",
"Reveal Height": "Altura visible",
"Reverse": "Inverter",
"Reverse Direction": "Inverter a dirección",
"Revert": "Reverter",
@@ -462,12 +507,15 @@
"Revert Position": "Reverter a posición",
"Review the report and screenshot before opening GitHub.": "Revisa o informe e a captura de pantalla antes de abrir GitHub.",
"Review your report on GitHub and click Submit new issue to publish it.": "Revisa o informe en GitHub e preme Submit new issue para publicalo.",
"Revolute": "Xiratoria",
"Rib Length": "Lonxitude do nervio",
"Rib Offset": "Desprazamento do nervio",
"Rib Thickness": "Espesor do nervio",
"Ribs": "Nervios",
"Right Middle": "Centro dereita",
"Rigid": "Ríxida",
"Rim Edge": "Aresta do bordo",
"Rim face": "Cara do bordo",
"Ring of tools on the right button": "Anel de ferramentas no botón dereito",
"Roll Back Here": "Retroceder ata aquí",
"Roll History Marker Here": "Levar o marcador do historial aquí",
@@ -492,7 +540,9 @@
"Search": "Buscar",
"Search commands": "Buscar ordes",
"Search this library": "Buscar nesta biblioteca",
"Second edge": "Segunda aresta",
"Second plane": "Plano 2",
"Second point": "Segundo punto",
"Section": "Sección",
"Section (optional)": "Sección (opcional)",
"Section position": "Posición da sección",
@@ -503,6 +553,15 @@
"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",
"Select edges": "Seleccionar arestas",
"Select points of the sketch being edited": "Selecciona puntos do bosquexo que se está a editar",
"Select the centre the pattern turns around first": "Selecciona primeiro o centro arredor do cal xira o patrón",
"Select the curves to offset first": "Selecciona primeiro as curvas que desprazar",
"Select the mirror line": "Selecciona a liña de simetría",
"Select the sketch curves or points to mirror first": "Selecciona primeiro as curvas ou os puntos do bosquexo aos que aplicar a simetría",
"Select the sketch curves or points to move first": "Selecciona primeiro as curvas ou os puntos do bosquexo que mover",
"Select the sketch curves or points to pattern first": "Selecciona primeiro as curvas ou os puntos do bosquexo para o patrón",
"Select the sketch curves or points to scale first": "Selecciona primeiro as curvas ou os puntos do bosquexo que escalar",
"Select the sketch curves or points to transform first": "Selecciona primeiro as curvas ou os puntos do bosquexo que transformar",
"Selection filter": "Filtro de selección",
"Semantic faces share their appearance across instances.": "As caras semánticas comparten a súa aparencia entre instancias.",
"Send your report on GitHub": "Envía o informe en GitHub",
@@ -522,9 +581,12 @@
"Sketch geometry and profiles": "Xeometría e perfís do bosquexo",
"Sketch plane has no body-face attachment.": "O plano do bosquexo non está ancorado a ningunha cara dun corpo.",
"Sketch points": "Puntos do bosquexo",
"Slider": "Deslizante",
"Slot width": "Largura da ranura",
"Snap to face and edge centres": "Axustar aos centros de caras e arestas",
"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.",
"Spacing 2 must be a non-zero number": "O espazado 2 debe ser un número distinto de cero",
"Spacing must be a non-zero number": "O espazado debe ser un número distinto de cero",
"Specialise": "Especializar",
"Speed": "Velocidade",
"Splitting tool": "Ferramenta de división",
@@ -535,6 +597,7 @@
"Start angle": "Ángulo inicial",
"Start bulge": "Abaulamento inicial",
"Start continuity": "Continuidade inicial",
"Stationary Side": "Lado fixo",
"Step": "Paso",
"Steps": "Pasos",
"Steps to reproduce (optional)": "Pasos para reproducilo (opcional)",
@@ -549,6 +612,7 @@
"Suppressed": "Suprimida",
"Surface": "Superficie",
"Surface, or faces to turn over": "Superficie ou caras que se van inverter",
"Surfaces": "Superficies",
"Swap Ends": "Intercambiar os extremos",
"TRIM": "RECORTAR",
"Tangent": "Tanxente",
@@ -559,24 +623,34 @@
"Tap drill diameter": "Diámetro da broca para roscar",
"Target": "Destino",
"Target Body": "Corpo de destino",
"Target Face (optional)": "Cara de destino (opcional)",
"Tear": "Rasgado",
"Technical details": "Detalles técnicos",
"The dashed sketch is a saved plane guide. Select a new face to repair the attachment.": "O bosquexo descontinuo é unha guía de plano gardada. Selecciona unha cara nova para reparar a ancoraxe.",
"The design changed. Reopen Change Part Size for the component.": "O deseño cambiou. Volve abrir Cambiar o tamaño da peza para o compoñente.",
"The direction must be an axis or a straight sketch curve that lies in the sketch plane": "A dirección debe ser un eixe ou unha curva recta do bosquexo que estea no plano do bosquexo",
"The mirror line cannot also be mirrored": "A liña de simetría non pode ser tamén un dos elementos aos que se aplica a simetría",
"The mirror line must be a straight curve of this sketch, not an origin axis": "A liña de simetría debe ser unha curva recta deste bosquexo, non un eixe da orixe",
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "Falta a aparición do autor gardada. Selecciona unha colocación de autor válida antes de volver vincular.",
"Then": "Entón",
"These features will be deleted:": "Eliminaranse estas operacións:",
"These features will fail:": "Fallarán estas operacións:",
"Thickness Outside": "Espesor cara a fóra",
"Thickness Variation": "Variación de espesor",
"Thin": "Delgado",
"Thin Extrude": "Extrusión delgada",
"Third plane": "Terceiro plano",
"Third point": "Terceiro punto",
"This child is shared. Deleting it removes it from every copy of its parent.": "Este fillo está compartido. Eliminalo quítao de todas as copias do seu pai.",
"This child is shared. Position changes apply to every copy of its parent.": "Este fillo está compartido. Os cambios de posición aplícanse a todas as copias do seu pai.",
"This face cannot provide a durable attachment. Choose another face or explicitly use a fixed plane.": "Esta cara non pode ofrecer unha ancoraxe duradeira. Escolle outra cara ou usa expresamente un plano fixo.",
"This image is not attached to GitHub yet. Copy or save it, then attach it on GitHub.": "Esta imaxe aínda non está anexada a GitHub. Copia ou garda a imaxe e despois anéxaa en GitHub.",
"Thread depth": "Profundidade da rosca",
"Thread diameter": "Diámetro da rosca",
"Through point": "Polo punto",
"To": "Ata",
"To Offset": "Ata o desprazamento",
"To Snap": "Axuste de destino",
"To edges": "Arestas de destino",
"Toe radius": "Raio da punta",
"Tolerance": "Tolerancia",
@@ -590,6 +664,7 @@
"Trimming tool (plane, face or curves)": "Ferramenta de recorte (plano, cara ou curvas)",
"Turn off section analysis before exporting the view.": "Desactiva a análise de sección antes de exportar a vista.",
"Type": "Tipo",
"Type a distance or an angle to move by": "Escribe unha distancia ou un ángulo para mover",
"UNTRIM": "DESRECORTAR",
"Unit": "Unidade",
"Unsuppress": "Deixar de suprimir",
@@ -634,6 +709,17 @@
"Z angle": "Ángulo Z",
"Z distance": "Distancia Z",
"Zebra": "Zebra",
"`joint` must be an id or a name": "`joint` debe ser un id ou un nome",
"`params` must be an object": "`params` debe ser un obxecto",
"`values` must be a list": "`values` debe ser unha lista",
"a chamfer needs a corner of exactly two lines": "un chafrán precisa unha esquina de exactamente dúas liñas",
"a file operation is in progress": "hai unha operación de ficheiro en curso",
"a picked curve is no longer in a sketch": "unha curva escollida xa non está nun bosquexo",
"a picked curve's sketch has not been solved": "o bosquexo dunha curva escollida non se resolveu",
"a picked edge has no points": "unha aresta escollida non ten puntos",
"a picked edge is gone": "unha aresta escollida desapareceu",
"a picked edge's body is gone": "o corpo dunha aresta escollida desapareceu",
"add a key for a joint first": "engade primeiro unha clave para unha unión",
"ai.connected_toast": "SolidCraft xa está dispoñible en {clients}. Reinícialos para usalo (Preferencias > Asistente).",
"ai.prefs.allow_control": "Permitir que os axentes de IA se conecten (canle de control / MCP)",
"ai.prefs.allow_control_note": "Abre unha canle de control só neste ordenador (127.0.0.1, porto 27878 ou o seguinte libre). Un programa que se conecte tamén ten que mostrar un token secreto que só pode ler a túa conta de usuario, xerado de novo en cada inicio. Aplícase a próxima vez que se inicie SolidCraft. Desactivado: nada escoita a non ser que inicies SolidCraft con --control.",
@@ -674,6 +760,8 @@
"appearance.pbr_opaque": "PBR opaco",
"appearance.roughness": "Rugosidade",
"appearance.surface_response": "Resposta da superficie",
"at the face": "na cara",
"bom.group_children": "Subpezas na fila da súa peza",
"browser.body_name": "Corpo{number}",
"browser.calibrate_title": "Calibrar: {name}",
"browser.capture_many": "Movéronse {count} compoñentes, pero as súas posicións non se capturaron.",
@@ -684,6 +772,8 @@
"browser.move_title": "Mover: {name}",
"browser.parameters": "Parámetros ({count})",
"browser.precision": "Precisión: {precision}",
"browser.sketch_attachment_lost": "{name} (ancoraxe perdida)",
"browser.sketch_editing": "{name} (en edición)",
"browser.sketch_name": "Bosquexo{number}",
"browser.units": "Unidades: {units}",
"cam.flat": "Cara plana",
@@ -693,7 +783,13 @@
"cam.roller": "Rolete",
"cam.roller_pick": "Rolete (opcional)",
"cam.tip_pick": "Punta (opcional)",
"choose a profile": "escolle un perfil",
"choose the cam's and the follower's joints": "escolle as unións da leva e do seguidor",
"choose the joints to couple": "escolle as unións que acoplar",
"click a plane or planar face": "fai clic nun plano ou nunha cara plana",
"click sketch curves": "fai clic en curvas do bosquexo",
"cmd.appearance_assign": "Aparencia",
"cmd.appearance_chain": "Cadea de aparencias",
"cmd.appearance_library": "Biblioteca de aparencias",
"cmd.appearance_list": "Listar as aparencias",
"cmd.appearance_paint": "Pintar aparencia",
@@ -1031,6 +1127,7 @@
"cmd.sketch_spline_control_point_5": "Spline por puntos de control (grao 5)",
"cmd.sketch_spline_fit_point": "Spline por puntos de paso",
"cmd.sketch_spline_insert_point": "Engadir punto ao spline",
"cmd.sketch_spline_reverse": "Inverter a dirección do spline",
"cmd.sketch_spun_profile": "Perfil de revolución",
"cmd.sketch_toggle_diameter": "Alternar raio / diámetro",
"cmd.sketch_toggle_driven": "Alternar cota conducida",
@@ -1191,6 +1288,8 @@
"dxf.progress.write": "Escribindo o DXF (5/5)",
"dxf.tolerance": "Tolerancia da curva",
"dxf.tolerance_help": "Só os corpos visibles e os bosquexos amosados. As curvas usan segmentos rectos. Unha tolerancia menor dá curvas máis finas e usa máis tempo e memoria.",
"each rail's curves must be in one sketch": "as curvas de cada guía deben estar nun mesmo bosquexo",
"feature computation ended without a result": "o cálculo da operación rematou sen resultado",
"file.open.drawing_filter": "Deseños, ficheiros CAD, mallas e debuxos",
"file_load.built": "Construído ata agora: {built} de {total} operacións.",
"file_load.converting": "Convertendo o sólido {done} de {total}",
@@ -1438,7 +1537,9 @@
"nav.free_orbit_upright.help": "Os arrastres laterais do rato fan xirar o modelo arredor do eixe vertical do mundo; os arrastres cara arriba e cara abaixo inclínano por riba sen límite.",
"nav.free_orbit_upright.label": "Xirar arredor do eixe vertical",
"nav.fusion.help": "Arrastrar co botón central desprázase, Shift+botón central orbita, arrastrar co botón dereito orbita",
"nav.hfss.help": "Alt+arrastrar co botón esquerdo desprázase, Shift+Alt+botón esquerdo fai zoom, Ctrl+arrastrar co botón esquerdo sobre un espazo baleiro orbita",
"nav.inventor.help": "Arrastrar co botón central desprázase, Shift+botón central orbita, Ctrl+botón central fai zoom, arrastrar co botón dereito orbita",
"nav.maya.help": "Alt+arrastrar co botón esquerdo orbita, Alt+botón central desprázase, Alt+botón dereito fai zoom; arrastrar co botón central tamén desprázase",
"nav.openscad.help": "Arrastrar co botón esquerdo sobre un espazo baleiro orbita, Shift+arrastrar co botón esquerdo selecciona por caixa, arrastrar a xeometría do bosquexo edítaa; arrastrar co botón dereito ou central desprázase, Shift+arrastrar co botón dereito ou central fai zoom",
"nav.perspective.sketch": "A edición de bosquexos usa a vista ortogonal. Remata o bosquexo para cambiar á perspectiva.",
"nav.solidworks.help": "Arrastrar co botón central orbita, Ctrl+botón central desprázase, Shift+botón central fai zoom, arrastrar co botón dereito orbita",
@@ -1546,12 +1647,34 @@
"no hole": "sen furado",
"no interference": "sen interferencias",
"no joint that moves yet": "aínda non hai ningunha unión que se mova",
"no motion study yet": "aínda non hai ningún estudo de movemento",
"no picture": "sen imaxe",
"no such joint": "non existe esa unión",
"none": "ningún",
"not a move": "non é un movemento",
"not a number": "non é un número",
"open a sketch first": "abre primeiro un bosquexo",
"parts.automatic_name": "automático",
"parts.dimension.across_flats": "Ancho entre planos",
"parts.dimension.bore": "Diámetro do furado",
"parts.dimension.d": "Diámetro nominal",
"parts.dimension.head_diameter": "Diámetro da cabeza",
"parts.dimension.head_height": "Altura da cabeza",
"parts.dimension.height": "Altura",
"parts.dimension.inner": "Diámetro interior",
"parts.dimension.outer": "Diámetro exterior",
"parts.dimension.pitch": "Paso",
"parts.dimension.socket": "Tamaño do encaixe",
"parts.dimension.socket_depth": "Profundidade do encaixe",
"parts.dimension.thickness": "Espesor",
"parts.dimension.width": "Largura",
"parts.family.bearing": "Rodamento rígido de bólas (envolvente)",
"parts.family.dowel_pin": "Pasador paralelo (espiga)",
"parts.family.hex_bolt": "Parafuso / perno de cabeza hexagonal",
"parts.family.hex_nut": "Porca hexagonal",
"parts.family.set_screw": "Parafuso sen cabeza de hexágono interior, punta plana",
"parts.family.socket_head_cap_screw": "Parafuso de cabeza cilíndrica con hexágono interior",
"parts.family.washer": "Arandela plana",
"pattern.alignment_2": "Aliñamento 2",
"pattern.alignment_hint": "O número inclúe o orixinal. Centrar equilibra as copias; Inverter escolle o lado da copia extra cando o total é par.",
"pattern.direction_1": "Dirección 1",
@@ -1567,9 +1690,25 @@
"perf.ui_time": "Tempo da interface",
"perf.viewport": "Vista 3D",
"perf.worst": "peor",
"pick a body or some of its faces": "escolle un corpo ou algunhas das súas caras",
"pick a hole to seat it, or OK places it at the origin": "escolle un furado onde asentala, ou Aceptar colócaa na orixe",
"pick a planar face": "escolle unha cara plana",
"pick a plane or a face as the tool": "escolle un plano ou unha cara como ferramenta",
"pick a size": "escolle un tamaño",
"pick edges of a body": "escolle arestas dun corpo",
"pick edges of one body": "escolle arestas dun só corpo",
"pick edges of the holes to remove": "escolle as arestas dos furados que se van quitar",
"pick faces of one body": "escolle caras dun só corpo",
"pick faces to offset": "escolle as caras que se van desprazar",
"pick lines of one sketch": "escolle liñas dun só bosquexo",
"pick one or two vertices, edges or faces": "escolle un ou dous vértices, arestas ou caras",
"pick one plane or face as the tool, or sketch curves": "escolle un plano ou unha cara como ferramenta, ou curvas de bosquexo",
"pick sections in two or more sketches": "escolle seccións en dous ou máis bosquexos",
"pick sketch curves or edges": "escolle curvas de bosquexo ou arestas",
"pick the face to untrim": "escolle a cara que se vai desrecortar",
"pick the path": "escolle a traxectoria",
"pick what to keep": "escolle o que se vai manter",
"pick what to keep of the other surface": "escolle o que se vai manter da outra superficie",
"prefs.add_item": "Engadir o elemento",
"prefs.alt_frees_manipulators": "Alt / Option tamén libera as frechas do manipulador",
"prefs.anti_aliasing_amd_dx12": "O suavizado usa 1× porque na comprobación de inicio non se debuxou nada con multimostraxe e pasou con 1×. Podes escoller outro axuste arriba; reinicia para aplicalo se a comprobación se supera.",
@@ -1653,6 +1792,13 @@
"prefs.left_click": "Clic esquerdo",
"prefs.live_input": "Entrada en directo",
"prefs.live_preview": "Vista previa en directo",
"prefs.max_file_size": "Tamaño máximo de ficheiro ao abrir",
"prefs.max_file_size_design": "Ficheiros de deseño",
"prefs.max_file_size_flag": "Esta execución iniciouse con --max-file-size {size}, que se aplica ata pechar SolidCraft. Estes axustes aplícanse ao iniciar sen esa opción.",
"prefs.max_file_size_note": "Valores predeterminados: 512 MB e 256 MB; máximo 16 GB. Abrir un ficheiro require varias veces o seu tamaño en memoria. Un deseño que conserva unha importación grande pode precisar un límite maior para gardarse e abrirse de novo.",
"prefs.max_file_size_step": "Ficheiros STEP e de importación",
"prefs.max_file_size_web": "A aplicación web abre ficheiros STEP e de importación de ata 512 MB e deseños de ata 256 MB: unha lapela do navegador non admite ficheiros maiores. A aplicación de escritorio permite aumentar estes límites.",
"prefs.megabytes": "MB",
"prefs.minutes": "minutos",
"prefs.minutes_suffix": " min",
"prefs.mouse": "Rato e panel táctil",
@@ -1785,6 +1931,7 @@
"render.all_controls_help": "Inspección densa de todos os controis actuais. As páxinas de sección permiten editar de forma enfocada sen cambiar a solicitude subxacente.",
"render.all_shots_label": "Todas as tomas",
"render.appearance_physics_separate": "A aparencia visual non asigna densidade, datos térmicos nin outras propiedades de enxeñaría.",
"render.appearances": "Aparencias",
"render.availability_native": "Composición nativa; revísaa antes de aplicar",
"render.availability_projection": "Proxección admitida; consulta o alcance e as omisións",
"render.availability_reference": "Datos de referencia; non é unha asignación automática",
@@ -1839,6 +1986,10 @@
"render.camera_set": "Definir a cámara de renderizado",
"render.camera_target": "Posición do destino · mm",
"render.camera_yaw": "Guiñada",
"render.category_metal": "Metal",
"render.category_paint": "Pintura",
"render.category_plastic": "Plástico",
"render.category_wood": "Madeira",
"render.chromatic_offset": "Desprazamento cromático dos bordos",
"render.clear_stage": "Quitar o escenario gardado",
"render.copy_request": "Copiar JSON",
@@ -1938,6 +2089,7 @@
"render.effects_reset_lens": "Restablecer os efectos de lente",
"render.effects_seed": "Semente do patrón",
"render.emitted_axis": "Eixe de emisión",
"render.environment_tiles_require_raytrace": "Os contornos requiren trazado de raios na CPU ou na GPU. Selecciona ese método no editor de Saída.",
"render.error_area_samples": "area_samples debe ser 1, 4, 16 ou 64",
"render.error_binding_allocation": "non se puido reservar a vinculación do material",
"render.error_body_bound": "a vista previa do produto supera o límite de corpos capturados",
@@ -2009,6 +2161,8 @@
"render.flare_strength": "Intensidade do destello dos realces",
"render.flare_threshold": "Limiar do destello lineal da escena",
"render.fold_browser": "Contraer o navegador",
"render.frame_presets": "Axustes predefinidos de encadre",
"render.frame_presets_pending": "Chegará co editor de Tomas",
"render.gpu_depth": "Profundidade máxima de traxectoria",
"render.gpu_options_help": "As mostras, a profundidade, a semente e a exposición gárdanse con esta configuración. Só se garda o método de imaxe activo.",
"render.gpu_path_failed": "Trazador de traxectorias na GPU non dispoñible",
@@ -2080,8 +2234,12 @@
"render.lens_effects_title": "Inspirado na lente e fotográfico",
"render.library_all": "Catálogo",
"render.library_all_states": "Todos os estados de dispoñibilidade",
"render.library_appearance_available": "Aparencia dispoñible",
"render.library_applicable": "Recursos aplicables",
"render.library_applied": "Proxección do recurso aplicada. Comproba a solicitude de renderizado completa antes de iniciar.",
"render.library_apply_appearance": "Aplicar a aparencia",
"render.library_apply_hdr": "Gardar con este contorno HDR",
"render.library_apply_hint": "Fai clic para aplicar · arrastra sobre o modelo",
"render.library_apply_maps": "Usar os mapas de superficie de autor da GPU",
"render.library_apply_studio": "Gardar a proxección de Studio de autor",
"render.library_apply_uniform": "Aplicar a aparencia opaca uniforme",
@@ -2097,15 +2255,23 @@
"render.library_extent": "Extensión do suxeito",
"render.library_favorite": "Engadir aos favoritos",
"render.library_favorites": "Favoritos",
"render.library_filters": "Catálogo e dispoñibilidade",
"render.library_grid": "Grella",
"render.library_hdr": "Contornos HDR integrados",
"render.library_hdr_help": "Incorpora o HDR orixinal na configuración gardada. A orixe de lonxitude de autor convértese de forma explícita; despois de gardar non fai falta ningún ficheiro externo.",
"render.library_in_design": "No deseño",
"render.library_inspect_only": "Selecciona para inspeccionar; non se aplica nada automaticamente. Os favoritos e o historial recente pertencen a esta sesión do documento aberto.",
"render.library_ior": "IOR",
"render.library_light_concepts": "Conceptos de iluminación",
"render.library_list": "Lista",
"render.library_maps_help": "Os mapas integrados pódense gardar con fontes fixadas e mapeado local de ocorrencias. As substitucións ópticas temporais seguen sendo un modo á parte.",
"render.library_material_concepts": "Conceptos de material",
"render.library_metallic": "Metálico",
"render.library_native_scenes": "Studios nativos",
"render.library_next": "Páxina seguinte",
"render.library_omitted": "Non incluído",
"render.library_opacity": "Opacidade",
"render.library_other": "Outras aparencias",
"render.library_physical_profiles": "Perfís de referencia física",
"render.library_previous": "Páxina anterior",
"render.library_quality_concepts": "Especificacións de calidade",
@@ -2115,6 +2281,7 @@
"render.library_results": "Recursos coincidentes",
"render.library_rig_help": "Axusta unha vez cun centro e unha extensión física explícitos. As montaxes completas inclúen tarxetas pasivas; os bloqueos de calibración seguen rexeitando. Antes de gardar compróbase a folga conservadora entre emisores e tarxetas.",
"render.library_rigs": "Montaxes de iluminación de autor",
"render.library_roughness": "Rugosidade",
"render.library_save_maps": "Gardar os mapas nesta configuración",
"render.library_scene_bounds": "Usar os límites da escena capturada",
"render.library_scenes": "Especificacións de escena",
@@ -2138,6 +2305,7 @@
"render.library_unfavorite": "Quitar dos favoritos",
"render.library_uniform_ack": "Usar a aproximación uniforme sen efectos espaciais nin avanzados",
"render.library_usable": "Proxeccións executables",
"render.library_users": "Usado por",
"render.light_anchor_pending": "Aínda non se admite cambiar o sistema de coordenadas da luz.",
"render.lights_fixed_to": "Luces fixadas a",
"render.linear_help": "Conserva un máster RGBA de coma flotante de 32 bits antes da eliminación de ruído, a exposición, o mapeado tonal, os filtros e o telón de fondo. sRGB lineal/D65; premultiplicado pola cobertura. A memoria adicional admítese de forma explícita. Sen compresión nin pasadas adicionais.",
@@ -2356,6 +2524,7 @@
"render.scene_review": "Resolver e revisar",
"render.scene_review_help": "Revisa a composición nativa completa antes de aplicala. As especificacións SCENE avanzadas sen vincular non se substitúen por outras.",
"render.scene_scope_details": "Alcance da composición",
"render.search_appearances": "Buscar {count} aparencias",
"render.setup_legacy_environment": "Esta configuración antiga usa a rotación ou a intensidade do contorno herdadas. Eses valores consérvanse; conviértenos explicitamente antes de gardar unha tarefa de imaxe completa.",
"render.setup_name": "Nome da configuración",
"render.setup_numeric_help": "Gardar rexistra o método de imaxe activo e todos os seus axustes numéricos. Recargar ou escoller outra configuración substitúe este borrador; restaura a cámara por separado.",
@@ -2374,10 +2543,19 @@
"render.shot_remove_title": "Quitar a toma gardada?",
"render.shot_rename": "Renomear a toma gardada",
"render.shot_saved_only": "Garda primeiro o borrador actual. Duplicar e renomear conservan exactamente as opcións gardadas e as referencias aos recursos.",
"render.shots_camera_changed": "A cámara cambiou desde que se gardou esta toma",
"render.shots_name_help": "Escribe un nome único para a toma actual.",
"render.shots_no_result": "Aínda sen renderizado",
"render.shots_result": "Renderizado {number} · {width} × {height}",
"render.shots_revert": "Reverter",
"render.shots_save": "Gardar toma",
"render.shots_update": "Actualizar a toma",
"render.show_shots_label": "Mostrar a banda de tomas",
"render.size_4k_uhd": "4K UHD",
"render.size_full_hd": "Full HD",
"render.size_hd": "HD",
"render.size_presets": "Axustes predefinidos de tamaño",
"render.size_presets_pending": "Chegará co editor de Saída",
"render.sky_ground": "Chan · RGB lineal",
"render.sky_horizon": "Horizonte · RGB lineal",
"render.sky_zenith": "Cenit · RGB lineal",
@@ -2428,8 +2606,10 @@
"render.studio_sky_help": "Gradiente de ceo orixinal para a iluminación; non é un modelo atmosférico calibrado.",
"render.studio_sun": "Sol",
"render.studio_sun_radius": "Raio angular",
"render.studio_tiles_require_gpu": "Os Studios requiren trazado de camiños na GPU. Selecciona GPU no editor de Saída.",
"render.studio_title": "Estudio de renderizado",
"render.studio_tone_map": "Mapeado de tons",
"render.studios_environments": "Studios e contornos",
"render.subsurface_enable": "Paseo aleatorio de dispersión interior",
"render.subsurface_help": "Só para dieléctricos pechados. A dispersión e a absorción RGB son coeficientes independentes por mm. As traxectorias dispérsanse dentro da xeometría real e despois refractan ou reflicten no límite. Desactiva a transmisión directa das sombras, a redución de ruído da superficie e o volume exterior; usa un só estrato de Fresnel. Non é un sombreador de difusión, unha grella de densidade nin un material medido. Os medios densos poden necesitar máis mostras e máis profundidade de traxectoria.",
"render.subsurface_rate": "Taxa de colisións de dispersión",
@@ -2515,6 +2695,26 @@
"search.try": "Escribe o nome dunha orde, unha operación deste deseño ou un parámetro. Para executar unha orde con valores, escribe o seu id e un obxecto JSON: {example}",
"search.valid": "Válido",
"section.off_hint": "Non se escolleu ningún plano: ACEPTAR desactiva a sección.",
"select a direction (an axis, a sketch line or a straight edge) first": "selecciona primeiro unha dirección (un eixe, unha liña de bosquexo ou unha aresta recta)",
"select a direction (an axis, a sketch line or a straight edge) for the profile to follow": "selecciona unha dirección (un eixe, unha liña de bosquexo ou unha aresta recta) que o perfil debe seguir",
"select a point on a component": "selecciona un punto nun compoñente",
"select a profile, edges or sketch lines first": "selecciona primeiro un perfil, arestas ou liñas de bosquexo",
"select an axis": "selecciona un eixe",
"select an axis, a straight or circular edge, or a cylinder, cone or torus face first": "selecciona primeiro un eixe, unha aresta recta ou circular, ou unha cara de cilindro, cono ou toro",
"select bodies first": "selecciona primeiro os corpos",
"select corner points, or the two lines of one corner": "selecciona puntos de esquina ou as dúas liñas dunha esquina",
"select one corner point, or the two lines of one corner": "selecciona un punto de esquina ou as dúas liñas dunha esquina",
"select sheet edges first": "selecciona primeiro as arestas da chapa",
"select the bend line (a sketch line) first": "selecciona primeiro a liña de dobra (unha liña de bosquexo)",
"select the cam's profile (a sketch profile or the cam's edges)": "selecciona o perfil da leva (un perfil de bosquexo ou as arestas da leva)",
"select the corner to cut first": "selecciona primeiro a esquina que se vai cortar",
"select the corners to round first": "selecciona primeiro as esquinas que se van arredondar",
"select the fixed face first (a plate's large face)": "selecciona primeiro a cara fixa (a cara grande dunha placa)",
"select the frame sketch's lines first": "selecciona primeiro as liñas do bosquexo do marco",
"select the path (sketch lines across the edge)": "selecciona a traxectoria (liñas de bosquexo a través da aresta)",
"select the rim face first": "selecciona primeiro a cara do bordo",
"select the sheet edge first": "selecciona primeiro a aresta da chapa",
"select two components": "selecciona dous compoñentes",
"set by the sketch's lines": "definido polas liñas do bosquexo",
"settings.display_quality_note": "Os niveis máis finos debuxan as caras e arestas curvas máis suaves e tardan máis en xerar a malla. Só cambia a vista, nunca o deseño nin as exportacións.",
"settings.frame_rate_details_note": "Engade un gráfico dos tempos dos últimos fotogramas cos triángulos debuxados por riba, un histograma dos tempos de fotograma e o que debuxou a GPU. Fai clic nas imaxes por segundo da vista para mostralos ou ocultalos.",
@@ -2532,17 +2732,23 @@
"settings.window_system_details": "Detalles",
"settings.window_system_note_now": "Aplícase despois de reiniciar. Agora: {now}.",
"settings.window_system_wayland_limited": "Algunhas funcións están limitadas en Wayland nativo.",
"sheet.flange_type_base": "Base",
"sheet.flange_type_contour": "Contorno",
"sheet.flange_type_edge": "Aresta",
"sheet.no_body": "non hai ningún corpo de chapa: crea un con Pestana > Base, ou converte unha placa en chapa con Converter en chapa (escolle a súa cara plana grande)",
"shortcut.json_error": "os parámetros deben estar en formato JSON: {error}",
"shortcut.no_command": "non existe o comando `{id}`; o texto anterior ao JSON debe ser un ID de comando",
"shortcut.pin": "Fixar na parte superior da caixa S",
"shortcut.unpin": "Desafixar",
"sketch is missing": "falta o bosquexo",
"sketch.degrees_of_freedom": "{count} graos de liberdade",
"sketch.dof_left": "Quedan {count} graos de liberdade",
"sketch.dof_left_one": "Queda {count} grao de liberdade",
"sketch.draw_curves": "Debuxar como {linetype}",
"sketch.make_selection": "Converter a selección en {linetype}",
"sketch.open_ends": "Abertos {count}",
"sketch.status_conflict": "as restricións están en conflito",
"sketch.status_fully_constrained": "totalmente restrinxido",
"sketch.untied_ends": "Sen unir {count}",
"sketch_panel.attached": "Ligado a {body} / {path}",
"sketch_panel.author": "Localización de autoría: {path}",
@@ -2569,7 +2775,24 @@
"sketch_text.path_hint": "Unha curva do bosquexo que segue o texto: fai clic aquí e despois na curva",
"sketch_text.use_font_file": "Usar o ficheiro de tipo de letra {path}",
"solid": "sólido",
"the angle must be a number": "o ángulo ten que ser un número",
"the curves must be in one sketch": "as curvas teñen que estar nun só bosquexo",
"the direction must be an axis, a sketch line or a straight edge": "a dirección ten que ser un eixe, unha liña de bosquexo ou unha aresta recta",
"the duration must be a number of seconds, 0.001 or more": "a duración ten que ser un número de segundos, 0.001 ou máis",
"the line's sketch is gone": "o bosquexo da liña xa non existe",
"the lines must be in one sketch": "as liñas teñen que estar nun só bosquexo",
"the lines must be in one sketch, not the section's": "as liñas teñen que estar nun só bosquexo, non no da sección",
"the parts library is empty": "a biblioteca de pezas está baleira",
"the parts library is not available": "a biblioteca de pezas non está dispoñible",
"the path curves must be in one other sketch": "as curvas da traxectoria teñen que estar noutro bosquexo",
"the path's sketch is gone": "o bosquexo da traxectoria xa non existe",
"the position must be a number": "a posición ten que ser un número",
"the preview failed": "fallou a vista previa",
"the profiles and curves must be in one sketch": "os perfís e as curvas teñen que estar nun só bosquexo",
"the reference face must be on the edges' body": "a cara de referencia ten que estar no corpo das arestas",
"the scale must be above 0": "a escala ten que ser superior a 0",
"the section's profiles must be in one sketch": "os perfís da sección teñen que estar nun só bosquexo",
"the two origins are on the same component": "as dúas orixes están no mesmo compoñente",
"thread.automatic_size": "Tamaño automático",
"thread.designation": "Designación",
"thread.direction": "Dirección da rosca",
@@ -2609,6 +2832,7 @@
"trace.points": "{count} puntos ao longo da traxectoria",
"trace.stopped_by_contact": "Remata na mostra {sample}: {a} atravesaría {b}",
"trace.stopped_by_loop": "Remata na mostra {sample}: o bucle a través de {joint} non pode pecharse máis",
"trim by sketch curves, or by one plane or face": "recorta con curvas de bosquexo ou cun só plano ou cara",
"ui.1_degree_of_freedom": "1 grao de liberdade",
"ui.3d_mouse_axes_reassigned": "Os eixes están reasignados en Preferencias, e iso segue aplicándose sobre esta calibración.",
"ui.3d_mouse_button": "Botón {number}",
@@ -3085,6 +3309,8 @@
"ui.design_properties.revision": "Revisión",
"ui.diameter": "Diámetro",
"ui.dimension": "Cota",
"ui.direct_interaction": "Interacción directa",
"ui.direct_interaction_note": "Traballa directamente co modelo na vista. Preme unha cara plana ou un perfil e tira: cara arriba extrúese, cara abaixo dentro dun corpo córtase, e o movemento lateral fixa o tamaño do paso",
"ui.direction": "Dirección",
"ui.display_quality": "Calidade de visualización",
"ui.display_quality_coarse": "Baixa",
@@ -3108,6 +3334,7 @@
"ui.editJointLimits": "Editar os límites da unión",
"ui.edit_section_analysis": "Editar a análise de sección",
"ui.ellipse": "Elipse",
"ui.emboss_wrap_note": "Os perfís de liñas ortogonais tanxentes a un cilindro compatible envólvense automaticamente. Escolle unha parede para elixir un destino.",
"ui.end_radius": "Raio final",
"ui.end_scale": "Escala final",
"ui.entities": "Entidades",
@@ -3180,6 +3407,7 @@
"ui.heading_extrude": "EXTRUÍR",
"ui.heading_fillet": "ARREDONDAMENTO",
"ui.heading_finish_sketch": "REMATAR O BOSQUEXO",
"ui.heading_form": "FORMA",
"ui.heading_front": "FRONTE",
"ui.heading_hole": "FURADO",
"ui.heading_home": "INICIO",
@@ -3266,6 +3494,8 @@
"ui.intersect": "Intersecar",
"ui.isolate": "Illar",
"ui.join": "Unir",
"ui.joint_play": "▶ Reproducir",
"ui.joint_stop": "■ Deter",
"ui.joints": "Unións",
"ui.keyboard_shortcuts": "Atallos de teclado",
"ui.language": "Idioma",
@@ -3283,6 +3513,7 @@
"ui.loft": "Transición",
"ui.look_at": "Mirar cara a",
"ui.look_at_anim_desc": "Vista de eixe, Mirar cara a, Inicio e Axustar",
"ui.major_radius": "Raio maior",
"ui.manipulator_free_alt": "Arrastre libre do manipulador tamén con Alt/Option",
"ui.manipulator_free_alt_tip": "Activado: Alt/Option ou Ctrl/Cmd nas frechas de extrusión / do gizmo desactiva o axuste de valores. Desactivado: só Ctrl/Cmd.",
"ui.manufacture": "Fabricación",
@@ -3312,8 +3543,10 @@
"ui.mesh_format_3mf": "3MF",
"ui.mesh_format_3mf_tagged": "3MF con etiquetas de cara",
"ui.mesh_format_stl": "STL",
"ui.minor_radius": "Raio menor",
"ui.mirror": "Simetría",
"ui.mirror_line": "Liña de simetría",
"ui.modifiers_either": "{first} ou {second}",
"ui.move": "Mover",
"ui.multiple": "Múltiple",
"ui.named_views": "Vistas con nome",
@@ -3370,6 +3603,7 @@
"ui.pinToShortcuts": "Fixar nos atallos",
"ui.pinToToolbar": "Fixar na barra de ferramentas",
"ui.pipe": "Tubo",
"ui.pitch": "Paso",
"ui.placement": "Colocación",
"ui.placement_handles": "Asas de colocación",
"ui.placement_handles_tip": "Asas na vista previa dun Apoio, un Saínte ou un Encaixe a presión: arrastra o marcador para movelo polo modelo, xírao polo seu anel",
@@ -3432,6 +3666,7 @@
"ui.reverse_horizontal_pan_direction": "Inverter a dirección do desprazamento horizontal",
"ui.reverse_horizontal_pan_tip": "O desprazamento horizontal con dous dedos e a roda do polgar desprázanse en sentido contrario",
"ui.reverse_zoom_direction": "Inverter a dirección do zoom",
"ui.revolutions": "Voltas",
"ui.revolve": "Revolución",
"ui.ribbon_exit": "Saír",
"ui.ribbon_labels": "Mostrar texto baixo as iconas da cinta",
@@ -3448,6 +3683,7 @@
"ui.scale": "Escala",
"ui.second_rail": "Segunda guía",
"ui.section_analysis": "Análise de sección",
"ui.section_size": "Tamaño da sección",
"ui.select": "Seleccionar",
"ui.selectOther": "Seleccionar outro…",
"ui.selectTangentChain": "Seleccionar cadea tanxente",
@@ -3519,6 +3755,7 @@
"ui.start_page": "Páxina de inicio",
"ui.step_values": "Cambiar valores por pasos coas teclas de frecha e a roda",
"ui.step_values_tip": "Nun campo de valor no que estás escribindo, Arriba/Abaixo cambian un número simple en 1 (Shift: 10, Alt: 0.1), e a roda do rato sobre o campo tamén. As expresións déixanse tal como as escribiches.",
"ui.stitch_tolerance_hint": "axustada",
"ui.stretch": "Estirar",
"ui.supersampling": "Supermostraxe (SSAA)",
"ui.supersampling_adaptive": "Só cando a vista está quieta",
@@ -3549,6 +3786,8 @@
"ui.to_object": "Ata o obxecto",
"ui.tool_direction": "Dirección da ferramenta",
"ui.top": "Superior",
"ui.top_diameter": "Diámetro superior",
"ui.top_radius": "Raio superior",
"ui.torus": "Toro",
"ui.total_angle": "Ángulo total",
"ui.trackpad_pan_tip": "Os xestos do panel táctil desprazan (desactivado: fan zoom como a roda); a roda do rato fai zoom, a roda do polgar despraza de lado e a pinza sempre fai zoom",
@@ -3638,6 +3877,9 @@
"update.up_to_date": "SolidCraft está actualizado.",
"update.update_now": "Actualizar agora",
"update.you_have": "Tes a versión {version}.",
"use a positive nominal diameter and a pitch smaller than it": "usa un diámetro nominal positivo e un paso menor que el",
"values must be finite numbers": "os valores teñen que ser números finitos",
"values must be numbers or expressions": "os valores teñen que ser números ou expresións",
"view.auto_ortho_faces": "Perspectiva ao orbitar, ortogonal nas caras",
"web.files.insert_drawings_hint": "Ficheiros CAD, mallas, DXF ou SVG no deseño aberto",
"web.files.insert_stored_drawings_hint": "No deseño aberto (ficheiros CAD, mallas, DXF, SVG)",
+169
View File
@@ -181,6 +181,8 @@
"Designs you open or save appear here.": "תכנים שנפתחים או נשמרים מופיעים כאן.",
"Determined by To Object. Choose Angle to edit the angular extent.": "נקבע לפי \"עד אובייקט\". יש לבחור \"זווית\" כדי לערוך את התחום הזוויתי.",
"Diameter": "קוטר",
"Did this assembly open the wrong way up?": "האם המכלול הזה נפתח הפוך?",
"Did this part open the wrong way up?": "האם החלק הזה נפתח הפוך?",
"Dimension new shapes": "הוספת מידות לצורות חדשות",
"Direction": "כיוון",
"Discard": "השלכה",
@@ -593,6 +595,7 @@
"Square": "ריבוע",
"Stand it up": "העמדה",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "העמדה: רבע סיבוב סביב X, כפיצ'ר הזזה אחד שאפשר לבטל.",
"Stand this assembly up?": "להעמיד את המכלול הזה?",
"Stand this part up?": "להעמיד את החלק הזה?",
"Start angle": "זווית התחלה",
"Start bulge": "תפיחה בהתחלה",
@@ -760,6 +763,7 @@
"appearance.pbr_opaque": "PBR אטום",
"appearance.roughness": "חספוס",
"appearance.surface_response": "תגובת המשטח",
"at the face": "על הפאה",
"bom.group_children": "תת-חלקים בשורת החלק שלהם",
"browser.body_name": "גוף{number}",
"browser.calibrate_title": "כיול: {name}",
@@ -768,6 +772,7 @@
"browser.capture_one": "רכיב הוזז, אך המיקום שלו לא נלכד.",
"browser.component_name": "רכיב{number}",
"browser.edit_canvas_title": "עריכת קנבס: {name}",
"browser.edit_point_cloud_title": "עריכת ענן נקודות: {name}",
"browser.move_title": "הזזה: {name}",
"browser.parameters": "פרמטרים ({count})",
"browser.precision": "דיוק: {precision}",
@@ -995,6 +1000,10 @@
"cmd.plastic_rest": "משען",
"cmd.plastic_rules": "רשימת כללי פלסטיק",
"cmd.plastic_snap_fit": "חיבור הצמדה",
"cmd.pointcloud_delete": "מחיקת ענן נקודות",
"cmd.pointcloud_edit": "עריכת ענן נקודות",
"cmd.pointcloud_insert": "הוספת ענן נקודות",
"cmd.pointcloud_list": "ענני נקודות",
"cmd.render_aging_describe": "הזדקנות משטח בשכבות",
"cmd.render_backends": "מנועי הדמיה",
"cmd.render_batch": "רינדור כל הצילומים",
@@ -1017,15 +1026,20 @@
"cmd.render_library_surface_prepare": "הכנת מפות משטח מעוצבות",
"cmd.render_library_surface_unbind": "הסרת קישור משטח",
"cmd.render_library_targets": "חיפוש יעדים לספריית ההדמיה",
"cmd.render_lights_add": "הוסף אור רינדור",
"cmd.render_lights_solo": "בידוד אור",
"cmd.render_optics_bind": "שמירת קישור אופטי",
"cmd.render_optics_unbind": "הסרת קישור אופטי",
"cmd.render_output_size": "הגדר גודל פלט רינדור",
"cmd.render_preflight": "בדיקת בקשת הדמיה",
"cmd.render_preset_apply": "החלת סטודיו מקורי",
"cmd.render_preset_resolve": "סקירת סטודיו מקורי",
"cmd.render_quality_prepare": "סקירת תקציב דגימה קבוע",
"cmd.render_result_close": "סגור תמונת רינדור",
"cmd.render_result_copy": "העתק תמונת רינדור",
"cmd.render_result_pin": "שמירת תוצאת ההדמיה המוצגת",
"cmd.render_result_regrade": "תיקון צבע מחדש למאסטר הליניארי",
"cmd.render_result_view": "הצג תמונת רינדור",
"cmd.render_scene": "בדיקת סצנת ההדמיה",
"cmd.render_settings_apply": "החלת מצלמת ההדמיה",
"cmd.render_settings_capture_hdr": "שמירת הגדרות הדמיה עם HDR",
@@ -1128,6 +1142,7 @@
"cmd.sketch_spline_control_point_5": "ספליין נקודות בקרה (מעלה 5)",
"cmd.sketch_spline_fit_point": "ספליין נקודות התאמה",
"cmd.sketch_spline_insert_point": "הוספת נקודה לספליין",
"cmd.sketch_spline_reverse": "היפוך כיוון הספליין",
"cmd.sketch_spun_profile": "פרופיל סיבובי",
"cmd.sketch_toggle_diameter": "מעבר בין רדיוס לקוטר",
"cmd.sketch_toggle_driven": "מעבר בין מידה מונעת למניעה",
@@ -1255,6 +1270,7 @@
"dialog.dimension_insert_hint": "יש ללחוץ על מידה מניעה אחרת כדי להוסיף את שמה",
"dialog.dimension_name": "מידה: {name}",
"dialog.edit_title": "עריכת {title}",
"dialog.no_shown_body": "אין גוף מוצג לשינוי: הציגו גוף, או בחרו „גוף חדש”",
"dialog.select": "↖ בחירה",
"dialog.selected_count": "↖ {count} נבחרו",
"dialog.selection_guidance": "יש לבחור קלט כדי לשנות אותו. לחיצה על אובייקט שנבחר מסירה אותו; גם Ctrl/Cmd+לחיצה מחליפה את מצבו. לאחר בדיקת התצוגה המקדימה יש ללחוץ על אישור.",
@@ -1325,6 +1341,7 @@
"help.mit": "רישיון MIT",
"help.notice": "NOTICE",
"help.parameters.change": "פתיחת הטבלה של ערכים בעלי שם וביטויים שקובעים את מידות התכנון. כך משנים מידה במקום אחד וכל הפיצ'רים שמשתמשים בה מתעדכנים.",
"help.pointcloud.insert": "הוספת סריקה בפורמט PLY, XYZ או PTS כענן נקודות לצד המודל. משמש כעזר למידול על סמך נתוני מדידה.",
"help.solid.align": "הזזת הגופים שנבחרו כך שנקודה, פאה או מישור שלהם יפגשו נקודה, פאה או מישור במקום אחר. מתאים להצמדת חלק לחלק אחר בלי למדוד את המרחק.",
"help.solid.chamfer": "חיתוך המקצועות שנבחרו בשיפוע ישר במרחק שנקבע. כך שוברים מקצועות חדים או מקלים על הכנסה של פין או בורג.",
"help.solid.combine": "איחוד גופים, חיסורם מגוף יעד או השארת הנפח המשותף להם בלבד, עם אפשרות לשמור את גופי הכלי. מתאים למיזוג חלקים, לחיתוך צורה אחת בעזרת אחרת או למציאת החפיפה בין שני גופים.",
@@ -1350,6 +1367,8 @@
"help.third_party": "רישיונות צד שלישי",
"help.timeline.compute_all": "בנייה מחדש של כל הפיצ'רים בציר הזמן מההתחלה. מתאים לרענון המודל ולבדיקה אילו פיצ'רים נכשלים.",
"help.timeline.delete": "מחיקת הפיצ'רים שנבחרו מציר הזמן; מחיקת סקיצה מוחקת גם את הפיצ'רים שנבנו ממנה. מתאים להסרת שלב שכבר אין בו צורך.",
"history.sketch_under_constrained": "אילוצים חסרים ({count} דרגות חופש)",
"history.sketch_under_constrained_one": "אילוצים חסרים (דרגת חופש {count})",
"hole_presets.confirm_delete": "אישור מחיקה",
"hole_presets.delete": "מחיקה",
"hole_presets.delete_prompt": "למחוק את ההגדרה המוכנה האישית הזו? חורים קיימים לא ישתנו.",
@@ -1717,6 +1736,8 @@
"prefs.anti_aliasing_note": "מחליק קצוות משוננים.",
"prefs.applies_on_restart": "ייכנס לתוקף בהפעלה מחדש",
"prefs.assistant": "עוזר",
"prefs.auto_operation": "בחירת הפעולה באופן אוטומטי",
"prefs.auto_operation_note": "חלונות הפיצ'רים בוחרים „איחוד”, „גוף חדש” או „הסרה” לפי מה שהחומר החדש נוגע בו, עד שתבחרו פעולה בעצמכם. כבוי: הם שומרים על הפעולה שאיתה נפתחו.",
"prefs.axes": "צירים",
"prefs.axis": "ציר",
"prefs.bodies_folders": "תיקיות גופים",
@@ -1798,6 +1819,13 @@
"prefs.logo": "לוגו",
"prefs.logo_accent": "צבע הדגשה",
"prefs.logo_theme": "צבעי ערכת הנושא",
"prefs.max_file_size": "גודל קובץ מרבי לפתיחה",
"prefs.max_file_size_design": "קובצי תכן",
"prefs.max_file_size_flag": "ההפעלה הנוכחית החלה עם --max-file-size {size}, שחל עד סגירת SolidCraft. הגדרות אלה חלות בהפעלה ללא אפשרות זו.",
"prefs.max_file_size_note": "ברירות מחדל: 512 MB ו־256 MB; לכל היותר 16 GB. פתיחת קובץ דורשת זיכרון בנפח גדול פי כמה מגודלו. תכן ששומר ייבוא גדול עשוי לדרוש מגבלה גבוהה יותר לקובצי תכן כדי לשמור ולפתוח אותו מחדש.",
"prefs.max_file_size_step": "קובצי STEP וייבוא",
"prefs.max_file_size_web": "יישום הדפדפן פותח קובצי STEP וייבוא עד 512 MB וקובצי תכן עד 256 MB: לשונית דפדפן אינה יכולה להכיל קבצים גדולים יותר. ביישום למחשב ניתן להעלות מגבלות אלה.",
"prefs.megabytes": "MB",
"prefs.minutes": "דקות",
"prefs.minutes_suffix": " דק'",
"prefs.mouse": "עכבר ומשטח מגע",
@@ -1846,6 +1874,8 @@
"prefs.sketch_selection": "סקיצה ובחירה",
"prefs.sketch_selection_heading": "סקיצה ובחירה",
"prefs.sketch_selection_note": "איך סקיצות נצמדות ואיך לחיצות בוחרות.",
"prefs.sketch_state_icons": "סמלי סקיצות בצבע המצב שלהן",
"prefs.sketch_state_icons_note": "בהיסטוריה ובדפדפן, סמל הסקיצה מקבל את צבע המצב שלה (אילוצים חסרים, מאולצת במלואה או סותרת) וחלונית העזרה מציינת את המצב. כבוי: לכל הסקיצות אותו סמל.",
"prefs.sketch_tab_always": "השארת לשונית הסקיצה מוצגת",
"prefs.sketch_tab_always_note": "לשונית הסקיצה נשארת ברצועה, מיד אחרי מודל, גם כשאין סקיצה פתוחה. כלי השרטוט בה מתחילים אז סקיצה על המישור או הפאה שנבחרו, או על המישור שתבחרו בהמשך, והכלי מוכן בה.",
"prefs.sketch_tracking_guides": "קווי עזר ליישור מרכזים ונקודות אמצע",
@@ -1855,6 +1885,13 @@
"prefs.source": "מקור",
"prefs.speed": "מהירות",
"prefs.stand_up_note": "אחרי שקובץ STEP או IGES נפתח כש־Z למעלה, להציע רבע סיבוב סביב X.",
"prefs.stand_up_unknown": "לקבצים מתוכנות לא מוכרות",
"prefs.stand_up_unknown.ask": "לשאול בתיבת דו־שיח",
"prefs.stand_up_unknown.note": "להציג הערה",
"prefs.stand_up_unknown.off": "לא להציע",
"prefs.stand_up_unknown_note": "איך ההצעה מוצגת כשכותרת הקובץ לא מציינת תוכנה שידוע שהיא שומרת עם Y למעלה. הערה מוצגת רק כשנראה שהקובץ שוכב על גבו; ״לשאול בתיבת דו־שיח״ שואל על כל קובץ כזה, כמו קודם.",
"prefs.status_bar": "שורת מצב",
"prefs.status_bar_note": "פס דק לאורך הקצה התחתון של החלון מציג מה נערך, את מצב הסקיצה הפתוחה בצבע שלו ואת יחידות התכנון; לחיצה על היחידות משנה אותן. מצב הסקיצה לא מוצג אז עוד בפינת התצוגה.",
"prefs.status_done": "הושלם",
"prefs.status_error": "שגיאה",
"prefs.status_selected": "נבחר",
@@ -1937,6 +1974,45 @@
"render.aging_upward": "שקיעה על משטחים הפונים מעלה",
"render.all_controls_help": "בדיקה צפופה של כל הפקדים הנוכחיים. דפי המקטעים מאפשרים עריכה ממוקדת בלי לשנות את הבקשה עצמה.",
"render.all_shots_label": "כל הצילומים",
"render.appearance_editor.absorption": "ספיגה",
"render.appearance_editor.base_colour": "צבע בסיס",
"render.appearance_editor.body_reason": "קישורים אופטיים מכוונים לגוף שלם אחד. בחר גוף כדי לערוך את הבקרות האלה.",
"render.appearance_editor.boundary": "גבול",
"render.appearance_editor.box": "תיבה",
"render.appearance_editor.choose_image": "בחר תמונה…",
"render.appearance_editor.closed": "גוף מלא סגור",
"render.appearance_editor.coat": "ציפוי שקוף",
"render.appearance_editor.cpu_reason": "דורש מעקב קרניים במעבד.",
"render.appearance_editor.glass": "זכוכית וספיגה",
"render.appearance_editor.glass_enable": "השתמש בזכוכית פיזיקלית בגוף זה",
"render.appearance_editor.glass_note": "זכוכית סגורה משתמשת במרחק גאומטרי. אי אפשר לשלב אותה עם ציפוי שקוף או מפות באותו גוף.",
"render.appearance_editor.gpu_reason": "דורש מעקב נתיב ב-GPU.",
"render.appearance_editor.ior": "מקדם שבירה",
"render.appearance_editor.maps": "מפות",
"render.appearance_editor.normal": "נורמל",
"render.appearance_editor.normal_strength": "עוצמת נורמל",
"render.appearance_editor.origin": "מקור",
"render.appearance_editor.recipe": "מתכון",
"render.appearance_editor.reflection_sampling": "דגימה לכל הגופים המחזירים",
"render.appearance_editor.reflections": "השתקפויות",
"render.appearance_editor.remove": "הסר את השמורה",
"render.appearance_editor.remove_note": "הסר את האופטיקה השמורה מהצילום הזה. הגאומטריה והמראות שהוקצו נשמרים.",
"render.appearance_editor.remove_title": "להסיר את האופטיקה השמורה?",
"render.appearance_editor.replace": "החלף את האופטיקה השמורה של גוף זה",
"render.appearance_editor.revalidate": "אמת מחדש גוף זה",
"render.appearance_editor.save": "שמור אופטיקה זו",
"render.appearance_editor.save_reason": "שמירת האופטיקה הזו דורשת זכוכית פיזיקלית בגוף GPU עדכני. הציפוי ומפות התמונה המקומיות נשארים זמניים.",
"render.appearance_editor.saved": "נשמר עם הצילום הזה",
"render.appearance_editor.saved_state": "האופטיקה הזו נשמרת עם הצילום הזה.",
"render.appearance_editor.scatter_reason": "פיזור פנימי דורש גבול של גוף מלא סגור.",
"render.appearance_editor.temporary": "האופטיקה הזו זמנית בלשונית זו. שמור אותה כדי לשמור אותה עם הצילום.",
"render.appearance_editor.thickness": "עובי",
"render.appearance_editor.thickness_reason": "עובי חל על יריעות דקות; זכוכית סגורה משתמשת במרחק גאומטרי.",
"render.appearance_editor.thin": "יריעה דקה",
"render.appearance_editor.u_repeat": "חזרת U",
"render.appearance_editor.v_repeat": "חזרת V",
"render.appearance_editor.v_up": "ציר V של הטקסטורה פונה כלפי מעלה",
"render.appearance_editor.weight": "משקל",
"render.appearance_physics_separate": "המראה החזותי אינו מקצה צפיפות, נתונים תרמיים או תכונות הנדסיות אחרות.",
"render.appearances": "מראות",
"render.availability_native": "קומפוזיציה מקורית; יש לסקור לפני ההחלה",
@@ -1997,6 +2073,9 @@
"render.category_paint": "צבע",
"render.category_plastic": "פלסטיק",
"render.category_wood": "עץ",
"render.check_opaque": "שקיפות: משטחים אטומים בלבד; אלפא של הרקע נתמכת.",
"render.check_sorted_alpha": "שקיפות: קירוב אלפא ממוין.",
"render.check_transmission": "שקיפות: מעבר אור פיזיקלי.",
"render.chromatic_offset": "היסט כרומטי בקצוות",
"render.clear_stage": "הסרת הבמה השמורה",
"render.copy_request": "העתקת JSON",
@@ -2230,6 +2309,36 @@
"render.inner_half_angle": "חצי זווית פנימית",
"render.inspect_scene": "רענון סיכום הסצנה שנלכדה",
"render.inspector": "מפקח",
"render.inspector_advanced": "מתקדם",
"render.inspector_all_users": "כל {count} המשתמשים",
"render.inspector_applies_to": "חל על",
"render.inspector_both_users": "שני המשתמשים",
"render.inspector_chain": "מהיכן המראה הזה מגיע",
"render.inspector_colour": "צבע",
"render.inspector_edit": "עריכה",
"render.inspector_finish": "גימור",
"render.inspector_from_material": "מחומר",
"render.inspector_gloss": "ברק",
"render.inspector_material": "חומר",
"render.inspector_matte": "מט",
"render.inspector_metal": "מתכת",
"render.inspector_opacity": "אטימות",
"render.inspector_opaque_help": "שדות הגימור והאופטיקה משתמשים ב-PBR אטום (אטימות 1). אטימות נמוכה יותר משתמשת בהצללה סקלרית.",
"render.inspector_pick_target": "בחר יעד מסוג זה כדי לבדוק את המראה שלו.",
"render.inspector_remove": "הסר עקיפה",
"render.inspector_replace": "החלף…",
"render.inspector_reset": "איפוס",
"render.inspector_satin": "סאטן",
"render.inspector_save_library": "שמור בספרייה…",
"render.inspector_set_all": "מראות מעורבים. העריכה קובעת את המראה בכל היעדים שנבחרו.",
"render.inspector_soon": "בקרוב",
"render.inspector_target_limit": "בחר עד 10000 יעדים כדי לבדוק מראות.",
"render.inspector_textures": "טקסטורות",
"render.inspector_this_body": "גוף זה",
"render.inspector_this_component": "רכיב זה",
"render.inspector_this_face": "פאה זו",
"render.inspector_this_target": "יעד זה",
"render.inspector_used_by": "בשימוש של {count}",
"render.keep_editing": "המשך עריכה",
"render.layer_down": "הזזה למטה",
"render.layer_enabled": "השכבה מופעלת",
@@ -2396,12 +2505,34 @@
"render.optics_saved": "יעדים אופטיים שמורים",
"render.optics_saved_help": "זהות ההגדרה, הפיצ'ר והמופע המדויקת נשמרת גם בפתיחה מחדש ללא חיבור. יעדים חסרים, מוחלפים או מוסתרים נדחים; החומרים ההנדסיים אינם משתנים. יש לשמור או לבטל עריכות מספריות לפני הסרת יעד שמור.",
"render.outer_half_angle": "חצי זווית חיצונית",
"render.output_advanced": "מתקדם",
"render.output_backend_ready": "מוכן",
"render.output_check": "בדיקה",
"render.output_check_help": "הפעל בדיקה כדי לסקור בקשה זו בלי להתחיל רינדור.",
"render.output_check_summary": "בדיקה · הערות: {count}",
"render.output_copy": "העתק",
"render.output_destination": "יעד",
"render.output_destination_help": "בחר את קובץ היעד בעת ייצוא התמונה שהושלמה.",
"render.output_dimensions": "גודל",
"render.output_effects": "אפקטים",
"render.output_export": "ייצא PNG…",
"render.output_exr_unavailable": "הגדרת פלט EXR אינה זמינה כאן. ייצוא EXR לינארי דורש תמונת GPU שהושלמה עם מאסטר לינארי שנשמר.",
"render.output_fit": "התאם",
"render.output_format": "פורמט",
"render.output_format_status": "PNG הוא תמונת התצוגה הגמורה המדויקת. EXR אופציונלי ב־GPU שומר קרינה גולמית ליניארית של הסצנה ב־FLOAT32 לפני מסנני התצוגה. הייצוא לעולם אינו מחליף אותה במטמון התצוגה המקדימה או במשימה אחרת.",
"render.output_image": "תמונה",
"render.output_largest": "מקס׳ {width} × {height}",
"render.output_lock_ratio": "נעילת יחס הממדים",
"render.output_over_budget": "התמונה המבוקשת חורגת ממעטפת המשאבים שנבחרה. יש לבחור במפורש גודל או פרופיל מתאימים; אין הורדה אוטומטית.",
"render.output_presets_help": "פורמט, איכות ורשימת הבדיקה נמצאים בעורך הפלט.",
"render.output_quality": "איכות",
"render.output_quality_waiting": "ממתין לחוזה האיכות",
"render.output_ratio_refused": "הממד המקושר חורג ממגבלת הגודל של השיטה הזאת. הגודל הקודם נשמר; יש לבטל את נעילת היחס או לבחור ערך קטן יותר.",
"render.output_ready": "מוכן לרינדור",
"render.output_size_ok": "הגודל תקין · {width} × {height} פיקסלים",
"render.output_swap_orientation": "החלפת רוחב וגובה",
"render.output_tools_inspector": "ערוך גודל ופורמט במפקח הפלט.",
"render.output_view_image": "הצג תמונה",
"render.page_aging": "הזדקנות וזיהום",
"render.page_all": "כל הפקדים",
"render.page_appearance": "מראה",
@@ -2462,10 +2593,18 @@
"render.property_unknown": "לא ידוע",
"render.quality_ack": "אישור ההשלכה בתקציב קבוע וההשמטות",
"render.quality_apply": "שימוש בתקציב הדגימה הקבוע הזה",
"render.quality_balanced": "מאוזן",
"render.quality_custom": "מותאם",
"render.quality_draft": "טיוטה",
"render.quality_high": "גבוהה",
"render.quality_projection_help": "פרופילי Q מוכנים כוללים דרישות עתידיות. פעולה זו משליכה רק תקציב PNG נתמך בדגימות קבועות, ומשמיטה במפורש עצירה אדפטיבית, פלט ליניארי גולמי ומגבלות העברה עצמאיות.",
"render.quality_review": "סקירת תקציב הדגימה",
"render.queue_pending": "בממשק זה אין תור הפקה אוטומטי ואין מנוע הדמיה לאנימציה. תמונות סטילס גמורות נשארות ניתנות לייצוא כל אחת בנפרד.",
"render.radial_assign": "הקצה מראה",
"render.radial_compare": "השוואה",
"render.radial_distortion": "עיוות רדיאלי",
"render.radial_fit": "התאם מוצר",
"render.radial_save": "שמור צילום",
"render.regrade_action": "תיקון צבע מחדש למאסטר הגולמי (ללא הסרת רעש)",
"render.regrade_help": "יוצר PNG נפרד מהמאסטר הגולמי הגמור שנבחר, באמצעות פקדי התצוגה האלה. אינו עוקב אחר קרניים, אינו משנה את הגודל, המצלמה או האורות שנלכדו ואינו דורס את ה־EXR. הסרת רעש אינה מוחלת שוב: מאסטרים גולמיים אינם שומרים את המדריכים שלה. התוצר הנגזר נשאר ניתן לייצוא באותם פקדי משימה.",
"render.regrade_label": "כיול מחדש",
@@ -2474,6 +2613,7 @@
"render.replace_label": "החלפה",
"render.replace_saved_surface": "החלפת קישור שמור קיים ליעד הזה",
"render.restore_camera": "שחזור המצלמה השמורה",
"render.result_actual": "100%",
"render.result_actual_help": "פיקסל תמונה אחד לכל פיקסל תצוגה.",
"render.result_actual_size": "100%",
"render.result_capture": "הגרסה שנלכדה",
@@ -2541,6 +2681,7 @@
"render.sharpening": "חידוד פרטים",
"render.shell_tab_appearance": "מראה רינדור",
"render.shell_tab_camera": "מצלמת רינדור",
"render.shell_tab_lighting": "תאורה",
"render.shell_tab_scene": "סצנת רינדור",
"render.shot_duplicate": "שכפול הצילום השמור",
"render.shot_manager": "צילומים שמורים",
@@ -2558,11 +2699,15 @@
"render.shots_save": "שמור צילום",
"render.shots_update": "עדכן צילום",
"render.show_shots_label": "הצג את סרגל הצילומים",
"render.size_4k": "4K",
"render.size_4k_uhd": "4K UHD",
"render.size_custom": "מותאם…",
"render.size_full_hd": "Full HD",
"render.size_hd": "HD",
"render.size_presets": "הגדרות גודל מוכנות",
"render.size_presets_pending": "יגיע עם עורך הפלט",
"render.size_preview": "תצוגה מקדימה",
"render.size_square": "ריבוע",
"render.sky_ground": "קרקע · RGB ליניארי",
"render.sky_horizon": "אופק · RGB ליניארי",
"render.sky_zenith": "זנית · RGB ליניארי",
@@ -2575,6 +2720,9 @@
"render.stage_output": "פלט",
"render.stage_panel_count": "לוחות שמורים",
"render.stage_preview_note": "תצוגה מקדימה משולבת · אור ישיר",
"render.state_completed": "תמונה שהושלמה",
"render.state_in_progress": "תמונה בתהליך",
"render.state_model": "תצוגת מודל",
"render.strip_label": "סרגל",
"render.studio_add_area": "הוספת אור שטח",
"render.studio_ambient": "קרינת סביבה קבועה",
@@ -2670,6 +2818,7 @@
"render.volume_max": "גבולות מרביים · mm בעולם",
"render.volume_min": "גבולות מזעריים · mm בעולם",
"render.volume_title": "נפח הומוגני תחום",
"render.wait_add_light": "לא נתמך עדיין: יצירת אורות בעלי שם",
"render.wait_batch": "לא נתמך עדיין: תור רינדור של צילומים שמורים",
"render.wait_compare": "לא נתמך עדיין: השוואת תמונות שנשמרו",
"render.wait_paint": "לא נתמך עדיין: בחירת מוצר וצביעת מראה",
@@ -2784,6 +2933,10 @@
"sketch_text.path_hint": "עקומה בסקיצה שהטקסט עוקב אחריה: יש ללחוץ כאן ואז על העקומה",
"sketch_text.use_font_file": "שימוש בקובץ הגופן {path}",
"solid": "מוצק",
"status.editing": "עריכת {name}",
"status.under_constrained": "אילוצים חסרים ({count} דרגות חופש)",
"status.under_constrained_one": "אילוצים חסרים (דרגת חופש {count})",
"status.units_tip": "יחידות התכנון. לחצו כדי לשנות אותן",
"the angle must be a number": "הזווית חייבת להיות מספר",
"the curves must be in one sketch": "העקומות חייבות להיות בסקיצה אחת",
"the direction must be an axis, a sketch line or a straight edge": "הכיוון חייב להיות ציר, קו סקיצה או מקצוע ישר",
@@ -2822,8 +2975,10 @@
"timeline.move_to_group": "העברת פיצ'רים לתיקייה",
"timeline.rename_group": "שינוי שם תיקיית פיצ'רים",
"timeline.reorder_many": "שינוי סדר פיצ'רים",
"timeline.suppress_features": "השבתת פיצ'רים",
"timeline.suppress_folders_tip": "השבתה או ביטול השבתה של כל הפיצ'רים בתיקיות ובתיקיות המקוננות בהן",
"timeline.ungroup": "הסרת תיקיות פיצ'רים",
"timeline.unsuppress_features": "ביטול השבתת פיצ'רים",
"to the face": "עד הפאה",
"to the next face": "עד הפאה הבאה",
"toolbar.customize_note": "לשונית {tab}. סימון פקודה מוסיף לה לחצן בסרגל הכלים. אפשר גם לגרור שורה מהרשימה הנפתחת של חלונית אל סרגל הכלים, לגרור לחצן אל מחוץ לסרגל הכלים כדי להסיר אותו, או להשתמש בנעץ שבשורה של רשימה נפתחת.",
@@ -3234,6 +3389,8 @@
"ui.cancel": "ביטול",
"ui.canvases": "קנבסים",
"ui.center": "מרכז",
"ui.center_arc_follows_cursor": "קשת מרכז ונקודה עוקבת אחר כיוון הסמן",
"ui.center_arc_follows_cursor_tip": "קשת מרכז ונקודה נמשכת בכיוון השעון או נגדו, לפי הכיוון שבו הסמן מסתובב סביב המרכז. כבוי: תמיד נגד כיוון השעון מנקודת ההתחלה.",
"ui.centerline": "קו מרכז",
"ui.chain": "שרשרת",
"ui.chamfer": "פאזה",
@@ -3646,6 +3803,15 @@
"ui.plugins_skipped": "לא ניתן היה לקרוא {count} תיקיות.",
"ui.plugins_version": "{version}",
"ui.point": "נקודה",
"ui.point_cloud_auto_range": "טווח אוטומטי",
"ui.point_cloud_color_by": "צביעה לפי",
"ui.point_cloud_file_colors": "צבעי הקובץ",
"ui.point_cloud_height_high": "הגובה המרבי",
"ui.point_cloud_height_low": "הגובה המזערי",
"ui.point_cloud_no_file_colors": "אין בקובץ צבעים",
"ui.point_cloud_single_color": "צבע אחיד",
"ui.point_cloud_size": "גודל נקודה",
"ui.point_cloud_units": "יחידות הקובץ",
"ui.point_under_the_cursor": "הנקודה שמתחת לסמן",
"ui.points": "נקודות",
"ui.polygon": "מצולע",
@@ -3899,11 +4065,13 @@
"update.installed": "SolidCraft {version} הותקן. יש להפעיל מחדש כדי להשתמש בו.",
"update.installer_started": "תוכנית ההתקנה תתחיל כש־SolidCraft ייסגר.",
"update.later": "תזכורת מאוחר יותר",
"update.managed": "יש עדכון זמין: עדכנו את SolidCraft ב-{manager}",
"update.no_notes": "אין הערות לגרסה זו.",
"update.package_hint": "עותק זה הגיע מ־{kind}. יש להוריד את החבילה החדשה ולהתקין אותה באמצעות:",
"update.prefs_dev": "זוהי גרסת פיתוח ({version}): היא אינה בודקת עדכונים.",
"update.prefs_flatpak": "Flatpak מעדכן עותק זה ({version}): יש להריץ {command} או להשתמש במרכז התוכנה. SolidCraft אינו בודק עדכונים.",
"update.prefs_label": "עדכונים",
"update.prefs_managed": "זהו SolidCraft {version}, בניהול {manager}. הבדיקה שואלת את GitHub מהי הגרסה האחרונה ולא שולחת דבר נוסף; עדכנו את SolidCraft ב-{manager}.",
"update.prefs_note": "זהו SolidCraft {version} ({kind}). הבדיקה שואלת את GitHub מהי הגרסה האחרונה ואינה שולחת שום דבר אחר.",
"update.prefs_web": "יש לטעון מחדש את הדף כדי לקבל את גרסת אפליקציית הרשת העדכנית. אם אין רשת או שהעדכון לא מסיים לרדת בזמן, תיפתח במקומה הגרסה השמורה; הורדה שבתהליך ממשיכה לטעינה הבאה.",
"update.release_page": "פתיחת דף הגרסה",
@@ -3920,6 +4088,7 @@
"values must be finite numbers": "הערכים חייבים להיות מספרים סופיים",
"values must be numbers or expressions": "הערכים חייבים להיות מספרים או ביטויים",
"view.auto_ortho_faces": "פרספקטיבה בזמן סיבוב, אורתוגרפי על פאות",
"view.render_inspector": "מפקח רינדור",
"web.files.insert_drawings_hint": "קובצי CAD, רשתות משולשים, DXF או SVG אל התכן הפתוח",
"web.files.insert_stored_drawings_hint": "אל התכן הפתוח (קובצי CAD, רשתות משולשים, DXF, SVG)",
"web.open_full": "פתיחה ב־SolidCraft",
+367
View File
@@ -1,4 +1,5 @@
{
"(active rule)": "(aktív szabály)",
"0 turns it off": "A 0 kikapcsolja",
"1. Open GitHub draft below, then paste the report copied to your clipboard.": "1. Nyissa meg az alábbi GitHub-vázlatot, majd illessze be a vágólapra másolt jelentést.",
"1. Open GitHub draft below. Your title and report will be filled in.": "1. Nyissa meg az alábbi GitHub-vázlatot. A cím és a jelentés előre ki lesz töltve.",
@@ -52,16 +53,20 @@
"Apply your appearance first. Exports assigned appearances and materials created in this design.": "Először alkalmazza a megjelenést. Exportálja a hozzárendelt megjelenéseket és az ebben a tervben létrehozott anyagokat.",
"Arc length": "Ívhossz",
"Area": "Terület",
"As Picked": "Kiválasztás szerint",
"As-Built": "Beépített állapot",
"Assign Rule": "Szabály hozzárendelése",
"Attachment": "Rögzítés",
"BLEND SURFACE": "ÁTMENETI FELÜLET",
"BOUNDARY SURFACE": "HATÁRFELÜLET",
"BROWSER": "BÖNGÉSZŐ",
"Ball": "Gömbcsukló",
"Bar": "Rúd",
"Base Center": "Alap középpontja",
"Base Height": "Alapmagasság",
"Bend": "Hajlítás",
"Bend Edges": "Hajlítási élek",
"Bend Line": "Hajlítási vonal",
"Bend Line Position": "Hajlítóvonal helyzete",
"Bend Position": "Hajlítás helyzete",
"Bend Radius": "Hajlítási sugár",
@@ -73,6 +78,7 @@
"Body (last if none)": "Test (ha nincs, az utolsó)",
"Body to split": "Felosztandó test",
"Body, or faces to take out": "Test vagy eltávolítandó lapok",
"Both": "Mindkettő",
"Bottom Left": "Bal alsó",
"Bottom Middle": "Alsó közép",
"Bottom Right": "Jobb alsó",
@@ -104,6 +110,7 @@
"Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.": "Válasszon új síkot. A vázlatgeometria és a méretek változatlanok maradnak a sík kétdimenziós koordinátáiban.",
"Choose…": "Válasszon…",
"Circle center": "Kör középpontja",
"Circle or sphere": "Kör vagy gömb",
"Circular": "Kör alakú",
"Circumference": "Kerület",
"Clockwise": "Óramutató járásával megegyező",
@@ -120,6 +127,7 @@
"Config Table": "Konfigurációs táblázat",
"Configuration": "Konfiguráció",
"Configuration rules": "Konfigurációs szabályok",
"Constant radius": "Állandó sugár",
"Contact": "Érintkezés",
"Contact Sets": "Érintkezési készletek",
"Contact sets": "Érintkezési készletek",
@@ -155,7 +163,9 @@
"Custom": "Egyéni",
"Customize Toolbar": "Eszköztár testreszabása",
"Customize…": "Testreszabás…",
"Cylindrical": "Hengeres",
"Cylindrical face": "Hengeres lap",
"Deboss": "Mélynyomás",
"Delete": "Törlés",
"Delete Folders and Features…": "Mappák és elemek törlése…",
"Delete Selected Features…": "Kijelölt elemek törlése…",
@@ -190,6 +200,7 @@
"Duration": "Időtartam",
"Each new shape is also dimensioned from the sketch origin until nothing of it is left free. A shape already attached to the origin or to placed geometry gets none.": "Minden új alakzat méretet kap a vázlat origójától is, amíg semmi sem marad szabadon. Az origóhoz vagy elhelyezett geometriához már kötött alakzat nem kap.",
"Each shape you draw gets all its dimensions, at the sizes you drew (typed values win). A size the sketch already sets is left out.": "Minden megrajzolt alakzat megkapja az összes méretét a rajzolt méretekkel (a beírt értékek elsőbbséget élveznek). A vázlat által már meghatározott méret kimarad.",
"Edge": "Él",
"Edges": "Élek",
"Edit": "Szerkesztés",
"Edit Profile Sketch": "Profilvázlat szerkesztése",
@@ -198,6 +209,7 @@
"End Radius": "Végi sugár",
"End bulge": "Végi ívelés",
"End continuity": "Végi folytonosság",
"Equal Distance": "Egyenlő távolság",
"Expand": "Kibontás",
"Expected result (optional)": "Várt eredmény (opcionális)",
"Explode": "Szétszedés",
@@ -225,6 +237,8 @@
"Faces": "Lapok",
"Faces / edges / vertices": "Lapok / élek / csúcsok",
"Faces to split": "Felosztandó lapok",
"Faces/Body": "Lapok/test",
"Factor must be a positive number": "A tényezőnek pozitív számnak kell lennie",
"Fastener": "Kötőelem",
"Favorites": "Kedvencek",
"Favourite": "Kedvenc",
@@ -234,18 +248,25 @@
"Filter…": "Szűrő…",
"Find in Browser": "Keresés a böngészőben",
"Finish": "Befejezés",
"First edge": "Első él",
"First plane": "Első sík",
"First point": "Első pont",
"Fit and G2 corners require Constant radius.": "Az Illesztéshez és a G2 sarkokhoz Állandó sugár szükséges.",
"Fixed Face": "Rögzített lap",
"Flange Gap": "Peremrés",
"Flange thickness": "Perem vastagsága",
"Flanges": "Peremek",
"Flat Pattern": "Lapminta",
"Flat size": "Kiterített méret",
"Flat size X": "Kiterített méret X",
"Flat size Y": "Kiterített méret Y",
"Follow camera (Look At sketches)": "Kamera követése (Nézet-vázlatok)",
"Follow the camera (Look At sketches)": "A kamera követése (Nézet-vázlatok)",
"Font": "Betűtípus",
"Free Move": "Szabad mozgatás",
"From": "Ettől",
"From Bodies": "Testekből",
"From Snap": "Illesztés innen",
"From edges": "Élekből",
"Full Length": "Teljes hossz",
"Full thread depth": "Teljes menetmélység",
@@ -257,10 +278,13 @@
"Halo under sketch curves": "Fényudvar a vázlatgörbék alatt",
"Height": "Magasság",
"Height From": "Magasság kezdete",
"Hem Gap": "Visszahajtás rése",
"Hidden edges": "Rejtett élek",
"History layout": "Előzmények elrendezése",
"History names": "Előzmények nevei",
"Hole (optional)": "Furat (opcionális)",
"Hole Depth": "Furat mélysége",
"Hole Diameter": "Furat átmérője",
"Holes": "Furatok",
"Holes (edges on them)": "Furatok (rajtuk lévő élek)",
"Home view": "Kezdőnézet",
@@ -283,12 +307,16 @@
"Issue title": "Hibajegy címe",
"Items": "Elemek",
"JOINTS": "CSUKLÓK",
"Joint 1": "1. csukló",
"Joint 2": "2. csukló",
"Joint Origin1": "Csuklóeredet1",
"K Factor": "K-tényező",
"Keep (points on it)": "Megtartás (rajta lévő pontok)",
"Keep Tools": "Szerszámok megtartása",
"Keep of the other surface": "A másik felület megtartása",
"Key a joint's value at this step": "Csukló értékének rögzítése kulcsként ennél a lépésnél",
"Keys": "Kulcsok",
"Knife Edge": "Késél",
"L-Angle": "L-profil",
"LIBRARY": "KÖNYVTÁR",
"LINKS": "HIVATKOZÁSOK",
@@ -301,9 +329,11 @@
"Letter Spacing": "Betűköz",
"Library": "Könyvtár",
"Library file": "Könyvtárfájl",
"Library response is missing": "A könyvtár válasza hiányzik",
"Limits": "Korlátok",
"Line Spacing": "Sortávolság",
"Line Type": "Vonaltípus",
"Line on plane": "Vonal a síkban",
"Lines": "Vonalak",
"Listing the installed fonts…": "A telepített betűtípusok listázása…",
"Load library": "Könyvtár betöltése",
@@ -323,11 +353,14 @@
"Material Outside": "Anyag kívül",
"Material copied. Existing body assignments are unchanged.": "Az anyag átmásolva. A meglévő testhozzárendelések nem változtak.",
"Max": "Max",
"Max Thickness": "Legnagyobb vastagság",
"Maximum": "Maximum",
"Mesh body": "Hálótest",
"Mid Radius": "Középső sugár",
"Middle double-click": "Középső dupla kattintás",
"Min": "Min",
"Min Draft": "Legkisebb dőlés",
"Min Thickness": "Legkisebb vastagság",
"Minimum": "Minimum",
"Mirror Plane": "Tükrözési sík",
"Mitre": "Gérvágás",
@@ -347,6 +380,7 @@
"Name": "Név",
"Name on import": "Név importáláskor",
"Nearest": "Legközelebbi",
"Neutral plane": "Semleges sík",
"New": "Új",
"New Comp.": "Új komp.",
"New Sketch": "Új vázlat",
@@ -357,6 +391,7 @@
"No matching appearances": "Nincs egyező megjelenés",
"No matching physical materials.": "Nincs egyező fizikai anyag.",
"No visible bodies or sketches to export.": "Nincs exportálható látható test vagy vázlat.",
"Nominal Radius": "Névleges sugár",
"Nominal diameter": "Névleges átmérő",
"None": "Nincs",
"Not available yet": "Még nem érhető el",
@@ -385,22 +420,28 @@
"Overall length": "Teljes hossz",
"Param. Graph": "Param. gráf",
"Parameter": "Paraméter",
"Part length must be greater than zero and at most 2000 mm": "Az alkatrész hosszának nagyobbnak kell lennie nullánál, és legfeljebb 2000 mm lehet",
"Parts": "Alkatrészek",
"Pass through": "Átvezetés",
"Path": "Útvonal",
"Pattern": "Minta",
"Per Edge": "Élenként",
"Physical material definitions": "Fizikai anyagdefiníciók",
"Pick a part size": "Válasszon alkatrészméretet",
"Pick the tool bodies": "Válassza ki a szerszámtesteket",
"Picked": "Kiválasztva",
"Picked point": "Kiválasztott pont",
"Pin-slot": "Csap-horony",
"Pitch": "Emelkedés",
"Pivot": "Forgáspont",
"Place at the origin instead": "Helyezze inkább az origóba",
"Planar": "Síkbeli",
"Plastic Rules": "Műanyag szabályok",
"Play the joint's motion in the view": "A csukló mozgásának lejátszása a nézetben",
"Playback": "Lejátszás",
"Plot": "Diagram",
"Point on line": "Pont a vonalon",
"Point on plane": "Pont a síkon",
"Position": "Pozíció",
"Position (0 to 1)": "Pozíció (0-tól 1-ig)",
"Position (G0)": "Pozíció (G0)",
@@ -413,6 +454,7 @@
"Profiles or curves": "Profilok vagy görbék",
"Profiles/Curves": "Profilok/Görbék",
"Quantity": "Mennyiség",
"Quantity must be at least 2": "A mennyiségnek legalább 2-nek kell lennie",
"RECENT": "LEGUTÓBBI",
"REFERENCE": "REFERENCIA",
"RULED SURFACE": "VONALAZOTT FELÜLET",
@@ -426,6 +468,8 @@
"Rectangular": "Téglalap alakú",
"Rectangular Tube": "Téglalap alakú cső",
"Reference face": "Referencialap",
"Relief Depth": "Tehermentesítés mélysége",
"Relief Width": "Tehermentesítés szélessége",
"Remove Tools": "Szerszámok eltávolítása",
"Remove condition": "Feltétel eltávolítása",
"Remove from Folder": "Eltávolítás a mappából",
@@ -454,6 +498,7 @@
"Restore this studio's tools": "A studio szerszámainak visszaállítása",
"Result": "Eredmény",
"Retake screenshot": "Képernyőkép újrakészítése",
"Reveal Height": "Kilátszó magasság",
"Reverse": "Megfordítás",
"Reverse Direction": "Irány megfordítása",
"Revert": "Visszaállítás",
@@ -462,12 +507,15 @@
"Revert Position": "Pozíció visszaállítása",
"Review the report and screenshot before opening GitHub.": "Nyitás előtt tekintse át a jelentést és a képernyőképet.",
"Review your report on GitHub and click Submit new issue to publish it.": "Nézze át a jelentést a GitHubon, és a közzétételhez kattintson a „Submit new issue” gombra.",
"Revolute": "Forgó",
"Rib Length": "Borda hossza",
"Rib Offset": "Borda eltolása",
"Rib Thickness": "Borda vastagsága",
"Ribs": "Bordák",
"Right Middle": "Jobb közép",
"Rigid": "Merev",
"Rim Edge": "Peremél",
"Rim face": "Peremlap",
"Ring of tools on the right button": "Szerszámgyűrű a jobb gombon",
"Roll Back Here": "Visszagörgetés ide",
"Roll History Marker Here": "Előzménymarker ide görgetése",
@@ -492,7 +540,9 @@
"Search": "Keresés",
"Search commands": "Parancsok keresése",
"Search this library": "Keresés ebben a könyvtárban",
"Second edge": "Második él",
"Second plane": "Második sík",
"Second point": "Második pont",
"Section": "Metszet",
"Section (optional)": "Keresztmetszet (nem kötelező)",
"Section position": "Szelvény helyzete",
@@ -503,6 +553,15 @@
"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",
"Select edges": "Élek kijelölése",
"Select points of the sketch being edited": "Jelölje ki a szerkesztett vázlat pontjait",
"Select the centre the pattern turns around first": "Először jelölje ki a középpontot, amely körül a minta forog",
"Select the curves to offset first": "Először jelölje ki az eltolandó görbéket",
"Select the mirror line": "Jelölje ki a tükörvonalat",
"Select the sketch curves or points to mirror first": "Először jelölje ki a tükrözendő vázlatgörbéket vagy pontokat",
"Select the sketch curves or points to move first": "Először jelölje ki a mozgatandó vázlatgörbéket vagy pontokat",
"Select the sketch curves or points to pattern first": "Először jelölje ki a mintában ismétlendő vázlatgörbéket vagy pontokat",
"Select the sketch curves or points to scale first": "Először jelölje ki az átléptékezendő vázlatgörbéket vagy pontokat",
"Select the sketch curves or points to transform first": "Először jelölje ki a transzformálandó vázlatgörbéket vagy pontokat",
"Selection filter": "Kijelölési szűrő",
"Semantic faces share their appearance across instances.": "A szemantikus lapok megjelenése közös a példányok között.",
"Send your report on GitHub": "Jelentés küldése a GitHubon",
@@ -522,9 +581,12 @@
"Sketch geometry and profiles": "Vázlatgeometria és profilok",
"Sketch plane has no body-face attachment.": "A vázlatsíknak nincs testlap-rögzítése.",
"Sketch points": "Vázlatpontok",
"Slider": "Csúszó",
"Slot width": "Horony szélessége",
"Snap to face and edge centres": "Illesztés a lapok és élek középpontjához",
"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ó.",
"Spacing 2 must be a non-zero number": "A 2. térköznek nullától eltérő számnak kell lennie",
"Spacing must be a non-zero number": "A térköznek nullától eltérő számnak kell lennie",
"Specialise": "Specializálás",
"Speed": "Sebesség",
"Splitting tool": "Felosztó szerszám",
@@ -535,6 +597,7 @@
"Start angle": "Kezdőszög",
"Start bulge": "Kezdő ívelés",
"Start continuity": "Kezdő folytonosság",
"Stationary Side": "Álló oldal",
"Step": "Lépés",
"Steps": "Lépések",
"Steps to reproduce (optional)": "A hiba reprodukálásának lépései (nem kötelező)",
@@ -549,6 +612,7 @@
"Suppressed": "Elnyomva",
"Surface": "Felület",
"Surface, or faces to turn over": "Felület vagy megfordítandó lapok",
"Surfaces": "Felületek",
"Swap Ends": "Végek cseréje",
"TRIM": "VÁGÁS",
"Tangent": "Érintő",
@@ -559,24 +623,34 @@
"Tap drill diameter": "Magfúró átmérője",
"Target": "Cél",
"Target Body": "Céltest",
"Target Face (optional)": "Céllap (opcionális)",
"Tear": "Szakítás",
"Technical details": "Műszaki részletek",
"The dashed sketch is a saved plane guide. Select a new face to repair the attachment.": "A szaggatott vázlat egy mentett síkvezető. A rögzítés javításához jelöljön ki új lapot.",
"The design changed. Reopen Change Part Size for the component.": "A terv megváltozott. Nyissa meg újra az Alkatrészméret módosítása parancsot a komponenshez.",
"The direction must be an axis or a straight sketch curve that lies in the sketch plane": "Az iránynak tengelynek vagy a vázlat síkjában fekvő egyenes vázlatgörbének kell lennie",
"The mirror line cannot also be mirrored": "A tükörvonalat nem lehet tükrözni",
"The mirror line must be a straight curve of this sketch, not an origin axis": "A tükörvonalnak ennek a vázlatnak egyenes görbéjének kell lennie, nem origótengelynek",
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "A mentett szerzői előfordulás hiányzik. Újrakötés előtt jelöljön ki érvényes szerzői elhelyezést.",
"Then": "Majd",
"These features will be deleted:": "Ezek az elemek törlődnek:",
"These features will fail:": "Ezek az elemek sikertelenek lesznek:",
"Thickness Outside": "Vastagság kívül",
"Thickness Variation": "Vastagságeltérés",
"Thin": "Vékony",
"Thin Extrude": "Vékony kihúzás",
"Third plane": "Harmadik sík",
"Third point": "Harmadik pont",
"This child is shared. Deleting it removes it from every copy of its parent.": "Ez a gyermekelem közös. Törlése eltávolítja a szülő minden másolatából.",
"This child is shared. Position changes apply to every copy of its parent.": "Ez a gyermekelem közös. A pozícióváltozások a szülő minden másolatára érvényesek.",
"This face cannot provide a durable attachment. Choose another face or explicitly use a fixed plane.": "Ez a lap nem biztosít tartós rögzítést. Válasszon másik lapot, vagy használjon kifejezetten rögzített síkot.",
"This image is not attached to GitHub yet. Copy or save it, then attach it on GitHub.": "Ez a kép még nincs csatolva a GitHubhoz. Másolja vagy mentse el, majd csatolja a GitHubon.",
"Thread depth": "Menetmélység",
"Thread diameter": "Menetátmérő",
"Through point": "Ponton át",
"To": "Eddig",
"To Offset": "Eltolásig",
"To Snap": "Illesztés ide",
"To edges": "Élekig",
"Toe radius": "Élkerekítési sugár",
"Tolerance": "Tűrés",
@@ -590,6 +664,7 @@
"Trimming tool (plane, face or curves)": "Vágószerszám (sík, lap vagy görbék)",
"Turn off section analysis before exporting the view.": "A nézet exportálása előtt kapcsolja ki a metszetelemzést.",
"Type": "Típus",
"Type a distance or an angle to move by": "Adja meg a mozgatás távolságát vagy szögét",
"UNTRIM": "VÁGÁS VISSZAVONÁSA",
"Unit": "Egység",
"Unsuppress": "Elnyomás megszüntetése",
@@ -634,6 +709,17 @@
"Z angle": "Z szög",
"Z distance": "Z távolság",
"Zebra": "Zebra",
"`joint` must be an id or a name": "A `joint` értékének azonosítónak vagy névnek kell lennie",
"`params` must be an object": "A `params` értékének objektumnak kell lennie",
"`values` must be a list": "A `values` értékének listának kell lennie",
"a chamfer needs a corner of exactly two lines": "a letöréshez pontosan két vonalból álló sarok kell",
"a file operation is in progress": "fájlművelet van folyamatban",
"a picked curve is no longer in a sketch": "a kiválasztott görbe már nincs vázlatban",
"a picked curve's sketch has not been solved": "a kiválasztott görbe vázlata nincs megoldva",
"a picked edge has no points": "a kiválasztott élnek nincsenek pontjai",
"a picked edge is gone": "a kiválasztott él már nem létezik",
"a picked edge's body is gone": "a kiválasztott él teste már nem létezik",
"add a key for a joint first": "előbb adjon hozzá kulcsot egy csuklóhoz",
"ai.connected_toast": "A SolidCraft mostantól elérhető itt: {clients}. A használatához indítsa újra őket (Beállítások > Asszisztens).",
"ai.prefs.allow_control": "AI-ügynökök csatlakozásának engedélyezése (vezérlőcsatorna / MCP)",
"ai.prefs.allow_control_note": "Csak ezen a számítógépen nyit vezérlőcsatornát (127.0.0.1, a 27878-as port vagy a következő szabad). A csatlakozó programnak ezenkívül fel kell mutatnia egy titkos tokent, amelyet csak az Ön felhasználói fiókja olvashat, és amely minden indításkor újra létrejön. A SolidCraft következő indításakor lép érvénybe. Kikapcsolva: semmi sem figyel, hacsak nem a --control kapcsolóval indítja a SolidCraftot.",
@@ -674,6 +760,8 @@
"appearance.pbr_opaque": "Átlátszatlan PBR",
"appearance.roughness": "Érdesség",
"appearance.surface_response": "Felületi válasz",
"at the face": "a lapnál",
"bom.group_children": "Részalkatrészek az alkatrészük sorában",
"browser.body_name": "Test{number}",
"browser.calibrate_title": "Kalibrálás: {name}",
"browser.capture_many": "{count} komponens el lett mozgatva, de a pozíciójuk nincs rögzítve.",
@@ -684,6 +772,8 @@
"browser.move_title": "Mozgatás: {name}",
"browser.parameters": "Paraméterek ({count})",
"browser.precision": "Pontosság: {precision}",
"browser.sketch_attachment_lost": "{name} (rögzítés elveszett)",
"browser.sketch_editing": "{name} (szerkesztés alatt)",
"browser.sketch_name": "Vázlat{number}",
"browser.units": "Egységek: {units}",
"cam.flat": "Sík lap",
@@ -693,7 +783,13 @@
"cam.roller": "Görgő",
"cam.roller_pick": "Görgő (opcionális)",
"cam.tip_pick": "Csúcs (opcionális)",
"choose a profile": "válasszon profilt",
"choose the cam's and the follower's joints": "válassza ki a bütyök és a követő csuklóit",
"choose the joints to couple": "válassza ki az összekapcsolandó csuklókat",
"click a plane or planar face": "kattintson egy síkra vagy sík lapra",
"click sketch curves": "kattintson vázlatgörbékre",
"cmd.appearance_assign": "Megjelenés",
"cmd.appearance_chain": "Megjelenéslánc",
"cmd.appearance_library": "Megjelenéstár",
"cmd.appearance_list": "Megjelenések listázása",
"cmd.appearance_paint": "Megjelenés festése",
@@ -723,7 +819,9 @@
"cmd.component_create": "Új komponens",
"cmd.component_cycle_color": "Komponens színének váltogatása",
"cmd.component_delete": "Komponens törlése",
"cmd.component_edit_in_place": "Szerkesztés helyben",
"cmd.component_extract": "Kivonás fájlba",
"cmd.component_finish_edit": "Szerkesztés befejezése",
"cmd.component_from_bodies": "Komponensek létrehozása testekből",
"cmd.component_insert_design": "Terv beszúrása",
"cmd.component_links": "Kapcsolatok listázása",
@@ -1031,6 +1129,7 @@
"cmd.sketch_spline_control_point_5": "Vezérlőpontos spline (5. fokú)",
"cmd.sketch_spline_fit_point": "Illesztőpontos spline",
"cmd.sketch_spline_insert_point": "Splinepont hozzáadása",
"cmd.sketch_spline_reverse": "Spline irányának megfordítása",
"cmd.sketch_spun_profile": "Forgatott profil",
"cmd.sketch_toggle_diameter": "Sugár / átmérő váltása",
"cmd.sketch_toggle_driven": "Vezérelt méret váltása",
@@ -1082,6 +1181,7 @@
"cmd.view.render_dock": "Renderpanel áthelyezése",
"cmd.view.render_focus": "Renderfókusz nézet",
"cmd.view_ambient_occlusion": "Környezeti takarás",
"cmd.view_angle_readout": "Nézetszögek",
"cmd.view_apply": "Elnevezett nézet visszaállítása",
"cmd.view_back": "Hátulnézet",
"cmd.view_bottom": "Alulnézet",
@@ -1191,6 +1291,8 @@
"dxf.progress.write": "DXF írása (5/5)",
"dxf.tolerance": "Görbetűrés",
"dxf.tolerance_help": "Csak a látható testek. A görbék egyenes szakaszokból állnak. Kisebb tűrés finomabb görbéket ad, de több időt és memóriát igényel.",
"each rail's curves must be in one sketch": "minden vezérgörbe görbéinek ugyanabban a vázlatban kell lenniük",
"feature computation ended without a result": "az elem számítása eredmény nélkül ért véget",
"file.open.drawing_filter": "Tervek, CAD-fájlok, hálók és rajzok",
"file_load.built": "Eddig felépítve: {built}/{total} elem.",
"file_load.converting": "Szilárd test átalakítása: {done}/{total}",
@@ -1214,6 +1316,7 @@
"help.appearance.assign": "A kijelölt testeknek, lapoknak vagy komponenseknek színt vagy anyagjellegű megjelenést ad, a tömegük megváltoztatása nélkül. Használja az alkatrészek megkülönböztetésére vagy egy felület előnézetéhez.",
"help.attributions": "Forrásmegjelölések (ATTRIBUTION.md)",
"help.build": "{version} verzió · commit: {commit} · készült: {date}",
"help.component.finish_edit": "Elmenti a helyben szerkesztett alkatrészt, visszatér az összeállításhoz, és frissíti azt az alkatrész alapján. Akkor használja, ha végzett a kapcsolt alkatrész módosításaival.",
"help.connect_note": "Indítsa újra az egyes asszisztenseket a SolidCraft szerver betöltéséhez. Minden fájlról .bak biztonsági másolat készült a módosítás előtt.",
"help.connect_title": "AI-asszisztensek csatlakoztatása",
"help.face.delete": "Eltávolítja a kijelölt lapokat, és hagyja, hogy a szomszédos lapok bezárják a rést, vagy felületként nyitva hagyja a rést. Használja egy útban lévő lekerekített él, furat vagy dudor eltávolítására.",
@@ -1438,7 +1541,9 @@
"nav.free_orbit_upright.help": "Az oldalirányú egérhúzás a modellt a világ függőleges tengelye körül forgatja; a fel-le húzás korlátlanul átbillenti a tetején át.",
"nav.free_orbit_upright.label": "Forgás a függőleges tengely körül",
"nav.fusion.help": "Középső húzás: pásztázás, Shift+középső húzás: keringés, jobb húzás: keringés",
"nav.hfss.help": "Alt+bal húzás: pásztázás, Shift+Alt+bal húzás: nagyítás, Ctrl+bal húzás üres területen: keringés",
"nav.inventor.help": "Középső húzás: pásztázás, Shift+középső húzás: keringés, Ctrl+középső húzás: nagyítás, jobb húzás: keringés",
"nav.maya.help": "Alt+bal húzás: keringés, Alt+középső húzás: pásztázás, Alt+jobb húzás: nagyítás; a középső húzás is pásztáz",
"nav.openscad.help": "Bal húzás üres helyen: keringés, Shift+bal húzás: kijelölés kerettel, a vázlatgeometria húzása szerkeszt; jobb vagy középső húzás: pásztázás, Shift+jobb vagy Shift+középső húzás: nagyítás",
"nav.perspective.sketch": "A vázlatszerkesztés ortografikus nézetet használ. A perspektívára váltáshoz fejezze be a vázlatot.",
"nav.solidworks.help": "Középső húzás: keringés, Ctrl+középső húzás: pásztázás, Shift+középső húzás: nagyítás, jobb húzás: keringés",
@@ -1546,12 +1651,34 @@
"no hole": "nincs furat",
"no interference": "nincs interferencia",
"no joint that moves yet": "még nincs mozgó csukló",
"no motion study yet": "még nincs mozgásvizsgálat",
"no picture": "nincs kép",
"no such joint": "nincs ilyen csukló",
"none": "nincs",
"not a move": "nem mozgatás",
"not a number": "nem szám",
"open a sketch first": "előbb nyisson meg egy vázlatot",
"parts.automatic_name": "automatikus",
"parts.dimension.across_flats": "Kulcsnyílás",
"parts.dimension.bore": "Furat",
"parts.dimension.d": "Névleges átmérő",
"parts.dimension.head_diameter": "Fejátmérő",
"parts.dimension.head_height": "Fejmagasság",
"parts.dimension.height": "Magasság",
"parts.dimension.inner": "Belső átmérő",
"parts.dimension.outer": "Külső átmérő",
"parts.dimension.pitch": "Menetemelkedés",
"parts.dimension.socket": "Belső kulcsnyílás",
"parts.dimension.socket_depth": "Belső kulcsnyílás mélysége",
"parts.dimension.thickness": "Vastagság",
"parts.dimension.width": "Szélesség",
"parts.family.bearing": "Mélyhornyú golyóscsapágy (befoglaló méret)",
"parts.family.dowel_pin": "Hengeres illesztőcsap",
"parts.family.hex_bolt": "Hatlapfejű csavar",
"parts.family.hex_nut": "Hatlapú anya",
"parts.family.set_screw": "Belső kulcsnyílású szárnélküli csavar, lapos végű",
"parts.family.socket_head_cap_screw": "Belső kulcsnyílású hengeresfejű csavar",
"parts.family.washer": "Sima alátét",
"pattern.alignment_2": "2. igazítás",
"pattern.alignment_hint": "A darabszám tartalmazza az eredetit. A Közép kiegyensúlyozza a másolatokat; a Megfordítás páros összegnél kijelöli a többletoldalt.",
"pattern.direction_1": "1. irány",
@@ -1567,9 +1694,25 @@
"perf.ui_time": "Felület ideje",
"perf.viewport": "Nézetablak",
"perf.worst": "legrosszabb",
"pick a body or some of its faces": "válasszon ki egy testet vagy néhány lapját",
"pick a hole to seat it, or OK places it at the origin": "jelöljön ki egy furatot a beültetéshez, vagy az OK az origóba helyezi",
"pick a planar face": "válasszon ki egy sík lapot",
"pick a plane or a face as the tool": "válasszon ki egy síkot vagy egy lapot szerszámként",
"pick a size": "válasszon méretet",
"pick edges of a body": "válassza ki egy test éleit",
"pick edges of one body": "válassza ki egyetlen test éleit",
"pick edges of the holes to remove": "válassza ki az eltávolítandó furatok éleit",
"pick faces of one body": "válassza ki egyetlen test lapjait",
"pick faces to offset": "válassza ki az eltolandó lapokat",
"pick lines of one sketch": "válassza ki egyetlen vázlat vonalait",
"pick one or two vertices, edges or faces": "jelöljön ki egy vagy két csúcsot, élt vagy lapot",
"pick one plane or face as the tool, or sketch curves": "válasszon ki egy síkot vagy lapot szerszámként, vagy vázlatgörbéket",
"pick sections in two or more sketches": "válasszon ki keresztmetszeteket két vagy több vázlatban",
"pick sketch curves or edges": "válasszon ki vázlatgörbéket vagy éleket",
"pick the face to untrim": "válassza ki a lapot a vágás visszavonásához",
"pick the path": "válassza ki az útvonalat",
"pick what to keep": "válassza ki, mit kíván megtartani",
"pick what to keep of the other surface": "válassza ki, mit kíván megtartani a másik felületből",
"prefs.add_item": "Az elem hozzáadása",
"prefs.alt_frees_manipulators": "Az Alt / Option a manipulátornyilakat is felszabadítja",
"prefs.anti_aliasing_amd_dx12": "Az élsimítás 1×-es beállítást használ, mert az indítási ellenőrzés többmintás mintavétellel semmit sem rajzolt, 1×-esnél viszont sikeres volt. Fent másik beállítást is választhat; ha az ellenőrzés sikeres, az alkalmazáshoz indítsa újra a programot.",
@@ -1651,8 +1794,19 @@
"prefs.layout_note": "A Kompakt a paneleket az ablak széleihez rögzíti. A Böngésző áttetszősége Lebegő módban érhető el. Az Asszisztens oldalát az Asszisztens oldalon módosíthatja.",
"prefs.left": "Bal",
"prefs.left_click": "Bal kattintás",
"prefs.linked_designs": "Kapcsolt tervek",
"prefs.live_input": "Élő bevitel",
"prefs.live_preview": "Élő előnézet",
"prefs.logo": "Logó",
"prefs.logo_accent": "Kiemelőszín",
"prefs.logo_theme": "A téma színei",
"prefs.max_file_size": "Megnyitható fájl legnagyobb mérete",
"prefs.max_file_size_design": "Tervfájlok",
"prefs.max_file_size_flag": "Ez a futtatás a --max-file-size {size} kapcsolóval indult, amely a SolidCraft bezárásáig érvényes. Ezek a beállítások a kapcsoló nélküli indításkor érvényesek.",
"prefs.max_file_size_note": "Alapértékek: 512 MB és 256 MB; legfeljebb 16 GB. Egy fájl megnyitásához a mérete többszörösének megfelelő memória kell. Nagy importot tároló terv mentéséhez és újbóli megnyitásához magasabb tervfájlkorlát lehet szükséges.",
"prefs.max_file_size_step": "STEP- és importfájlok",
"prefs.max_file_size_web": "A webalkalmazás legfeljebb 512 MB-os STEP- és importfájlokat, valamint 256 MB-os terveket nyit meg: egy böngészőlap nem tud nagyobb fájlokat tárolni. Az asztali alkalmazásban e korlátok növelhetők.",
"prefs.megabytes": "MB",
"prefs.minutes": "perc",
"prefs.minutes_suffix": " perc",
"prefs.mouse": "Egér és érintőpad",
@@ -1701,11 +1855,17 @@
"prefs.sketch_selection": "Vázlat és kijelölés",
"prefs.sketch_selection_heading": "Vázlat és kijelölés",
"prefs.sketch_selection_note": "Hogyan illeszkednek a vázlatok, és hogyan jelölnek ki a kattintások.",
"prefs.sketch_tab_always": "A Vázlat fül mindig látható",
"prefs.sketch_tab_always_note": "A Vázlat fül a szalagon marad, közvetlenül a Modell után, akkor is, ha nincs megnyitott vázlat. Rajzszerszámai ilyenkor a kijelölt síkon vagy lapon, illetve a következőként kiválasztott síkon indítanak vázlatot, benne a kiválasztott szerszámmal.",
"prefs.sketch_tracking_guides": "Középpont- és felezőpont-igazítási segédvonalak",
"prefs.sketch_tracking_guides_note": "Vigye az egérmutatót egy kör középpontja vagy egy egyenes él felezőpontja fölé, majd mozgassa vízszintesen vagy függőlegesen, hogy ideiglenes pontozott segédvonal jelenjen meg.",
"prefs.sketching": "Vázlatkészítés",
"prefs.snap_drawing_note": "Vázlatpont rajzolása vagy húzása közben.",
"prefs.source": "Forrás",
"prefs.speed": "Sebesség",
"prefs.stand_up_note": "Miután egy STEP- vagy IGES-fájl Z felfelé tájolással megnyílik, negyedfordulat felajánlása az X körül.",
"prefs.status_bar": "Állapotsor",
"prefs.status_bar_note": "Az ablak alsó szélén lévő vékony sáv megmutatja, hogy mit szerkeszt, a megnyitott vázlat állapotát a színével, valamint a terv egységeit; az egységekre kattintva módosíthatók. A vázlat állapota ilyenkor már nem jelenik meg a nézet sarkában.",
"prefs.status_done": "Kész",
"prefs.status_error": "Hiba",
"prefs.status_selected": "Kijelölve",
@@ -1713,12 +1873,16 @@
"prefs.supersampling_brief": "A nézetet nagyobbra rajzolja, majd leszűri: nagyon éles, de erőforrásigényesebb.",
"prefs.tab_accent_note": "A hangsúlyszín az aktív fület követi",
"prefs.theme_file": "témafájl",
"prefs.theme_file_or_paste": "fájl vagy beillesztés",
"prefs.theme_follow": "Témafájl követése",
"prefs.theme_follow_error": "Nem alkalmazható: {error}. Az utolsó érvényes téma marad aktív.",
"prefs.theme_follow_launch": "Követés csak erre a futásra (--theme-file); újratöltés, ha más eszköz felülírja a fájlt.",
"prefs.theme_follow_note": "Újratöltés, ha más eszköz felülírja a fájlt. Téma választása leállítja a követést.",
"prefs.theme_follow_off": "Ki",
"prefs.theme_follow_stop": "Követés leállítása",
"prefs.theme_import_hint": "Kattintson egy témafájl kiválasztásához, húzzon ide egyet, vagy nyomja meg a Ctrl+V (Macen Cmd+V) billentyűkombinációt egy kimásolt témával",
"prefs.theme_paste": "Téma beillesztése",
"prefs.theme_paste_none": "A vágólap nem tartalmaz SolidCraft-témát",
"prefs.timeline_half_hour": "−30 perc",
"prefs.timeline_now": "most",
"prefs.timeline_quarter_hour": "−15",
@@ -1784,7 +1948,47 @@
"render.aging_upward": "Felfelé néző lerakódás",
"render.all_controls_help": "Az összes jelenlegi vezérlő részletes áttekintése. A szakaszoldalak fókuszált szerkesztést tesznek lehetővé, a mögöttes kérés módosítása nélkül.",
"render.all_shots_label": "Összes felvétel",
"render.appearance_editor.absorption": "Elnyelés",
"render.appearance_editor.base_colour": "Alapszín",
"render.appearance_editor.body_reason": "Az optikai beállítások mindig egy teljes testre vonatkoznak. A vezérlők szerkesztéséhez válassza a Test lehetőséget.",
"render.appearance_editor.boundary": "Határ",
"render.appearance_editor.box": "Doboz",
"render.appearance_editor.choose_image": "Válasszon képet…",
"render.appearance_editor.closed": "Zárt szilárd test",
"render.appearance_editor.coat": "Lakkréteg",
"render.appearance_editor.cpu_reason": "CPU-s sugárkövetést igényel.",
"render.appearance_editor.glass": "Üveg és elnyelés",
"render.appearance_editor.glass_enable": "Fizikai üveg használata ezen a testen",
"render.appearance_editor.glass_note": "A zárt üveg geometriai távolságot használ. Nem használható ugyanazon a testen lakkréteggel vagy térképekkel együtt.",
"render.appearance_editor.gpu_reason": "GPU-s útkövetést igényel.",
"render.appearance_editor.ior": "IOR",
"render.appearance_editor.maps": "Térképek",
"render.appearance_editor.normal": "Normál",
"render.appearance_editor.normal_strength": "Normál erőssége",
"render.appearance_editor.origin": "Origó",
"render.appearance_editor.recipe": "Recept",
"render.appearance_editor.reflection_sampling": "Mintavételezés az összes tükröző testhez",
"render.appearance_editor.reflections": "Tükröződések",
"render.appearance_editor.remove": "Mentett eltávolítása",
"render.appearance_editor.remove_note": "Eltávolítja a mentett optikát erről a felvételről. A geometria és a hozzárendelt stílusok megmaradnak.",
"render.appearance_editor.remove_title": "Eltávolítja a mentett optikát?",
"render.appearance_editor.replace": "A test mentett optikájának cseréje",
"render.appearance_editor.revalidate": "A test újraellenőrzése",
"render.appearance_editor.save": "Optika mentése",
"render.appearance_editor.save_reason": "Az optika mentéséhez fizikai üveg szükséges egy aktuális GPU-s testen. A lakkréteg és a helyi képtérképek ideiglenesek maradnak.",
"render.appearance_editor.saved": "A felvétellel együtt mentve",
"render.appearance_editor.saved_state": "Ez az optika a felvétellel együtt van mentve.",
"render.appearance_editor.scatter_reason": "A belső szóráshoz zárt, szilárd test határa szükséges.",
"render.appearance_editor.temporary": "Ez az optika ideiglenes ezen a fülön. Mentse el, hogy megmaradjon a felvétellel.",
"render.appearance_editor.thickness": "Vastagság",
"render.appearance_editor.thickness_reason": "A vastagság vékony lemezekre vonatkozik; a zárt üveg geometriai távolságot használ.",
"render.appearance_editor.thin": "Vékonylemez",
"render.appearance_editor.u_repeat": "U ismétlés",
"render.appearance_editor.v_repeat": "V ismétlés",
"render.appearance_editor.v_up": "A textúra V iránya felfelé mutat",
"render.appearance_editor.weight": "Súly",
"render.appearance_physics_separate": "A vizuális megjelenés nem rendel hozzá sűrűséget, hőtani adatot vagy más mérnöki tulajdonságot.",
"render.appearances": "Megjelenések",
"render.availability_native": "Natív összeállítás; alkalmazás előtt tekintse át",
"render.availability_projection": "Támogatott vetítés; lásd a hatókört és a kihagyásokat",
"render.availability_reference": "Referenciaadat; nem automatikus hozzárendelés",
@@ -1839,6 +2043,10 @@
"render.camera_set": "Renderkamera beállítása",
"render.camera_target": "Célpozíció · mm",
"render.camera_yaw": "Elfordulás",
"render.category_metal": "Fém",
"render.category_paint": "Festék",
"render.category_plastic": "Műanyag",
"render.category_wood": "Fa",
"render.chromatic_offset": "Kromatikus éleltolás",
"render.clear_stage": "Mentett díszlet eltávolítása",
"render.copy_request": "JSON másolása",
@@ -1938,6 +2146,7 @@
"render.effects_reset_lens": "Lencseeffektusok visszaállítása",
"render.effects_seed": "Mintázat kezdőértéke",
"render.emitted_axis": "Kibocsátási tengely",
"render.environment_tiles_require_raytrace": "A környezetekhez CPU-s vagy GPU-s sugárkövetés szükséges. Válassza ki a Kimenet szerkesztőben.",
"render.error_area_samples": "az area_samples értéke 1, 4, 16 vagy 64 lehet",
"render.error_binding_allocation": "nem sikerült lefoglalni az anyagkötést",
"render.error_body_bound": "a termék-előnézet túllépi a rögzített testek korlátját",
@@ -2009,6 +2218,8 @@
"render.flare_strength": "Fénycsúcsok lencsefény-erőssége",
"render.flare_threshold": "Lencsefény küszöbértéke (jelenet-lineáris)",
"render.fold_browser": "Böngésző összecsukása",
"render.frame_presets": "Képkivágási előbeállítások",
"render.frame_presets_pending": "A Felvétel szerkesztővel érkezik",
"render.gpu_depth": "Maximális útvonalmélység",
"render.gpu_options_help": "A mintaszám, a mélység, a kezdőérték és az expozíció ezzel a beállítással együtt mentődik. Csak az aktív képalkotási módszer mentődik.",
"render.gpu_path_failed": "A GPU-s útkövető nem érhető el",
@@ -2069,6 +2280,36 @@
"render.inner_half_angle": "Belső félszög",
"render.inspect_scene": "A rögzített jelenet összefoglalójának frissítése",
"render.inspector": "Vizsgáló",
"render.inspector_advanced": "Speciális",
"render.inspector_all_users": "Az összes használó test ({count})",
"render.inspector_applies_to": "Alkalmazás erre",
"render.inspector_both_users": "Mindkét használó test",
"render.inspector_chain": "Ennek a stílusnak az eredete",
"render.inspector_colour": "Szín",
"render.inspector_edit": "Szerkesztés",
"render.inspector_finish": "Felületkidolgozás",
"render.inspector_from_material": "Anyagból",
"render.inspector_gloss": "Fényes",
"render.inspector_material": "Anyag",
"render.inspector_matte": "Matt",
"render.inspector_metal": "Fém",
"render.inspector_opacity": "Átlátszatlanság",
"render.inspector_opaque_help": "A felületkidolgozás és az optikai mezők átlátszatlan PBR-t használnak (átlátszatlanság 1). Kisebb átlátszatlanság skaláris árnyalást használ.",
"render.inspector_pick_target": "Jelöljön ki egy ilyen típusú célt a megjelenésének vizsgálatához.",
"render.inspector_remove": "Felülbírálás eltávolítása",
"render.inspector_replace": "Csere…",
"render.inspector_reset": "Visszaállítás",
"render.inspector_satin": "Szatén",
"render.inspector_save_library": "Mentés a könyvtárba…",
"render.inspector_set_all": "Vegyes megjelenések. A szerkesztés az összes kijelölt célon beállítja a stílust.",
"render.inspector_soon": "Hamarosan",
"render.inspector_target_limit": "Legfeljebb 10000 célt jelöljön ki a megjelenések vizsgálatához.",
"render.inspector_textures": "Textúrák",
"render.inspector_this_body": "Ez a test",
"render.inspector_this_component": "Ez a komponens",
"render.inspector_this_face": "Ez a lap",
"render.inspector_this_target": "Ez a cél",
"render.inspector_used_by": "Használja: {count}",
"render.keep_editing": "Szerkesztés folytatása",
"render.layer_down": "Mozgatás lefelé",
"render.layer_enabled": "Réteg engedélyezve",
@@ -2080,8 +2321,12 @@
"render.lens_effects_title": "Objektívhatások és fényképi effektek",
"render.library_all": "Katalógus",
"render.library_all_states": "Minden elérhetőségi állapot",
"render.library_appearance_available": "Elérhető megjelenés",
"render.library_applicable": "Alkalmazható erőforrások",
"render.library_applied": "Az erőforrás-vetítés alkalmazva. Indítás előtt ellenőrizze a teljes renderelési kérést.",
"render.library_apply_appearance": "Megjelenés alkalmazása",
"render.library_apply_hdr": "Mentés ezzel a HDR-környezettel",
"render.library_apply_hint": "Kattintson az alkalmazáshoz · húzza a modellre",
"render.library_apply_maps": "Saját készítésű GPU-felülettérképek használata",
"render.library_apply_studio": "Saját készítésű stúdióvetítés mentése",
"render.library_apply_uniform": "Egységes, átlátszatlan megjelenés alkalmazása",
@@ -2097,15 +2342,23 @@
"render.library_extent": "Tárgy kiterjedése",
"render.library_favorite": "Hozzáadás a kedvencekhez",
"render.library_favorites": "Kedvencek",
"render.library_filters": "Katalógus és elérhetőség",
"render.library_grid": "Rács",
"render.library_hdr": "Beépített HDR-környezetek",
"render.library_hdr_help": "Az eredeti HDR-t beágyazza a mentett beállításba. A szerzői hosszúsági kezdőpontot kifejezetten átszámítja; mentés után nincs szükség külső fájlra.",
"render.library_in_design": "A tervben",
"render.library_inspect_only": "Kijelöléssel megtekinthető; semmi sem alkalmazódik automatikusan. A kedvencek és a legutóbbiak előzményei ehhez a megnyitott dokumentum-munkamenethez tartoznak.",
"render.library_ior": "IOR",
"render.library_light_concepts": "Világítási koncepciók",
"render.library_list": "Lista",
"render.library_maps_help": "A beépített térképek rögzített forrásokkal és helyi példányleképezéssel menthetők. Az ideiglenes optikai felülbírálások külön mód maradnak.",
"render.library_material_concepts": "Anyagkoncepciók",
"render.library_metallic": "Fémes",
"render.library_native_scenes": "Natív stúdiók",
"render.library_next": "Következő oldal",
"render.library_omitted": "Nincs belefoglalva",
"render.library_opacity": "Átlátszatlanság",
"render.library_other": "Egyéb megjelenések",
"render.library_physical_profiles": "Fizikai referenciaprofilok",
"render.library_previous": "Előző oldal",
"render.library_quality_concepts": "Minőségi specifikációk",
@@ -2115,6 +2368,7 @@
"render.library_results": "Egyező erőforrások",
"render.library_rig_help": "Illessze be egyszer, egy megadott középponthoz és fizikai kiterjedéshez. A teljes elrendezések passzív derítőlapokat is tartalmaznak; a kalibrálási akadályok továbbra is elutasítást okoznak. A fényforrások és derítőlapok konzervatív hézagát mentés előtt ellenőrzi.",
"render.library_rigs": "Saját készítésű világítási elrendezések",
"render.library_roughness": "Érdesség",
"render.library_save_maps": "Térképek mentése ebbe a beállításba",
"render.library_scene_bounds": "A rögzített jelenet határainak használata",
"render.library_scenes": "Jelenetspecifikációk",
@@ -2138,6 +2392,7 @@
"render.library_unfavorite": "Eltávolítás a kedvencekből",
"render.library_uniform_ack": "Egységes közelítés használata térbeli vagy speciális effektek nélkül",
"render.library_usable": "Futtatható vetületek",
"render.library_users": "Használó testek",
"render.light_anchor_pending": "A fények koordináta-rendszerének módosítása még nem támogatott.",
"render.lights_fixed_to": "Fények rögzítve ehhez",
"render.linear_help": "32 bites lebegőpontos RGBA-mastert őriz meg a zajszűrés, az expozíció, a tónusleképezés, a szűrők és a háttér előtt. Lineáris sRGB/D65; lefedettséggel előreszorzott. A többletmemória csak kifejezett engedéllyel használható. Nincs tömörítés vagy további passz.",
@@ -2356,6 +2611,7 @@
"render.scene_review": "Feloldás és áttekintés",
"render.scene_review_help": "Az alkalmazás előtt tekintse át a teljes natív kompozíciót. A nem kötött, speciális JELENET-specifikációk nem kerülnek helyettesítésre.",
"render.scene_scope_details": "A kompozíció hatóköre",
"render.search_appearances": "Keresés {count} megjelenés között",
"render.setup_legacy_environment": "Ez a régebbi beállítás örökölt környezetforgatást vagy -intenzitást használ. Ezek az értékek megmaradnak; teljes képfeladat mentése előtt kifejezetten alakítsa át őket.",
"render.setup_name": "Beállítás neve",
"render.setup_numeric_help": "A Mentés rögzíti az aktív képmódszert és annak összes numerikus beállítását. Az újratöltés vagy másik beállítás választása felülírja ezt a tervezetet; a kamerát külön állítsa vissza.",
@@ -2365,6 +2621,7 @@
"render.sharpening": "Részletélesítés",
"render.shell_tab_appearance": "RENDERELÉS: MEGJELENÉS",
"render.shell_tab_camera": "RENDERELÉS: KAMERA",
"render.shell_tab_lighting": "MEGVILÁGÍTÁS",
"render.shell_tab_scene": "RENDERELÉS: JELENET",
"render.shot_duplicate": "Mentett felvétel kettőzése",
"render.shot_manager": "Mentett felvételek",
@@ -2374,10 +2631,19 @@
"render.shot_remove_title": "Eltávolítja a mentett felvételt?",
"render.shot_rename": "Mentett felvétel átnevezése",
"render.shot_saved_only": "Előbb mentse az aktuális tervezetet. A kettőzés és az átnevezés megőrzi a pontos mentett beállításokat és erőforrás-hivatkozásokat.",
"render.shots_camera_changed": "A kamera megváltozott a felvétel mentése óta",
"render.shots_name_help": "Adjon egyedi nevet az aktuális felvételnek.",
"render.shots_no_result": "Még nincs renderelés",
"render.shots_result": "{number}. renderelés · {width} × {height}",
"render.shots_revert": "Visszaállítás",
"render.shots_save": "Felvétel mentése",
"render.shots_update": "Felvétel frissítése",
"render.show_shots_label": "Felvételsáv megjelenítése",
"render.size_4k_uhd": "4K UHD",
"render.size_full_hd": "Full HD",
"render.size_hd": "HD",
"render.size_presets": "Méretelőbeállítások",
"render.size_presets_pending": "A Kimenet szerkesztővel érkezik",
"render.sky_ground": "Talaj · lineáris RGB",
"render.sky_horizon": "Horizont · lineáris RGB",
"render.sky_zenith": "Zenit · lineáris RGB",
@@ -2428,8 +2694,10 @@
"render.studio_sky_help": "Saját égbolt-színátmenet a megvilágításhoz; nem kalibrált légköri modell.",
"render.studio_sun": "Nap",
"render.studio_sun_radius": "Szögsugár",
"render.studio_tiles_require_gpu": "A stúdiókhoz GPU-s útkövetés szükséges. Válassza a GPU-t a Kimenet szerkesztőben.",
"render.studio_title": "Renderstúdió",
"render.studio_tone_map": "Tónusleképezés",
"render.studios_environments": "Stúdiók és környezetek",
"render.subsurface_enable": "Belső szóródás véletlen bolyongással",
"render.subsurface_help": "Csak zárt dielektrikumhoz. A szóródás és az RGB-elnyelés külön, milliméterenkénti együtthatók. A fényutak a tényleges geometrián belül szóródnak, majd a határfelületen megtörnek vagy visszaverődnek. Kapcsolja ki az egyenes árnyékáteresztést, a felületi zajcsökkentést és a külső térfogatot; egy Fresnel-réteget használjon. Nem diffúziós shader, sűrűségrács vagy mért anyag. A sűrű közegekhez több mintára és nagyobb útmélységre lehet szükség.",
"render.subsurface_rate": "Szóródási ütközési gyakoriság",
@@ -2515,8 +2783,29 @@
"search.try": "Írja be egy parancs nevét, a terv egy elemét vagy egy paramétert. Parancs értékekkel való futtatásához írja be az azonosítóját és egy JSON-objektumot: {example}",
"search.valid": "Érvényes",
"section.off_hint": "Nincs sík kiválasztva: az OK kikapcsolja a metszetet.",
"select a direction (an axis, a sketch line or a straight edge) first": "először jelöljön ki egy irányt (tengelyt, vázlatvonalat vagy egyenes élt)",
"select a direction (an axis, a sketch line or a straight edge) for the profile to follow": "jelöljön ki egy irányt (tengelyt, vázlatvonalat vagy egyenes élt), amelyet a profil követ",
"select a point on a component": "jelöljön ki egy pontot egy komponensen",
"select a profile, edges or sketch lines first": "először jelöljön ki egy profilt, éleket vagy vázlatvonalakat",
"select an axis": "jelöljön ki egy tengelyt",
"select an axis, a straight or circular edge, or a cylinder, cone or torus face first": "először jelöljön ki egy tengelyt, egy egyenes vagy kör alakú élt, illetve egy henger-, kúp- vagy tóruszlapot",
"select bodies first": "először jelöljön ki testeket",
"select corner points, or the two lines of one corner": "jelöljön ki sarokpontokat, vagy egy sarok két vonalát",
"select one corner point, or the two lines of one corner": "jelöljön ki egy sarokpontot, vagy egy sarok két vonalát",
"select sheet edges first": "először jelöljön ki lemezéleket",
"select the bend line (a sketch line) first": "először jelölje ki a hajlítási vonalat (egy vázlatvonalat)",
"select the cam's profile (a sketch profile or the cam's edges)": "jelölje ki a bütyök profilját (egy vázlatprofilt vagy a bütyök éleit)",
"select the corner to cut first": "először jelölje ki a kivágandó sarkot",
"select the corners to round first": "először jelölje ki a lekerekítendő sarkokat",
"select the fixed face first (a plate's large face)": "először jelölje ki a rögzített lapot (a lemez nagy lapját)",
"select the frame sketch's lines first": "először jelölje ki a keretvázlat vonalait",
"select the path (sketch lines across the edge)": "jelölje ki az útvonalat (az él mentén futó vázlatvonalakat)",
"select the rim face first": "először jelölje ki a peremlapot",
"select the sheet edge first": "először jelölje ki a lemezélt",
"select two components": "jelöljön ki két komponenst",
"set by the sketch's lines": "a vázlat vonalai határozzák meg",
"settings.display_quality_note": "A finomabb szintek simábban rajzolják az ívelt lapokat és éleket, de tovább tart a hálózás. Csak a nézetablak változik, a terv és az exportok soha.",
"settings.double_click_components": "Kattintson duplán egy komponensre az aktiválásához, vagy egy kapcsolt komponens helyben szerkesztéséhez",
"settings.frame_rate_details_note": "Grafikont ad az utolsó képkockák idejéről, rajta a megrajzolt háromszögekkel, valamint hisztogramot a képkockaidőkről és azt, amit a GPU megrajzolt. A nézetben a képkockasebességre kattintva megjelenítheti vagy elrejtheti őket.",
"settings.frame_rate_note": "Megjeleníti a másodpercenkénti képkockaszámot a nézet jobb alsó sarkában.",
"settings.measure_axis_distances": "X/Y/Z távolságok megjelenítése",
@@ -2529,20 +2818,27 @@
"settings.reset_intro": "Ezek a beállítások nem működnek ezen a számítógépen, és megakadályoznák a SolidCraft indulását, ezért módosultak:",
"settings.reset_title": "A beállítások módosultak",
"settings.supersampling_note": "A nézetet nagyobb felbontásban rajzolja, majd lefelé szűri: simább élek, vonalak és rács 4K alatti képernyőkön. GPU-időbe kerül: 1,5×-nél kb. 2,25-szörös, 2×-nél 4-szeres képpontszám.",
"settings.update_changed_links": "A módosult kapcsolt tervek frissítése, amikor egy fül újra előtérbe kerül",
"settings.window_system_details": "Részletek",
"settings.window_system_note_now": "Újraindítás után lép életbe. Jelenleg: {now}.",
"settings.window_system_wayland_limited": "Natív Wayland alatt egyes funkciók korlátozottak.",
"sheet.flange_type_base": "Alap",
"sheet.flange_type_contour": "Kontúr",
"sheet.flange_type_edge": "Él",
"sheet.no_body": "nincs lemeztest: kezdje a Perem > Alap paranccsal, vagy alakítson át egy lemezt lemezzé az Átalakítás lemezzé paranccsal (jelölje ki a nagy, sík lapját)",
"shortcut.json_error": "a paramétereknek JSON-nak kell lenniük: {error}",
"shortcut.no_command": "nincs `{id}` parancs; a JSON előtti szövegnek parancsazonosítónak kell lennie",
"shortcut.pin": "Rögzítés az S doboz tetejére",
"shortcut.unpin": "Rögzítés feloldása",
"sketch is missing": "a vázlat hiányzik",
"sketch.degrees_of_freedom": "{count} szabadságfok",
"sketch.dof_left": "{count} szabadságfok maradt",
"sketch.dof_left_one": "{count} szabadságfok maradt",
"sketch.draw_curves": "Rajzolás mint {linetype}",
"sketch.make_selection": "A kijelölés átalakítása: {linetype}",
"sketch.open_ends": "Nyitott: {count}",
"sketch.status_conflict": "ellentmondó kényszerek",
"sketch.status_fully_constrained": "teljesen meghatározott",
"sketch.untied_ends": "Összekötetlen: {count}",
"sketch_panel.attached": "Rögzítve: {body} / {path}",
"sketch_panel.author": "Szerzői elhelyezés: {path}",
@@ -2569,7 +2865,28 @@
"sketch_text.path_hint": "A vázlat egy görbéje, amelyet a szöveg követ: kattintson ide, majd a görbére",
"sketch_text.use_font_file": "A(z) {path} betűtípusfájl használata",
"solid": "szilárd test",
"status.editing": "Szerkesztés: {name}",
"status.under_constrained": "Alulkényszerezett ({count} szabadságfok)",
"status.under_constrained_one": "Alulkényszerezett ({count} szabadságfok)",
"status.units_tip": "A terv egységei. Kattintson a módosításukhoz",
"the angle must be a number": "a szögnek számnak kell lennie",
"the curves must be in one sketch": "a görbéknek egy vázlatban kell lenniük",
"the direction must be an axis, a sketch line or a straight edge": "az iránynak tengelynek, vázlatvonalnak vagy egyenes élnek kell lennie",
"the duration must be a number of seconds, 0.001 or more": "az időtartamnak másodpercben megadott számnak kell lennie, legalább 0.001",
"the line's sketch is gone": "a vonal vázlata már nem létezik",
"the lines must be in one sketch": "a vonalaknak egy vázlatban kell lenniük",
"the lines must be in one sketch, not the section's": "a vonalaknak egy vázlatban kell lenniük, de nem a keresztmetszet vázlatában",
"the parts library is empty": "az alkatrészkönyvtár üres",
"the parts library is not available": "az alkatrésztár nem érhető el",
"the path curves must be in one other sketch": "az útvonal görbéinek egy másik vázlatban kell lenniük",
"the path's sketch is gone": "az útvonal vázlata már nem létezik",
"the position must be a number": "a helyzetnek számnak kell lennie",
"the preview failed": "az előnézet sikertelen volt",
"the profiles and curves must be in one sketch": "a profiloknak és a görbéknek egy vázlatban kell lenniük",
"the reference face must be on the edges' body": "a referencialapnak az élek testén kell lennie",
"the scale must be above 0": "az átléptéknek 0-nál nagyobbnak kell lennie",
"the section's profiles must be in one sketch": "a keresztmetszet profiljainak egy vázlatban kell lenniük",
"the two origins are on the same component": "a két origó ugyanazon a komponensen van",
"thread.automatic_size": "Automatikus méret",
"thread.designation": "Jelölés",
"thread.direction": "Menet iránya",
@@ -2609,6 +2926,7 @@
"trace.points": "{count} pont az útvonal mentén",
"trace.stopped_by_contact": "Vége a(z) {sample}. mintánál: {a} áthatolna ezen: {b}",
"trace.stopped_by_loop": "Vége a(z) {sample}. mintánál: a(z) {joint} csuklón átmenő hurok nem zárható tovább",
"trim by sketch curves, or by one plane or face": "vágás vázlatgörbékkel, vagy egyetlen síkkal vagy lappal",
"ui.1_degree_of_freedom": "1 szabadságfok",
"ui.3d_mouse_axes_reassigned": "A tengelyek újrakiosztása a Beállításokban történik, és ez a kalibráción felül is érvényesül.",
"ui.3d_mouse_button": "{number}. gomb",
@@ -3085,6 +3403,8 @@
"ui.design_properties.revision": "Revízió",
"ui.diameter": "Átmérő",
"ui.dimension": "Méret",
"ui.direct_interaction": "Közvetlen interakció",
"ui.direct_interaction_note": "Dolgozzon közvetlenül a modellen a nézetablakban. Nyomjon rá egy sík lapra vagy egy profilra, és húzza: felfelé kihúz, lefelé a testbe kivág, az oldalirányú mozgás pedig a lépésközt állítja be",
"ui.direction": "Irány",
"ui.display_quality": "Megjelenítési minőség",
"ui.display_quality_coarse": "Durva",
@@ -3106,8 +3426,27 @@
"ui.easyprint_uploading": "Feltöltés a Printablesbe…",
"ui.edit": "Szerkesztés",
"ui.editJointLimits": "Csuklókorlátok szerkesztése",
"ui.edit_in_place": "Szerkesztés helyben",
"ui.edit_in_place_after_turn": "A szerkesztés akkor fejeződik be, amikor az ügynök köre véget ér",
"ui.edit_in_place_agent_busy": "Az ügynök még dolgozik ebben a tervben; próbálja újra, ha a köre véget ért",
"ui.edit_in_place_already_queued": "Egy szerkesztés már az ügynök körének végére vár",
"ui.edit_in_place_assembly_closed": "{name} már nincs megnyitva",
"ui.edit_in_place_banner": "Szerkesztés itt: {name}",
"ui.edit_in_place_busy": "Előbb fejezze be vagy vesse el, ami nyitva van",
"ui.edit_in_place_none": "Nincs helyben szerkesztett kapcsolt terv",
"ui.edit_in_place_not_saved": "Az alkatrész módosításai nincsenek mentve; mentse el a szerkesztés befejezéséhez",
"ui.edit_in_place_open_elsewhere": "Egy másik SolidCraftban meg van nyitva: {name}",
"ui.edit_in_place_opens_after_turn": "Az alkatrész az ügynök körének végén nyílik meg",
"ui.edit_in_place_refresh_dropped": "Az összeállítás megváltozott, miközben a Kapcsolatok frissítése futott; futtassa újra",
"ui.edit_in_place_refresh_stopped": "A Kapcsolatok frissítése leállt, mielőtt végzett volna",
"ui.edit_in_place_refreshing": "Kapcsolatok frissítése itt: {name}…",
"ui.edit_in_place_save_first": "Előbb mentse az összeállítást: a kapcsolt terveket a fájlja alapján találja meg",
"ui.edit_in_place_save_waits": "A Kapcsolatok frissítése fut itt: {name}; mentse, ha végzett",
"ui.edit_in_place_saved_elsewhere": "Az alkatrész másik fájlba lett mentve; az összeállítás továbbra is ide hivatkozik: {name}",
"ui.edit_in_place_wait": "Várja meg, amíg a tervben futó művelet befejeződik",
"ui.edit_section_analysis": "Metszetelemzés szerkesztése",
"ui.ellipse": "Ellipszis",
"ui.emboss_wrap_note": "Az egyetlen támogatott hengerhez érintő, merőleges vonalprofilok automatikusan a henger köré tekerednek. Válasszon ki egy falat a cél kiválasztásához.",
"ui.end_radius": "Végi sugár",
"ui.end_scale": "Végi léptékezés",
"ui.entities": "Elemek",
@@ -3179,7 +3518,9 @@
"ui.heading_edit": "SZERKESZTÉS",
"ui.heading_extrude": "KIHÚZÁS",
"ui.heading_fillet": "LEKEREKÍTÉS",
"ui.heading_finish_editing": "SZERKESZTÉS BEFEJEZÉSE",
"ui.heading_finish_sketch": "VÁZLAT BEFEJEZÉSE",
"ui.heading_form": "ALAK",
"ui.heading_front": "ELÖLNÉZET",
"ui.heading_hole": "FURAT",
"ui.heading_home": "KEZDŐLAP",
@@ -3266,6 +3607,8 @@
"ui.intersect": "Metszet",
"ui.isolate": "Elkülönítés",
"ui.join": "Egyesítés",
"ui.joint_play": "▶ Lejátszás",
"ui.joint_stop": "■ Leállítás",
"ui.joints": "Csuklók",
"ui.keyboard_shortcuts": "Billentyűparancsok",
"ui.language": "Nyelv",
@@ -3283,6 +3626,7 @@
"ui.loft": "Áthúzás",
"ui.look_at": "Ránézés",
"ui.look_at_anim_desc": "Tengelynézet, Ránézés, Kezdőlap és Illesztés",
"ui.major_radius": "Nagy sugár",
"ui.manipulator_free_alt": "A manipulátor szabad húzása Alt/Option billentyűvel is",
"ui.manipulator_free_alt_tip": "Be: az Alt/Option vagy Ctrl/Cmd a kihúzás / gizmo nyilakon letiltja az értékillesztést. Ki: csak a Ctrl/Cmd.",
"ui.manufacture": "Gyártás",
@@ -3312,8 +3656,10 @@
"ui.mesh_format_3mf": "3MF",
"ui.mesh_format_3mf_tagged": "3MF laplapcímkékkel",
"ui.mesh_format_stl": "STL",
"ui.minor_radius": "Kis sugár",
"ui.mirror": "Tükrözés",
"ui.mirror_line": "Tükörvonal",
"ui.modifiers_either": "{first} vagy {second}",
"ui.move": "Mozgatás",
"ui.multiple": "Több",
"ui.named_views": "Elnevezett nézetek",
@@ -3370,6 +3716,7 @@
"ui.pinToShortcuts": "Rögzítés a gyorsparancsokhoz",
"ui.pinToToolbar": "Rögzítés az eszköztárra",
"ui.pipe": "Cső",
"ui.pitch": "Emelkedés",
"ui.placement": "Elhelyezés",
"ui.placement_handles": "Elhelyezési fogantyúk",
"ui.placement_handles_tip": "Fogantyúk egy Támasz, Dudor vagy Pattintós illesztés előnézetén: húzza a jelölőt a modell fölött, a gyűrűjénél fogva forgassa el",
@@ -3431,7 +3778,10 @@
"ui.restore_defaults": "Alapértékek visszaállítása",
"ui.reverse_horizontal_pan_direction": "Vízszintes pásztázási irány megfordítása",
"ui.reverse_horizontal_pan_tip": "A vízszintes kétujjas görgetés és a hüvelykujjkerék ellenkező irányba pásztáz",
"ui.reverse_vertical_pan_direction": "Függőleges pásztázás irányának megfordítása",
"ui.reverse_vertical_pan_tip": "A függőleges kétujjas görgetés ellenkező irányba pásztáz",
"ui.reverse_zoom_direction": "Nagyítási irány megfordítása",
"ui.revolutions": "Fordulatok",
"ui.revolve": "Forgatás",
"ui.ribbon_exit": "Kilépés",
"ui.ribbon_labels": "Szöveg megjelenítése a menüszalag ikonjai alatt",
@@ -3448,6 +3798,7 @@
"ui.scale": "Átlépték",
"ui.second_rail": "Második vezérgörbe",
"ui.section_analysis": "Metszetelemzés",
"ui.section_size": "Keresztmetszet mérete",
"ui.select": "Kijelölés",
"ui.selectOther": "Más kijelölése…",
"ui.selectTangentChain": "Érintőlánc kijelölése",
@@ -3519,6 +3870,7 @@
"ui.start_page": "Kezdőoldal",
"ui.step_values": "Értékek léptetése a nyílbillentyűkkel és a görgővel",
"ui.step_values_tip": "Egy értékmezőben, amelybe éppen gépel, a Fel/Le egy egyszerű számot 1-gyel változtat (Shift: 10, Alt: 0,1), ugyanígy az egérgörgő is a mező fölött. A kifejezések úgy maradnak, ahogy beírta.",
"ui.stitch_tolerance_hint": "szűk",
"ui.stretch": "Nyújtás",
"ui.supersampling": "Szuperminta (SSAA)",
"ui.supersampling_adaptive": "Csak ha a nézet áll",
@@ -3549,6 +3901,8 @@
"ui.to_object": "Objektumig",
"ui.tool_direction": "Szerszám iránya",
"ui.top": "Felülnézet",
"ui.top_diameter": "Felső átmérő",
"ui.top_radius": "Felső sugár",
"ui.torus": "Tórusz",
"ui.total_angle": "Teljes szög",
"ui.trackpad_pan_tip": "Az érintőpad gesztusai pásztáznak (ki: a görgőhöz hasonlóan nagyítanak); az egérgörgő nagyít, a hüvelykujjkerék oldalra pásztáz, a csippentés mindig nagyít",
@@ -3571,6 +3925,15 @@
"ui.updateToCurrentView": "Frissítés az aktuális nézetre",
"ui.upload": "Feltöltés",
"ui.vertical_constraint": "Függőleges kényszer",
"ui.view_angles": "Nézetszögek",
"ui.view_angles_convention": "Fok a felfelé mutató tengely körül. Az elfordulás 0° az elölnézet, 90° a bal oldali, −90° a jobb oldali, 180° a hátulnézet; a 90°-os billentés a felülnézet; a pozitív gördülés az óramutató járásával megegyező irányba forgatja a modellt.",
"ui.view_angles_help": "Megjeleníti a nézet billentését, elfordulását és gördülését fokban, valamint a vetítési módját a tájolásjelző alatt. Kattintson a kijelzőre a pontos szögek beírásához.",
"ui.view_angles_ortho": "Ortog.",
"ui.view_angles_persp": "Persp.",
"ui.view_angles_pitch": "Billentés",
"ui.view_angles_readout": "Billentés {pitch} · Elfordulás {yaw} · Gördülés {roll}",
"ui.view_angles_roll": "Gördülés",
"ui.view_angles_yaw": "Elfordulás",
"ui.view_centre": "Nézet középpontja",
"ui.viewport_theme_follow": "Téma követése",
"ui.visual_shaded_hidden_edges": "Árnyalt rejtett élekkel",
@@ -3638,7 +4001,11 @@
"update.up_to_date": "A SolidCraft naprakész.",
"update.update_now": "Frissítés most",
"update.you_have": "Az Ön verziója: {version}.",
"use a positive nominal diameter and a pitch smaller than it": "használjon pozitív névleges átmérőt és annál kisebb emelkedést",
"values must be finite numbers": "az értékeknek véges számoknak kell lenniük",
"values must be numbers or expressions": "az értékeknek számoknak vagy kifejezéseknek kell lenniük",
"view.auto_ortho_faces": "Perspektíva keringés közben, ortografikus nézet a lapokon",
"view.render_inspector": "Render-vizsgáló",
"web.files.insert_drawings_hint": "CAD-fájlok, hálók, DXF vagy SVG a megnyitott tervbe",
"web.files.insert_stored_drawings_hint": "A megnyitott tervbe (CAD-fájlok, hálók, DXF, SVG)",
"web.open_full": "Megnyitás a SolidCraftban",
+388
View File
@@ -1,4 +1,5 @@
{
"(active rule)": "(aturan aktif)",
"0 turns it off": "0 menonaktifkannya",
"1. Open GitHub draft below, then paste the report copied to your clipboard.": "1. Buka draf GitHub di bawah, lalu tempel laporan yang disalin ke papan klip Anda.",
"1. Open GitHub draft below. Your title and report will be filled in.": "1. Buka draf GitHub di bawah. Judul dan laporan Anda akan terisi otomatis.",
@@ -52,16 +53,20 @@
"Apply your appearance first. Exports assigned appearances and materials created in this design.": "Terapkan tampilan Anda terlebih dahulu. Mengekspor tampilan yang ditetapkan dan material yang dibuat di desain ini.",
"Arc length": "Panjang busur",
"Area": "Luas",
"As Picked": "Sesuai Pilihan",
"As-Built": "As-Built",
"Assign Rule": "Tetapkan Aturan",
"Attachment": "Lampiran",
"BLEND SURFACE": "PERMUKAAN BLEND",
"BOUNDARY SURFACE": "PERMUKAAN BATAS",
"BROWSER": "PENJELAJAH",
"Ball": "Bola",
"Bar": "Batang",
"Base Center": "Pusat Dasar",
"Base Height": "Tinggi Dasar",
"Bend": "Tekukan",
"Bend Edges": "Tepi Tekukan",
"Bend Line": "Garis Tekukan",
"Bend Line Position": "Posisi Garis Tekuk",
"Bend Position": "Posisi Tekukan",
"Bend Radius": "Radius Tekukan",
@@ -73,6 +78,7 @@
"Body (last if none)": "Bodi (terakhir jika kosong)",
"Body to split": "Bodi yang dibelah",
"Body, or faces to take out": "Bodi, atau muka yang dikeluarkan",
"Both": "Keduanya",
"Bottom Left": "Kiri Bawah",
"Bottom Middle": "Tengah Bawah",
"Bottom Right": "Kanan Bawah",
@@ -104,6 +110,7 @@
"Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.": "Pilih bidang baru. Geometri dan dimensi sketsa tidak berubah dalam koordinat dua dimensinya.",
"Choose…": "Pilih…",
"Circle center": "Pusat lingkaran",
"Circle or sphere": "Lingkaran atau bola",
"Circular": "Melingkar",
"Circumference": "Keliling",
"Clockwise": "Searah jarum jam",
@@ -120,6 +127,7 @@
"Config Table": "Tabel Konfigurasi",
"Configuration": "Konfigurasi",
"Configuration rules": "Aturan konfigurasi",
"Constant radius": "Radius konstan",
"Contact": "Kontak",
"Contact Sets": "Set Kontak",
"Contact sets": "Set kontak",
@@ -155,7 +163,9 @@
"Custom": "Kustom",
"Customize Toolbar": "Kustomisasi Toolbar",
"Customize…": "Kustomisasi…",
"Cylindrical": "Silindris",
"Cylindrical face": "Muka silindris",
"Deboss": "Deboss",
"Delete": "Hapus",
"Delete Folders and Features…": "Hapus Folder dan Fitur…",
"Delete Selected Features…": "Hapus Fitur Terpilih…",
@@ -190,6 +200,7 @@
"Duration": "Durasi",
"Each new shape is also dimensioned from the sketch origin until nothing of it is left free. A shape already attached to the origin or to placed geometry gets none.": "Setiap bentuk baru juga diberi dimensi dari titik asal sketsa hingga tidak ada yang bebas. Bentuk yang sudah terhubung ke titik asal atau geometri yang ditempatkan tidak mendapat apa pun.",
"Each shape you draw gets all its dimensions, at the sizes you drew (typed values win). A size the sketch already sets is left out.": "Setiap bentuk yang Anda gambar mendapat semua dimensinya sesuai ukuran yang digambar (nilai yang diketik diutamakan). Ukuran yang sudah ditetapkan sketsa dilewati.",
"Edge": "Tepi",
"Edges": "Tepi",
"Edit": "Edit",
"Edit Profile Sketch": "Edit Sketsa Profil",
@@ -198,6 +209,7 @@
"End Radius": "Radius Akhir",
"End bulge": "Bulge akhir",
"End continuity": "Kontinuitas akhir",
"Equal Distance": "Jarak Sama",
"Expand": "Perluas",
"Expected result (optional)": "Hasil yang diharapkan (opsional)",
"Explode": "Urai",
@@ -225,6 +237,8 @@
"Faces": "Muka",
"Faces / edges / vertices": "Muka / tepi / titik sudut",
"Faces to split": "Muka yang dibelah",
"Faces/Body": "Muka/Bodi",
"Factor must be a positive number": "Faktor harus berupa bilangan positif",
"Fastener": "Pengencang",
"Favorites": "Favorit",
"Favourite": "Favorit",
@@ -234,18 +248,25 @@
"Filter…": "Filter…",
"Find in Browser": "Cari di Penjelajah",
"Finish": "Hasil Akhir",
"First edge": "Tepi pertama",
"First plane": "Bidang pertama",
"First point": "Titik pertama",
"Fit and G2 corners require Constant radius.": "Fit dan sudut G2 memerlukan Radius konstan.",
"Fixed Face": "Muka Tetap",
"Flange Gap": "Celah Flens",
"Flange thickness": "Tebal flens",
"Flanges": "Flens",
"Flat Pattern": "Pola Datar",
"Flat size": "Ukuran datar",
"Flat size X": "Ukuran datar X",
"Flat size Y": "Ukuran datar Y",
"Follow camera (Look At sketches)": "Ikuti kamera (sketsa Look At)",
"Follow the camera (Look At sketches)": "Ikuti kamera (sketsa Look At)",
"Font": "Font",
"Free Move": "Pemindahan Bebas",
"From": "Dari",
"From Bodies": "Dari Bodi",
"From Snap": "Dari Snap",
"From edges": "Dari tepi",
"Full Length": "Panjang Penuh",
"Full thread depth": "Kedalaman ulir penuh",
@@ -257,10 +278,13 @@
"Halo under sketch curves": "Halo di bawah kurva sketsa",
"Height": "Tinggi",
"Height From": "Tinggi Dari",
"Hem Gap": "Celah Hem",
"Hidden edges": "Tepi tersembunyi",
"History layout": "Tata letak riwayat",
"History names": "Nama riwayat",
"Hole (optional)": "Lubang (opsional)",
"Hole Depth": "Kedalaman Lubang",
"Hole Diameter": "Diameter Lubang",
"Holes": "Lubang",
"Holes (edges on them)": "Lubang (tepi di atasnya)",
"Home view": "Tampilan awal",
@@ -283,12 +307,16 @@
"Issue title": "Judul issue",
"Items": "Item",
"JOINTS": "SAMBUNGAN",
"Joint 1": "Sambungan 1",
"Joint 2": "Sambungan 2",
"Joint Origin1": "Origin Sambungan1",
"K Factor": "Faktor K",
"Keep (points on it)": "Pertahankan (titik di atasnya)",
"Keep Tools": "Pertahankan Alat",
"Keep of the other surface": "Pertahankan dari permukaan lain",
"Key a joint's value at this step": "Beri kunci pada nilai sambungan di langkah ini",
"Keys": "Kunci",
"Knife Edge": "Tepi Pisau",
"L-Angle": "Siku L",
"LIBRARY": "PUSTAKA",
"LINKS": "TAUTAN",
@@ -301,9 +329,11 @@
"Letter Spacing": "Spasi Huruf",
"Library": "Pustaka",
"Library file": "File pustaka",
"Library response is missing": "Respons pustaka tidak ada",
"Limits": "Limit",
"Line Spacing": "Spasi Baris",
"Line Type": "Tipe Garis",
"Line on plane": "Garis pada bidang",
"Lines": "Garis",
"Listing the installed fonts…": "Mencari font terpasang…",
"Load library": "Muat pustaka",
@@ -323,11 +353,14 @@
"Material Outside": "Material Luar",
"Material copied. Existing body assignments are unchanged.": "Material disalin. Penetapan bodi yang ada tidak berubah.",
"Max": "Maks",
"Max Thickness": "Ketebalan Maks",
"Maximum": "Maksimum",
"Mesh body": "Bodi mesh",
"Mid Radius": "Radius Tengah",
"Middle double-click": "Klik dua kali tombol tengah",
"Min": "Min",
"Min Draft": "Draft Min",
"Min Thickness": "Ketebalan Min",
"Minimum": "Minimum",
"Mirror Plane": "Bidang Cermin",
"Mitre": "Miter",
@@ -347,6 +380,7 @@
"Name": "Nama",
"Name on import": "Nama saat impor",
"Nearest": "Terdekat",
"Neutral plane": "Bidang netral",
"New": "Baru",
"New Comp.": "Komp. Baru",
"New Sketch": "Sketsa Baru",
@@ -357,6 +391,7 @@
"No matching appearances": "Tidak ada tampilan yang cocok",
"No matching physical materials.": "Tidak ada material fisik yang cocok.",
"No visible bodies or sketches to export.": "Tidak ada bodi atau sketsa terlihat untuk diekspor.",
"Nominal Radius": "Radius Nominal",
"Nominal diameter": "Diameter nominal",
"None": "Tidak ada",
"Not available yet": "Belum tersedia",
@@ -385,22 +420,28 @@
"Overall length": "Panjang keseluruhan",
"Param. Graph": "Graf Param.",
"Parameter": "Parameter",
"Part length must be greater than zero and at most 2000 mm": "Panjang part harus lebih besar dari nol dan paling besar 2000 mm",
"Parts": "Part",
"Pass through": "Lewatkan",
"Path": "Jalur",
"Pattern": "Pola",
"Per Edge": "Per Tepi",
"Physical material definitions": "Definisi material fisik",
"Pick a part size": "Pilih ukuran part",
"Pick the tool bodies": "Pilih bodi alat",
"Picked": "Terpilih",
"Picked point": "Titik terpilih",
"Pin-slot": "Pin-slot",
"Pitch": "Pitch",
"Pivot": "Pivot",
"Place at the origin instead": "Tempatkan di origin saja",
"Planar": "Planar",
"Plastic Rules": "Aturan Plastik",
"Play the joint's motion in the view": "Putar gerakan sambungan di tampilan",
"Playback": "Putar",
"Plot": "Plot",
"Point on line": "Titik pada garis",
"Point on plane": "Titik pada bidang",
"Position": "Posisi",
"Position (0 to 1)": "Posisi (0 sampai 1)",
"Position (G0)": "Posisi (G0)",
@@ -413,6 +454,7 @@
"Profiles or curves": "Profil atau kurva",
"Profiles/Curves": "Profil/Kurva",
"Quantity": "Jumlah",
"Quantity must be at least 2": "Jumlah minimal 2",
"RECENT": "TERBARU",
"REFERENCE": "REFERENSI",
"RULED SURFACE": "PERMUKAAN RULED",
@@ -426,6 +468,8 @@
"Rectangular": "Persegi panjang",
"Rectangular Tube": "Pipa Persegi Panjang",
"Reference face": "Muka referensi",
"Relief Depth": "Kedalaman Relief",
"Relief Width": "Lebar Relief",
"Remove Tools": "Hapus Alat",
"Remove condition": "Hapus kondisi",
"Remove from Folder": "Hapus dari Folder",
@@ -454,6 +498,7 @@
"Restore this studio's tools": "Pulihkan alat studio ini",
"Result": "Hasil",
"Retake screenshot": "Ambil ulang tangkapan layar",
"Reveal Height": "Tinggi Terlihat",
"Reverse": "Balik",
"Reverse Direction": "Balik Arah",
"Revert": "Kembalikan",
@@ -462,12 +507,15 @@
"Revert Position": "Kembalikan Posisi",
"Review the report and screenshot before opening GitHub.": "Tinjau laporan dan tangkapan layar sebelum membuka GitHub.",
"Review your report on GitHub and click Submit new issue to publish it.": "Tinjau laporan Anda di GitHub lalu klik Submit new issue untuk menerbitkannya.",
"Revolute": "Putar",
"Rib Length": "Panjang Rib",
"Rib Offset": "Offset Rib",
"Rib Thickness": "Tebal Rib",
"Ribs": "Rib",
"Right Middle": "Tengah Kanan",
"Rigid": "Kaku",
"Rim Edge": "Tepi Rim",
"Rim face": "Muka rim",
"Ring of tools on the right button": "Cincin alat pada tombol kanan",
"Roll Back Here": "Mundurkan ke Sini",
"Roll History Marker Here": "Pindahkan Penanda Riwayat ke Sini",
@@ -492,16 +540,28 @@
"Search": "Cari",
"Search commands": "Cari perintah",
"Search this library": "Cari di pustaka ini",
"Second edge": "Tepi kedua",
"Second plane": "Bidang kedua",
"Second point": "Titik kedua",
"Section": "Penampang",
"Section (optional)": "Penampang (opsional)",
"Section position": "Posisi penampang",
"Sections (one sketch each)": "Penampang (satu sketsa per penampang)",
"Select Other": "Pilih Lainnya",
"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 Same Look": "Pilih Tampilan yang Sama",
"Select bodies (a face picks its body)": "Pilih bodi (memilih muka berarti memilih bodinya)",
"Select bodies or faces instead": "Pilih bodi atau muka saja",
"Select edges": "Pilih tepi",
"Select points of the sketch being edited": "Pilih titik pada sketsa yang sedang diedit",
"Select the centre the pattern turns around first": "Pilih dahulu pusat tempat pola berputar",
"Select the curves to offset first": "Pilih dahulu kurva yang akan di-offset",
"Select the mirror line": "Pilih garis cermin",
"Select the sketch curves or points to mirror first": "Pilih dahulu kurva atau titik sketsa yang akan dicerminkan",
"Select the sketch curves or points to move first": "Pilih dahulu kurva atau titik sketsa yang akan dipindahkan",
"Select the sketch curves or points to pattern first": "Pilih dahulu kurva atau titik sketsa yang akan dibuat polanya",
"Select the sketch curves or points to scale first": "Pilih dahulu kurva atau titik sketsa yang akan diskalakan",
"Select the sketch curves or points to transform first": "Pilih dahulu kurva atau titik sketsa yang akan ditransformasikan",
"Selection filter": "Filter pilihan",
"Semantic faces share their appearance across instances.": "Muka semantik berbagi tampilannya di antara instans.",
"Send your report on GitHub": "Kirim laporan Anda di GitHub",
@@ -521,9 +581,12 @@
"Sketch geometry and profiles": "Geometri dan profil sketsa",
"Sketch plane has no body-face attachment.": "Bidang sketsa tidak memiliki lampiran muka bodi.",
"Sketch points": "Titik sketsa",
"Slider": "Geser",
"Slot width": "Lebar slot",
"Snap to face and edge centres": "Snap ke pusat muka dan tepi",
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft tertutup sebelum desain ini disimpan. Simpan otomatis terakhirnya dapat dibuka.",
"Spacing 2 must be a non-zero number": "Jarak antar 2 harus berupa bilangan bukan nol",
"Spacing must be a non-zero number": "Jarak antar harus berupa bilangan bukan nol",
"Specialise": "Spesialisasikan",
"Speed": "Kecepatan",
"Splitting tool": "Alat pemisah",
@@ -534,6 +597,7 @@
"Start angle": "Sudut awal",
"Start bulge": "Bulge awal",
"Start continuity": "Kontinuitas awal",
"Stationary Side": "Sisi Diam",
"Step": "Langkah",
"Steps": "Langkah",
"Steps to reproduce (optional)": "Langkah untuk mereproduksi (opsional)",
@@ -548,6 +612,7 @@
"Suppressed": "Ditekan",
"Surface": "Permukaan",
"Surface, or faces to turn over": "Permukaan, atau muka yang akan dibalik",
"Surfaces": "Permukaan",
"Swap Ends": "Tukar Ujung",
"TRIM": "TRIM",
"Tangent": "Tangen",
@@ -558,24 +623,34 @@
"Tap drill diameter": "Diameter bor tap",
"Target": "Target",
"Target Body": "Bodi Target",
"Target Face (optional)": "Muka Target (opsional)",
"Tear": "Sobek",
"Technical details": "Detail teknis",
"The dashed sketch is a saved plane guide. Select a new face to repair the attachment.": "Sketsa putus-putus adalah panduan bidang yang tersimpan. Pilih muka baru untuk memperbaiki lampiran.",
"The design changed. Reopen Change Part Size for the component.": "Desain berubah. Buka kembali Ubah Ukuran Part untuk komponen ini.",
"The direction must be an axis or a straight sketch curve that lies in the sketch plane": "Arah harus berupa sumbu atau kurva sketsa lurus yang terletak pada bidang sketsa",
"The mirror line cannot also be mirrored": "Garis cermin tidak dapat ikut dicerminkan",
"The mirror line must be a straight curve of this sketch, not an origin axis": "Garis cermin harus berupa kurva lurus dari sketsa ini, bukan sumbu origin",
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "Kemunculan author yang tersimpan hilang. Pilih penempatan author yang valid sebelum mengikat ulang.",
"Then": "Lalu",
"These features will be deleted:": "Fitur berikut akan dihapus:",
"These features will fail:": "Fitur berikut akan gagal:",
"Thickness Outside": "Ketebalan Luar",
"Thickness Variation": "Variasi Ketebalan",
"Thin": "Tipis",
"Thin Extrude": "Ekstrusi Tipis",
"Third plane": "Bidang ketiga",
"Third point": "Titik ketiga",
"This child is shared. Deleting it removes it from every copy of its parent.": "Anak ini dibagikan. Menghapusnya akan menghilangkannya dari setiap salinan induknya.",
"This child is shared. Position changes apply to every copy of its parent.": "Anak ini dibagikan. Perubahan posisi berlaku untuk setiap salinan induknya.",
"This face cannot provide a durable attachment. Choose another face or explicitly use a fixed plane.": "Muka ini tidak dapat menyediakan lampiran yang tahan lama. Pilih muka lain atau gunakan bidang tetap secara eksplisit.",
"This image is not attached to GitHub yet. Copy or save it, then attach it on GitHub.": "Gambar ini belum dilampirkan ke GitHub. Salin atau simpan, lalu lampirkan di GitHub.",
"Thread depth": "Kedalaman ulir",
"Thread diameter": "Diameter ulir",
"Through point": "Melalui titik",
"To": "Hingga",
"To Offset": "Hingga Offset",
"To Snap": "Hingga Snap",
"To edges": "Ke tepi",
"Toe radius": "Radius ujung",
"Tolerance": "Toleransi",
@@ -589,6 +664,7 @@
"Trimming tool (plane, face or curves)": "Alat trim (bidang, muka, atau kurva)",
"Turn off section analysis before exporting the view.": "Matikan analisis penampang sebelum mengekspor tampilan.",
"Type": "Tipe",
"Type a distance or an angle to move by": "Ketik jarak atau sudut untuk memindahkan",
"UNTRIM": "UNTRIM",
"Unit": "Satuan",
"Unsuppress": "Lepas Tekan",
@@ -633,6 +709,17 @@
"Z angle": "Sudut Z",
"Z distance": "Jarak Z",
"Zebra": "Zebra",
"`joint` must be an id or a name": "`joint` harus berupa id atau nama",
"`params` must be an object": "`params` harus berupa objek",
"`values` must be a list": "`values` harus berupa daftar",
"a chamfer needs a corner of exactly two lines": "chamfer memerlukan sudut dari tepat dua garis",
"a file operation is in progress": "operasi file sedang berlangsung",
"a picked curve is no longer in a sketch": "kurva yang dipilih tidak lagi berada dalam sketsa",
"a picked curve's sketch has not been solved": "sketsa dari kurva yang dipilih belum diselesaikan",
"a picked edge has no points": "tepi yang dipilih tidak memiliki titik",
"a picked edge is gone": "tepi yang dipilih sudah tidak ada",
"a picked edge's body is gone": "bodi dari tepi yang dipilih sudah tidak ada",
"add a key for a joint first": "tambahkan kunci untuk sambungan terlebih dahulu",
"ai.connected_toast": "SolidCraft kini tersedia di {clients}. Mulai ulang untuk menggunakannya (Pengaturan > Asisten).",
"ai.prefs.allow_control": "Izinkan agen AI terhubung (kanal kontrol / MCP)",
"ai.prefs.allow_control_note": "Membuka kanal kontrol hanya di komputer ini (127.0.0.1, port 27878 atau port kosong berikutnya). Program yang terhubung juga harus menunjukkan token rahasia yang hanya dapat dibaca akun pengguna Anda, dibuat baru setiap kali dimulai. Berlaku saat SolidCraft dimulai berikutnya. Nonaktif: tidak ada yang mendengarkan kecuali Anda memulai SolidCraft dengan --control.",
@@ -673,6 +760,8 @@
"appearance.pbr_opaque": "PBR Opak",
"appearance.roughness": "Kekasaran",
"appearance.surface_response": "Respons permukaan",
"at the face": "di muka",
"bom.group_children": "Sub-part dalam baris part induknya",
"browser.body_name": "Bodi{number}",
"browser.calibrate_title": "Kalibrasi: {name}",
"browser.capture_many": "{count} komponen dipindahkan tetapi posisinya belum ditangkap.",
@@ -683,6 +772,8 @@
"browser.move_title": "Pindah: {name}",
"browser.parameters": "Parameter ({count})",
"browser.precision": "Presisi: {precision}",
"browser.sketch_attachment_lost": "{name} (lampiran hilang)",
"browser.sketch_editing": "{name} (sedang diedit)",
"browser.sketch_name": "Sketsa{number}",
"browser.units": "Satuan: {units}",
"cam.flat": "Muka datar",
@@ -692,7 +783,13 @@
"cam.roller": "Rol",
"cam.roller_pick": "Rol (opsional)",
"cam.tip_pick": "Ujung (opsional)",
"choose a profile": "pilih profil",
"choose the cam's and the follower's joints": "pilih sambungan kam dan pengikutnya",
"choose the joints to couple": "pilih sambungan yang akan dikopel",
"click a plane or planar face": "klik bidang atau muka planar",
"click sketch curves": "klik kurva sketsa",
"cmd.appearance_assign": "Tampilan",
"cmd.appearance_chain": "Rantai Tampilan",
"cmd.appearance_library": "Pustaka Tampilan",
"cmd.appearance_list": "Daftar Tampilan",
"cmd.appearance_paint": "Cat Tampilan",
@@ -722,7 +819,9 @@
"cmd.component_create": "Komponen Baru",
"cmd.component_cycle_color": "Ganti Warna Komponen",
"cmd.component_delete": "Hapus Komponen",
"cmd.component_edit_in_place": "Edit di Tempat",
"cmd.component_extract": "Ekstrak ke File",
"cmd.component_finish_edit": "Selesai Edit",
"cmd.component_from_bodies": "Buat Komponen dari Bodi",
"cmd.component_insert_design": "Sisipkan Desain",
"cmd.component_links": "Daftar Tautan",
@@ -897,14 +996,48 @@
"cmd.plastic_rest": "Rest",
"cmd.plastic_rules": "Daftar Aturan Plastik",
"cmd.plastic_snap_fit": "Snap Fit",
"cmd.render_aging_describe": "Penuaan Permukaan Berlapis",
"cmd.render_backends": "Backend Render",
"cmd.render_batch": "Render Semua Bidikan",
"cmd.render_camera_fit": "Bingkaikan Subjek Render",
"cmd.render_camera_get": "Periksa Kamera Render",
"cmd.render_cancel": "Batalkan Render",
"cmd.render_compare": "Bandingkan Gambar",
"cmd.render_effects_describe": "Lensa dan Efek Kreatif",
"cmd.render_export": "Ekspor Gambar Render",
"cmd.render_hdr_inspect": "Periksa Sumber HDR",
"cmd.render_library_appearance_apply": "Terapkan Tampilan Opak dari Pustaka",
"cmd.render_library_describe": "Periksa Sumber Daya Pustaka Render",
"cmd.render_library_environment_apply": "Terapkan Lingkungan HDR Bawaan",
"cmd.render_library_list": "Jelajahi Pustaka Render",
"cmd.render_library_physical_prepare": "Siapkan Referensi Fisik",
"cmd.render_library_preview": "Periksa Peta Permukaan Orisinal",
"cmd.render_library_studio_apply": "Terapkan Pencahayaan Studio Rancangan",
"cmd.render_library_studio_prepare": "Siapkan Pencahayaan Studio Rancangan",
"cmd.render_library_surface_bind": "Simpan Binding Permukaan",
"cmd.render_library_surface_prepare": "Siapkan Peta Permukaan Rancangan",
"cmd.render_library_surface_unbind": "Hapus Binding Permukaan",
"cmd.render_library_targets": "Cari Target Pustaka Render",
"cmd.render_lights_solo": "Lampu Solo",
"cmd.render_optics_bind": "Simpan Binding Optik",
"cmd.render_optics_unbind": "Hapus Binding Optik",
"cmd.render_preflight": "Periksa Permintaan Render",
"cmd.render_preset_apply": "Terapkan Studio Native",
"cmd.render_preset_resolve": "Tinjau Studio Native",
"cmd.render_quality_prepare": "Tinjau Anggaran Sampling Tetap",
"cmd.render_result_pin": "Pertahankan Hasil Render yang Ditampilkan",
"cmd.render_result_regrade": "Koreksi Warna Ulang Master Linear",
"cmd.render_scene": "Inspeksi Scene Render",
"cmd.render_settings_apply": "Terapkan Kamera Render",
"cmd.render_settings_capture_hdr": "Simpan Pengaturan Render dengan HDR",
"cmd.render_settings_get": "Pengaturan Render",
"cmd.render_settings_set": "Simpan Pengaturan Render",
"cmd.render_shot_duplicate": "Duplikasi Bidikan Render",
"cmd.render_shot_remove": "Hapus Bidikan Render",
"cmd.render_shot_rename": "Ganti Nama Bidikan Render",
"cmd.render_stage_backdrop": "Simpan Latar Belakang Render Fisik",
"cmd.render_stage_clear": "Hapus Panggung Render",
"cmd.render_stage_ground": "Simpan Lantai Render",
"cmd.render_start": "Render Gambar",
"cmd.render_status": "Progres Render",
"cmd.select_clear": "Hapus Pilihan",
@@ -996,6 +1129,7 @@
"cmd.sketch_spline_control_point_5": "Spline Titik Kontrol (Derajat 5)",
"cmd.sketch_spline_fit_point": "Spline Titik Fit",
"cmd.sketch_spline_insert_point": "Tambah Titik Spline",
"cmd.sketch_spline_reverse": "Balik Arah Spline",
"cmd.sketch_spun_profile": "Profil Putar",
"cmd.sketch_toggle_diameter": "Beralih Radius / Diameter",
"cmd.sketch_toggle_driven": "Alihkan Dimensi Driven",
@@ -1043,7 +1177,11 @@
"cmd.timeline_roll_to": "Gulir Penanda Riwayat",
"cmd.timeline_rollback": "Pindahkan Penanda Linimasa",
"cmd.timeline_suppress": "Tekan Fitur",
"cmd.view.render_browser": "Penjelajah Render",
"cmd.view.render_dock": "Pindah Dok Render",
"cmd.view.render_focus": "Tampilan Fokus Render",
"cmd.view_ambient_occlusion": "Ambient Occlusion",
"cmd.view_angle_readout": "Sudut Tampilan",
"cmd.view_apply": "Pulihkan Tampilan Bernama",
"cmd.view_back": "Tampilan Belakang",
"cmd.view_bottom": "Tampilan Bawah",
@@ -1153,6 +1291,8 @@
"dxf.progress.write": "Menulis DXF (5/5)",
"dxf.tolerance": "Toleransi kurva",
"dxf.tolerance_help": "Hanya bodi yang terlihat. Kurva memakai segmen lurus. Toleransi lebih kecil menghasilkan kurva lebih halus dan memakai lebih banyak waktu dan memori.",
"each rail's curves must be in one sketch": "kurva setiap rail harus berada dalam satu sketsa",
"feature computation ended without a result": "perhitungan fitur berakhir tanpa hasil",
"file.open.drawing_filter": "Desain, file CAD, mesh, dan gambar",
"file_load.built": "Terbangun sejauh ini: {built} dari {total} fitur.",
"file_load.converting": "Mengonversi solid {done} dari {total}",
@@ -1176,6 +1316,7 @@
"help.appearance.assign": "Memberi bodi, muka, atau komponen terpilih warna atau tampilan material, tanpa mengubah bobotnya. Gunakan untuk membedakan part atau melihat pratinjau hasil akhir.",
"help.attributions": "Atribusi (ATTRIBUTION.md)",
"help.build": "Versi {version} · commit {commit} · dibangun {date}",
"help.component.finish_edit": "Menyimpan part yang sedang diedit di tempat, kembali ke rakitannya, dan memperbarui rakitan dari part tersebut. Gunakan setelah perubahan Anda pada part tertaut selesai.",
"help.connect_note": "Mulai ulang setiap asisten untuk memuat server SolidCraft. Setiap file dicadangkan sebagai .bak sebelum diubah.",
"help.connect_title": "Hubungkan asisten AI",
"help.face.delete": "Menghapus muka terpilih dan membiarkan muka di sekitarnya menutup celah, atau membiarkan celah terbuka sebagai permukaan. Gunakan untuk menghilangkan tepi membulat, lubang, atau boss yang menghalangi.",
@@ -1314,6 +1455,8 @@
"keymap.command_id": "ID Perintah",
"keymap.empty": "Tidak ada perintah yang cocok",
"keymap.use_anyway": "Tetap gunakan {key} di sini",
"label.move_to_left_side": "Pindah ke sisi kiri",
"label.move_to_right_side": "Pindah ke sisi kanan",
"manage.appearance_hint": "Pilih tampilan untuk pilihan, atau seret swatch ke bodi atau muka.",
"manage.assign_hint": "Pilih bodi dan pilih aturan desain plastiknya.",
"manage.config_hint": "Edit baris, kolom, dan aturan konfigurasi, lalu aktifkan atau pratinjau sebuah baris.",
@@ -1398,7 +1541,9 @@
"nav.free_orbit_upright.help": "Seret mouse ke samping memutar model mengelilingi sumbu vertikal dunia; seret ke atas dan bawah memiringkan model melewati puncak tanpa batas.",
"nav.free_orbit_upright.label": "Putar mengelilingi sumbu vertikal",
"nav.fusion.help": "Seret tombol tengah menggeser, Shift+tombol tengah memutar orbit, seret kanan memutar orbit",
"nav.hfss.help": "Alt+seret kiri menggeser, Shift+Alt+kiri memperbesar/memperkecil, Ctrl+seret kiri di area kosong mengorbit",
"nav.inventor.help": "Seret tombol tengah menggeser, Shift+tombol tengah memutar orbit, Ctrl+tombol tengah memperbesar/memperkecil, seret kanan memutar orbit",
"nav.maya.help": "Alt+seret kiri mengorbit, Alt+tengah menggeser, Alt+kanan memperbesar/memperkecil; seret tengah juga menggeser",
"nav.openscad.help": "Seret kiri di ruang kosong memutar orbit, Shift+seret kiri memilih dengan kotak, seret geometri sketsa mengeditnya; seret kanan atau tombol tengah menggeser, Shift+seret kanan atau Shift+seret tombol tengah memperbesar/memperkecil",
"nav.perspective.sketch": "Pengeditan sketsa memakai tampilan ortografis. Selesaikan sketsa untuk beralih ke perspektif.",
"nav.solidworks.help": "Seret tombol tengah memutar orbit, Ctrl+tombol tengah menggeser, Shift+tombol tengah memperbesar/memperkecil, seret kanan memutar orbit",
@@ -1506,12 +1651,34 @@
"no hole": "tanpa lubang",
"no interference": "tanpa interferensi",
"no joint that moves yet": "belum ada sambungan yang bergerak",
"no motion study yet": "belum ada studi gerakan",
"no picture": "tanpa gambar",
"no such joint": "sambungan tidak ditemukan",
"none": "tidak ada",
"not a move": "bukan pemindahan",
"not a number": "bukan angka",
"open a sketch first": "buka sketsa terlebih dahulu",
"parts.automatic_name": "otomatis",
"parts.dimension.across_flats": "Lebar antar sisi datar",
"parts.dimension.bore": "Diameter lubang",
"parts.dimension.d": "Diameter nominal",
"parts.dimension.head_diameter": "Diameter kepala",
"parts.dimension.head_height": "Tinggi kepala",
"parts.dimension.height": "Tinggi",
"parts.dimension.inner": "Diameter dalam",
"parts.dimension.outer": "Diameter luar",
"parts.dimension.pitch": "Pitch",
"parts.dimension.socket": "Ukuran soket",
"parts.dimension.socket_depth": "Kedalaman soket",
"parts.dimension.thickness": "Ketebalan",
"parts.dimension.width": "Lebar",
"parts.family.bearing": "Bearing bola alur dalam (selubung)",
"parts.family.dowel_pin": "Pin paralel (dowel)",
"parts.family.hex_bolt": "Baut / sekrup kepala segi enam",
"parts.family.hex_nut": "Mur segi enam",
"parts.family.set_screw": "Sekrup set soket segi enam, ujung datar",
"parts.family.socket_head_cap_screw": "Sekrup kepala soket (cap screw)",
"parts.family.washer": "Ring datar (washer)",
"pattern.alignment_2": "Perataan 2",
"pattern.alignment_hint": "Jumlah termasuk yang asli. Center menyeimbangkan salinan; Flip memilih sisi tambahan untuk total genap.",
"pattern.direction_1": "Arah 1",
@@ -1527,9 +1694,25 @@
"perf.ui_time": "Waktu UI",
"perf.viewport": "Viewport",
"perf.worst": "terburuk",
"pick a body or some of its faces": "pilih bodi atau beberapa mukanya",
"pick a hole to seat it, or OK places it at the origin": "pilih lubang untuk memasangnya, atau OK menempatkannya di origin",
"pick a planar face": "pilih muka planar",
"pick a plane or a face as the tool": "pilih bidang atau muka sebagai alat",
"pick a size": "pilih ukuran",
"pick edges of a body": "pilih tepi sebuah bodi",
"pick edges of one body": "pilih tepi dari satu bodi",
"pick edges of the holes to remove": "pilih tepi lubang yang akan dihapus",
"pick faces of one body": "pilih muka dari satu bodi",
"pick faces to offset": "pilih muka yang akan di-offset",
"pick lines of one sketch": "pilih garis dari satu sketsa",
"pick one or two vertices, edges or faces": "pilih satu atau dua titik sudut, tepi, atau muka",
"pick one plane or face as the tool, or sketch curves": "pilih satu bidang atau muka sebagai alat, atau kurva sketsa",
"pick sections in two or more sketches": "pilih penampang pada dua sketsa atau lebih",
"pick sketch curves or edges": "pilih kurva sketsa atau tepi",
"pick the face to untrim": "pilih muka yang pangkasnya akan dibatalkan",
"pick the path": "pilih jalur",
"pick what to keep": "pilih bagian yang dipertahankan",
"pick what to keep of the other surface": "pilih bagian permukaan lain yang dipertahankan",
"prefs.add_item": "Tambah item",
"prefs.alt_frees_manipulators": "Alt / Option juga melepaskan panah manipulator",
"prefs.anti_aliasing_amd_dx12": "Anti-aliasing memakai 1× karena pemeriksaan saat startup tidak menggambar apa pun dengan multisampling dan lulus pada 1×. Anda dapat memilih pengaturan lain di atas; mulai ulang untuk menerapkannya jika pemeriksaan lulus.",
@@ -1611,8 +1794,19 @@
"prefs.layout_note": "Ringkas menempelkan panel ke tepi jendela. Translusensi penjelajah tersedia di Mengambang. Ubah sisi Asisten di halaman Asisten.",
"prefs.left": "Kiri",
"prefs.left_click": "Klik kiri",
"prefs.linked_designs": "Desain tertaut",
"prefs.live_input": "Input langsung",
"prefs.live_preview": "Pratinjau langsung",
"prefs.logo": "Logo",
"prefs.logo_accent": "Aksen",
"prefs.logo_theme": "Warna tema",
"prefs.max_file_size": "Ukuran file maksimum untuk dibuka",
"prefs.max_file_size_design": "File desain",
"prefs.max_file_size_flag": "Sesi ini dimulai dengan --max-file-size {size}, yang berlaku hingga SolidCraft ditutup. Pengaturan ini berlaku saat dimulai tanpa opsi tersebut.",
"prefs.max_file_size_note": "Bawaan: 512 MB dan 256 MB; maksimum 16 GB. Membuka file memerlukan memori beberapa kali ukurannya. Desain yang menyimpan impor besar mungkin memerlukan batas file desain lebih tinggi agar dapat disimpan dan dibuka kembali.",
"prefs.max_file_size_step": "File STEP dan impor",
"prefs.max_file_size_web": "Aplikasi web membuka file STEP dan impor hingga 512 MB serta desain hingga 256 MB: tab peramban tidak dapat menampung file yang lebih besar. Aplikasi desktop dapat menaikkan batas ini.",
"prefs.megabytes": "MB",
"prefs.minutes": "menit",
"prefs.minutes_suffix": " mnt",
"prefs.mouse": "Mouse dan trackpad",
@@ -1661,11 +1855,17 @@
"prefs.sketch_selection": "Sketsa & pilihan",
"prefs.sketch_selection_heading": "Sketsa dan pilihan",
"prefs.sketch_selection_note": "Cara sketsa melakukan snap dan cara klik memilih.",
"prefs.sketch_tab_always": "Selalu tampilkan tab Sketsa",
"prefs.sketch_tab_always_note": "Tab Sketsa tetap ada di ribbon, tepat setelah Model, selama tidak ada sketsa yang terbuka. Alat gambarnya lalu memulai sketsa pada bidang atau muka yang dipilih, atau pada bidang yang Anda pilih berikutnya, dengan alat yang sudah siap di dalamnya.",
"prefs.sketch_tracking_guides": "Pemandu perataan pusat dan titik tengah",
"prefs.sketch_tracking_guides_note": "Arahkan kursor ke pusat lingkaran atau titik tengah tepi lurus, lalu gerakkan secara horizontal atau vertikal untuk menampilkan pemandu titik-titik sementara.",
"prefs.sketching": "Menggambar sketsa",
"prefs.snap_drawing_note": "Saat menggambar atau menyeret titik sketsa.",
"prefs.source": "Sumber",
"prefs.speed": "Kecepatan",
"prefs.stand_up_note": "Setelah file STEP atau IGES terbuka dengan Z ke atas, tawarkan seperempat putaran mengelilingi X.",
"prefs.status_bar": "Bilah status",
"prefs.status_bar_note": "Bilah tipis di sepanjang tepi bawah jendela menampilkan apa yang sedang diedit, status sketsa yang terbuka dengan warnanya, dan satuan desain; klik pada satuan akan mengubahnya. Status sketsa lalu tidak lagi tampil di sudut tampilan.",
"prefs.status_done": "Selesai",
"prefs.status_error": "Error",
"prefs.status_selected": "Terpilih",
@@ -1673,12 +1873,16 @@
"prefs.supersampling_brief": "Menggambar tampilan lebih besar lalu menyaringnya turun: sangat tajam, tetapi lebih berat.",
"prefs.tab_accent_note": "Aksen mengikuti tab aktif",
"prefs.theme_file": "file tema",
"prefs.theme_file_or_paste": "file atau tempel",
"prefs.theme_follow": "Ikuti file tema",
"prefs.theme_follow_error": "Tidak diterapkan: {error}. Tema valid terakhir tetap aktif.",
"prefs.theme_follow_launch": "Diikuti hanya untuk sesi ini (--theme-file); dimuat ulang setiap kali alat lain menulis ulang file.",
"prefs.theme_follow_note": "Dimuat ulang setiap kali alat lain menulis ulang file. Memilih tema menghentikan pengikutan.",
"prefs.theme_follow_off": "Nonaktif",
"prefs.theme_follow_stop": "Berhenti mengikuti",
"prefs.theme_import_hint": "Klik untuk memilih file tema, seret satu ke sini, atau tekan Ctrl+V (Cmd+V di Mac) setelah menyalin tema",
"prefs.theme_paste": "Tempel tema",
"prefs.theme_paste_none": "Papan klip tidak berisi tema SolidCraft",
"prefs.timeline_half_hour": "−30 mnt",
"prefs.timeline_now": "sekarang",
"prefs.timeline_quarter_hour": "−15",
@@ -1744,6 +1948,45 @@
"render.aging_upward": "Pengendapan menghadap ke atas",
"render.all_controls_help": "Pemeriksaan padat atas semua kontrol saat ini. Halaman bagian menyediakan pengeditan terfokus tanpa mengubah permintaan yang mendasarinya.",
"render.all_shots_label": "Semua Bidikan",
"render.appearance_editor.absorption": "Absorpsi",
"render.appearance_editor.base_colour": "Warna dasar",
"render.appearance_editor.body_reason": "Pengikatan optik menargetkan satu bodi utuh. Pilih Bodi untuk mengedit kontrol ini.",
"render.appearance_editor.boundary": "Batas",
"render.appearance_editor.box": "Kotak",
"render.appearance_editor.choose_image": "Pilih gambar…",
"render.appearance_editor.closed": "Solid tertutup",
"render.appearance_editor.coat": "Clear coat",
"render.appearance_editor.cpu_reason": "Memerlukan ray tracing CPU.",
"render.appearance_editor.glass": "Kaca dan absorpsi",
"render.appearance_editor.glass_enable": "Gunakan kaca fisik pada bodi ini",
"render.appearance_editor.glass_note": "Kaca tertutup memakai jarak geometris. Tidak dapat berbagi bodi dengan clear coat atau peta.",
"render.appearance_editor.gpu_reason": "Memerlukan path tracing GPU.",
"render.appearance_editor.ior": "IOR",
"render.appearance_editor.maps": "Peta",
"render.appearance_editor.normal": "Normal",
"render.appearance_editor.normal_strength": "Kekuatan normal",
"render.appearance_editor.origin": "Origin",
"render.appearance_editor.recipe": "Resep",
"render.appearance_editor.reflection_sampling": "Sampling untuk semua bodi reflektif",
"render.appearance_editor.reflections": "Refleksi",
"render.appearance_editor.remove": "Hapus yang tersimpan",
"render.appearance_editor.remove_note": "Hapus optik tersimpan dari bidikan ini. Geometri dan look yang ditetapkan tetap dipertahankan.",
"render.appearance_editor.remove_title": "Hapus optik tersimpan?",
"render.appearance_editor.replace": "Ganti optik tersimpan bodi ini",
"render.appearance_editor.revalidate": "Validasi ulang bodi ini",
"render.appearance_editor.save": "Simpan optik ini",
"render.appearance_editor.save_reason": "Menyimpan optik ini memerlukan kaca fisik pada bodi GPU saat ini. Clear coat dan peta gambar lokal tetap bersifat sementara.",
"render.appearance_editor.saved": "Tersimpan bersama bidikan ini",
"render.appearance_editor.saved_state": "Optik ini tersimpan bersama bidikan ini.",
"render.appearance_editor.scatter_reason": "Hamburan interior memerlukan batas solid tertutup.",
"render.appearance_editor.temporary": "Optik ini bersifat sementara pada tab ini. Simpan agar tetap ada bersama bidikan.",
"render.appearance_editor.thickness": "Ketebalan",
"render.appearance_editor.thickness_reason": "Ketebalan berlaku untuk lembaran tipis; kaca tertutup memakai jarak geometris.",
"render.appearance_editor.thin": "Lembaran tipis",
"render.appearance_editor.u_repeat": "Pengulangan U",
"render.appearance_editor.v_repeat": "Pengulangan V",
"render.appearance_editor.v_up": "V tekstur mengarah ke atas",
"render.appearance_editor.weight": "Bobot",
"render.appearance_physics_separate": "Tampilan visual tidak menetapkan massa jenis, data termal, atau properti rekayasa lainnya.",
"render.availability_native": "Komposisi native; tinjau sebelum diterapkan",
"render.availability_projection": "Proyeksi yang didukung; lihat cakupan dan bagian yang dihilangkan",
@@ -2029,6 +2272,36 @@
"render.inner_half_angle": "Setengah sudut dalam",
"render.inspect_scene": "Segarkan ringkasan adegan yang ditangkap",
"render.inspector": "Inspektor",
"render.inspector_advanced": "Lanjutan",
"render.inspector_all_users": "Semua {count} pemakai",
"render.inspector_applies_to": "Berlaku untuk",
"render.inspector_both_users": "Kedua pemakai",
"render.inspector_chain": "Asal look ini",
"render.inspector_colour": "Warna",
"render.inspector_edit": "Edit",
"render.inspector_finish": "Hasil Akhir",
"render.inspector_from_material": "Dari Material",
"render.inspector_gloss": "Kilap",
"render.inspector_material": "Material",
"render.inspector_matte": "Matte",
"render.inspector_metal": "Logam",
"render.inspector_opacity": "Opasitas",
"render.inspector_opaque_help": "Kolom hasil akhir dan optik memakai PBR opak (opasitas 1). Opasitas yang lebih rendah memakai pembayangan skalar.",
"render.inspector_pick_target": "Pilih target bertipe ini untuk memeriksa tampilannya.",
"render.inspector_remove": "Hapus override",
"render.inspector_replace": "Ganti…",
"render.inspector_reset": "Reset",
"render.inspector_satin": "Satin",
"render.inspector_save_library": "Simpan ke pustaka…",
"render.inspector_set_all": "Tampilan campuran. Pengeditan menetapkan look pada semua target terpilih.",
"render.inspector_soon": "Segera",
"render.inspector_target_limit": "Pilih paling banyak 10000 target untuk memeriksa tampilan.",
"render.inspector_textures": "Tekstur",
"render.inspector_this_body": "Bodi ini",
"render.inspector_this_component": "Komponen ini",
"render.inspector_this_face": "Muka ini",
"render.inspector_this_target": "Target ini",
"render.inspector_used_by": "Dipakai oleh {count}",
"render.keep_editing": "Lanjutkan mengedit",
"render.layer_down": "Pindah ke bawah",
"render.layer_enabled": "Lapisan aktif",
@@ -2325,6 +2598,7 @@
"render.sharpening": "Penajaman detail",
"render.shell_tab_appearance": "TAMPILAN RENDER",
"render.shell_tab_camera": "KAMERA RENDER",
"render.shell_tab_lighting": "PENCAHAYAAN",
"render.shell_tab_scene": "ADEGAN RENDER",
"render.shot_duplicate": "Duplikasi bidikan tersimpan",
"render.shot_manager": "Bidikan tersimpan",
@@ -2334,6 +2608,13 @@
"render.shot_remove_title": "Hapus bidikan tersimpan?",
"render.shot_rename": "Ganti nama bidikan tersimpan",
"render.shot_saved_only": "Simpan draf saat ini terlebih dahulu. Duplikasi dan ganti nama mempertahankan opsi tersimpan dan referensi sumber daya secara persis.",
"render.shots_camera_changed": "Kamera berubah sejak bidikan ini disimpan",
"render.shots_name_help": "Masukkan nama unik untuk bidikan saat ini.",
"render.shots_no_result": "Belum ada render",
"render.shots_result": "Render {number} · {width} × {height}",
"render.shots_revert": "Kembalikan",
"render.shots_save": "Simpan Bidikan",
"render.shots_update": "Perbarui bidikan",
"render.show_shots_label": "Tampilkan strip Bidikan",
"render.size_4k_uhd": "4K UHD",
"render.size_full_hd": "Full HD",
@@ -2475,8 +2756,29 @@
"search.try": "Ketik nama perintah, fitur di desain ini, atau parameter. Untuk menjalankan perintah dengan nilai, ketik id-nya dan objek JSON: {example}",
"search.valid": "Valid",
"section.off_hint": "Tidak ada bidang dipilih: OK mematikan penampang.",
"select a direction (an axis, a sketch line or a straight edge) first": "pilih arah (sumbu, garis sketsa, atau tepi lurus) terlebih dahulu",
"select a direction (an axis, a sketch line or a straight edge) for the profile to follow": "pilih arah yang diikuti profil (sumbu, garis sketsa, atau tepi lurus) terlebih dahulu",
"select a point on a component": "pilih titik pada komponen",
"select a profile, edges or sketch lines first": "pilih profil, tepi, atau garis sketsa terlebih dahulu",
"select an axis": "pilih sumbu",
"select an axis, a straight or circular edge, or a cylinder, cone or torus face first": "pilih sumbu, tepi lurus atau melingkar, atau muka silinder, kerucut, atau torus terlebih dahulu",
"select bodies first": "pilih bodi terlebih dahulu",
"select corner points, or the two lines of one corner": "pilih titik sudut, atau dua garis dari satu sudut",
"select one corner point, or the two lines of one corner": "pilih satu titik sudut, atau dua garis dari satu sudut",
"select sheet edges first": "pilih tepi pelat terlebih dahulu",
"select the bend line (a sketch line) first": "pilih garis tekukan (garis sketsa) terlebih dahulu",
"select the cam's profile (a sketch profile or the cam's edges)": "pilih profil cam (profil sketsa atau tepi cam)",
"select the corner to cut first": "pilih sudut yang akan dipotong terlebih dahulu",
"select the corners to round first": "pilih sudut yang akan dibulatkan terlebih dahulu",
"select the fixed face first (a plate's large face)": "pilih muka tetap terlebih dahulu (muka besar sebuah pelat)",
"select the frame sketch's lines first": "pilih garis sketsa rangka terlebih dahulu",
"select the path (sketch lines across the edge)": "pilih jalur (garis sketsa di sepanjang tepi)",
"select the rim face first": "pilih muka rim terlebih dahulu",
"select the sheet edge first": "pilih tepi pelat terlebih dahulu",
"select two components": "pilih dua komponen",
"set by the sketch's lines": "ditetapkan oleh garis sketsa",
"settings.display_quality_note": "Level lebih halus menggambar muka dan tepi lengkung lebih mulus dan butuh waktu meshing lebih lama. Hanya viewport yang berubah, tidak pernah desain atau ekspor.",
"settings.double_click_components": "Klik dua kali komponen untuk mengaktifkannya, atau untuk mengedit komponen tertaut di tempat",
"settings.frame_rate_details_note": "Menambahkan grafik waktu frame terakhir dengan segitiga yang digambar di atasnya, histogram waktu frame, dan apa yang digambar GPU. Klik frame rate di tampilan untuk menampilkan atau menyembunyikannya.",
"settings.frame_rate_note": "Menampilkan frame per detik di sudut kanan bawah tampilan.",
"settings.measure_axis_distances": "Tampilkan jarak X/Y/Z",
@@ -2489,20 +2791,27 @@
"settings.reset_intro": "Pengaturan ini tidak berfungsi di komputer ini dan akan mencegah SolidCraft dimulai, sehingga diubah:",
"settings.reset_title": "Pengaturan diubah",
"settings.supersampling_note": "Menggambar tampilan pada resolusi lebih tinggi lalu menyaringnya turun: tepi, garis, dan grid lebih halus pada layar di bawah 4K. Membutuhkan waktu GPU: sekitar 2,25× piksel pada 1,5× dan 4× pada 2×.",
"settings.update_changed_links": "Perbarui desain tertaut yang berubah saat tab kembali aktif",
"settings.window_system_details": "Detail",
"settings.window_system_note_now": "Berlaku setelah mulai ulang. Sekarang: {now}.",
"settings.window_system_wayland_limited": "Beberapa fitur terbatas di Wayland native.",
"sheet.flange_type_base": "Dasar",
"sheet.flange_type_contour": "Kontur",
"sheet.flange_type_edge": "Tepi",
"sheet.no_body": "tidak ada bodi pelat logam: mulai satu dengan Flens > Base, atau ubah pelat menjadi pelat logam dengan Konversi ke Pelat Logam (pilih muka datar besarnya)",
"shortcut.json_error": "parameter harus berupa JSON: {error}",
"shortcut.no_command": "tidak ada perintah `{id}`; teks sebelum JSON harus berupa id perintah",
"shortcut.pin": "Sematkan di atas kotak S",
"shortcut.unpin": "Lepas sematan",
"sketch is missing": "sketsa tidak ditemukan",
"sketch.degrees_of_freedom": "{count} derajat kebebasan",
"sketch.dof_left": "{count} derajat kebebasan tersisa",
"sketch.dof_left_one": "{count} derajat kebebasan tersisa",
"sketch.draw_curves": "Gambar sebagai {linetype}",
"sketch.make_selection": "Ubah pilihan menjadi {linetype}",
"sketch.open_ends": "Terbuka {count}",
"sketch.status_conflict": "batasan saling bertentangan",
"sketch.status_fully_constrained": "terbatasi penuh",
"sketch.untied_ends": "Tak terikat {count}",
"sketch_panel.attached": "Terpasang ke {body} / {path}",
"sketch_panel.author": "Penempatan author: {path}",
@@ -2529,7 +2838,28 @@
"sketch_text.path_hint": "Kurva sketsa yang diikuti teks: klik di sini, lalu kurvanya",
"sketch_text.use_font_file": "Gunakan file font {path}",
"solid": "solid",
"status.editing": "Mengedit {name}",
"status.under_constrained": "Kurang dibatasi ({count} derajat kebebasan)",
"status.under_constrained_one": "Kurang dibatasi ({count} derajat kebebasan)",
"status.units_tip": "Satuan desain. Klik untuk mengubahnya",
"the angle must be a number": "sudut harus berupa angka",
"the curves must be in one sketch": "kurva harus berada dalam satu sketsa",
"the direction must be an axis, a sketch line or a straight edge": "arah harus berupa sumbu, garis sketsa, atau tepi lurus",
"the duration must be a number of seconds, 0.001 or more": "durasi harus berupa angka dalam detik, 0.001 atau lebih",
"the line's sketch is gone": "sketsa garis itu sudah tidak ada",
"the lines must be in one sketch": "garis harus berada dalam satu sketsa",
"the lines must be in one sketch, not the section's": "garis harus berada dalam satu sketsa, bukan sketsa penampang",
"the parts library is empty": "pustaka part kosong",
"the parts library is not available": "pustaka part tidak tersedia",
"the path curves must be in one other sketch": "kurva jalur harus berada dalam satu sketsa lain",
"the path's sketch is gone": "sketsa jalur sudah tidak ada",
"the position must be a number": "posisi harus berupa angka",
"the preview failed": "pratinjau gagal",
"the profiles and curves must be in one sketch": "profil dan kurva harus berada dalam satu sketsa",
"the reference face must be on the edges' body": "muka referensi harus berada pada bodi milik tepi tersebut",
"the scale must be above 0": "skala harus lebih dari 0",
"the section's profiles must be in one sketch": "profil penampang harus berada dalam satu sketsa",
"the two origins are on the same component": "kedua origin berada pada komponen yang sama",
"thread.automatic_size": "Ukuran otomatis",
"thread.designation": "Penunjukan",
"thread.direction": "Arah ulir",
@@ -2557,10 +2887,19 @@
"toolbar.customize_note": "Tab {tab}. Centang perintah untuk memberinya tombol toolbar. Anda juga dapat menyeret baris dropdown panel ke toolbar, menyeret tombol keluar dari toolbar untuk menghapusnya, atau memakai pin pada baris dropdown.",
"toolbar.image": "GAMBAR",
"toolbar.render": "RENDER",
"toolbar.render_appearance": "TAMPILAN",
"toolbar.render_camera": "KAMERA",
"toolbar.render_environment": "LINGKUNGAN",
"toolbar.render_lighting": "PENCAHAYAAN",
"toolbar.render_lights": "LAMPU",
"toolbar.render_result": "HASIL",
"toolbar.render_scene": "SCENE",
"toolbar.render_stage": "PANGGUNG",
"toolbar.render_studio": "STUDIO",
"trace.points": "{count} titik sepanjang jalur",
"trace.stopped_by_contact": "Berakhir di sampel {sample}: {a} akan menembus {b}",
"trace.stopped_by_loop": "Berakhir di sampel {sample}: loop melalui {joint} tidak dapat menutup lebih jauh",
"trim by sketch curves, or by one plane or face": "pangkas dengan kurva sketsa, atau dengan satu bidang atau muka",
"ui.1_degree_of_freedom": "1 derajat kebebasan",
"ui.3d_mouse_axes_reassigned": "Sumbu ditetapkan ulang di Preferensi, dan itu tetap berlaku di atas kalibrasi ini.",
"ui.3d_mouse_button": "Tombol {number}",
@@ -3037,6 +3376,8 @@
"ui.design_properties.revision": "Revisi",
"ui.diameter": "Diameter",
"ui.dimension": "Dimensi",
"ui.direct_interaction": "Interaksi langsung",
"ui.direct_interaction_note": "Kerjakan model langsung di viewport. Tekan muka datar atau profil lalu tarik: ke atas mengekstrusi, ke bawah ke dalam bodi memotong, dan gerakan ke samping menentukan ukuran langkah",
"ui.direction": "Arah",
"ui.display_quality": "Kualitas tampilan",
"ui.display_quality_coarse": "Kasar",
@@ -3058,8 +3399,27 @@
"ui.easyprint_uploading": "Mengunggah ke Printables…",
"ui.edit": "Edit",
"ui.editJointLimits": "Edit Batas Sambungan",
"ui.edit_in_place": "Edit di Tempat",
"ui.edit_in_place_after_turn": "Pengeditan selesai saat giliran agen berakhir",
"ui.edit_in_place_agent_busy": "Agen masih bekerja di desain ini; coba lagi setelah gilirannya selesai",
"ui.edit_in_place_already_queued": "Sebuah pengeditan sudah menunggu giliran agen berakhir",
"ui.edit_in_place_assembly_closed": "{name} tidak lagi terbuka",
"ui.edit_in_place_banner": "Mengedit di {name}",
"ui.edit_in_place_busy": "Selesaikan atau batalkan dulu yang sedang terbuka",
"ui.edit_in_place_none": "Tidak ada desain tertaut yang diedit di tempat",
"ui.edit_in_place_not_saved": "Part memiliki perubahan yang belum disimpan; simpan untuk menyelesaikan pengeditan",
"ui.edit_in_place_open_elsewhere": "{name} terbuka di SolidCraft lain",
"ui.edit_in_place_opens_after_turn": "Part terbuka saat giliran agen berakhir",
"ui.edit_in_place_refresh_dropped": "Rakitan berubah saat Perbarui Tautan berjalan; jalankan lagi",
"ui.edit_in_place_refresh_stopped": "Perbarui Tautan berhenti sebelum selesai",
"ui.edit_in_place_refreshing": "Memperbarui tautan di {name}…",
"ui.edit_in_place_save_first": "Simpan rakitan dulu: desain tertautnya ditemukan dari filenya",
"ui.edit_in_place_save_waits": "Perbarui Tautan sedang berjalan di {name}; simpan setelah selesai",
"ui.edit_in_place_saved_elsewhere": "Part disimpan ke file lain; rakitan masih menautkan {name}",
"ui.edit_in_place_wait": "Tunggu sampai proses yang berjalan di desain selesai",
"ui.edit_section_analysis": "Edit Analisis Penampang",
"ui.ellipse": "Elips",
"ui.emboss_wrap_note": "Profil garis ortogonal yang tangen pada satu silinder yang didukung dibungkus otomatis. Pilih dinding untuk menentukan target.",
"ui.end_radius": "Radius Akhir",
"ui.end_scale": "Skala Akhir",
"ui.entities": "Entitas",
@@ -3131,7 +3491,9 @@
"ui.heading_edit": "EDIT",
"ui.heading_extrude": "EKSTRUSI",
"ui.heading_fillet": "FILLET",
"ui.heading_finish_editing": "SELESAI EDIT",
"ui.heading_finish_sketch": "SELESAI SKETSA",
"ui.heading_form": "BENTUK",
"ui.heading_front": "DEPAN",
"ui.heading_hole": "LUBANG",
"ui.heading_home": "AWAL",
@@ -3218,6 +3580,8 @@
"ui.intersect": "Irisan",
"ui.isolate": "Isolasi",
"ui.join": "Gabung",
"ui.joint_play": "▶ Putar",
"ui.joint_stop": "■ Berhenti",
"ui.joints": "Sambungan",
"ui.keyboard_shortcuts": "Pintasan Keyboard",
"ui.language": "Bahasa",
@@ -3235,6 +3599,7 @@
"ui.loft": "Loft",
"ui.look_at": "Look At",
"ui.look_at_anim_desc": "Tampilan sumbu, Look At, Home, dan Fit",
"ui.major_radius": "Radius mayor",
"ui.manipulator_free_alt": "Seret bebas manipulator juga dengan Alt/Option",
"ui.manipulator_free_alt_tip": "Aktif: Alt/Option atau Ctrl/Cmd pada panah ekstrusi / gizmo menonaktifkan snap nilai. Mati: hanya Ctrl/Cmd.",
"ui.manufacture": "Manufaktur",
@@ -3264,8 +3629,10 @@
"ui.mesh_format_3mf": "3MF",
"ui.mesh_format_3mf_tagged": "3MF dengan tag muka",
"ui.mesh_format_stl": "STL",
"ui.minor_radius": "Radius minor",
"ui.mirror": "Cermin",
"ui.mirror_line": "Garis cermin",
"ui.modifiers_either": "{first} atau {second}",
"ui.move": "Pindah",
"ui.multiple": "Banyak",
"ui.named_views": "Tampilan Bernama",
@@ -3322,6 +3689,7 @@
"ui.pinToShortcuts": "Sematkan ke Pintasan",
"ui.pinToToolbar": "Sematkan ke Toolbar",
"ui.pipe": "Pipa",
"ui.pitch": "Pitch",
"ui.placement": "Penempatan",
"ui.placement_handles": "Handle penempatan",
"ui.placement_handles_tip": "Handle pada pratinjau Rest, Boss, atau Snap Fit: seret penanda untuk memindahkannya di model, putar lewat cincinnya",
@@ -3383,7 +3751,10 @@
"ui.restore_defaults": "Pulihkan Default",
"ui.reverse_horizontal_pan_direction": "Balik arah pan horizontal",
"ui.reverse_horizontal_pan_tip": "Gulir dua jari horizontal dan thumb wheel melakukan pan ke arah lain",
"ui.reverse_vertical_pan_direction": "Balik arah pan vertikal",
"ui.reverse_vertical_pan_tip": "Gulir vertikal dua jari menggeser tampilan ke arah sebaliknya",
"ui.reverse_zoom_direction": "Balik arah zoom",
"ui.revolutions": "Putaran",
"ui.revolve": "Revolve",
"ui.ribbon_exit": "Keluar",
"ui.ribbon_labels": "Tampilkan teks di bawah ikon pita",
@@ -3400,6 +3771,7 @@
"ui.scale": "Skala",
"ui.second_rail": "Rail Kedua",
"ui.section_analysis": "Analisis Penampang",
"ui.section_size": "Ukuran penampang",
"ui.select": "Pilih",
"ui.selectOther": "Pilih Lainnya…",
"ui.selectTangentChain": "Pilih Rantai Tangen",
@@ -3471,6 +3843,7 @@
"ui.start_page": "Halaman awal",
"ui.step_values": "Ubah nilai bertahap dengan tombol panah dan roda",
"ui.step_values_tip": "Di kolom nilai yang sedang Anda ketik, Up/Down mengubah angka biasa sebesar 1 (Shift: 10, Alt: 0.1), begitu juga roda mouse di atas kolom. Ekspresi dibiarkan seperti yang diketik.",
"ui.stitch_tolerance_hint": "ketat",
"ui.stretch": "Regangkan",
"ui.supersampling": "Supersampling (SSAA)",
"ui.supersampling_adaptive": "Hanya saat tampilan diam",
@@ -3501,6 +3874,8 @@
"ui.to_object": "Ke Objek",
"ui.tool_direction": "Arah alat",
"ui.top": "Atas",
"ui.top_diameter": "Diameter atas",
"ui.top_radius": "Radius atas",
"ui.torus": "Torus",
"ui.total_angle": "Sudut total",
"ui.trackpad_pan_tip": "Gestur trackpad melakukan pan (mati: zoom seperti roda); roda mouse zoom, thumb wheel pan ke samping, cubit selalu zoom",
@@ -3523,6 +3898,15 @@
"ui.updateToCurrentView": "Perbarui ke Tampilan Saat Ini",
"ui.upload": "Unggah",
"ui.vertical_constraint": "Batasan Vertikal",
"ui.view_angles": "Sudut tampilan",
"ui.view_angles_convention": "Derajat terhadap sumbu atas. Yaw 0° adalah tampilan Depan, 90° Kiri, −90° Kanan, 180° Belakang; pitch 90° adalah tampilan Atas; roll positif memutar model searah jarum jam.",
"ui.view_angles_help": "Menampilkan pitch, yaw, dan roll tampilan dalam derajat serta proyeksinya di bawah gizmo orientasi. Klik pembacaan untuk mengetik sudut yang tepat.",
"ui.view_angles_ortho": "Ortho",
"ui.view_angles_persp": "Persp",
"ui.view_angles_pitch": "Pitch",
"ui.view_angles_readout": "Pitch {pitch} · Yaw {yaw} · Roll {roll}",
"ui.view_angles_roll": "Roll",
"ui.view_angles_yaw": "Yaw",
"ui.view_centre": "Pusat tampilan",
"ui.viewport_theme_follow": "Ikuti tema",
"ui.visual_shaded_hidden_edges": "Shaded dengan tepi tersembunyi",
@@ -3590,7 +3974,11 @@
"update.up_to_date": "SolidCraft sudah terbaru.",
"update.update_now": "Perbarui sekarang",
"update.you_have": "Anda memiliki {version}.",
"use a positive nominal diameter and a pitch smaller than it": "gunakan diameter nominal positif dan pitch yang lebih kecil darinya",
"values must be finite numbers": "nilai harus berupa angka berhingga",
"values must be numbers or expressions": "nilai harus berupa angka atau ekspresi",
"view.auto_ortho_faces": "Perspektif saat orbit, ortho pada muka",
"view.render_inspector": "Inspektor Render",
"web.files.insert_drawings_hint": "File CAD, mesh, DXF, atau SVG ke desain yang terbuka",
"web.files.insert_stored_drawings_hint": "Ke desain yang terbuka (file CAD, mesh, DXF, SVG)",
"web.open_full": "Buka di SolidCraft",
+175
View File
@@ -258,6 +258,8 @@
"Flanges": "Flange",
"Flat Pattern": "Sviluppo",
"Flat size": "Dimensioni dello sviluppo",
"Flat size X": "Dimensioni dello sviluppo X",
"Flat size Y": "Dimensioni dello sviluppo Y",
"Follow camera (Look At sketches)": "Segui la camera (Vista normale agli schizzi)",
"Follow the camera (Look At sketches)": "Segui la camera (Vista normale agli schizzi)",
"Font": "Carattere",
@@ -547,6 +549,7 @@
"Sections (one sketch each)": "Sezioni (uno schizzo ciascuna)",
"Select Other": "Seleziona altro",
"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 Same Look": "Seleziona stesso aspetto",
"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",
"Select edges": "Seleziona spigoli",
@@ -757,6 +760,7 @@
"appearance.pbr_opaque": "PBR opaco",
"appearance.roughness": "Rugosità",
"appearance.surface_response": "Risposta della superficie",
"at the face": "sulla faccia",
"bom.group_children": "Sottoparti nella riga della loro parte",
"browser.body_name": "Corpo{number}",
"browser.calibrate_title": "Calibra: {name}",
@@ -785,6 +789,7 @@
"click a plane or planar face": "fare clic su un piano o una faccia piana",
"click sketch curves": "fare clic sulle curve dello schizzo",
"cmd.appearance_assign": "Aspetto",
"cmd.appearance_chain": "Catena degli aspetti",
"cmd.appearance_library": "Libreria degli aspetti",
"cmd.appearance_list": "Elenca aspetti",
"cmd.appearance_paint": "Applica aspetto",
@@ -989,14 +994,48 @@
"cmd.plastic_rest": "Appoggio",
"cmd.plastic_rules": "Elenca regole per plastica",
"cmd.plastic_snap_fit": "Aggancio a scatto",
"cmd.render_aging_describe": "Invecchiamento a strati delle superfici",
"cmd.render_backends": "Motori di rendering",
"cmd.render_batch": "Renderizza tutte le inquadrature",
"cmd.render_camera_fit": "Inquadra il soggetto del rendering",
"cmd.render_camera_get": "Esamina camera di rendering",
"cmd.render_cancel": "Annulla rendering",
"cmd.render_compare": "Confronta immagini",
"cmd.render_effects_describe": "Obiettivo ed effetti creativi",
"cmd.render_export": "Esporta immagine renderizzata",
"cmd.render_hdr_inspect": "Esamina sorgente HDR",
"cmd.render_library_appearance_apply": "Applica aspetto opaco della libreria",
"cmd.render_library_describe": "Esamina risorsa della libreria di rendering",
"cmd.render_library_environment_apply": "Applica ambiente HDR integrato",
"cmd.render_library_list": "Sfoglia libreria di rendering",
"cmd.render_library_physical_prepare": "Prepara riferimento fisico",
"cmd.render_library_preview": "Esamina mappa originale della superficie",
"cmd.render_library_studio_apply": "Applica luci dello studio predisposte",
"cmd.render_library_studio_prepare": "Prepara luci dello studio predisposte",
"cmd.render_library_surface_bind": "Salva associazione della superficie",
"cmd.render_library_surface_prepare": "Prepara mappe di superficie predisposte",
"cmd.render_library_surface_unbind": "Rimuovi associazione della superficie",
"cmd.render_library_targets": "Trova destinazioni della libreria di rendering",
"cmd.render_lights_solo": "Isola luce",
"cmd.render_optics_bind": "Salva associazione ottica",
"cmd.render_optics_unbind": "Rimuovi associazione ottica",
"cmd.render_preflight": "Verifica richiesta di rendering",
"cmd.render_preset_apply": "Applica studio nativo",
"cmd.render_preset_resolve": "Verifica studio nativo",
"cmd.render_quality_prepare": "Verifica budget di campionamento fisso",
"cmd.render_result_pin": "Conserva risultato di rendering mostrato",
"cmd.render_result_regrade": "Correggi il colore del master lineare",
"cmd.render_scene": "Esamina scena di rendering",
"cmd.render_settings_apply": "Applica camera di rendering",
"cmd.render_settings_capture_hdr": "Salva impostazioni di rendering con HDR",
"cmd.render_settings_get": "Impostazioni di rendering",
"cmd.render_settings_set": "Salva impostazioni di rendering",
"cmd.render_shot_duplicate": "Duplica inquadratura di rendering",
"cmd.render_shot_remove": "Rimuovi inquadratura di rendering",
"cmd.render_shot_rename": "Rinomina inquadratura di rendering",
"cmd.render_stage_backdrop": "Salva sfondo fisico del rendering",
"cmd.render_stage_clear": "Rimuovi scenografia di rendering",
"cmd.render_stage_ground": "Salva piano d'appoggio del rendering",
"cmd.render_start": "Renderizza immagine",
"cmd.render_status": "Avanzamento del rendering",
"cmd.select_clear": "Deseleziona tutto",
@@ -1088,6 +1127,7 @@
"cmd.sketch_spline_control_point_5": "Spline per punti di controllo (grado 5)",
"cmd.sketch_spline_fit_point": "Spline interpolante",
"cmd.sketch_spline_insert_point": "Aggiungi punto alla spline",
"cmd.sketch_spline_reverse": "Inverti direzione della spline",
"cmd.sketch_spun_profile": "Profilo di tornitura",
"cmd.sketch_toggle_diameter": "Alterna raggio / diametro",
"cmd.sketch_toggle_driven": "Attiva/Disattiva quota di riferimento",
@@ -1135,6 +1175,9 @@
"cmd.timeline_roll_to": "Sposta marcatore della cronologia",
"cmd.timeline_rollback": "Sposta marcatore della sequenza temporale",
"cmd.timeline_suppress": "Sospendi lavorazione",
"cmd.view.render_browser": "Struttura del rendering",
"cmd.view.render_dock": "Sposta pannello di rendering",
"cmd.view.render_focus": "Vista focalizzata sul rendering",
"cmd.view_ambient_occlusion": "Occlusione ambientale",
"cmd.view_apply": "Ripristina vista con nome",
"cmd.view_back": "Vista posteriore",
@@ -1408,6 +1451,8 @@
"keymap.command_id": "ID comando",
"keymap.empty": "Nessun comando corrispondente",
"keymap.use_anyway": "Usa comunque {key} qui",
"label.move_to_left_side": "Sposta sul lato sinistro",
"label.move_to_right_side": "Sposta sul lato destro",
"manage.appearance_hint": "Scegli un aspetto per la selezione o trascina un campione su un corpo o una faccia.",
"manage.assign_hint": "Seleziona i corpi e scegli la loro regola di progettazione per parti in plastica.",
"manage.config_hint": "Modifica righe, colonne e regole di configurazione, poi attiva una riga o visualizzane l’anteprima.",
@@ -1492,7 +1537,9 @@
"nav.free_orbit_upright.help": "Il trascinamento laterale del mouse fa girare il modello attorno all'asse verticale del mondo; il trascinamento in alto e in basso lo inclina oltre la sommità senza limiti.",
"nav.free_orbit_upright.label": "Ruota attorno all'asse verticale",
"nav.fusion.help": "Il trascinamento centrale fa la panoramica, Shift+centrale orbita, il trascinamento destro orbita",
"nav.hfss.help": "Alt+trascinamento con il pulsante sinistro esegue la panoramica, Shift+Alt+pulsante sinistro lo zoom, Ctrl+trascinamento con il pulsante sinistro su uno spazio vuoto orbita",
"nav.inventor.help": "Il trascinamento centrale fa la panoramica, Shift+centrale orbita, Ctrl+centrale fa lo zoom, il trascinamento destro orbita",
"nav.maya.help": "Alt+trascinamento con il pulsante sinistro orbita, Alt+pulsante centrale esegue la panoramica, Alt+pulsante destro lo zoom; anche il trascinamento con il pulsante centrale esegue la panoramica",
"nav.openscad.help": "Il trascinamento sinistro nello spazio vuoto orbita, Shift+trascinamento sinistro seleziona con un riquadro, il trascinamento sulla geometria dello schizzo la modifica; il trascinamento destro o centrale fa la panoramica, Shift+destro o Shift+centrale fa lo zoom",
"nav.perspective.sketch": "La modifica dello schizzo usa la vista ortogonale. Termina lo schizzo per passare alla prospettiva.",
"nav.solidworks.help": "Trascina con il tasto centrale per orbitare, Ctrl+centrale per la panoramica, Shift+centrale per lo zoom, trascina con il destro per orbitare",
@@ -1503,6 +1550,93 @@
"nav.touchpad.pan": "Panoramica (tieni premuto Shift e muovi il puntatore; senza pulsanti del mouse)",
"nav.touchpad.scroll": "Il touchpad usa lo scorrimento verticale per lo zoom. L'impostazione di panoramica a due dita viene conservata per le altre preimpostazioni.",
"nav.touchpad.zoom": "Zoom (scorrimento verticale o gesto di pizzicamento)",
"navbar.agent_chat": "Pannello chat dell'assistente",
"navbar.agent_cursor": "Mostra cursore dell'agente",
"navbar.ambient_occlusion": "Occlusione ambientale",
"navbar.assistant": "Assistente",
"navbar.custom.add_slot": "Aggiungi {slot}",
"navbar.custom.always": "Sempre visibile",
"navbar.custom.available": "{available} · trascina sulla barra o premi Aggiungi",
"navbar.custom.buttons": "Pulsanti",
"navbar.custom.compact": "Compatta",
"navbar.custom.count": "{count} di 16 elementi",
"navbar.custom.customise": "Personalizza barra di navigazione…",
"navbar.custom.design": "Progettazione",
"navbar.custom.duplicate": "Questo elemento è già sulla barra.",
"navbar.custom.fade": "Attenua",
"navbar.custom.full": "Rimuovi un elemento per aggiungerne un altro.",
"navbar.custom.hidden_design": "Nascosto in Progettazione",
"navbar.custom.hidden_render": "Nascosto in Rendering",
"navbar.custom.hide": "Nascondi barra di navigazione",
"navbar.custom.hide_idle": "Nascondi finché non ci passi sopra",
"navbar.custom.icon_only": "Solo icona",
"navbar.custom.icon_value": "Icona e valore",
"navbar.custom.icons": "Icone",
"navbar.custom.idle": "Quando inattiva",
"navbar.custom.intro": "Scegli i comandi della barra nella parte inferiore della vista 3D e il loro ordine. Trascina un elemento per spostarlo, oppure trascinalo fuori dalla barra per rimuoverlo.",
"navbar.custom.labels": "Icone ed etichette",
"navbar.custom.left": "Sposta a sinistra",
"navbar.custom.position": "Posizione",
"navbar.custom.position_centre": "Centro",
"navbar.custom.position_left": "Sinistra",
"navbar.custom.position_right": "Destra",
"navbar.custom.preview_as": "Anteprima come",
"navbar.custom.regular": "Normale",
"navbar.custom.remove": "Rimuovi",
"navbar.custom.remove_bar": "Rimuovi dalla barra",
"navbar.custom.render": "Rendering",
"navbar.custom.reset": "Reimposta barra di navigazione",
"navbar.custom.reset_default": "Reimposta ai valori predefiniti",
"navbar.custom.right": "Sposta a destra",
"navbar.custom.select": "Nessun elemento selezionato. Seleziona un elemento sulla barra per spostarlo, rimuoverlo o cambiarne l'aspetto.",
"navbar.custom.separators_full": "La barra ha già quattro separatori.",
"navbar.custom.show": "Mostra barra di navigazione",
"navbar.custom.shows": "Mostra",
"navbar.custom.size": "Dimensioni",
"navbar.custom.stale": "La barra è cambiata. Seleziona di nuovo l'elemento.",
"navbar.custom.title": "Barra di navigazione",
"navbar.custom.unknown": "Questo elemento non è disponibile.",
"navbar.description.ambient_occlusion": "Scurisce incavi e angoli per dare profondità.",
"navbar.description.display": "Raccoglie le impostazioni della vista: preimpostazione grafica, griglia, ombra sul piano di appoggio, occlusione ambientale e proiezione.",
"navbar.description.fit": "Inquadra la selezione o l'intero modello se non è selezionato nulla.",
"navbar.description.grid": "Mostra o nasconde la griglia di base.",
"navbar.description.ground_shadow": "Mostra o nasconde l'ombra sotto il modello.",
"navbar.description.home": "Torna alla vista isometrica dell'intero modello.",
"navbar.description.look_at": "Orienta la vista verso la faccia, il piano o lo schizzo aperto selezionato.",
"navbar.description.orbit": "Ruota la vista attorno al modello.",
"navbar.description.pan": "Sposta la vista lateralmente, in alto o in basso.",
"navbar.description.projection": "Passa dalla vista ortografica a quella prospettica.",
"navbar.description.separator": "Una linea sottile che raggruppa gli elementi vicini.",
"navbar.description.spin": "Mantiene la vista in rotazione finché non fai clic o premi un tasto.",
"navbar.description.visual_style": "Cambia il modo in cui è disegnato il modello: ombreggiato, reticolo o con spigoli nascosti.",
"navbar.description.zoom": "Avvicina o allontana la vista dal modello.",
"navbar.display": "Visualizzazione",
"navbar.fit": "Adatta",
"navbar.graphics": "Grafica",
"navbar.grid": "Griglia",
"navbar.ground_shadow": "Ombra sul piano di appoggio",
"navbar.home": "Inizio",
"navbar.later": "Più tardi",
"navbar.look_at": "Vista normale",
"navbar.more": "Altro",
"navbar.orbit": "Orbita",
"navbar.orthographic": "Ortografica",
"navbar.pan": "Panoramica",
"navbar.perspective": "Prospettica",
"navbar.preset": "Preimpostazione",
"navbar.projection": "Proiezione",
"navbar.projection_heading": "Proiezione",
"navbar.separator": "Separatore",
"navbar.show": "Mostra",
"navbar.spin": "Rotazione continua",
"navbar.style.shaded": "Ombreggiato",
"navbar.style.shaded_with_edges": "Ombreggiato con spigoli",
"navbar.style.shaded_with_hidden_edges": "Ombreggiato con spigoli nascosti",
"navbar.style.wireframe": "Reticolo",
"navbar.translucent": "Traslucido",
"navbar.value_tip": "{name} · {value}",
"navbar.visual_style": "Stile visualizzazione",
"navbar.zoom": "Zoom",
"needs two joints": "richiede due giunti",
"network.allow_once": "Consenti una volta",
"network.always_allow": "Consenti sempre questo server",
@@ -1658,6 +1792,13 @@
"prefs.left_click": "Clic sinistro",
"prefs.live_input": "Input in tempo reale",
"prefs.live_preview": "Anteprima dal vivo",
"prefs.max_file_size": "Dimensione massima dei file da aprire",
"prefs.max_file_size_design": "File di progettazione",
"prefs.max_file_size_flag": "Questo avvio usa --max-file-size {size}, valido fino alla chiusura di SolidCraft. Queste impostazioni si applicano all’avvio senza tale opzione.",
"prefs.max_file_size_note": "Valori predefiniti: 512 MB e 256 MB; massimo 16 GB. Aprire un file richiede memoria pari a più volte la sua dimensione. Un progetto che conserva un’importazione grande può richiedere un limite maggiore per essere salvato e riaperto.",
"prefs.max_file_size_step": "File STEP e di importazione",
"prefs.max_file_size_web": "L’app web apre file STEP e di importazione fino a 512 MB e progetti fino a 256 MB: una scheda del browser non può contenere file più grandi. L’app desktop permette di aumentare questi limiti.",
"prefs.megabytes": "MB",
"prefs.minutes": "minuti",
"prefs.minutes_suffix": " min",
"prefs.mouse": "Mouse e trackpad",
@@ -1790,6 +1931,7 @@
"render.all_controls_help": "Ispezione compatta di tutti i controlli attuali. Le pagine delle sezioni consentono una modifica mirata senza cambiare la richiesta sottostante.",
"render.all_shots_label": "Tutte le inquadrature",
"render.appearance_physics_separate": "L'aspetto visivo non assegna densità, dati termici o altre proprietà ingegneristiche.",
"render.appearances": "Aspetti",
"render.availability_native": "Composizione nativa; verifica prima di applicare",
"render.availability_projection": "Proiezione supportata; vedi ambito e omissioni",
"render.availability_reference": "Dati di riferimento; non è un'assegnazione automatica",
@@ -1844,6 +1986,10 @@
"render.camera_set": "Imposta camera di rendering",
"render.camera_target": "Posizione del punto di mira · mm",
"render.camera_yaw": "Imbardata",
"render.category_metal": "Metallo",
"render.category_paint": "Vernice",
"render.category_plastic": "Plastica",
"render.category_wood": "Legno",
"render.chromatic_offset": "Scostamento cromatico dei bordi",
"render.clear_stage": "Rimuovi la scenografia salvata",
"render.copy_request": "Copia JSON",
@@ -1943,6 +2089,7 @@
"render.effects_reset_lens": "Reimposta effetti obiettivo",
"render.effects_seed": "Seme del motivo",
"render.emitted_axis": "Asse di emissione",
"render.environment_tiles_require_raytrace": "Gli ambienti richiedono il ray tracing su CPU o GPU. Selezionalo nell'editor Uscita.",
"render.error_area_samples": "area_samples deve essere 1, 4, 16 o 64",
"render.error_binding_allocation": "impossibile riservare l'associazione del materiale",
"render.error_body_bound": "l'anteprima del prodotto supera il limite dei corpi acquisiti",
@@ -2014,6 +2161,8 @@
"render.flare_strength": "Intensità del flare sulle alte luci",
"render.flare_threshold": "Soglia del flare lineare della scena",
"render.fold_browser": "Comprimi struttura",
"render.frame_presets": "Preimpostazioni del fotogramma",
"render.frame_presets_pending": "In arrivo con l'editor delle inquadrature",
"render.gpu_depth": "Profondità massima del percorso",
"render.gpu_options_help": "Campioni, profondità, seed ed esposizione vengono salvati con questa impostazione. Viene salvato solo il metodo di generazione attivo.",
"render.gpu_path_failed": "Path tracer GPU non disponibile",
@@ -2085,8 +2234,12 @@
"render.lens_effects_title": "Effetti d'obiettivo e fotografici",
"render.library_all": "Catalogo",
"render.library_all_states": "Tutti gli stati di disponibilità",
"render.library_appearance_available": "Aspetto disponibile",
"render.library_applicable": "Risorse applicabili",
"render.library_applied": "Proiezione della risorsa applicata. Verifica la richiesta di rendering completa prima di avviare.",
"render.library_apply_appearance": "Applica aspetto",
"render.library_apply_hdr": "Salva con questo ambiente HDR",
"render.library_apply_hint": "Fai clic per applicare · trascina sul modello",
"render.library_apply_maps": "Usa le mappe di superficie GPU predisposte",
"render.library_apply_studio": "Salva la proiezione dello studio predisposta",
"render.library_apply_uniform": "Applica aspetto opaco uniforme",
@@ -2102,15 +2255,23 @@
"render.library_extent": "Estensione del soggetto",
"render.library_favorite": "Aggiungi ai preferiti",
"render.library_favorites": "Preferiti",
"render.library_filters": "Catalogo e disponibilità",
"render.library_grid": "Griglia",
"render.library_hdr": "Ambienti HDR integrati",
"render.library_hdr_help": "Incorpora l'HDR originale nell'impostazione salvata. L'origine della longitudine predisposta viene convertita esplicitamente; dopo il salvataggio non serve alcun file esterno.",
"render.library_in_design": "Nel progetto",
"render.library_inspect_only": "Seleziona per esaminare; non viene applicato nulla automaticamente. I preferiti e la cronologia recente appartengono a questa sessione del documento aperto.",
"render.library_ior": "IOR",
"render.library_light_concepts": "Concetti di illuminazione",
"render.library_list": "Elenco",
"render.library_maps_help": "Le mappe integrate possono essere salvate con sorgenti fissate e mappatura locale delle occorrenze. Le sostituzioni ottiche temporanee restano una modalità separata.",
"render.library_material_concepts": "Concetti di materiale",
"render.library_metallic": "Metallicità",
"render.library_native_scenes": "Studi nativi",
"render.library_next": "Pagina successiva",
"render.library_omitted": "Non incluso",
"render.library_opacity": "Opacità",
"render.library_other": "Altri aspetti",
"render.library_physical_profiles": "Profili fisici di riferimento",
"render.library_previous": "Pagina precedente",
"render.library_quality_concepts": "Specifiche di qualità",
@@ -2120,6 +2281,7 @@
"render.library_results": "Risorse corrispondenti",
"render.library_rig_help": "Adatta una sola volta a un centro e a un'estensione fisica espliciti. I rig completi includono pannelli passivi; i blocchi di calibrazione continuano a rifiutare l'operazione. Prima del salvataggio viene verificata una distanza libera prudenziale tra emettitori e pannelli.",
"render.library_rigs": "Rig di illuminazione predisposti",
"render.library_roughness": "Rugosità",
"render.library_save_maps": "Salva le mappe in questa impostazione",
"render.library_scene_bounds": "Usa i limiti della scena acquisita",
"render.library_scenes": "Specifiche della scena",
@@ -2143,6 +2305,7 @@
"render.library_unfavorite": "Rimuovi dai preferiti",
"render.library_uniform_ack": "Usa l'approssimazione uniforme senza effetti spaziali o avanzati",
"render.library_usable": "Proiezioni eseguibili",
"render.library_users": "Usato da",
"render.light_anchor_pending": "La modifica del sistema di coordinate delle luci non è ancora supportata.",
"render.lights_fixed_to": "Luci fissate a",
"render.linear_help": "Mantiene un master RGBA float a 32 bit prima di riduzione del rumore, esposizione, tone mapping, filtri e sfondo. sRGB lineare/D65; con alfa premoltiplicato dalla copertura. La memoria aggiuntiva viene ammessa esplicitamente. Nessuna compressione né passate aggiuntive.",
@@ -2361,6 +2524,7 @@
"render.scene_review": "Risolvi ed esamina",
"render.scene_review_help": "Esamina la composizione nativa completa prima di applicarla. Le specifiche SCENE avanzate non associate non vengono sostituite.",
"render.scene_scope_details": "Ambito della composizione",
"render.search_appearances": "Cerca tra {count} aspetti",
"render.setup_legacy_environment": "Questa impostazione precedente usa la rotazione o l'intensità dell'ambiente nel formato obsoleto. Questi valori vengono conservati; convertili esplicitamente prima di salvare un lavoro immagine completo.",
"render.setup_name": "Nome dell'impostazione",
"render.setup_numeric_help": "Salva registra il metodo immagine attivo e tutte le sue impostazioni numeriche. Ricaricare o scegliere un'altra impostazione sostituisce questa bozza; ripristina la camera separatamente.",
@@ -2379,10 +2543,19 @@
"render.shot_remove_title": "Rimuovere l'inquadratura salvata?",
"render.shot_rename": "Rinomina l'inquadratura salvata",
"render.shot_saved_only": "Salva prima la bozza attuale. Duplica e Rinomina conservano esattamente le opzioni salvate e i riferimenti alle risorse.",
"render.shots_camera_changed": "La camera è cambiata da quando l'inquadratura è stata salvata",
"render.shots_name_help": "Inserisci un nome univoco per l'inquadratura corrente.",
"render.shots_no_result": "Ancora nessun rendering",
"render.shots_result": "Rendering {number} · {width} × {height}",
"render.shots_revert": "Ripristina",
"render.shots_save": "Salva inquadratura",
"render.shots_update": "Aggiorna inquadratura",
"render.show_shots_label": "Mostra la barra delle inquadrature",
"render.size_4k_uhd": "4K UHD",
"render.size_full_hd": "Full HD",
"render.size_hd": "HD",
"render.size_presets": "Preimpostazioni delle dimensioni",
"render.size_presets_pending": "In arrivo con l'editor Uscita",
"render.sky_ground": "Suolo · RGB lineare",
"render.sky_horizon": "Orizzonte · RGB lineare",
"render.sky_zenith": "Zenit · RGB lineare",
@@ -2433,8 +2606,10 @@
"render.studio_sky_help": "Gradiente di cielo originale per l'illuminazione; non è un modello atmosferico calibrato.",
"render.studio_sun": "Sole",
"render.studio_sun_radius": "Raggio angolare",
"render.studio_tiles_require_gpu": "Gli studi richiedono il path tracing su GPU. Seleziona GPU nell'editor Uscita.",
"render.studio_title": "Studio di rendering",
"render.studio_tone_map": "Mappatura tonale",
"render.studios_environments": "Studi e ambienti",
"render.subsurface_enable": "Random walk di diffusione interna",
"render.subsurface_help": "Solo dielettrici chiusi. Lo scattering e l'assorbimento RGB sono coefficienti separati per mm. I percorsi si diffondono all'interno della geometria effettiva, poi si rifrangono o si riflettono sul contorno. Richiede di disattivare la trasmissione diretta delle ombre, la riduzione del rumore sulle superfici e il volume esterno, e di usare uno strato di Fresnel. Non è uno shader a diffusione, una griglia di densità né un materiale misurato. I mezzi densi possono richiedere più campioni e una maggiore profondità dei percorsi.",
"render.subsurface_rate": "Frequenza di collisione dello scattering",
+198 -29
View File
@@ -181,6 +181,8 @@
"Designs you open or save appear here.": "開いたデザインや保存したデザインがここに表示されます。",
"Determined by To Object. Choose Angle to edit the angular extent.": "「対象オブジェクトまで」により決定されます。角度範囲を編集するには「角度」を選択してください。",
"Diameter": "直径",
"Did this assembly open the wrong way up?": "このアセンブリは逆向きに開かれましたか?",
"Did this part open the wrong way up?": "このパーツは逆向きに開かれましたか?",
"Dimension new shapes": "新しい図形に寸法を付ける",
"Direction": "方向",
"Discard": "破棄",
@@ -593,6 +595,7 @@
"Square": "正方形",
"Stand it up": "正立させる",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "正立させる: 取り消し可能な1つの移動フィーチャーとして、X軸周りに90度回転します。",
"Stand this assembly up?": "このアセンブリを正立させますか?",
"Stand this part up?": "このパーツを正立させますか?",
"Start angle": "開始角度",
"Start bulge": "開始側の膨らみ",
@@ -760,6 +763,7 @@
"appearance.pbr_opaque": "不透明PBR",
"appearance.roughness": "粗さ",
"appearance.surface_response": "表面の応答",
"at the face": "面の位置",
"bom.group_children": "サブパーツを親パーツの行に含める",
"browser.body_name": "ボディ{number}",
"browser.calibrate_title": "尺度を設定: {name}",
@@ -768,6 +772,7 @@
"browser.capture_one": "コンポーネントが移動されましたが、位置がキャプチャされていません。",
"browser.component_name": "コンポーネント{number}",
"browser.edit_canvas_title": "キャンバスを編集: {name}",
"browser.edit_point_cloud_title": "点群を編集: {name}",
"browser.move_title": "移動: {name}",
"browser.parameters": "パラメータ ({count})",
"browser.precision": "精度: {precision}",
@@ -995,6 +1000,10 @@
"cmd.plastic_rest": "レスト",
"cmd.plastic_rules": "プラスチック ルールを一覧表示",
"cmd.plastic_snap_fit": "スナップ フィット",
"cmd.pointcloud_delete": "点群を削除",
"cmd.pointcloud_edit": "点群を編集",
"cmd.pointcloud_insert": "点群を挿入",
"cmd.pointcloud_list": "点群",
"cmd.render_aging_describe": "レイヤー式表面エイジング",
"cmd.render_backends": "レンダリング バックエンド",
"cmd.render_batch": "すべてのショットをレンダリング",
@@ -1009,23 +1018,28 @@
"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_physical_prepare": "物性参照を準備",
"cmd.render_library_preview": "元の表面マップを確認",
"cmd.render_library_studio_apply": "作成済みスタジオ照明を適用",
"cmd.render_library_studio_prepare": "作成済みスタジオ照明を準備",
"cmd.render_library_surface_bind": "Save Surface Binding",
"cmd.render_library_surface_bind": "サーフェス割り当てを保存",
"cmd.render_library_surface_prepare": "作成済みサーフェスマップを準備",
"cmd.render_library_surface_unbind": "Remove Surface Binding",
"cmd.render_library_surface_unbind": "サーフェス割り当てを削除",
"cmd.render_library_targets": "レンダリングリソースの対象を検索",
"cmd.render_lights_add": "ライトを追加",
"cmd.render_lights_solo": "ライトを単独表示",
"cmd.render_optics_bind": "光学割り当てを保存",
"cmd.render_optics_unbind": "光学割り当てを削除",
"cmd.render_output_size": "レンダー出力サイズを設定",
"cmd.render_preflight": "レンダリング要求を確認",
"cmd.render_preset_apply": "ネイティブスタジオを適用",
"cmd.render_preset_resolve": "ネイティブスタジオを確認",
"cmd.render_quality_prepare": "固定サンプリング予算を確認",
"cmd.render_result_close": "レンダー画像を閉じる",
"cmd.render_result_copy": "レンダー画像をコピー",
"cmd.render_result_pin": "表示中のレンダリング結果を保持",
"cmd.render_result_regrade": "リニアマスターを再調整",
"cmd.render_result_view": "レンダー画像を表示",
"cmd.render_scene": "レンダリングシーンを確認",
"cmd.render_settings_apply": "レンダリングカメラを適用",
"cmd.render_settings_capture_hdr": "HDRとレンダリング設定を保存",
@@ -1035,8 +1049,8 @@
"cmd.render_shot_remove": "レンダーショットを削除",
"cmd.render_shot_rename": "レンダーショットの名前を変更",
"cmd.render_stage_backdrop": "物理レンダー背景を保存",
"cmd.render_stage_clear": "Remove Render Stage",
"cmd.render_stage_ground": "Save Render Ground",
"cmd.render_stage_clear": "レンダーステージを削除",
"cmd.render_stage_ground": "レンダーの床を保存",
"cmd.render_start": "画像をレンダリング",
"cmd.render_status": "レンダリングの進行状況",
"cmd.select_clear": "選択をクリア",
@@ -1128,6 +1142,7 @@
"cmd.sketch_spline_control_point_5": "制御点スプライン (次数 5)",
"cmd.sketch_spline_fit_point": "通過点スプライン",
"cmd.sketch_spline_insert_point": "スプライン点を追加",
"cmd.sketch_spline_reverse": "スプラインの方向を反転",
"cmd.sketch_spun_profile": "回転プロファイル",
"cmd.sketch_toggle_diameter": "半径 / 直径の切り替え",
"cmd.sketch_toggle_driven": "従動寸法の切り替え",
@@ -1255,6 +1270,7 @@
"dialog.dimension_insert_hint": "別の駆動寸法をクリックして名前を挿入",
"dialog.dimension_name": "寸法: {name}",
"dialog.edit_title": "{title}を編集",
"dialog.no_shown_body": "変更できる表示中のボディがありません。ボディを表示するか、新規ボディを選択してください",
"dialog.select": "↖ 選択",
"dialog.selected_count": "↖ {count} 個選択中",
"dialog.selection_guidance": "入力欄を選んで変更します。選択済みのオブジェクトをクリックすると解除できます。Ctrl/Cmd を押しながらクリックしても切り替えられます。プレビューを確認してから OK を押してください。",
@@ -1325,6 +1341,7 @@
"help.mit": "MITライセンス",
"help.notice": "NOTICE",
"help.parameters.change": "デザインの寸法を決める名前付きの値と式の表を開きます。1か所で寸法を変え、それを使うすべてのフィーチャに反映させるときに使います。",
"help.pointcloud.insert": "PLY・XYZ・PTS形式のスキャンを点群としてモデルと一緒に挿入します。計測データを参考にモデリングするときに使います。",
"help.solid.align": "選択した立体を動かし、その点、面、平面を別の場所の点、面、平面に合わせます。距離を測らずに部品を別の部品に当てて配置するときに使います。",
"help.solid.chamfer": "選択した辺を、指定した距離で平らに斜めに削ります。鋭い角を落とすときや、ピンやねじを入れやすくするときに使います。",
"help.solid.combine": "複数の立体を結合したり、対象の立体から切り取ったり、共通する体積だけを残したりでき、工具の立体を残すこともできます。部品の結合、ある形での切り抜き、2つの立体が重なる範囲の確認に使います。",
@@ -1350,6 +1367,8 @@
"help.third_party": "サードパーティライセンス",
"help.timeline.compute_all": "タイムラインのすべてのフィーチャを最初から作り直します。モデルを更新し、どのフィーチャが失敗するかを確認するときに使います。",
"help.timeline.delete": "選択したフィーチャをタイムラインから削除します。スケッチを削除すると、そこから作ったフィーチャも削除されます。不要になったステップを取り除くときに使います。",
"history.sketch_under_constrained": "拘束不足(自由度 {count})",
"history.sketch_under_constrained_one": "拘束不足(自由度 {count})",
"hole_presets.confirm_delete": "削除を確認",
"hole_presets.delete": "削除",
"hole_presets.delete_prompt": "この個人用プリセットを削除しますか?既存の穴は変更されません。",
@@ -1717,6 +1736,8 @@
"prefs.anti_aliasing_note": "輪郭のぎざぎざを滑らかにします。",
"prefs.applies_on_restart": "再起動後に適用",
"prefs.assistant": "アシスタント",
"prefs.auto_operation": "操作を自動で選択",
"prefs.auto_operation_note": "フィーチャーのウィンドウは、新しい形状が何に接するかから結合・新規ボディ・切り取りを選びます(手動で選ぶまで)。オフ:開いたときの操作のままです。",
"prefs.axes": "軸",
"prefs.axis": "軸",
"prefs.bodies_folders": "ボディフォルダー",
@@ -1798,6 +1819,13 @@
"prefs.logo": "ロゴ",
"prefs.logo_accent": "アクセント",
"prefs.logo_theme": "テーマの色",
"prefs.max_file_size": "開けるファイルの最大サイズ",
"prefs.max_file_size_design": "デザインファイル",
"prefs.max_file_size_flag": "今回は --max-file-size {size} を指定して起動したため、SolidCraft を閉じるまでその値が適用されます。この設定は、そのオプションなしで起動したときに適用されます。",
"prefs.max_file_size_note": "既定値は 512 MB と 256 MB、上限は 16 GB です。ファイルを開くには、サイズの数倍のメモリが必要です。大きなインポートを保持するデザインの保存と再読み込みには、デザインファイルの上限を上げる必要がある場合があります。",
"prefs.max_file_size_step": "STEP・インポートファイル",
"prefs.max_file_size_web": "ウェブ版で開けるのは STEP・インポートファイルが 512 MB、デザインが 256 MB までです。ブラウザーのタブはそれ以上のファイルを保持できません。デスクトップ版では上限を上げられます。",
"prefs.megabytes": "MB",
"prefs.minutes": "分",
"prefs.minutes_suffix": " min",
"prefs.mouse": "マウスとトラックパッド",
@@ -1846,6 +1874,8 @@
"prefs.sketch_selection": "スケッチと選択",
"prefs.sketch_selection_heading": "スケッチと選択",
"prefs.sketch_selection_note": "スケッチのスナップとクリックによる選択。",
"prefs.sketch_state_icons": "スケッチアイコンを状態の色で表示",
"prefs.sketch_state_icons_note": "履歴とブラウザーで、スケッチのアイコンがその状態(拘束不足、完全拘束、競合)の色になり、ツールチップに状態が表示されます。オフ: すべてのスケッチが同じアイコンになります。",
"prefs.sketch_tab_always": "スケッチ タブを常に表示",
"prefs.sketch_tab_always_note": "スケッチを開いていないときも、スケッチ タブをリボンのモデルのすぐ後に表示します。そこにある作図ツールは、選択した平面または面に、なければ次に選ぶ平面にスケッチを開始し、そのスケッチでツールをすぐ使える状態にします。",
"prefs.sketch_tracking_guides": "中心・中点の整列ガイド",
@@ -1855,6 +1885,13 @@
"prefs.source": "入力元",
"prefs.speed": "速度",
"prefs.stand_up_note": "STEP または IGES ファイルを Z 上向きで開いた後、X 軸周りに 90° 回転するか確認します。",
"prefs.stand_up_unknown": "不明なプログラムのファイル",
"prefs.stand_up_unknown.ask": "ダイアログで確認",
"prefs.stand_up_unknown.note": "メモを表示",
"prefs.stand_up_unknown.off": "確認しない",
"prefs.stand_up_unknown_note": "ファイルのヘッダーに Y 上向きで保存することが知られているプログラム名がない場合の確認方法です。メモはファイルが横倒しに見える場合にのみ表示されます。「ダイアログで確認」は従来どおり、このようなファイルすべてについて確認します。",
"prefs.status_bar": "ステータスバー",
"prefs.status_bar_note": "ウィンドウの下端の細いバーに、編集中のもの、開いているスケッチの状態(その色で)、デザインの単位を表示します。単位をクリックすると変更できます。スケッチの状態はビューの隅には表示されなくなります。",
"prefs.status_done": "完了",
"prefs.status_error": "エラー",
"prefs.status_selected": "選択中",
@@ -1906,7 +1943,7 @@
"render.adaptive_pending": "GPU の独立予備サンプルによる適応配分と条件付きフレネル層化が利用できます。収束の認証、まれな事象の保証、CPU 適応輸送は未対応です。",
"render.adaptive_pilots": "ピクセルごとの予備サンプル",
"render.adaptive_relative": "RGB 相対誤差の目標",
"render.add_ground": "Add fitted ground to setup",
"render.add_ground": "フィットした床をセットアップに追加",
"render.advanced_material_scope": "明示的な制限付きで、有限の均一 GPU 媒質を利用できます。密度グリッド、ガラスとの入れ子、表面の内部散乱、ディスプレイスメント、分光輸送、未接続の高度な素材は未対応です。",
"render.aging_add": "エイジング層を追加",
"render.aging_airborne": "空中のほこり・散乱媒質",
@@ -1937,6 +1974,45 @@
"render.aging_upward": "上向き面への堆積",
"render.all_controls_help": "現在の全コントロールをまとめて表示します。各ページでは同じ要求を保ちながら項目別に編集できます。",
"render.all_shots_label": "すべてのショット",
"render.appearance_editor.absorption": "吸収",
"render.appearance_editor.base_colour": "ベースカラー",
"render.appearance_editor.body_reason": "光学設定はボディ全体に適用されます。編集するにはボディを選択してください。",
"render.appearance_editor.boundary": "境界",
"render.appearance_editor.box": "ボックス",
"render.appearance_editor.choose_image": "画像を選択…",
"render.appearance_editor.closed": "閉じたソリッド",
"render.appearance_editor.coat": "クリアコート",
"render.appearance_editor.cpu_reason": "CPUレイトレーシングが必要です。",
"render.appearance_editor.glass": "ガラスと吸収",
"render.appearance_editor.glass_enable": "このボディに物理ガラスを使用",
"render.appearance_editor.glass_note": "閉じたガラスは形状から距離を求めます。同じボディにクリアコートやマップを併用できません。",
"render.appearance_editor.gpu_reason": "GPUパストレーシングが必要です。",
"render.appearance_editor.ior": "IOR",
"render.appearance_editor.maps": "マップ",
"render.appearance_editor.normal": "法線",
"render.appearance_editor.normal_strength": "法線強度",
"render.appearance_editor.origin": "原点",
"render.appearance_editor.recipe": "レシピ",
"render.appearance_editor.reflection_sampling": "すべての反射ボディのサンプリング",
"render.appearance_editor.reflections": "反射",
"render.appearance_editor.remove": "保存済みを削除",
"render.appearance_editor.remove_note": "このショットから保存済み光学設定を削除します。形状と割り当てた外観は保持されます。",
"render.appearance_editor.remove_title": "保存済み光学設定を削除しますか?",
"render.appearance_editor.replace": "このボディの保存済み光学設定を置換",
"render.appearance_editor.revalidate": "このボディを再検証",
"render.appearance_editor.save": "この光学設定を保存",
"render.appearance_editor.save_reason": "保存には現在のGPUボディの物理ガラスが必要です。コートとローカル画像マップは一時設定のままです。",
"render.appearance_editor.saved": "このショットに保存",
"render.appearance_editor.saved_state": "この光学設定はこのショットに保存されています。",
"render.appearance_editor.scatter_reason": "内部散乱には閉じたソリッド境界が必要です。",
"render.appearance_editor.temporary": "この光学設定はこのタブで一時的に使用されます。ショットに保持するには保存してください。",
"render.appearance_editor.thickness": "厚さ",
"render.appearance_editor.thickness_reason": "厚さは薄板に適用されます。閉じたガラスは形状から距離を求めます。",
"render.appearance_editor.thin": "薄板",
"render.appearance_editor.u_repeat": "Uリピート",
"render.appearance_editor.v_repeat": "Vリピート",
"render.appearance_editor.v_up": "テクスチャのVは上向き",
"render.appearance_editor.weight": "重み",
"render.appearance_physics_separate": "外観の設定では、密度、熱特性などの工学的な物性値は割り当てられません。",
"render.appearances": "外観",
"render.availability_native": "ネイティブ構成。適用前に確認してください",
@@ -1997,8 +2073,11 @@
"render.category_paint": "塗装",
"render.category_plastic": "プラスチック",
"render.category_wood": "木材",
"render.check_opaque": "透明度:不透明サーフェスのみ。背景アルファに対応。",
"render.check_sorted_alpha": "透明度:ソート済みアルファによる近似。",
"render.check_transmission": "透明度:物理透過。",
"render.chromatic_offset": "色収差のずれ",
"render.clear_stage": "Remove saved stage",
"render.clear_stage": "保存済みステージを削除",
"render.copy_request": "JSONをコピー",
"render.cpu_aa_help": "空間サンプル数を増やすと輪郭が滑らかになりますが、描画時間も増えます。",
"render.cpu_area_add": "CPU矩形ライトを追加",
@@ -2226,10 +2305,40 @@
"render.image_unknown": "レンダリング状態を取得できません",
"render.image_use_largest": "この縦横比での最大サイズを使用:",
"render.image_width": "幅",
"render.include_rig_cards": "Include passive cards and flags",
"render.include_rig_cards": "受動的なカードとフラッグを含める",
"render.inner_half_angle": "内側半角",
"render.inspect_scene": "記録するシーンの概要を更新",
"render.inspector": "インスペクター",
"render.inspector_advanced": "詳細",
"render.inspector_all_users": "すべての{count}箇所",
"render.inspector_applies_to": "適用先",
"render.inspector_both_users": "両方の使用先",
"render.inspector_chain": "外観の継承元",
"render.inspector_colour": "色",
"render.inspector_edit": "編集",
"render.inspector_finish": "仕上げ",
"render.inspector_from_material": "材料から継承",
"render.inspector_gloss": "光沢",
"render.inspector_material": "材料",
"render.inspector_matte": "つや消し",
"render.inspector_metal": "金属",
"render.inspector_opacity": "不透明度",
"render.inspector_opaque_help": "仕上げと光学特性には不透明PBR(不透明度1)を使用します。不透明度を下げるとスカラーシェーディングになります。",
"render.inspector_pick_target": "この種類の対象を選択して外観を確認します。",
"render.inspector_remove": "上書きを削除",
"render.inspector_replace": "置換…",
"render.inspector_reset": "リセット",
"render.inspector_satin": "半光沢",
"render.inspector_save_library": "ライブラリに保存…",
"render.inspector_set_all": "外観が混在しています。編集すると選択したすべての対象に外観を設定します。",
"render.inspector_soon": "近日公開",
"render.inspector_target_limit": "外観の確認対象は10000個以下にしてください。",
"render.inspector_textures": "テクスチャ",
"render.inspector_this_body": "このボディ",
"render.inspector_this_component": "このコンポーネント",
"render.inspector_this_face": "この面",
"render.inspector_this_target": "この対象",
"render.inspector_used_by": "使用箇所: {count}",
"render.keep_editing": "編集を続ける",
"render.layer_down": "下へ",
"render.layer_enabled": "層を有効にする",
@@ -2271,7 +2380,7 @@
"render.library_ior": "屈折率",
"render.library_light_concepts": "照明構想",
"render.library_list": "リスト",
"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_maps_help": "組み込みマップは、固定したソースとローカルの配置マッピングとともに保存できます。一時的な光学上書きは別のモードのままです。",
"render.library_material_concepts": "マテリアル構想",
"render.library_metallic": "金属度",
"render.library_native_scenes": "ネイティブスタジオ",
@@ -2279,17 +2388,17 @@
"render.library_omitted": "含まれない項目",
"render.library_opacity": "不透明度",
"render.library_other": "その他の外観",
"render.library_physical_profiles": "Physical reference profiles",
"render.library_physical_profiles": "物性参照プロファイル",
"render.library_previous": "前のページ",
"render.library_quality_concepts": "品質仕様",
"render.library_recent": "最近使用した項目",
"render.library_replace_environment": "既存の環境を明示的に置換",
"render.library_replace_studio": "Replace saved lighting, environment and stage",
"render.library_replace_studio": "保存済みの照明・環境・ステージを置換",
"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_rig_help": "明示した中心と物理的な大きさで一度だけフィットします。完全なリグには受動的なカードが含まれ、校正が必要な場合は引き続き拒否されます。保存前に、光源とカードの保守的なクリアランスを確認します。",
"render.library_rigs": "作成済み照明リグ",
"render.library_roughness": "粗さ",
"render.library_save_maps": "Save maps to this setup",
"render.library_save_maps": "マップをこのセットアップに保存",
"render.library_scene_bounds": "取得したシーン境界を使用",
"render.library_scenes": "シーン仕様",
"render.library_search": "ライブラリを検索",
@@ -2396,12 +2505,34 @@
"render.optics_saved": "保存済み光学対象",
"render.optics_saved_help": "定義・フィーチャー・配置の正確な識別子をオフライン再開時も保持します。欠落・置換・非表示の対象は拒否し、工学材料は変更しません。削除前に数値編集を保存または破棄してください。",
"render.outer_half_angle": "外側半角",
"render.output_advanced": "詳細設定",
"render.output_backend_ready": "準備完了",
"render.output_check": "チェック",
"render.output_check_help": "チェックを実行すると、レンダーを開始せずにリクエストを確認できます。",
"render.output_check_summary": "チェック · {count}件の注意",
"render.output_copy": "コピー",
"render.output_destination": "保存先",
"render.output_destination_help": "完了した画像をエクスポートするときに保存先ファイルを選択します。",
"render.output_dimensions": "サイズ",
"render.output_effects": "エフェクト",
"render.output_export": "PNGを書き出す…",
"render.output_exr_unavailable": "ここではEXR出力設定を利用できません。リニアEXR書き出しには、リニアマスターを保持した完成GPU画像が必要です。",
"render.output_fit": "全体表示",
"render.output_format": "形式",
"render.output_format_status": "PNGは完了した表示画像そのものです。GPUの任意EXRは表示フィルター前の生の線形FLOAT32放射輝度を保持します。プレビューキャッシュや別ジョブへの置換はありません。",
"render.output_image": "画像",
"render.output_largest": "{width} × {height} 最大",
"render.output_lock_ratio": "縦横比を固定",
"render.output_over_budget": "要求画像が選択したリソース枠を超えています。対応するサイズかプロファイルを明示的に選択してください。自動的な品質低下は行いません。",
"render.output_presets_help": "形式、品質、チェックリストは出力エディターにあります。",
"render.output_quality": "品質",
"render.output_quality_waiting": "品質仕様の確定待ち",
"render.output_ratio_refused": "連動する寸法が方式の上限を超えます。以前のサイズを維持します。縦横比固定を解除するか、小さい値を選択してください。",
"render.output_ready": "レンダーの準備完了",
"render.output_size_ok": "サイズOK · {width} × {height} px",
"render.output_swap_orientation": "幅と高さを交換",
"render.output_tools_inspector": "サイズと形式は出力インスペクターで編集してください。",
"render.output_view_image": "画像を表示",
"render.page_aging": "エイジング・付着物",
"render.page_all": "全コントロール",
"render.page_appearance": "外観",
@@ -2415,20 +2546,20 @@
"render.page_scene": "シーン",
"render.paint_label": "ペイント",
"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.passive_stage_help": "有限の受動パネルはレンダリング画像にのみ属します。CADボディや質量は追加しません。配置は適用後に固定され、製品の境界と交差する場合は拒否されます。CPUでは拡散バウンス照明は得られません。",
"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_acknowledge": "この部分的な参照にはグレード・状態・出典の確認が必要であることを理解しました",
"render.physical_associations": "物性の候補と役割",
"render.physical_background_help": "カメラ背景とは別の実際の受動的な床と壁です。サイクロラマは24枚の多角形帯で近似し、無限面や解析的な滑面ではありません。保存済みステージの置換には確認が必要です。",
"render.physical_copy_plan": "Copy plan with sources and omissions",
"render.physical_copy_plan": "出典と省略項目を含む計画をコピー",
"render.physical_cyclorama": "多角形サイクロラマをフィット",
"render.physical_floor_wall": "物理的な床と壁をフィット",
"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.physical_material_name": "提案する参照マテリアル名",
"render.physical_omissions": "ネイティブのマテリアルカードで表現できない特性",
"render.physical_plan_only": "読み取り専用の計画を準備しました。マテリアルの作成や割り当ては行っていません。ネイティブのマテリアルカードを確認する際は、拡張プロファイルと省略項目も保持してください。",
"render.physical_prepare_plan": "作成計画を準備(適用はしません)",
"render.physical_separate": "外観から材料を特定することはできません。これらは出典付きの参照であり、認証された実効特性ではありません。既存の質量、工学材料、光学設定は変更されません。",
"render.physical_unknown": "数値の候補は確定していません。グレード、配合、または構成を指定する必要があります。不明はゼロではありません。",
"render.pick_body": "ボディ",
"render.pick_component": "コンポーネント",
"render.pick_face": "面",
@@ -2462,18 +2593,27 @@
"render.property_unknown": "不明",
"render.quality_ack": "固定予算への部分適用と省略項目を確認",
"render.quality_apply": "この固定サンプリング予算を使用",
"render.quality_balanced": "バランス",
"render.quality_custom": "カスタム",
"render.quality_draft": "ドラフト",
"render.quality_high": "高品質",
"render.quality_projection_help": "Qプリセットには将来の要件も含まれます。この操作は対応する固定サンプルPNG予算のみを適用し、適応的停止、生の線形出力、独立した透過制限は含みません。",
"render.quality_review": "サンプリング予算を確認",
"render.queue_pending": "このUIには自動の本番キューやアニメーションレンダラーはありません。完了した静止画像は個別に書き出せます。",
"render.radial_assign": "外観を割り当て",
"render.radial_compare": "比較",
"render.radial_distortion": "放射状歪み",
"render.radial_fit": "製品をフィット",
"render.radial_save": "ショットを保存",
"render.regrade_action": "生マスターを再調整(ノイズ除去なし)",
"render.regrade_help": "選択した完了済み生マスターから、この表示設定で別の PNG を作成します。光線追跡、撮影サイズ・カメラ・照明の変更、EXR の上書きは行いません。ガイドを保持しないためノイズ除去は再適用しません。通常のジョブ操作で書き出せます。",
"render.regrade_label": "再グレーディング",
"render.reload_settings": "保存した設定を再読み込み",
"render.replace_ground": "既存ステージの置換を確認",
"render.replace_label": "置換",
"render.replace_saved_surface": "Replace an existing saved binding for this target",
"render.replace_saved_surface": "この対象の既存の保存済み割り当てを置換",
"render.restore_camera": "保存したカメラを復元",
"render.result_actual": "100%",
"render.result_actual_help": "画像の1ピクセルを画面の1ピクセルで表示します。",
"render.result_actual_size": "100%",
"render.result_capture": "キャプチャ時のリビジョン",
@@ -2519,10 +2659,10 @@
"render.save_continue": "保存して続行",
"render.save_setup": "セットアップを保存",
"render.save_shot": "ショットを保存",
"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.saved_maps_runtime_conflict": "保存済みサーフェス割り当ては、一時的な光学上書きや未保存のHDRと併用できません。HDRを保存するか、一時的な入力を明示的に削除してください。",
"render.saved_surface_remove": "保存済み割り当てを削除",
"render.saved_surfaces": "保存済みサーフェス割り当て",
"render.saved_surfaces_help": "固定した組み込みマップは、選択した配置にローカルのミリメートル単位で追従します。置換または欠落した対象はエラーとなり、自動的に再割り当てされることはありません。",
"render.scene_apply": "確認したスタジオを適用",
"render.scene_consent": "照明・環境・露出・ステージの置換を確認",
"render.scene_preserved_fields": "カメラ、サンプル設定、画像寸法、光学割り当て、CAD形状、工学材料は保持されます。",
@@ -2541,6 +2681,7 @@
"render.sharpening": "ディテールのシャープ化",
"render.shell_tab_appearance": "外観",
"render.shell_tab_camera": "カメラ",
"render.shell_tab_lighting": "ライティング",
"render.shell_tab_scene": "シーン",
"render.shot_duplicate": "保存済みショットを複製",
"render.shot_manager": "保存済みショット",
@@ -2558,11 +2699,15 @@
"render.shots_save": "ショットを保存",
"render.shots_update": "ショットを更新",
"render.show_shots_label": "ショットストリップを表示",
"render.size_4k": "4K",
"render.size_4k_uhd": "4K UHD",
"render.size_custom": "カスタム…",
"render.size_full_hd": "フルHD",
"render.size_hd": "HD",
"render.size_presets": "サイズプリセット",
"render.size_presets_pending": "出力エディターとともに追加予定",
"render.size_preview": "プレビュー",
"render.size_square": "正方形",
"render.sky_ground": "地面・線形RGB",
"render.sky_horizon": "地平線・線形RGB",
"render.sky_zenith": "天頂・線形RGB",
@@ -2573,8 +2718,11 @@
"render.stage_look": "外観",
"render.stage_orthographic": "平行投影",
"render.stage_output": "出力",
"render.stage_panel_count": "Saved panels",
"render.stage_panel_count": "保存済みパネル",
"render.stage_preview_note": "統合プレビュー · 直接光",
"render.state_completed": "完成画像",
"render.state_in_progress": "画像を生成中",
"render.state_model": "モデルプレビュー",
"render.strip_label": "ストリップ",
"render.studio_add_area": "エリアライトを追加",
"render.studio_ambient": "一定の環境放射輝度",
@@ -2670,6 +2818,7 @@
"render.volume_max": "最大境界 · ワールド mm",
"render.volume_min": "最小境界 · ワールド mm",
"render.volume_title": "有限の均一ボリューム",
"render.wait_add_light": "未対応:名前付きライトの作成",
"render.wait_batch": "未対応: 保存ショットのレンダーキュー",
"render.wait_compare": "未対応: 保持された画像の比較",
"render.wait_paint": "未対応: 製品の選択と外観のペイント",
@@ -2784,6 +2933,10 @@
"sketch_text.path_hint": "文字が沿うスケッチの曲線: ここをクリックしてから曲線をクリック",
"sketch_text.use_font_file": "フォント ファイル {path} を使用",
"solid": "中実",
"status.editing": "編集中: {name}",
"status.under_constrained": "拘束不足(自由度 {count})",
"status.under_constrained_one": "拘束不足(自由度 {count})",
"status.units_tip": "デザインの単位。クリックして変更",
"the angle must be a number": "角度は数値にしてください",
"the curves must be in one sketch": "曲線は1つのスケッチ内にある必要があります",
"the direction must be an axis, a sketch line or a straight edge": "方向は、軸、スケッチ線、または直線のエッジにしてください",
@@ -2822,8 +2975,10 @@
"timeline.move_to_group": "フィーチャーをフォルダへ移動",
"timeline.rename_group": "フィーチャーフォルダの名前を変更",
"timeline.reorder_many": "フィーチャーの順序を変更",
"timeline.suppress_features": "フィーチャーを抑制",
"timeline.suppress_folders_tip": "フォルダーと入れ子のフォルダー内のすべてのフィーチャーを抑制、または抑制を解除します",
"timeline.ungroup": "フィーチャーフォルダを削除",
"timeline.unsuppress_features": "フィーチャーの抑制を解除",
"to the face": "面まで",
"to the next face": "次の面まで",
"toolbar.customize_note": "{tab}タブ。コマンドにチェックを入れると、ツールバーにボタンが追加されます。パネルのドロップダウンの行をツールバーにドラッグしたり、ボタンをツールバーの外にドラッグして削除したり、ドロップダウンの行のピンを使うこともできます。",
@@ -3234,6 +3389,8 @@
"ui.cancel": "キャンセル",
"ui.canvases": "キャンバス",
"ui.center": "中心",
"ui.center_arc_follows_cursor": "中心点円弧はカーソルの向きに従う",
"ui.center_arc_follows_cursor_tip": "中心点円弧は、カーソルが中心の周りを回る向きに合わせて時計回りまたは反時計回りに描かれます。オフ: 常に始点から反時計回りです。",
"ui.centerline": "中心線",
"ui.chain": "チェーン",
"ui.chamfer": "面取り",
@@ -3646,6 +3803,15 @@
"ui.plugins_skipped": "{count} 個のフォルダを読めませんでした。",
"ui.plugins_version": "{version}",
"ui.point": "点",
"ui.point_cloud_auto_range": "範囲を自動設定",
"ui.point_cloud_color_by": "色の付け方",
"ui.point_cloud_file_colors": "ファイルの色",
"ui.point_cloud_height_high": "最高の高さ",
"ui.point_cloud_height_low": "最低の高さ",
"ui.point_cloud_no_file_colors": "ファイルに色情報がありません",
"ui.point_cloud_single_color": "単色",
"ui.point_cloud_size": "点のサイズ",
"ui.point_cloud_units": "ファイルの単位",
"ui.point_under_the_cursor": "カーソル下の点",
"ui.points": "点",
"ui.polygon": "ポリゴン",
@@ -3899,11 +4065,13 @@
"update.installed": "SolidCraft {version} をインストールしました。再起動すると使えます。",
"update.installer_started": "SolidCraft の終了後にインストーラーが起動します。",
"update.later": "後で通知",
"update.managed": "アップデートがあります。{manager} で SolidCraft を更新してください",
"update.no_notes": "このリリースにはリリースノートがありません。",
"update.package_hint": "このコピーは {kind} からインストールされました。新しいパッケージをダウンロードし、次のコマンドでインストールします:",
"update.prefs_dev": "これは開発ビルド ({version}) のため、アップデートを確認しません。",
"update.prefs_flatpak": "このコピー ({version}) は Flatpak が更新します: {command} を実行するか、ソフトウェアセンターを使ってください。SolidCraft 自身は確認しません。",
"update.prefs_label": "アップデート",
"update.prefs_managed": "これは {manager} が管理している SolidCraft {version} です。確認では GitHub に最新リリースを問い合わせるだけで、ほかには何も送信しません。SolidCraft は {manager} で更新してください。",
"update.prefs_note": "これは SolidCraft {version} ({kind}) です。確認では GitHub に最新リリースを問い合わせるだけで、ほかには何も送信しません。",
"update.prefs_web": "ページを再読み込みすると、最新のウェブアプリを取得します。ネットワークが利用できない場合や、更新のダウンロードが時間内に完了しない場合は、保存済みのバージョンを開きます。進行中のダウンロードは次回の再読み込みに向けて続行されます。",
"update.release_page": "リリースページを開く",
@@ -3920,6 +4088,7 @@
"values must be finite numbers": "値は有限の数値にしてください",
"values must be numbers or expressions": "値は数値または式にしてください",
"view.auto_ortho_faces": "回転中は透視投影、面を正面に見ると平行投影",
"view.render_inspector": "レンダーインスペクター",
"web.files.insert_drawings_hint": "CADファイル、メッシュ、DXF、SVGを開いているデザインに挿入",
"web.files.insert_stored_drawings_hint": "開いているデザインに挿入(CADファイル、メッシュ、DXF、SVG)",
"web.open_full": "SolidCraft で開く",
+154
View File
@@ -668,6 +668,7 @@
"appearance.pbr_opaque": "불투명 PBR",
"appearance.roughness": "거칠기",
"appearance.surface_response": "표면 반응",
"at the face": "면에서",
"bom.group_children": "하위 부품을 상위 부품 행에 표시",
"browser.body_name": "바디{number}",
"browser.calibrate_title": "보정: {name}",
@@ -676,6 +677,7 @@
"browser.capture_one": "구성요소가 이동되었지만 위치가 캡처되지 않았습니다.",
"browser.component_name": "컴포넌트{number}",
"browser.edit_canvas_title": "캔버스 편집: {name}",
"browser.edit_point_cloud_title": "포인트 클라우드 편집: {name}",
"browser.move_title": "이동: {name}",
"browser.parameters": "매개변수 ({count})",
"browser.precision": "정밀도: {precision}",
@@ -888,6 +890,10 @@
"cmd.plastic_rest": "레스트",
"cmd.plastic_rules": "플라스틱 규칙 목록",
"cmd.plastic_snap_fit": "스냅핏",
"cmd.pointcloud_delete": "포인트 클라우드 삭제",
"cmd.pointcloud_edit": "포인트 클라우드 편집",
"cmd.pointcloud_insert": "포인트 클라우드 삽입",
"cmd.pointcloud_list": "포인트 클라우드",
"cmd.render_backends": "렌더링 백엔드",
"cmd.render_batch": "모든 샷 렌더링",
"cmd.render_camera_fit": "렌더 대상 화면 맞춤",
@@ -907,9 +913,14 @@
"cmd.render_library_surface_prepare": "제작된 표면 맵 준비",
"cmd.render_library_surface_unbind": "Remove Surface Binding",
"cmd.render_library_targets": "렌더 리소스 대상 찾기",
"cmd.render_lights_add": "조명 추가",
"cmd.render_lights_solo": "광원 단독 표시",
"cmd.render_output_size": "렌더 출력 크기 설정",
"cmd.render_preflight": "렌더 요청 확인",
"cmd.render_result_close": "렌더 이미지 닫기",
"cmd.render_result_copy": "렌더 이미지 복사",
"cmd.render_result_pin": "표시된 렌더 결과 유지",
"cmd.render_result_view": "렌더 이미지 보기",
"cmd.render_scene": "렌더링 장면 검사",
"cmd.render_settings_apply": "렌더링 카메라 적용",
"cmd.render_settings_capture_hdr": "HDR과 렌더 설정 저장",
@@ -1004,6 +1015,7 @@
"cmd.sketch_spline_control_point": "제어점 스플라인",
"cmd.sketch_spline_control_point_5": "제어점 스플라인(5차)",
"cmd.sketch_spline_fit_point": "맞춤점 스플라인",
"cmd.sketch_spline_reverse": "스플라인 방향 반전",
"cmd.sketch_spun_profile": "회전 프로파일",
"cmd.sketch_toggle_driven": "종속 치수 전환",
"cmd.sketch_transform": "변환",
@@ -1162,6 +1174,7 @@
"help.licences": "라이선스",
"help.mit": "MIT 라이선스",
"help.notice": "NOTICE",
"help.pointcloud.insert": "PLY, XYZ 또는 PTS 스캔을 모델 옆에 포인트 클라우드로 삽입합니다. 측정 데이터를 참고하여 모델링할 때 사용합니다.",
"help.solid.chamfer": "선택한 모서리를 지정한 거리만큼 평평하게 깎습니다. 날카로운 모서리를 다듬거나 핀 또는 나사가 쉽게 들어가도록 할 때 사용합니다.",
"help.solid.extrude": "스케치 프로파일을 지정한 거리만큼 3D로 밀어내 재료를 더하거나 제거합니다. 평면 윤곽을 블록, 보스 또는 포켓으로 만들 때 사용합니다.",
"help.solid.fillet": "선택한 모서리를 지정한 반지름의 매끄러운 원호로 둥글게 만듭니다. 강도, 편의성 또는 외관을 위해 날카로운 모서리를 부드럽게 할 때 사용합니다.",
@@ -1169,6 +1182,8 @@
"help.solid.revolve": "스케치 프로파일을 축을 중심으로 회전시켜 둥근 솔리드를 만듭니다. 샤프트, 손잡이, 그릇 및 중심선을 기준으로 둥근 모든 부품에 사용합니다.",
"help.solid.shell": "솔리드의 속을 비우고 지정한 두께의 벽을 남기며, 선택한 면을 열 수도 있습니다. 상자, 커버 및 기타 얇은 벽 부품에 사용합니다.",
"help.third_party": "타사 라이선스",
"history.sketch_under_constrained": "구속 부족 (자유도 {count}개)",
"history.sketch_under_constrained_one": "구속 부족 (자유도 {count}개)",
"home.back": "{name}(으)로 돌아가기",
"home.crashes": "충돌 보고서",
"home.design_workspace": "설계",
@@ -1521,6 +1536,13 @@
"prefs.left_click": "왼쪽 클릭",
"prefs.live_input": "실시간 입력",
"prefs.live_preview": "실시간 미리보기",
"prefs.max_file_size": "열 수 있는 최대 파일 크기",
"prefs.max_file_size_design": "디자인 파일",
"prefs.max_file_size_flag": "이번 실행은 --max-file-size {size}로 시작했으며 SolidCraft를 닫을 때까지 적용됩니다. 이 설정은 해당 옵션 없이 시작할 때 적용됩니다.",
"prefs.max_file_size_note": "기본값: 512 MB 및 256 MB, 최대 16 GB. 파일을 열려면 파일 크기의 몇 배에 달하는 메모리가 필요합니다. 큰 가져오기 데이터를 보관하는 디자인은 저장하고 다시 열기 위해 디자인 파일 한도를 높여야 할 수 있습니다.",
"prefs.max_file_size_step": "STEP 및 가져오기 파일",
"prefs.max_file_size_web": "웹 앱은 STEP 및 가져오기 파일을 512 MB까지, 디자인을 256 MB까지 엽니다. 브라우저 탭은 더 큰 파일을 담을 수 없습니다. 데스크톱 앱에서는 이 한도를 높일 수 있습니다.",
"prefs.megabytes": "MB",
"prefs.minutes": "분",
"prefs.minutes_suffix": " min",
"prefs.mouse": "마우스 및 트랙패드",
@@ -1558,6 +1580,8 @@
"prefs.sketch_selection": "스케치 및 선택",
"prefs.sketch_selection_heading": "스케치 및 선택",
"prefs.sketch_selection_note": "스케치가 스냅되는 방식과 클릭으로 선택하는 방식입니다.",
"prefs.sketch_state_icons": "스케치 아이콘을 상태 색상으로 표시",
"prefs.sketch_state_icons_note": "이력과 브라우저에서 스케치 아이콘이 상태(구속 부족, 완전 구속, 충돌)의 색상을 띠고 도구 설명에 상태가 표시됩니다. 끄기: 모든 스케치가 같은 아이콘을 사용합니다.",
"prefs.sketch_tab_always": "스케치 탭 항상 표시",
"prefs.sketch_tab_always_note": "스케치가 열려 있지 않을 때도 스케치 탭이 리본에서 모델 바로 다음에 남아 있습니다. 그 그리기 도구는 선택한 평면이나 면, 또는 다음에 고르는 평면에서 스케치를 시작하고 그 안에서 도구를 바로 쓸 수 있게 합니다.",
"prefs.sketch_tracking_guides": "중심 및 중점 정렬 안내선",
@@ -1567,6 +1591,8 @@
"prefs.source": "소스",
"prefs.speed": "속도",
"prefs.stand_up_note": "STEP 또는 IGES 파일이 Z가 위쪽인 상태로 열린 후 X축 기준 90도 회전을 제안합니다.",
"prefs.status_bar": "상태 표시줄",
"prefs.status_bar_note": "창 아래쪽 가장자리의 얇은 표시줄에 편집 중인 항목, 열려 있는 스케치의 상태(해당 색상으로), 설계 단위가 표시됩니다. 단위를 클릭하면 변경할 수 있습니다. 스케치 상태는 더 이상 뷰 모서리에 표시되지 않습니다.",
"prefs.status_done": "완료",
"prefs.status_error": "오류",
"prefs.status_selected": "선택됨",
@@ -1608,6 +1634,45 @@
"recovery.never_saved": "저장된 적 없음",
"render.add_ground": "Add fitted ground to setup",
"render.all_shots_label": "모든 샷",
"render.appearance_editor.absorption": "흡수",
"render.appearance_editor.base_colour": "기본 색상",
"render.appearance_editor.body_reason": "광학 바인딩은 하나의 전체 바디를 대상으로 합니다. 편집하려면 바디를 선택하세요.",
"render.appearance_editor.boundary": "경계",
"render.appearance_editor.box": "상자",
"render.appearance_editor.choose_image": "이미지 선택…",
"render.appearance_editor.closed": "닫힌 솔리드",
"render.appearance_editor.coat": "클리어 코트",
"render.appearance_editor.cpu_reason": "CPU 광선 추적이 필요합니다.",
"render.appearance_editor.glass": "유리와 흡수",
"render.appearance_editor.glass_enable": "이 바디에 물리 유리 사용",
"render.appearance_editor.glass_note": "닫힌 유리는 형상 거리를 사용합니다. 같은 바디에 클리어 코트나 맵을 함께 사용할 수 없습니다.",
"render.appearance_editor.gpu_reason": "GPU 경로 추적이 필요합니다.",
"render.appearance_editor.ior": "IOR",
"render.appearance_editor.maps": "맵",
"render.appearance_editor.normal": "노멀",
"render.appearance_editor.normal_strength": "노멀 강도",
"render.appearance_editor.origin": "원점",
"render.appearance_editor.recipe": "레시피",
"render.appearance_editor.reflection_sampling": "모든 반사 바디의 샘플링",
"render.appearance_editor.reflections": "반사",
"render.appearance_editor.remove": "저장된 설정 제거",
"render.appearance_editor.remove_note": "이 샷에서 저장된 광학 설정을 제거합니다. 형상과 지정된 외관은 유지됩니다.",
"render.appearance_editor.remove_title": "저장된 광학 설정을 제거할까요?",
"render.appearance_editor.replace": "이 바디의 저장된 광학 설정 교체",
"render.appearance_editor.revalidate": "이 바디 다시 검증",
"render.appearance_editor.save": "이 광학 설정 저장",
"render.appearance_editor.save_reason": "저장하려면 현재 GPU 바디에 물리 유리가 필요합니다. 코트와 로컬 이미지 맵은 임시로 유지됩니다.",
"render.appearance_editor.saved": "이 샷에 저장됨",
"render.appearance_editor.saved_state": "이 광학 설정은 이 샷과 함께 저장되어 있습니다.",
"render.appearance_editor.scatter_reason": "내부 산란에는 닫힌 솔리드 경계가 필요합니다.",
"render.appearance_editor.temporary": "이 광학 설정은 이 탭에서 임시로 사용됩니다. 샷과 함께 유지하려면 저장하세요.",
"render.appearance_editor.thickness": "두께",
"render.appearance_editor.thickness_reason": "두께는 얇은 시트에 적용됩니다. 닫힌 유리는 형상 거리를 사용합니다.",
"render.appearance_editor.thin": "얇은 시트",
"render.appearance_editor.u_repeat": "U 반복",
"render.appearance_editor.v_repeat": "V 반복",
"render.appearance_editor.v_up": "텍스처 V가 위쪽을 향함",
"render.appearance_editor.weight": "가중치",
"render.appearances": "외관",
"render.background": "장면 배경",
"render.bloom_enable": "표시 블룸",
@@ -1620,6 +1685,9 @@
"render.category_paint": "페인트",
"render.category_plastic": "플라스틱",
"render.category_wood": "목재",
"render.check_opaque": "투명도: 불투명 표면만 지원하며 배경 알파를 지원합니다.",
"render.check_sorted_alpha": "투명도: 정렬된 알파 근사.",
"render.check_transmission": "투명도: 물리적 투과.",
"render.clear_stage": "Remove saved stage",
"render.cpu_budget": "CPU 리소스 프로필",
"render.cpu_budget_bodies": "바디 수 상한",
@@ -1632,6 +1700,7 @@
"render.cpu_budget_studio": "스튜디오 (큰 이미지)",
"render.cpu_budget_triangles": "삼각형 수 상한",
"render.cpu_budget_workstation": "워크스테이션 (대규모 장면)",
"render.effects_gpu_only": "이 효과를 사용하려면 GPU 경로 추적이 필요합니다. 페이지를 바꿔도 렌더러는 자동으로 전환되지 않습니다.",
"render.emitted_axis": "방출 축",
"render.environment_tiles_require_raytrace": "환경에는 CPU 또는 GPU 광선 추적이 필요합니다. 출력 편집기에서 선택하세요.",
"render.error_area_samples": "area_samples는 1, 4, 16 또는 64여야 함",
@@ -1730,6 +1799,36 @@
"render.include_rig_cards": "Include passive cards and flags",
"render.inner_half_angle": "내부 반각",
"render.inspector": "속성 검사기",
"render.inspector_advanced": "고급",
"render.inspector_all_users": "사용 대상 {count}개 모두",
"render.inspector_applies_to": "적용 범위",
"render.inspector_both_users": "두 사용 대상",
"render.inspector_chain": "외관 출처",
"render.inspector_colour": "색상",
"render.inspector_edit": "편집",
"render.inspector_finish": "마감",
"render.inspector_from_material": "재질에서 상속",
"render.inspector_gloss": "유광",
"render.inspector_material": "재질",
"render.inspector_matte": "무광",
"render.inspector_metal": "금속",
"render.inspector_opacity": "불투명도",
"render.inspector_opaque_help": "마감 및 광학 필드는 불투명 PBR(불투명도 1)을 사용합니다. 불투명도를 낮추면 스칼라 음영을 사용합니다.",
"render.inspector_pick_target": "이 유형의 대상을 선택하여 외관을 확인하세요.",
"render.inspector_remove": "재정의 제거",
"render.inspector_replace": "바꾸기…",
"render.inspector_reset": "초기화",
"render.inspector_satin": "반광",
"render.inspector_save_library": "라이브러리에 저장…",
"render.inspector_set_all": "외관이 혼합되어 있습니다. 편집하면 선택한 모든 대상에 외관을 설정합니다.",
"render.inspector_soon": "출시 예정",
"render.inspector_target_limit": "외관을 확인할 대상을 10000개 이하로 선택하세요.",
"render.inspector_textures": "텍스처",
"render.inspector_this_body": "이 바디",
"render.inspector_this_component": "이 구성요소",
"render.inspector_this_face": "이 면",
"render.inspector_this_target": "이 대상",
"render.inspector_used_by": "사용 대상 {count}개",
"render.layer_off_suffix": " (꺼짐)",
"render.library_appearance_available": "사용 가능한 외관",
"render.library_applicable": "적용 가능한 리소스",
@@ -1792,8 +1891,30 @@
"render.opaque_image_summary": "미리보기 PNG · 불투명 PBR · 블렌딩 투명도",
"render.opaque_preview_help": "불투명 PBR 및 단색 표시, 블렌딩 투명도를 지원합니다. 굴절, HDRI 및 최종 렌더링은 아직 지원하지 않습니다.",
"render.outer_half_angle": "외부 반각",
"render.output_advanced": "고급",
"render.output_backend_ready": "준비 완료",
"render.output_check": "확인",
"render.output_check_help": "확인을 실행하면 렌더링을 시작하지 않고 요청을 검토합니다.",
"render.output_check_summary": "검사 · 참고 {count}개",
"render.output_copy": "복사",
"render.output_destination": "저장 위치",
"render.output_destination_help": "완성된 이미지를 내보낼 때 대상 파일을 선택합니다.",
"render.output_dimensions": "크기",
"render.output_effects": "효과",
"render.output_export": "PNG 내보내기…",
"render.output_exr_unavailable": "여기서는 EXR 출력을 설정할 수 없습니다. 선형 EXR 내보내기에는 선형 원본을 보관한 완료된 GPU 이미지가 필요합니다.",
"render.output_fit": "맞춤",
"render.output_format": "형식",
"render.output_format_status": "PNG는 완료된 표시 이미지 그대로입니다. 선택적 GPU EXR은 표시 필터 전 선형 FLOAT32 방사 휘도를 보존합니다. 미리보기 캐시나 다른 작업으로 바꾸어 내보내지 않습니다.",
"render.output_image": "이미지",
"render.output_largest": "{width} × {height} 최대",
"render.output_presets_help": "형식, 품질 및 검사 목록은 출력 편집기에 있습니다.",
"render.output_quality": "품질",
"render.output_quality_waiting": "품질 규약 대기 중",
"render.output_ready": "렌더링 준비 완료",
"render.output_size_ok": "크기 정상 · {width} × {height} px",
"render.output_tools_inspector": "출력 검사기에서 크기와 형식을 편집하세요.",
"render.output_view_image": "이미지 보기",
"render.paint_label": "칠하기",
"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.",
@@ -1823,11 +1944,20 @@
"render.property_source": "출처: {id} - {locator}",
"render.property_uncertainty": "불확도: {value}",
"render.property_unknown": "알 수 없음",
"render.quality_balanced": "균형",
"render.quality_custom": "사용자 지정",
"render.quality_draft": "초안",
"render.quality_high": "높음",
"render.radial_assign": "외관 지정",
"render.radial_compare": "비교",
"render.radial_fit": "제품 맞추기",
"render.radial_save": "샷 저장",
"render.regrade_label": "색상 재조정",
"render.reload_settings": "저장된 설정 다시 불러오기",
"render.replace_label": "바꾸기",
"render.replace_saved_surface": "Replace an existing saved binding for this target",
"render.restore_camera": "저장된 카메라 복원",
"render.result_actual": "100%",
"render.result_actual_size": "100%",
"render.result_reduced_display": "표시 미리보기를 뷰포트 장치 또는 캐시 제한에 맞춰 축소했습니다. 보관된 PNG와 내보내기는 전체 해상도를 유지합니다. 이 축소 보기에서는 원본 100% 검사가 불가능합니다.",
"render.return_design": "설계로 돌아가기",
@@ -1871,11 +2001,15 @@
"render.shots_save": "샷 저장",
"render.shots_update": "샷 업데이트",
"render.show_shots_label": "샷 스트립 표시",
"render.size_4k": "4K",
"render.size_4k_uhd": "4K UHD",
"render.size_custom": "사용자 지정…",
"render.size_full_hd": "풀 HD",
"render.size_hd": "HD",
"render.size_presets": "크기 프리셋",
"render.size_presets_pending": "출력 편집기와 함께 제공 예정",
"render.size_preview": "미리보기",
"render.size_square": "정사각형",
"render.spot_cone": "스폿 원뿔",
"render.stage_frame": "구도",
"render.stage_light": "조명",
@@ -1884,6 +2018,9 @@
"render.stage_output": "출력",
"render.stage_panel_count": "Saved panels",
"render.stage_preview_note": "통합 미리 보기 · 직접광",
"render.state_completed": "완료된 이미지",
"render.state_in_progress": "이미지 생성 중",
"render.state_model": "모델 미리보기",
"render.strip_label": "스트립",
"render.studio_tiles_require_gpu": "스튜디오에는 GPU 경로 추적이 필요합니다. 출력 편집기에서 GPU를 선택하세요.",
"render.studios_environments": "스튜디오 및 환경",
@@ -1926,6 +2063,7 @@
"render.unavailable_preview": "이 설정은 사용할 수 없는 미리보기 프로필을 요청합니다.",
"render.unsaved": "저장하지 않은 미리보기 변경 사항",
"render.use_preview": "통합 미리보기 사용",
"render.wait_add_light": "아직 지원되지 않음: 이름이 있는 조명 만들기",
"render.wait_batch": "아직 지원되지 않음: 저장된 샷 렌더 대기열",
"render.wait_compare": "아직 지원되지 않음: 보관된 이미지 비교",
"render.wait_paint": "아직 지원되지 않음: 제품 선택 및 모양 페인팅",
@@ -2027,6 +2165,10 @@
"sketch_text.path_hint": "텍스트가 따라갈 스케치 곡선: 여기를 클릭한 다음 곡선을 클릭",
"sketch_text.use_font_file": "글꼴 파일 {path} 사용",
"solid": "솔리드",
"status.editing": "{name} 편집 중",
"status.under_constrained": "구속 부족 (자유도 {count}개)",
"status.under_constrained_one": "구속 부족 (자유도 {count}개)",
"status.units_tip": "설계 단위입니다. 클릭하여 변경",
"the angle must be a number": "각도는 숫자여야 합니다",
"the curves must be in one sketch": "곡선은 하나의 스케치 안에 있어야 합니다",
"the direction must be an axis, a sketch line or a straight edge": "방향은 축, 스케치 선 또는 직선 모서리여야 합니다",
@@ -2055,7 +2197,9 @@
"timeline.move_to_group": "특성을 폴더로 이동",
"timeline.rename_group": "특성 폴더 이름 변경",
"timeline.reorder_many": "특성 순서 변경",
"timeline.suppress_features": "특성 비활성화",
"timeline.ungroup": "특성 폴더 제거",
"timeline.unsuppress_features": "특성 비활성화 해제",
"to the face": "면까지",
"to the next face": "다음 면까지",
"toolbar.customize_note": "{tab} 탭입니다. 명령을 선택하면 도구 모음에 해당 버튼이 추가됩니다. 패널의 드롭다운에서 행을 도구 모음으로 끌거나, 버튼을 도구 모음 밖으로 끌어 제거하거나, 드롭다운 행의 고정 핀을 사용할 수도 있습니다.",
@@ -2799,6 +2943,15 @@
"ui.plugins_skipped": "폴더 {count}개를 읽을 수 없습니다.",
"ui.plugins_version": "{version}",
"ui.point": "점",
"ui.point_cloud_auto_range": "범위 자동 설정",
"ui.point_cloud_color_by": "색상 기준",
"ui.point_cloud_file_colors": "파일 색상",
"ui.point_cloud_height_high": "최고 높이",
"ui.point_cloud_height_low": "최저 높이",
"ui.point_cloud_no_file_colors": "파일에 색상 정보가 없습니다",
"ui.point_cloud_single_color": "단색",
"ui.point_cloud_size": "점 크기",
"ui.point_cloud_units": "파일 단위",
"ui.point_under_the_cursor": "커서 아래의 점",
"ui.points": "점",
"ui.polygon": "다각형",
@@ -3046,5 +3199,6 @@
"use a positive nominal diameter and a pitch smaller than it": "양수인 호칭 지름과 그보다 작은 피치를 사용하세요",
"values must be finite numbers": "값은 유한한 숫자여야 합니다",
"values must be numbers or expressions": "값은 숫자 또는 식이어야 합니다",
"view.render_inspector": "렌더 검사기",
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ 선택 · Enter / Tab 완성 · Esc 닫기"
}
+242
View File
@@ -1,4 +1,5 @@
{
"(active rule)": "(peraturan aktif)",
"0 turns it off": "0 untuk matikan",
"1. Open GitHub draft below, then paste the report copied to your clipboard.": "1. Buka draf GitHub di bawah, kemudian tampal laporan yang disalin ke papan klip anda.",
"1. Open GitHub draft below. Your title and report will be filled in.": "1. Buka draf GitHub di bawah. Tajuk dan laporan anda akan diisi secara automatik.",
@@ -52,16 +53,20 @@
"Apply your appearance first. Exports assigned appearances and materials created in this design.": "Guna penampilan anda dahulu. Mengeksport penampilan yang ditetapkan dan bahan yang dicipta dalam reka bentuk ini.",
"Arc length": "Panjang lengkok",
"Area": "Luas",
"As Picked": "Seperti Dipilih",
"As-Built": "Seperti Dibina",
"Assign Rule": "Tetapkan Peraturan",
"Attachment": "Pautan",
"BLEND SURFACE": "PERMUKAAN GAUNAN",
"BOUNDARY SURFACE": "PERMUKAAN SEMPADAN",
"BROWSER": "PELAYAR",
"Ball": "Bebola",
"Bar": "Bar",
"Base Center": "Pusat Tapak",
"Base Height": "Ketinggian Tapak",
"Bend": "Lenturan",
"Bend Edges": "Tepi Lenturan",
"Bend Line": "Garis Lenturan",
"Bend Line Position": "Kedudukan Garis Lenturan",
"Bend Position": "Kedudukan Lenturan",
"Bend Radius": "Jejari Lenturan",
@@ -73,6 +78,7 @@
"Body (last if none)": "Jasad (yang terakhir jika tiada)",
"Body to split": "Jasad untuk dibelah",
"Body, or faces to take out": "Jasad, atau muka untuk dikeluarkan",
"Both": "Kedua-duanya",
"Bottom Left": "Bawah Kiri",
"Bottom Middle": "Bawah Tengah",
"Bottom Right": "Bawah Kanan",
@@ -104,6 +110,7 @@
"Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.": "Pilih satah baharu. Geometri dan dimensi lakaran dikekalkan dalam koordinat dua dimensi lakaran tersebut.",
"Choose…": "Pilih…",
"Circle center": "Pusat bulatan",
"Circle or sphere": "Bulatan atau sfera",
"Circular": "Bulat",
"Circumference": "Lilitan",
"Clockwise": "Ikut arah jam",
@@ -120,6 +127,7 @@
"Config Table": "Jadual Konfigurasi",
"Configuration": "Konfigurasi",
"Configuration rules": "Peraturan konfigurasi",
"Constant radius": "Jejari malar",
"Contact": "Sentuhan",
"Contact Sets": "Set Sentuhan",
"Contact sets": "Set sentuhan",
@@ -155,7 +163,9 @@
"Custom": "Tersuai",
"Customize Toolbar": "Sesuaikan Bar Alat",
"Customize…": "Suaikan…",
"Cylindrical": "Silinder",
"Cylindrical face": "Muka silinder",
"Deboss": "Tenggelamkan",
"Delete": "Padam",
"Delete Folders and Features…": "Padam Folder dan Ciri…",
"Delete Selected Features…": "Padam Ciri Terpilih…",
@@ -190,6 +200,7 @@
"Duration": "Tempoh",
"Each new shape is also dimensioned from the sketch origin until nothing of it is left free. A shape already attached to the origin or to placed geometry gets none.": "Setiap bentuk baharu juga didimensikan dari asalan lakaran sehingga tiada yang bebas. Bentuk yang sudah terikat pada asalan atau geometri yang diletakkan tidak mendapat apa-apa.",
"Each shape you draw gets all its dimensions, at the sizes you drew (typed values win). A size the sketch already sets is left out.": "Setiap bentuk yang anda lukis mendapat semua dimensinya pada saiz yang dilukis (nilai yang ditaip diutamakan). Saiz yang sudah ditetapkan oleh lakaran ditinggalkan.",
"Edge": "Tepi",
"Edges": "Tepi",
"Edit": "Sunting",
"Edit Profile Sketch": "Sunting Lakaran Profil",
@@ -198,6 +209,7 @@
"End Radius": "Jejari Akhir",
"End bulge": "Bonjolan hujung",
"End continuity": "Keselanjaran hujung",
"Equal Distance": "Jarak Sama",
"Expand": "Kembangkan",
"Expected result (optional)": "Hasil yang dijangka (pilihan)",
"Explode": "Leraikan",
@@ -225,6 +237,8 @@
"Faces": "Muka",
"Faces / edges / vertices": "Muka / tepi / bucu",
"Faces to split": "Muka untuk dibelah",
"Faces/Body": "Muka/Jasad",
"Factor must be a positive number": "Faktor mestilah nombor positif",
"Fastener": "Pengikat",
"Favorites": "Kegemaran",
"Favourite": "Kegemaran",
@@ -234,18 +248,25 @@
"Filter…": "Tapis…",
"Find in Browser": "Cari dalam Pelayar",
"Finish": "Siap",
"First edge": "Tepi pertama",
"First plane": "Satah pertama",
"First point": "Titik pertama",
"Fit and G2 corners require Constant radius.": "Padanan dan sudut G2 memerlukan Jejari malar.",
"Fixed Face": "Muka Tetap",
"Flange Gap": "Jarak Bebibir",
"Flange thickness": "Ketebalan bebibir",
"Flanges": "Bebibir",
"Flat Pattern": "Bentangan Rata",
"Flat size": "Saiz rata",
"Flat size X": "Saiz rata X",
"Flat size Y": "Saiz rata Y",
"Follow camera (Look At sketches)": "Ikut kamera (Pandang Tepat pada lakaran)",
"Follow the camera (Look At sketches)": "Ikut kamera (Pandang Tepat pada lakaran)",
"Font": "Fon",
"Free Move": "Gerakan Bebas",
"From": "Dari",
"From Bodies": "Daripada Jasad",
"From Snap": "Dari Lekapan",
"From edges": "Daripada tepi",
"Full Length": "Panjang Penuh",
"Full thread depth": "Kedalaman bebenang penuh",
@@ -257,10 +278,13 @@
"Halo under sketch curves": "Halo di bawah lengkung lakaran",
"Height": "Ketinggian",
"Height From": "Ketinggian Dari",
"Hem Gap": "Jarak Kelim",
"Hidden edges": "Tepi tersembunyi",
"History layout": "Susun atur sejarah",
"History names": "Nama sejarah",
"Hole (optional)": "Lubang (pilihan)",
"Hole Depth": "Kedalaman Lubang",
"Hole Diameter": "Diameter Lubang",
"Holes": "Lubang",
"Holes (edges on them)": "Lubang (tepi lubang)",
"Home view": "Pandangan utama",
@@ -283,12 +307,16 @@
"Issue title": "Tajuk isu",
"Items": "Item",
"JOINTS": "SENDI",
"Joint 1": "Sendi 1",
"Joint 2": "Sendi 2",
"Joint Origin1": "Titik Asal Sendi1",
"K Factor": "Faktor K",
"Keep (points on it)": "Kekalkan (titik padanya)",
"Keep Tools": "Kekalkan Alat",
"Keep of the other surface": "Kekalkan permukaan lain",
"Key a joint's value at this step": "Tetapkan bingkai utama bagi nilai sendi pada langkah ini",
"Keys": "Bingkai Utama",
"Knife Edge": "Tepi Pisau",
"L-Angle": "Sudut-L",
"LIBRARY": "KOLEKSI",
"LINKS": "PAUTAN",
@@ -301,9 +329,11 @@
"Letter Spacing": "Jarak Huruf",
"Library": "Koleksi",
"Library file": "Fail koleksi",
"Library response is missing": "Respons koleksi tiada",
"Limits": "Had",
"Line Spacing": "Jarak Baris",
"Line Type": "Jenis Garis",
"Line on plane": "Garis pada satah",
"Lines": "Garis",
"Listing the installed fonts…": "Menyenaraikan fon yang dipasang…",
"Load library": "Muat koleksi",
@@ -323,11 +353,14 @@
"Material Outside": "Bahan di Luar",
"Material copied. Existing body assignments are unchanged.": "Bahan disalin. Penetapan jasad sedia ada tidak berubah.",
"Max": "Maks",
"Max Thickness": "Ketebalan Maks",
"Maximum": "Maksimum",
"Mesh body": "Jasad mesh",
"Mid Radius": "Jejari Tengah",
"Middle double-click": "Klik dua kali butang tengah",
"Min": "Min",
"Min Draft": "Sudut Draf Min",
"Min Thickness": "Ketebalan Min",
"Minimum": "Minimum",
"Mirror Plane": "Satah Cermin",
"Mitre": "Serong",
@@ -347,6 +380,7 @@
"Name": "Nama",
"Name on import": "Nama semasa import",
"Nearest": "Terdekat",
"Neutral plane": "Satah neutral",
"New": "Baharu",
"New Comp.": "Komp. Baharu",
"New Sketch": "Lakaran Baharu",
@@ -357,6 +391,7 @@
"No matching appearances": "Tiada penampilan yang sepadan",
"No matching physical materials.": "Tiada bahan fizikal yang sepadan.",
"No visible bodies or sketches to export.": "Tiada jasad atau lakaran kelihatan untuk dieksport.",
"Nominal Radius": "Jejari Nominal",
"Nominal diameter": "Diameter nominal",
"None": "Tiada",
"Not available yet": "Belum tersedia",
@@ -385,22 +420,28 @@
"Overall length": "Panjang keseluruhan",
"Param. Graph": "Graf Param.",
"Parameter": "Parameter",
"Part length must be greater than zero and at most 2000 mm": "Panjang bahagian mestilah lebih besar daripada sifar dan paling banyak 2000 mm",
"Parts": "Bahagian",
"Pass through": "Tembus",
"Path": "Laluan",
"Pattern": "Corak",
"Per Edge": "Setiap Tepi",
"Physical material definitions": "Takrifan bahan fizikal",
"Pick a part size": "Pilih saiz bahagian",
"Pick the tool bodies": "Pilih jasad alat",
"Picked": "Dipilih",
"Picked point": "Titik yang dipilih",
"Pin-slot": "Pin-slot",
"Pitch": "Pic",
"Pivot": "Pangsi",
"Place at the origin instead": "Letak di titik asal",
"Planar": "Satah",
"Plastic Rules": "Peraturan Plastik",
"Play the joint's motion in the view": "Mainkan gerakan sendi dalam paparan",
"Playback": "Main balik",
"Plot": "Plot",
"Point on line": "Titik pada garis",
"Point on plane": "Titik pada satah",
"Position": "Kedudukan",
"Position (0 to 1)": "Kedudukan (0 hingga 1)",
"Position (G0)": "Kedudukan (G0)",
@@ -413,6 +454,7 @@
"Profiles or curves": "Profil atau lengkung",
"Profiles/Curves": "Profil/Lengkung",
"Quantity": "Kuantiti",
"Quantity must be at least 2": "Kuantiti mestilah sekurang-kurangnya 2",
"RECENT": "TERKINI",
"REFERENCE": "RUJUKAN",
"RULED SURFACE": "PERMUKAAN BERGARIS",
@@ -426,6 +468,8 @@
"Rectangular": "Segi empat tepat",
"Rectangular Tube": "Tiub Segi Empat Tepat",
"Reference face": "Muka rujukan",
"Relief Depth": "Kedalaman Pelepasan",
"Relief Width": "Lebar Pelepasan",
"Remove Tools": "Buang Alat",
"Remove condition": "Buang syarat",
"Remove from Folder": "Buang daripada Folder",
@@ -454,6 +498,7 @@
"Restore this studio's tools": "Pulihkan alat studio ini",
"Result": "Hasil",
"Retake screenshot": "Tangkap semula",
"Reveal Height": "Ketinggian Terdedah",
"Reverse": "Songsangkan",
"Reverse Direction": "Songsangkan Arah",
"Revert": "Kembalikan",
@@ -462,12 +507,15 @@
"Revert Position": "Kembalikan Kedudukan",
"Review the report and screenshot before opening GitHub.": "Semak laporan dan tangkapan skrin sebelum membuka GitHub.",
"Review your report on GitHub and click Submit new issue to publish it.": "Semak laporan anda di GitHub dan klik Submit new issue untuk menerbitkannya.",
"Revolute": "Berputar",
"Rib Length": "Panjang Rusuk",
"Rib Offset": "Ofset Rusuk",
"Rib Thickness": "Ketebalan Rusuk",
"Ribs": "Rusuk",
"Right Middle": "Tengah Kanan",
"Rigid": "Tegar",
"Rim Edge": "Tepi Bibir",
"Rim face": "Muka bibir",
"Ring of tools on the right button": "Cincin alat pada butang kanan",
"Roll Back Here": "Undur ke Sini",
"Roll History Marker Here": "Alih Penanda Sejarah ke Sini",
@@ -492,7 +540,9 @@
"Search": "Cari",
"Search commands": "Cari arahan",
"Search this library": "Cari dalam koleksi ini",
"Second edge": "Tepi kedua",
"Second plane": "Satah kedua",
"Second point": "Titik kedua",
"Section": "Keratan",
"Section (optional)": "Keratan (pilihan)",
"Section position": "Kedudukan keratan",
@@ -503,6 +553,15 @@
"Select bodies (a face picks its body)": "Pilih jasad (klik muka untuk pilih jasadnya)",
"Select bodies or faces instead": "Pilih jasad atau muka",
"Select edges": "Pilih tepi",
"Select points of the sketch being edited": "Pilih titik bagi lakaran yang sedang disunting",
"Select the centre the pattern turns around first": "Pilih pusat putaran corak dahulu",
"Select the curves to offset first": "Pilih lengkung yang hendak diofset dahulu",
"Select the mirror line": "Pilih garis cermin",
"Select the sketch curves or points to mirror first": "Pilih lengkung atau titik lakaran yang hendak dicermin dahulu",
"Select the sketch curves or points to move first": "Pilih lengkung atau titik lakaran yang hendak dialihkan dahulu",
"Select the sketch curves or points to pattern first": "Pilih lengkung atau titik lakaran yang hendak dicorakkan dahulu",
"Select the sketch curves or points to scale first": "Pilih lengkung atau titik lakaran yang hendak diskalakan dahulu",
"Select the sketch curves or points to transform first": "Pilih lengkung atau titik lakaran yang hendak ditransformasikan dahulu",
"Selection filter": "Penapis pilihan",
"Semantic faces share their appearance across instances.": "Muka semantik berkongsi penampilan pada semua salinan.",
"Send your report on GitHub": "Hantar laporan anda di GitHub",
@@ -522,9 +581,12 @@
"Sketch geometry and profiles": "Geometri dan profil lakaran",
"Sketch plane has no body-face attachment.": "Satah lakaran tiada pautan ke muka jasad.",
"Sketch points": "Titik lakaran",
"Slider": "Gelangsar",
"Slot width": "Lebar slot",
"Snap to face and edge centres": "Lekap pada pusat muka dan tepi",
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft ditutup sebelum reka bentuk disimpan. Buka simpanan automatik terakhirnya.",
"Spacing 2 must be a non-zero number": "Selang 2 mestilah nombor bukan sifar",
"Spacing must be a non-zero number": "Selang mestilah nombor bukan sifar",
"Specialise": "Khususkan",
"Speed": "Kelajuan",
"Splitting tool": "Alat pembelah",
@@ -535,6 +597,7 @@
"Start angle": "Sudut mula",
"Start bulge": "Bonjolan permulaan",
"Start continuity": "Keselanjaran permulaan",
"Stationary Side": "Sisi Pegun",
"Step": "Langkah",
"Steps": "Langkah",
"Steps to reproduce (optional)": "Langkah untuk menghasilkan semula (pilihan)",
@@ -549,6 +612,7 @@
"Suppressed": "Dinyahaktifkan",
"Surface": "Permukaan",
"Surface, or faces to turn over": "Permukaan, atau muka untuk diterbalikkan",
"Surfaces": "Permukaan",
"Swap Ends": "Tukar Hujung",
"TRIM": "PANGKAS",
"Tangent": "Tangen",
@@ -559,24 +623,34 @@
"Tap drill diameter": "Diameter gerudi tap",
"Target": "Sasaran",
"Target Body": "Jasad Sasaran",
"Target Face (optional)": "Muka Sasaran (pilihan)",
"Tear": "Koyakan",
"Technical details": "Butiran teknikal",
"The dashed sketch is a saved plane guide. Select a new face to repair the attachment.": "Lakaran putus-putus ialah panduan satah disimpan. Pilih muka baharu untuk baiki pautan.",
"The design changed. Reopen Change Part Size for the component.": "Reka bentuk telah berubah. Buka semula Tukar Saiz Bahagian untuk komponen itu.",
"The direction must be an axis or a straight sketch curve that lies in the sketch plane": "Arah mestilah paksi atau lengkung lakaran lurus yang terletak pada satah lakaran",
"The mirror line cannot also be mirrored": "Garis cermin tidak boleh turut dicerminkan",
"The mirror line must be a straight curve of this sketch, not an origin axis": "Garis cermin mestilah lengkung lurus bagi lakaran ini, bukan paksi titik asal",
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "Salinan asal komponen lakaran tiada. Pilih penempatan komponen yang sah sebelum paut semula.",
"Then": "Kemudian",
"These features will be deleted:": "Ciri-ciri berikut akan dipadam:",
"These features will fail:": "Ciri-ciri berikut akan gagal:",
"Thickness Outside": "Ketebalan Luar",
"Thickness Variation": "Variasi Ketebalan",
"Thin": "Nipis",
"Thin Extrude": "Penyemperitan Nipis",
"Third plane": "Satah ketiga",
"Third point": "Titik ketiga",
"This child is shared. Deleting it removes it from every copy of its parent.": "Komponen anak dikongsi. Jika dipadam, semua salinan induk turut kehilangannya.",
"This child is shared. Position changes apply to every copy of its parent.": "Komponen anak dikongsi. Kedudukan berubah pada semua salinan induk.",
"This face cannot provide a durable attachment. Choose another face or explicitly use a fixed plane.": "Muka ini tidak dapat kekalkan pautan. Pilih muka lain atau gunakan satah tetap.",
"This image is not attached to GitHub yet. Copy or save it, then attach it on GitHub.": "Imej ini belum dilampirkan pada GitHub. Salin atau simpannya, kemudian lampirkan di GitHub.",
"Thread depth": "Kedalaman bebenang",
"Thread diameter": "Diameter bebenang",
"Through point": "Melalui titik",
"To": "Ke",
"To Offset": "Ke Ofset",
"To Snap": "Ke Lekapan",
"To edges": "Ke tepi",
"Toe radius": "Jejari hujung",
"Tolerance": "Toleransi",
@@ -590,6 +664,7 @@
"Trimming tool (plane, face or curves)": "Alat pemangkas (satah, muka atau lengkung)",
"Turn off section analysis before exporting the view.": "Matikan analisis keratan sebelum mengeksport pandangan.",
"Type": "Jenis",
"Type a distance or an angle to move by": "Taip jarak atau sudut untuk mengalihkan",
"UNTRIM": "NYAHPANGKAS",
"Unit": "Unit",
"Unsuppress": "Aktifkan Semula",
@@ -634,6 +709,17 @@
"Z angle": "Sudut Z",
"Z distance": "Jarak Z",
"Zebra": "Zebra",
"`joint` must be an id or a name": "`joint` mestilah id atau nama",
"`params` must be an object": "`params` mestilah objek",
"`values` must be a list": "`values` mestilah senarai",
"a chamfer needs a corner of exactly two lines": "serong memerlukan sudut yang terdiri daripada tepat dua garis",
"a file operation is in progress": "operasi fail sedang berjalan",
"a picked curve is no longer in a sketch": "lengkung yang dipilih tidak lagi berada dalam lakaran",
"a picked curve's sketch has not been solved": "lakaran bagi lengkung yang dipilih belum diselesaikan",
"a picked edge has no points": "tepi yang dipilih tidak mempunyai titik",
"a picked edge is gone": "tepi yang dipilih sudah tiada",
"a picked edge's body is gone": "jasad bagi tepi yang dipilih sudah tiada",
"add a key for a joint first": "tambah bingkai utama untuk sendi dahulu",
"ai.connected_toast": "SolidCraft kini tersedia dalam {clients}. Mulakan semula untuk menggunakannya (Tetapan > Pembantu).",
"ai.prefs.allow_control": "Benarkan ejen AI menyambung (saluran kawalan / MCP)",
"ai.prefs.allow_control_note": "Membuka saluran kawalan pada komputer ini sahaja (127.0.0.1, port 27878 atau port bebas yang seterusnya). Program yang menyambung juga mesti menunjukkan token rahsia yang hanya boleh dibaca oleh akaun pengguna anda, dijana baharu pada setiap permulaan. Berkuat kuasa apabila SolidCraft dimulakan seterusnya. Dimatikan: tiada apa-apa yang mendengar melainkan anda memulakan SolidCraft dengan --control.",
@@ -674,6 +760,8 @@
"appearance.pbr_opaque": "PBR Legap",
"appearance.roughness": "Kekasaran",
"appearance.surface_response": "Tindak balas permukaan",
"at the face": "pada muka",
"bom.group_children": "Sub-bahagian dalam baris bahagiannya",
"browser.body_name": "Jasad{number}",
"browser.calibrate_title": "Tentukur: {name}",
"browser.capture_many": "{count} komponen telah dialihkan tetapi kedudukannya belum dirakam.",
@@ -684,6 +772,8 @@
"browser.move_title": "Alih: {name}",
"browser.parameters": "Parameter ({count})",
"browser.precision": "Ketepatan: {precision}",
"browser.sketch_attachment_lost": "{name} (pautan hilang)",
"browser.sketch_editing": "{name} (sedang disunting)",
"browser.sketch_name": "Lakaran{number}",
"browser.units": "Unit: {units}",
"cam.flat": "Muka rata",
@@ -693,7 +783,13 @@
"cam.roller": "Penggelek",
"cam.roller_pick": "Penggelek (tidak wajib)",
"cam.tip_pick": "Hujung (tidak wajib)",
"choose a profile": "pilih profil",
"choose the cam's and the follower's joints": "pilih sendi sesondol dan sendi pengikut",
"choose the joints to couple": "pilih sendi yang hendak digandingkan",
"click a plane or planar face": "klik satah atau muka satah",
"click sketch curves": "klik lengkung lakaran",
"cmd.appearance_assign": "Penampilan",
"cmd.appearance_chain": "Rantaian Penampilan",
"cmd.appearance_library": "Koleksi Penampilan",
"cmd.appearance_list": "Senarai Penampilan",
"cmd.appearance_paint": "Cat Penampilan",
@@ -1031,6 +1127,7 @@
"cmd.sketch_spline_control_point_5": "Splin Titik Kawalan (Darjah 5)",
"cmd.sketch_spline_fit_point": "Splin Titik Padanan",
"cmd.sketch_spline_insert_point": "Tambah Titik Splin",
"cmd.sketch_spline_reverse": "Songsangkan Arah Splin",
"cmd.sketch_spun_profile": "Profil Putaran",
"cmd.sketch_toggle_diameter": "Togol Jejari / Diameter",
"cmd.sketch_toggle_driven": "Togol Dimensi Dipacu",
@@ -1191,6 +1288,8 @@
"dxf.progress.write": "Menulis DXF (5/5)",
"dxf.tolerance": "Toleransi lengkung",
"dxf.tolerance_help": "Jasad kelihatan dan lakaran yang ditunjukkan sahaja. Lengkung guna segmen lurus. Toleransi kecil lebih halus tetapi guna lebih masa dan memori.",
"each rail's curves must be in one sketch": "lengkung setiap rel mestilah dalam satu lakaran",
"feature computation ended without a result": "pengiraan ciri berakhir tanpa hasil",
"file.open.drawing_filter": "Reka bentuk, fail CAD, mesh dan lukisan",
"file_load.built": "Dibina setakat ini: {built} daripada {total} ciri.",
"file_load.converting": "Menukar pepejal {done} daripada {total}",
@@ -1438,7 +1537,9 @@
"nav.free_orbit_upright.help": "Seretan tetikus ke sisi memutar model mengelilingi paksi menegak dunia; seretan ke atas dan ke bawah mencondongkan model merentasi bahagian atas tanpa had.",
"nav.free_orbit_upright.label": "Pusing mengelilingi paksi menegak",
"nav.fusion.help": "Seret tengah menyorot, Shift+tengah mengorbit, seret kanan mengorbit",
"nav.hfss.help": "Alt+seret kiri menyorot, Shift+Alt+seret kiri menzum, Ctrl+seret kiri pada ruang kosong mengorbit",
"nav.inventor.help": "Seret tengah menyorot, Shift+tengah mengorbit, Ctrl+tengah menzum, seret kanan mengorbit",
"nav.maya.help": "Alt+seret kiri mengorbit, Alt+seret tengah menyorot, Alt+seret kanan menzum; seret tengah juga menyorot",
"nav.openscad.help": "Seret kiri pada ruang kosong mengorbit, Shift+seret kiri memilih dengan kotak, seretan geometri lakaran menyunting; seret kanan atau tengah menyorot, Shift+seret kanan atau Shift+seret tengah menzum",
"nav.perspective.sketch": "Penyuntingan lakaran menggunakan pandangan ortografik. Siapkan lakaran untuk bertukar ke perspektif.",
"nav.solidworks.help": "Seret tengah mengorbit, Ctrl+tengah menyorot, Shift+tengah menzum, seret kanan mengorbit",
@@ -1546,12 +1647,34 @@
"no hole": "tiada lubang",
"no interference": "tiada pertindihan",
"no joint that moves yet": "belum ada sendi yang bergerak",
"no motion study yet": "belum ada kajian gerakan",
"no picture": "tiada gambar",
"no such joint": "tiada sendi sedemikian",
"none": "tiada",
"not a move": "bukan gerakan",
"not a number": "bukan nombor",
"open a sketch first": "buka lakaran dahulu",
"parts.automatic_name": "automatik",
"parts.dimension.across_flats": "Merentasi rata",
"parts.dimension.bore": "Diameter lubang",
"parts.dimension.d": "Diameter nominal",
"parts.dimension.head_diameter": "Diameter kepala",
"parts.dimension.head_height": "Ketinggian kepala",
"parts.dimension.height": "Ketinggian",
"parts.dimension.inner": "Diameter dalam",
"parts.dimension.outer": "Diameter luar",
"parts.dimension.pitch": "Pic",
"parts.dimension.socket": "Saiz soket",
"parts.dimension.socket_depth": "Kedalaman soket",
"parts.dimension.thickness": "Ketebalan",
"parts.dimension.width": "Lebar",
"parts.family.bearing": "Galas bebola alur dalam (sampul)",
"parts.family.dowel_pin": "Pin selari (dowel)",
"parts.family.hex_bolt": "Bolt / skru kepala heks",
"parts.family.hex_nut": "Nat heks",
"parts.family.set_screw": "Skru penetap soket heks, hujung rata",
"parts.family.socket_head_cap_screw": "Skru kepala soket",
"parts.family.washer": "Sesendal biasa",
"pattern.alignment_2": "Penjajaran 2",
"pattern.alignment_hint": "Bilangan termasuk asal. Tengah seimbangkan salinan; Balik pilih sisi lebih untuk bilangan genap.",
"pattern.direction_1": "Arah 1",
@@ -1567,9 +1690,25 @@
"perf.ui_time": "Masa UI",
"perf.viewport": "Tetingkap pandang",
"perf.worst": "terburuk",
"pick a body or some of its faces": "pilih jasad atau beberapa mukanya",
"pick a hole to seat it, or OK places it at the origin": "pilih lubang, atau tekan OK untuk letak di titik asal",
"pick a planar face": "pilih muka satah",
"pick a plane or a face as the tool": "pilih satah atau muka sebagai alat",
"pick a size": "pilih saiz",
"pick edges of a body": "pilih tepi sesebuah jasad",
"pick edges of one body": "pilih tepi satu jasad",
"pick edges of the holes to remove": "pilih tepi lubang yang hendak dibuang",
"pick faces of one body": "pilih muka satu jasad",
"pick faces to offset": "pilih muka untuk diofset",
"pick lines of one sketch": "pilih garis daripada satu lakaran",
"pick one or two vertices, edges or faces": "pilih satu atau dua bucu, tepi atau muka",
"pick one plane or face as the tool, or sketch curves": "pilih satu satah atau muka sebagai alat, atau lengkung lakaran",
"pick sections in two or more sketches": "pilih keratan dalam dua atau lebih lakaran",
"pick sketch curves or edges": "pilih lengkung lakaran atau tepi",
"pick the face to untrim": "pilih muka untuk dinyahpangkas",
"pick the path": "pilih laluan",
"pick what to keep": "pilih apa yang hendak dikekalkan",
"pick what to keep of the other surface": "pilih apa yang hendak dikekalkan pada permukaan yang satu lagi",
"prefs.add_item": "Tambah item",
"prefs.alt_frees_manipulators": "Alt / Option turut membebaskan anak panah manipulator",
"prefs.anti_aliasing_amd_dx12": "Anti-aliasing menggunakan 1× kerana semakan permulaan tidak melukis apa-apa dengan multisampel dan lulus pada 1×. Anda boleh memilih tetapan lain di atas; mulakan semula untuk menggunakannya jika semakan itu lulus.",
@@ -1653,6 +1792,13 @@
"prefs.left_click": "Klik kiri",
"prefs.live_input": "Input langsung",
"prefs.live_preview": "Pratonton langsung",
"prefs.max_file_size": "Saiz fail maksimum untuk dibuka",
"prefs.max_file_size_design": "Fail reka bentuk",
"prefs.max_file_size_flag": "Pelaksanaan ini bermula dengan --max-file-size {size}, yang terpakai sehingga SolidCraft ditutup. Tetapan ini terpakai apabila dimulakan tanpa pilihan itu.",
"prefs.max_file_size_note": "Lalai: 512 MB dan 256 MB; maksimum 16 GB. Membuka fail memerlukan memori beberapa kali saiznya. Reka bentuk yang menyimpan import besar mungkin memerlukan had fail reka bentuk lebih tinggi untuk disimpan dan dibuka semula.",
"prefs.max_file_size_step": "Fail STEP dan import",
"prefs.max_file_size_web": "Aplikasi web membuka fail STEP dan import sehingga 512 MB serta reka bentuk sehingga 256 MB: tab pelayar tidak dapat memuatkan fail lebih besar. Aplikasi desktop boleh menaikkan had ini.",
"prefs.megabytes": "MB",
"prefs.minutes": "minit",
"prefs.minutes_suffix": " min",
"prefs.mouse": "Tetikus dan pad jejak",
@@ -1785,6 +1931,7 @@
"render.all_controls_help": "Pemeriksaan padat bagi semua kawalan semasa. Halaman seksyen menyediakan penyuntingan berfokus tanpa mengubah permintaan asas.",
"render.all_shots_label": "Semua Syot",
"render.appearance_physics_separate": "Penampilan visual tidak menetapkan ketumpatan, data terma atau sifat kejuruteraan lain.",
"render.appearances": "Penampilan",
"render.availability_native": "Gubahan asli; semak sebelum menggunakan",
"render.availability_projection": "Unjuran disokong; lihat skop dan perkara yang ditinggalkan",
"render.availability_reference": "Data rujukan; bukan penetapan automatik",
@@ -1839,6 +1986,10 @@
"render.camera_set": "Tetapkan Kamera Render",
"render.camera_target": "Kedudukan sasaran · mm",
"render.camera_yaw": "Yaw",
"render.category_metal": "Logam",
"render.category_paint": "Cat",
"render.category_plastic": "Plastik",
"render.category_wood": "Kayu",
"render.chromatic_offset": "Ofset tepi kromatik",
"render.clear_stage": "Buang pentas yang disimpan",
"render.copy_request": "Salin JSON",
@@ -1938,6 +2089,7 @@
"render.effects_reset_lens": "Set semula kesan kanta",
"render.effects_seed": "Benih corak",
"render.emitted_axis": "Paksi pancaran",
"render.environment_tiles_require_raytrace": "Persekitaran memerlukan penjejakan sinar CPU atau GPU. Pilih kaedah itu dalam penyunting Keluaran.",
"render.error_area_samples": "area_samples mestilah 1, 4, 16 atau 64",
"render.error_binding_allocation": "tidak dapat memperuntukkan pengikatan bahan",
"render.error_body_bound": "pratonton produk melebihi had jasad yang dirakam",
@@ -2009,6 +2161,8 @@
"render.flare_strength": "Kekuatan kilau bahagian terang",
"render.flare_threshold": "Ambang kilau linear adegan",
"render.fold_browser": "Ringkaskan Pelayar",
"render.frame_presets": "Pratetap bingkai",
"render.frame_presets_pending": "Akan hadir bersama penyunting Syot",
"render.gpu_depth": "Kedalaman laluan maksimum",
"render.gpu_options_help": "Sampel, kedalaman, benih dan pendedahan disimpan bersama persediaan ini. Hanya kaedah imej yang aktif disimpan.",
"render.gpu_path_failed": "Penjejak laluan GPU tidak tersedia",
@@ -2080,8 +2234,12 @@
"render.lens_effects_title": "Berinspirasikan lensa & fotografi",
"render.library_all": "Katalog",
"render.library_all_states": "Semua status ketersediaan",
"render.library_appearance_available": "Penampilan tersedia",
"render.library_applicable": "Sumber yang boleh digunakan",
"render.library_applied": "Unjuran sumber telah digunakan. Semak permintaan render yang lengkap sebelum memulakan.",
"render.library_apply_appearance": "Gunakan penampilan",
"render.library_apply_hdr": "Simpan dengan persekitaran HDR ini",
"render.library_apply_hint": "Klik untuk menggunakan · seret ke atas model",
"render.library_apply_maps": "Gunakan peta permukaan GPU ciptaan",
"render.library_apply_studio": "Simpan unjuran studio ciptaan",
"render.library_apply_uniform": "Gunakan penampilan legap seragam",
@@ -2097,15 +2255,23 @@
"render.library_extent": "Julat subjek",
"render.library_favorite": "Tambah kegemaran",
"render.library_favorites": "Kegemaran",
"render.library_filters": "Katalog dan ketersediaan",
"render.library_grid": "Grid",
"render.library_hdr": "Persekitaran HDR terbina dalam",
"render.library_hdr_help": "Membenamkan HDR asal dalam persediaan yang disimpan. Asal longitud ciptaan ditukar secara eksplisit; tiada fail luaran diperlukan selepas disimpan.",
"render.library_in_design": "Dalam reka bentuk",
"render.library_inspect_only": "Pilih untuk memeriksa; tiada apa-apa digunakan secara automatik. Kegemaran dan sejarah terkini terikat pada sesi dokumen terbuka ini.",
"render.library_ior": "IOR",
"render.library_light_concepts": "Konsep pencahayaan",
"render.library_list": "Senarai",
"render.library_maps_help": "Peta terbina dalam boleh disimpan bersama sumber yang disemat dan pemetaan kejadian setempat. Penimpaan optik sementara kekal sebagai mod yang berasingan.",
"render.library_material_concepts": "Konsep bahan",
"render.library_metallic": "Metalik",
"render.library_native_scenes": "Studio asli",
"render.library_next": "Halaman seterusnya",
"render.library_omitted": "Tidak disertakan",
"render.library_opacity": "Kelegapan",
"render.library_other": "Penampilan lain",
"render.library_physical_profiles": "Profil rujukan fizikal",
"render.library_previous": "Halaman sebelumnya",
"render.library_quality_concepts": "Spesifikasi kualiti",
@@ -2115,6 +2281,7 @@
"render.library_results": "Sumber yang sepadan",
"render.library_rig_help": "Muatkan sekali pada pusat dan julat fizikal yang eksplisit. Rig lengkap termasuk kad pasif; penyekat tentukuran tetap menolak. Kelegaan pemancar/kad yang konservatif disemak sebelum menyimpan.",
"render.library_rigs": "Rig pencahayaan ciptaan",
"render.library_roughness": "Kekasaran",
"render.library_save_maps": "Simpan peta ke persediaan ini",
"render.library_scene_bounds": "Gunakan sempadan adegan yang dirakam",
"render.library_scenes": "Spesifikasi adegan",
@@ -2138,6 +2305,7 @@
"render.library_unfavorite": "Buang kegemaran",
"render.library_uniform_ack": "Guna penghampiran seragam tanpa kesan ruang atau lanjutan",
"render.library_usable": "Unjuran boleh laku",
"render.library_users": "Digunakan oleh",
"render.light_anchor_pending": "Menukar rangka koordinat lampu belum disokong lagi.",
"render.lights_fixed_to": "Lampu tetap pada",
"render.linear_help": "Menyimpan induk RGBA titik apungan 32-bit sebelum penyahhingaran, dedahan, pemetaan ton, penapis dan latar. sRGB linear/D65; didarab awal dengan liputan. Memori tambahan dibenarkan secara jelas. Tiada mampatan atau lintasan tambahan.",
@@ -2356,6 +2524,7 @@
"render.scene_review": "Selesaikan dan tinjau",
"render.scene_review_help": "Tinjau komposisi natif yang lengkap sebelum menggunakan. Spesifikasi SCENE lanjutan yang tidak terikat tidak digantikan.",
"render.scene_scope_details": "Skop komposisi",
"render.search_appearances": "Cari {count} penampilan",
"render.setup_legacy_environment": "Persediaan lama ini menggunakan putaran atau keamatan persekitaran warisan. Nilai itu dikekalkan; tukarkannya secara eksplisit sebelum menyimpan kerja imej penuh.",
"render.setup_name": "Nama persediaan",
"render.setup_numeric_help": "Simpan merekodkan kaedah imej aktif dan semua tetapan angkanya. Muat semula atau memilih persediaan lain menggantikan draf ini; pulihkan kamera secara berasingan.",
@@ -2374,10 +2543,19 @@
"render.shot_remove_title": "Buang syot tersimpan?",
"render.shot_rename": "Tukar nama syot tersimpan",
"render.shot_saved_only": "Simpan draf semasa dahulu. Gandakan dan tukar nama mengekalkan pilihan tersimpan dan rujukan sumber dengan tepat.",
"render.shots_camera_changed": "Kamera berubah sejak syot ini disimpan",
"render.shots_name_help": "Masukkan nama unik untuk syot semasa.",
"render.shots_no_result": "Belum ada render",
"render.shots_result": "Render {number} · {width} × {height}",
"render.shots_revert": "Kembalikan",
"render.shots_save": "Simpan Syot",
"render.shots_update": "Kemas kini syot",
"render.show_shots_label": "Papar jalur Syot",
"render.size_4k_uhd": "4K UHD",
"render.size_full_hd": "HD Penuh",
"render.size_hd": "HD",
"render.size_presets": "Pratetap saiz",
"render.size_presets_pending": "Akan hadir bersama penyunting Keluaran",
"render.sky_ground": "Tanah · RGB linear",
"render.sky_horizon": "Ufuk · RGB linear",
"render.sky_zenith": "Zenit · RGB linear",
@@ -2428,8 +2606,10 @@
"render.studio_sky_help": "Kecerunan langit asli untuk pencahayaan; bukan model atmosfera yang ditentukur.",
"render.studio_sun": "Matahari",
"render.studio_sun_radius": "Jejari sudut",
"render.studio_tiles_require_gpu": "Studio memerlukan penjejakan laluan GPU. Pilih GPU dalam penyunting Keluaran.",
"render.studio_title": "Studio render",
"render.studio_tone_map": "Pemetaan ton",
"render.studios_environments": "Studio dan persekitaran",
"render.subsurface_enable": "Perjalanan rawak serakan dalaman",
"render.subsurface_help": "Dielektrik tertutup sahaja. Serakan dan penyerapan RGB ialah pekali berasingan per mm. Laluan menyerak dalam geometri sebenar, kemudian dibiaskan atau dipantulkan pada sempadan. Matikan penghantaran bayang lurus, pengurangan hingar permukaan dan isi padu luaran; gunakan satu stratum Fresnel. Bukan shader resapan, grid ketumpatan atau bahan yang diukur. Medium tumpat mungkin memerlukan lebih banyak sampel dan kedalaman laluan.",
"render.subsurface_rate": "Kadar perlanggaran serakan",
@@ -2515,6 +2695,26 @@
"search.try": "Taip nama arahan, ciri dalam reka bentuk ini, atau parameter. Untuk menjalankan arahan dengan nilai, taip ID-nya dan objek JSON: {example}",
"search.valid": "Sah",
"section.off_hint": "Tiada satah dipilih: OK mematikan keratan.",
"select a direction (an axis, a sketch line or a straight edge) first": "pilih arah (paksi, garis lakaran atau tepi lurus) dahulu",
"select a direction (an axis, a sketch line or a straight edge) for the profile to follow": "pilih arah (paksi, garis lakaran atau tepi lurus) untuk diikuti oleh profil",
"select a point on a component": "pilih satu titik pada komponen",
"select a profile, edges or sketch lines first": "pilih profil, tepi atau garis lakaran dahulu",
"select an axis": "pilih paksi",
"select an axis, a straight or circular edge, or a cylinder, cone or torus face first": "pilih paksi, tepi lurus atau bulat, atau muka silinder, kon atau torus dahulu",
"select bodies first": "pilih jasad dahulu",
"select corner points, or the two lines of one corner": "pilih titik sudut, atau dua garis bagi satu sudut",
"select one corner point, or the two lines of one corner": "pilih satu titik sudut, atau dua garis bagi satu sudut",
"select sheet edges first": "pilih tepi kepingan dahulu",
"select the bend line (a sketch line) first": "pilih garis lenturan (garis lakaran) dahulu",
"select the cam's profile (a sketch profile or the cam's edges)": "pilih profil sesondol (profil lakaran atau tepi sesondol)",
"select the corner to cut first": "pilih sudut yang hendak dipotong dahulu",
"select the corners to round first": "pilih sudut yang hendak dibulatkan dahulu",
"select the fixed face first (a plate's large face)": "pilih muka tetap dahulu (muka besar plat)",
"select the frame sketch's lines first": "pilih garis lakaran rangka dahulu",
"select the path (sketch lines across the edge)": "pilih laluan (garis lakaran merentasi tepi)",
"select the rim face first": "pilih muka bibir dahulu",
"select the sheet edge first": "pilih tepi kepingan dahulu",
"select two components": "pilih dua komponen",
"set by the sketch's lines": "ditetapkan oleh garis lakaran",
"settings.display_quality_note": "Tahap halus melicinkan muka dan tepi melengkung, tetapi mesh lebih lambat disiapkan. Hanya paparan berubah, bukan reka bentuk atau eksport.",
"settings.frame_rate_details_note": "Menambah graf masa bingkai terkini dengan segi tiga yang dilukis di atasnya, histogram masa bingkai dan apa yang dilukis oleh GPU. Klik kadar bingkai dalam paparan untuk memaparkan atau menyembunyikannya.",
@@ -2532,17 +2732,23 @@
"settings.window_system_details": "Butiran",
"settings.window_system_note_now": "Berkuat kuasa selepas dimulakan semula. Sekarang: {now}.",
"settings.window_system_wayland_limited": "Sesetengah ciri terhad pada Wayland natif.",
"sheet.flange_type_base": "Tapak",
"sheet.flange_type_contour": "Kontur",
"sheet.flange_type_edge": "Tepi",
"sheet.no_body": "tiada jasad logam kepingan: mulakan satu dengan Bebibir > Asas, atau tukarkan plat kepada logam kepingan dengan Tukar kepada Logam Kepingan (pilih muka rata yang besar)",
"shortcut.json_error": "parameter mestilah JSON: {error}",
"shortcut.no_command": "tiada arahan `{id}`; teks sebelum JSON mestilah ID arahan",
"shortcut.pin": "Semat di atas kotak S",
"shortcut.unpin": "Nyahsemat",
"sketch is missing": "lakaran tiada",
"sketch.degrees_of_freedom": "{count} darjah kebebasan",
"sketch.dof_left": "{count} darjah kebebasan berbaki",
"sketch.dof_left_one": "{count} darjah kebebasan berbaki",
"sketch.draw_curves": "Lukis sebagai {linetype}",
"sketch.make_selection": "Tukar pilihan kepada {linetype}",
"sketch.open_ends": "Terbuka {count}",
"sketch.status_conflict": "kekangan bercanggah",
"sketch.status_fully_constrained": "terkekang sepenuhnya",
"sketch.untied_ends": "Tak terikat {count}",
"sketch_panel.attached": "Dipaut pada {body} / {path}",
"sketch_panel.author": "Penempatan pengarang: {path}",
@@ -2569,7 +2775,24 @@
"sketch_text.path_hint": "Lengkung lakaran yang akan diikuti teks: klik di sini, kemudian klik lengkung",
"sketch_text.use_font_file": "Gunakan fail fon {path}",
"solid": "pepejal",
"the angle must be a number": "sudut mestilah nombor",
"the curves must be in one sketch": "lengkung mestilah dalam satu lakaran",
"the direction must be an axis, a sketch line or a straight edge": "arah mestilah paksi, garis lakaran atau tepi lurus",
"the duration must be a number of seconds, 0.001 or more": "tempoh mestilah bilangan saat, 0.001 atau lebih",
"the line's sketch is gone": "lakaran bagi garis itu sudah tiada",
"the lines must be in one sketch": "garis mestilah dalam satu lakaran",
"the lines must be in one sketch, not the section's": "garis mestilah dalam satu lakaran, bukan lakaran milik keratan",
"the parts library is empty": "koleksi bahagian kosong",
"the parts library is not available": "koleksi bahagian tidak tersedia",
"the path curves must be in one other sketch": "lengkung laluan mestilah dalam satu lakaran lain",
"the path's sketch is gone": "lakaran laluan sudah tiada",
"the position must be a number": "kedudukan mestilah nombor",
"the preview failed": "pratonton gagal",
"the profiles and curves must be in one sketch": "profil dan lengkung mestilah dalam satu lakaran",
"the reference face must be on the edges' body": "muka rujukan mestilah pada jasad bagi tepi itu",
"the scale must be above 0": "skala mestilah melebihi 0",
"the section's profiles must be in one sketch": "profil keratan mestilah dalam satu lakaran",
"the two origins are on the same component": "kedua-dua titik asal berada pada komponen yang sama",
"thread.automatic_size": "Saiz automatik",
"thread.designation": "Penamaan",
"thread.direction": "Arah bebenang",
@@ -2609,6 +2832,7 @@
"trace.points": "{count} titik sepanjang laluan",
"trace.stopped_by_contact": "Berakhir pada sampel {sample}: {a} akan menembusi {b}",
"trace.stopped_by_loop": "Berakhir pada sampel {sample}: gelung melalui {joint} tidak lagi dapat ditutup",
"trim by sketch curves, or by one plane or face": "pangkas dengan lengkung lakaran, atau dengan satu satah atau muka",
"ui.1_degree_of_freedom": "1 darjah kebebasan",
"ui.3d_mouse_axes_reassigned": "Paksi ditetapkan semula dalam Keutamaan, dan itu masih terpakai di atas penentukuran ini.",
"ui.3d_mouse_button": "Butang {number}",
@@ -3085,6 +3309,8 @@
"ui.design_properties.revision": "Semakan",
"ui.diameter": "Diameter",
"ui.dimension": "Dimensi",
"ui.direct_interaction": "Interaksi langsung",
"ui.direct_interaction_note": "Kerjakan model terus dalam viewport. Tekan muka rata atau profil lalu tarik: ke atas menyemperit, ke bawah ke dalam jasad memotong, dan gerakan ke sisi menetapkan saiz langkah",
"ui.direction": "Arah",
"ui.display_quality": "Kualiti paparan",
"ui.display_quality_coarse": "Kasar",
@@ -3108,6 +3334,7 @@
"ui.editJointLimits": "Sunting Had Sendi",
"ui.edit_section_analysis": "Sunting Analisis Keratan",
"ui.ellipse": "Elips",
"ui.emboss_wrap_note": "Profil garis ortogon yang tangen pada satu silinder yang disokong dibalut secara automatik. Pilih dinding untuk memilih sasaran.",
"ui.end_radius": "Jejari Akhir",
"ui.end_scale": "Skala Akhir",
"ui.entities": "Entiti",
@@ -3180,6 +3407,7 @@
"ui.heading_extrude": "SEMPERIT",
"ui.heading_fillet": "JEJARI SUDUT",
"ui.heading_finish_sketch": "TAMATKAN LAKARAN",
"ui.heading_form": "BENTUK",
"ui.heading_front": "HADAPAN",
"ui.heading_hole": "LUBANG",
"ui.heading_home": "UTAMA",
@@ -3266,6 +3494,8 @@
"ui.intersect": "Silang",
"ui.isolate": "Asingkan",
"ui.join": "Cantum",
"ui.joint_play": "▶ Main",
"ui.joint_stop": "■ Hentikan",
"ui.joints": "Sendi",
"ui.keyboard_shortcuts": "Pintasan Kekunci",
"ui.language": "Bahasa",
@@ -3283,6 +3513,7 @@
"ui.loft": "Loft",
"ui.look_at": "Pandang Tepat",
"ui.look_at_anim_desc": "Pandangan paksi, Pandang Tepat, Utama dan Muatkan",
"ui.major_radius": "Jejari utama",
"ui.manipulator_free_alt": "Seret bebas manipulator juga dengan Alt/Option",
"ui.manipulator_free_alt_tip": "Hidup: Alt/Option atau Ctrl/Cmd pada anak panah ekstrud / gizmo mematikan pelekapan nilai. Mati: Ctrl/Cmd sahaja.",
"ui.manufacture": "Pembuatan",
@@ -3312,8 +3543,10 @@
"ui.mesh_format_3mf": "3MF",
"ui.mesh_format_3mf_tagged": "3MF dengan tag muka",
"ui.mesh_format_stl": "STL",
"ui.minor_radius": "Jejari kecil",
"ui.mirror": "Cermin",
"ui.mirror_line": "Garis cermin",
"ui.modifiers_either": "{first} atau {second}",
"ui.move": "Alih",
"ui.multiple": "Berbilang",
"ui.named_views": "Pandangan Bernama",
@@ -3370,6 +3603,7 @@
"ui.pinToShortcuts": "Semat pada Pintasan",
"ui.pinToToolbar": "Semat pada Bar Alat",
"ui.pipe": "Paip",
"ui.pitch": "Pic",
"ui.placement": "Penempatan",
"ui.placement_handles": "Pemegang penempatan",
"ui.placement_handles_tip": "Pemegang pada pratonton Penyokong, Bonggol atau Sambungan Sentap: seret penanda untuk menggerakkannya merentasi model, putarkannya melalui cincinnya",
@@ -3432,6 +3666,7 @@
"ui.reverse_horizontal_pan_direction": "Songsangkan arah pan mendatar",
"ui.reverse_horizontal_pan_tip": "Tatal mendatar dua jari dan roda ibu jari membuat pan ke arah bertentangan",
"ui.reverse_zoom_direction": "Songsangkan arah zum",
"ui.revolutions": "Pusingan",
"ui.revolve": "Putar",
"ui.ribbon_exit": "Keluar",
"ui.ribbon_labels": "Tunjukkan teks di bawah ikon reben",
@@ -3448,6 +3683,7 @@
"ui.scale": "Skala",
"ui.second_rail": "Rel Kedua",
"ui.section_analysis": "Analisis Keratan",
"ui.section_size": "Saiz keratan",
"ui.select": "Pilih",
"ui.selectOther": "Pilih Lain…",
"ui.selectTangentChain": "Pilih Rantaian Tangen",
@@ -3519,6 +3755,7 @@
"ui.start_page": "Halaman mula",
"ui.step_values": "Langkahkan nilai dengan kekunci anak panah dan roda",
"ui.step_values_tip": "Dalam medan nilai yang sedang anda taip, Up/Down mengubah nombor biasa sebanyak 1 (Shift: 10, Alt: 0.1), begitu juga roda tetikus di atas medan. Ungkapan dibiarkan seperti yang ditaip.",
"ui.stitch_tolerance_hint": "ketat",
"ui.stretch": "Regang",
"ui.supersampling": "Pensampelan super (SSAA)",
"ui.supersampling_adaptive": "Hanya apabila pandangan pegun",
@@ -3549,6 +3786,8 @@
"ui.to_object": "Ke Objek",
"ui.tool_direction": "Arah alat",
"ui.top": "Atas",
"ui.top_diameter": "Diameter atas",
"ui.top_radius": "Jejari atas",
"ui.torus": "Torus",
"ui.total_angle": "Jumlah sudut",
"ui.trackpad_pan_tip": "Pad sentuh: sorot (jika mati: zum). Roda tetikus: zum. Roda ibu jari: sorot sisi. Cubit sentiasa zum.",
@@ -3638,6 +3877,9 @@
"update.up_to_date": "SolidCraft sudah terkini.",
"update.update_now": "Kemas kini",
"update.you_have": "Versi anda: {version}.",
"use a positive nominal diameter and a pitch smaller than it": "gunakan diameter nominal positif dan pic yang lebih kecil daripadanya",
"values must be finite numbers": "nilai mestilah nombor terhingga",
"values must be numbers or expressions": "nilai mestilah nombor atau ungkapan",
"view.auto_ortho_faces": "Perspektif semasa mengorbit, ortografik pada muka",
"web.files.insert_drawings_hint": "Fail CAD, mesh, DXF atau SVG ke dalam reka bentuk yang dibuka",
"web.files.insert_stored_drawings_hint": "Ke dalam reka bentuk yang dibuka (fail CAD, mesh, DXF, SVG)",
+362
View File
@@ -1,4 +1,5 @@
{
"(active rule)": "(aktiv regel)",
"0 turns it off": "0 slår det av",
"1. Open GitHub draft below, then paste the report copied to your clipboard.": "1. Åpne GitHub-utkastet nedenfor, og lim deretter inn rapporten som er kopiert til utklippstavlen.",
"1. Open GitHub draft below. Your title and report will be filled in.": "1. Åpne GitHub-utkastet nedenfor. Tittelen og rapporten din fylles inn.",
@@ -52,16 +53,20 @@
"Apply your appearance first. Exports assigned appearances and materials created in this design.": "Tildel utseendet først. Eksporterer tildelte utseender og materialer som er opprettet i dette designet.",
"Arc length": "Buelengde",
"Area": "Areal",
"As Picked": "Som valgt",
"As-Built": "Som bygget",
"Assign Rule": "Tildel regel",
"Attachment": "Tilknytning",
"BLEND SURFACE": "OVERGANGSFLATE",
"BOUNDARY SURFACE": "GRENSEFLATE",
"BROWSER": "MODELLTRE",
"Ball": "Kule",
"Bar": "Stang",
"Base Center": "Basesenter",
"Base Height": "Basehøyde",
"Bend": "Bøyning",
"Bend Edges": "Bøyekanter",
"Bend Line": "Bøyelinje",
"Bend Line Position": "Bøyelinjens posisjon",
"Bend Position": "Bøyeposisjon",
"Bend Radius": "Bøyeradius",
@@ -73,6 +78,7 @@
"Body (last if none)": "Kropp (siste hvis ingen er valgt)",
"Body to split": "Kropp som skal deles",
"Body, or faces to take out": "Kropp eller flater som skal fjernes",
"Both": "Begge",
"Bottom Left": "Nederst til venstre",
"Bottom Middle": "Nederst i midten",
"Bottom Right": "Nederst til høyre",
@@ -104,6 +110,7 @@
"Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.": "Velg et nytt plan. Skissegeometrien og målene beholder sine todimensjonale koordinater.",
"Choose…": "Velg…",
"Circle center": "Sirkelsenter",
"Circle or sphere": "Sirkel eller kule",
"Circular": "Sirkulær",
"Circumference": "Omkrets",
"Clockwise": "Med klokken",
@@ -120,6 +127,7 @@
"Config Table": "Konfigurasjonstabell",
"Configuration": "Konfigurasjon",
"Configuration rules": "Konfigurasjonsregler",
"Constant radius": "Konstant radius",
"Contact": "Kontakt",
"Contact Sets": "Kontaktsett",
"Contact sets": "Kontaktsett",
@@ -155,7 +163,9 @@
"Custom": "Tilpasset",
"Customize Toolbar": "Tilpass verktøylinje",
"Customize…": "Tilpass…",
"Cylindrical": "Sylindrisk",
"Cylindrical face": "Sylindrisk flate",
"Deboss": "Preg inn",
"Delete": "Slett",
"Delete Folders and Features…": "Slett mapper og funksjoner…",
"Delete Selected Features…": "Slett valgte funksjoner…",
@@ -190,6 +200,7 @@
"Duration": "Varighet",
"Each new shape is also dimensioned from the sketch origin until nothing of it is left free. A shape already attached to the origin or to placed geometry gets none.": "Hver ny figur får også mål fra skissens origo til ingenting av den er fritt. En figur som allerede er festet til origo eller til plassert geometri, får ingen.",
"Each shape you draw gets all its dimensions, at the sizes you drew (typed values win). A size the sketch already sets is left out.": "Hver figur du tegner får alle målene sine i størrelsene du tegnet (inntastede verdier går foran). En størrelse skissen allerede bestemmer, utelates.",
"Edge": "Kant",
"Edges": "Kanter",
"Edit": "Rediger",
"Edit Profile Sketch": "Rediger profilskisse",
@@ -198,6 +209,7 @@
"End Radius": "Sluttradius",
"End bulge": "Utbuling ved slutten",
"End continuity": "Kontinuitet ved slutten",
"Equal Distance": "Lik avstand",
"Expand": "Utvid",
"Expected result (optional)": "Forventet resultat (valgfritt)",
"Explode": "Eksploder",
@@ -225,6 +237,8 @@
"Faces": "Flater",
"Faces / edges / vertices": "Flater / kanter / hjørner",
"Faces to split": "Flater som skal deles",
"Faces/Body": "Flater/kropp",
"Factor must be a positive number": "Faktoren må være et positivt tall",
"Fastener": "Festemiddel",
"Favorites": "Favoritter",
"Favourite": "Favoritt",
@@ -234,18 +248,25 @@
"Filter…": "Filtrer…",
"Find in Browser": "Finn i modelltreet",
"Finish": "Fullfør",
"First edge": "Første kant",
"First plane": "Første plan",
"First point": "Første punkt",
"Fit and G2 corners require Constant radius.": "Tilpasning og G2-hjørner krever konstant radius.",
"Fixed Face": "Fast flate",
"Flange Gap": "Flensmellomrom",
"Flange thickness": "Flenstykkelse",
"Flanges": "Flenser",
"Flat Pattern": "Utbretting",
"Flat size": "Utbrettede mål",
"Flat size X": "Utbrettet mål X",
"Flat size Y": "Utbrettet mål Y",
"Follow camera (Look At sketches)": "Følg kameraet (orienter mot skisser)",
"Follow the camera (Look At sketches)": "Følg kameraet (orienter mot skisser)",
"Font": "Skrift",
"Free Move": "Fri flytting",
"From": "Fra",
"From Bodies": "Fra kropper",
"From Snap": "Fra punktfangst",
"From edges": "Fra kanter",
"Full Length": "Full lengde",
"Full thread depth": "Full gjengedybde",
@@ -257,10 +278,13 @@
"Halo under sketch curves": "Glorie under skissekurver",
"Height": "Høyde",
"Height From": "Høyde fra",
"Hem Gap": "Mellomrom i kantomslag",
"Hidden edges": "Skjulte kanter",
"History layout": "Tidslinjens plassering",
"History names": "Navn på tidslinjen",
"Hole (optional)": "Hull (valgfritt)",
"Hole Depth": "Hulldybde",
"Hole Diameter": "Hulldiameter",
"Holes": "Hull",
"Holes (edges on them)": "Hull (kanter langs hullene)",
"Home view": "Hjemvisning",
@@ -283,12 +307,16 @@
"Issue title": "Saktittel",
"Items": "Elementer",
"JOINTS": "LEDD",
"Joint 1": "Ledd 1",
"Joint 2": "Ledd 2",
"Joint Origin1": "Leddorigo1",
"K Factor": "K-faktor",
"Keep (points on it)": "Behold (punkter på den)",
"Keep Tools": "Behold verktøy",
"Keep of the other surface": "Del av den andre flaten som beholdes",
"Key a joint's value at this step": "Angi leddets verdi ved dette trinnet",
"Keys": "Nøkkelbilder",
"Knife Edge": "Knivegg",
"L-Angle": "L-profil",
"LIBRARY": "BIBLIOTEK",
"LINKS": "LENKER",
@@ -301,9 +329,11 @@
"Letter Spacing": "Bokstavavstand",
"Library": "Bibliotek",
"Library file": "Bibliotekfil",
"Library response is missing": "Biblioteksvaret mangler",
"Limits": "Grenser",
"Line Spacing": "Linjeavstand",
"Line Type": "Linjetype",
"Line on plane": "Linje i plan",
"Lines": "Linjer",
"Listing the installed fonts…": "Henter installerte skrifter…",
"Load library": "Last inn bibliotek",
@@ -323,11 +353,14 @@
"Material Outside": "Materiale på utsiden",
"Material copied. Existing body assignments are unchanged.": "Materialet er kopiert. Eksisterende kroppstildelinger er uendret.",
"Max": "Maks",
"Max Thickness": "Maks tykkelse",
"Maximum": "Maksimum",
"Mesh body": "Nettkropp",
"Mid Radius": "Mellomradius",
"Middle double-click": "Dobbeltklikk med midtknappen",
"Min": "Min",
"Min Draft": "Min. slippvinkel",
"Min Thickness": "Min. tykkelse",
"Minimum": "Minimum",
"Mirror Plane": "Speilplan",
"Mitre": "Gjæring",
@@ -347,6 +380,7 @@
"Name": "Navn",
"Name on import": "Navn ved import",
"Nearest": "Nærmeste",
"Neutral plane": "Nøytralt plan",
"New": "Ny",
"New Comp.": "Ny komp.",
"New Sketch": "Ny skisse",
@@ -357,6 +391,7 @@
"No matching appearances": "Ingen utseender samsvarer med søket",
"No matching physical materials.": "Ingen samsvarende fysiske materialer.",
"No visible bodies or sketches to export.": "Ingen synlige kropper eller skisser å eksportere.",
"Nominal Radius": "Nominell radius",
"Nominal diameter": "Nominell diameter",
"None": "Ingen",
"Not available yet": "Ikke tilgjengelig ennå",
@@ -385,22 +420,28 @@
"Overall length": "Total lengde",
"Param. Graph": "Parametergraf",
"Parameter": "Parameter",
"Part length must be greater than zero and at most 2000 mm": "Delens lengde må være større enn null og høyst 2000 mm",
"Parts": "Deler",
"Pass through": "Gå gjennom",
"Path": "Bane",
"Pattern": "Mønster",
"Per Edge": "Per kant",
"Physical material definitions": "Definisjoner av fysiske materialer",
"Pick a part size": "Velg en delstørrelse",
"Pick the tool bodies": "Velg verktøykroppene",
"Picked": "Valgt",
"Picked point": "Valgt punkt",
"Pin-slot": "Tapp i spor",
"Pitch": "Stigning",
"Pivot": "Rotasjonspunkt",
"Place at the origin instead": "Plasser ved origo i stedet",
"Planar": "Plan",
"Plastic Rules": "Plastregler",
"Play the joint's motion in the view": "Spill av leddets bevegelse i visningen",
"Playback": "Avspilling",
"Plot": "Graf",
"Point on line": "Punkt på linje",
"Point on plane": "Punkt på plan",
"Position": "Posisjon",
"Position (0 to 1)": "Posisjon (0 til 1)",
"Position (G0)": "Posisjon (G0)",
@@ -413,6 +454,7 @@
"Profiles or curves": "Profiler eller kurver",
"Profiles/Curves": "Profiler/kurver",
"Quantity": "Antall",
"Quantity must be at least 2": "Antallet må være minst 2",
"RECENT": "NYLIGE",
"REFERENCE": "REFERANSE",
"RULED SURFACE": "REGELFLATE",
@@ -426,6 +468,8 @@
"Rectangular": "Rektangulær",
"Rectangular Tube": "Rektangulært rør",
"Reference face": "Referanseflate",
"Relief Depth": "Avlastningsdybde",
"Relief Width": "Avlastningsbredde",
"Remove Tools": "Fjern verktøy",
"Remove condition": "Fjern betingelse",
"Remove from Folder": "Fjern fra mappe",
@@ -454,6 +498,7 @@
"Restore this studio's tools": "Gjenopprett verktøyene i dette arbeidsområdet",
"Result": "Resultat",
"Retake screenshot": "Ta nytt skjermbilde",
"Reveal Height": "Synlig høyde",
"Reverse": "Reverser",
"Reverse Direction": "Reverser retning",
"Revert": "Tilbakestill",
@@ -462,12 +507,15 @@
"Revert Position": "Tilbakestill posisjon",
"Review the report and screenshot before opening GitHub.": "Gjennomgå rapporten og skjermbildet før du åpner GitHub.",
"Review your report on GitHub and click Submit new issue to publish it.": "Se gjennom rapporten på GitHub og klikk på Submit new issue for å publisere den.",
"Revolute": "Roterende",
"Rib Length": "Ribbelengde",
"Rib Offset": "Ribbeforskyvning",
"Rib Thickness": "Ribbetykkelse",
"Ribs": "Ribber",
"Right Middle": "Midt til høyre",
"Rigid": "Stiv",
"Rim Edge": "Randkant",
"Rim face": "Randflate",
"Ring of tools on the right button": "Verktøyring med høyre museknapp",
"Roll Back Here": "Rull tilbake hit",
"Roll History Marker Here": "Flytt tidslinjemarkøren hit",
@@ -492,7 +540,9 @@
"Search": "Søk",
"Search commands": "Søk i kommandoer",
"Search this library": "Søk i dette biblioteket",
"Second edge": "Andre kant",
"Second plane": "Andre plan",
"Second point": "Andre punkt",
"Section": "Snitt",
"Section (optional)": "Tverrsnitt (valgfritt)",
"Section position": "Snittposisjon",
@@ -503,6 +553,15 @@
"Select bodies (a face picks its body)": "Velg kropper (klikk på en flate for å velge kroppen)",
"Select bodies or faces instead": "Velg kropper eller flater i stedet",
"Select edges": "Velg kanter",
"Select points of the sketch being edited": "Velg punkter i skissen som redigeres",
"Select the centre the pattern turns around first": "Velg først senteret som mønsteret roterer rundt",
"Select the curves to offset first": "Velg først kurvene som skal forskyves",
"Select the mirror line": "Velg speillinjen",
"Select the sketch curves or points to mirror first": "Velg først skissekurvene eller punktene som skal speiles",
"Select the sketch curves or points to move first": "Velg først skissekurvene eller punktene som skal flyttes",
"Select the sketch curves or points to pattern first": "Velg først skissekurvene eller punktene som skal inngå i mønsteret",
"Select the sketch curves or points to scale first": "Velg først skissekurvene eller punktene som skal skaleres",
"Select the sketch curves or points to transform first": "Velg først skissekurvene eller punktene som skal transformeres",
"Selection filter": "Utvalgsfilter",
"Semantic faces share their appearance across instances.": "Semantiske flater deler utseende på tvers av forekomster.",
"Send your report on GitHub": "Send rapporten på GitHub",
@@ -522,9 +581,12 @@
"Sketch geometry and profiles": "Skissegeometri og profiler",
"Sketch plane has no body-face attachment.": "Skisseplanet er ikke knyttet til en flate på en kropp.",
"Sketch points": "Skissepunkter",
"Slider": "Glidende",
"Slot width": "Sporbredde",
"Snap to face and edge centres": "Fang til flate- og kantsentre",
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft ble lukket før disse designene ble lagret. Du kan åpne den siste automatisk lagrede versjonen av hvert design.",
"Spacing 2 must be a non-zero number": "Avstand 2 må være et tall forskjellig fra null",
"Spacing must be a non-zero number": "Avstand må være et tall forskjellig fra null",
"Specialise": "Spesialiser",
"Speed": "Hastighet",
"Splitting tool": "Deleverktøy",
@@ -535,6 +597,7 @@
"Start angle": "Startvinkel",
"Start bulge": "Utbuling ved starten",
"Start continuity": "Kontinuitet ved starten",
"Stationary Side": "Fast side",
"Step": "Trinn",
"Steps": "Trinn",
"Steps to reproduce (optional)": "Trinn for å gjenskape (valgfritt)",
@@ -549,6 +612,7 @@
"Suppressed": "Undertrykt",
"Surface": "Flate",
"Surface, or faces to turn over": "Overflate eller flater som skal snus",
"Surfaces": "Flater",
"Swap Ends": "Bytt ender",
"TRIM": "TRIM",
"Tangent": "Tangent",
@@ -559,24 +623,34 @@
"Tap drill diameter": "Gjengeboringsdiameter",
"Target": "Mål",
"Target Body": "Målkropp",
"Target Face (optional)": "Målflate (valgfritt)",
"Tear": "Splitt",
"Technical details": "Tekniske detaljer",
"The dashed sketch is a saved plane guide. Select a new face to repair the attachment.": "Den stiplede skissen viser det lagrede planet. Velg en ny flate for å gjenopprette tilknytningen.",
"The design changed. Reopen Change Part Size for the component.": "Designet er endret. Åpne Endre delstørrelse på nytt for komponenten.",
"The direction must be an axis or a straight sketch curve that lies in the sketch plane": "Retningen må være en akse eller en rett skissekurve som ligger i skisseplanet",
"The mirror line cannot also be mirrored": "Speillinjen kan ikke også speiles",
"The mirror line must be a straight curve of this sketch, not an origin axis": "Speillinjen må være en rett kurve i denne skissen, ikke en origoakse",
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "Forekomsten skissen ble opprettet i, finnes ikke lenger. Velg en gyldig forekomst for skissen før du knytter den til en flate på nytt.",
"Then": "Deretter",
"These features will be deleted:": "Disse funksjonene vil bli slettet:",
"These features will fail:": "Disse funksjonene vil feile:",
"Thickness Outside": "Tykkelse på utsiden",
"Thickness Variation": "Tykkelsesvariasjon",
"Thin": "Tynn",
"Thin Extrude": "Tynn ekstrudering",
"Third plane": "Tredje plan",
"Third point": "Tredje punkt",
"This child is shared. Deleting it removes it from every copy of its parent.": "Denne underkomponenten er delt. Hvis du sletter den, fjernes den fra alle kopier av den overordnede komponenten.",
"This child is shared. Position changes apply to every copy of its parent.": "Denne underkomponenten er delt. Posisjonsendringer gjelder for alle kopier av den overordnede komponenten.",
"This face cannot provide a durable attachment. Choose another face or explicitly use a fixed plane.": "Denne flaten kan ikke gi en varig tilknytning. Velg en annen flate eller velg å bruke et fast plan.",
"This image is not attached to GitHub yet. Copy or save it, then attach it on GitHub.": "Dette bildet er ikke lagt ved på GitHub ennå. Kopier eller lagre det, og legg det deretter ved på GitHub.",
"Thread depth": "Gjengedybde",
"Thread diameter": "Gjengediameter",
"Through point": "Gjennom punkt",
"To": "Til",
"To Offset": "Til Offset",
"To Snap": "Til punktfangst",
"To edges": "Til kanter",
"Toe radius": "Tåradius",
"Tolerance": "Toleranse",
@@ -590,6 +664,7 @@
"Trimming tool (plane, face or curves)": "Trimmeverktøy (plan, flate eller kurver)",
"Turn off section analysis before exporting the view.": "Slå av snittanalyse før du eksporterer visningen.",
"Type": "Type",
"Type a distance or an angle to move by": "Skriv inn en avstand eller en vinkel å flytte med",
"UNTRIM": "FJERN TRIM",
"Unit": "Enhet",
"Unsuppress": "Opphev undertrykking",
@@ -634,6 +709,17 @@
"Z angle": "Z-vinkel",
"Z distance": "Z-avstand",
"Zebra": "Zebra",
"`joint` must be an id or a name": "`joint` må være en id eller et navn",
"`params` must be an object": "`params` må være et objekt",
"`values` must be a list": "`values` må være en liste",
"a chamfer needs a corner of exactly two lines": "en fas krever et hjørne av nøyaktig to linjer",
"a file operation is in progress": "en filoperasjon pågår",
"a picked curve is no longer in a sketch": "en valgt kurve er ikke lenger i en skisse",
"a picked curve's sketch has not been solved": "skissen til en valgt kurve er ikke løst",
"a picked edge has no points": "en valgt kant har ingen punkter",
"a picked edge is gone": "en valgt kant finnes ikke lenger",
"a picked edge's body is gone": "kroppen til en valgt kant finnes ikke lenger",
"add a key for a joint first": "legg først til et nøkkelbilde for et ledd",
"ai.connected_toast": "SolidCraft er nå tilgjengelig i {clients}. Start dem på nytt for å bruke det (Innstillinger > Assistent).",
"ai.prefs.allow_control": "La KI-agenter koble til (kontrollkanal / MCP)",
"ai.prefs.allow_control_note": "Åpner en kontrollkanal bare på denne datamaskinen (127.0.0.1, port 27878 eller den neste ledige). Et program som kobler til, må også vise et hemmelig token som bare brukerkontoen din kan lese, og som lages på nytt ved hver oppstart. Trer i kraft neste gang SolidCraft starter. Av: ingenting lytter med mindre du starter SolidCraft med --control.",
@@ -674,6 +760,8 @@
"appearance.pbr_opaque": "Ugjennomsiktig PBR",
"appearance.roughness": "Ruhet",
"appearance.surface_response": "Overflatens lysrespons",
"at the face": "ved flaten",
"bom.group_children": "Underdeler i raden til overordnet del",
"browser.body_name": "Kropp{number}",
"browser.calibrate_title": "Kalibrer: {name}",
"browser.capture_many": "{count} komponenter er flyttet, men de nye posisjonene er ikke registrert.",
@@ -684,6 +772,8 @@
"browser.move_title": "Flytt: {name}",
"browser.parameters": "Parametere ({count})",
"browser.precision": "Presisjon: {precision}",
"browser.sketch_attachment_lost": "{name} (tilknytning mistet)",
"browser.sketch_editing": "{name} (redigeres)",
"browser.sketch_name": "Skisse{number}",
"browser.units": "Enheter: {units}",
"cam.flat": "Plan flate",
@@ -693,7 +783,13 @@
"cam.roller": "Rulle",
"cam.roller_pick": "Rulle (valgfritt)",
"cam.tip_pick": "Spiss (valgfritt)",
"choose a profile": "velg en profil",
"choose the cam's and the follower's joints": "velg leddene til kammen og følgeren",
"choose the joints to couple": "velg leddene som skal kobles",
"click a plane or planar face": "klikk på et plan eller en plan flate",
"click sketch curves": "klikk på skissekurver",
"cmd.appearance_assign": "Utseende",
"cmd.appearance_chain": "Utseendekjede",
"cmd.appearance_library": "Utseendebibliotek",
"cmd.appearance_list": "Vis utseender",
"cmd.appearance_paint": "Mal utseende",
@@ -723,7 +819,9 @@
"cmd.component_create": "Ny komponent",
"cmd.component_cycle_color": "Bytt komponentfarge",
"cmd.component_delete": "Slett komponent",
"cmd.component_edit_in_place": "Rediger på stedet",
"cmd.component_extract": "Trekk ut til fil",
"cmd.component_finish_edit": "Fullfør redigering",
"cmd.component_from_bodies": "Opprett komponenter fra kropper",
"cmd.component_insert_design": "Sett inn design",
"cmd.component_links": "Vis koblinger",
@@ -1031,6 +1129,7 @@
"cmd.sketch_spline_control_point_5": "Spline med kontrollpunkter (grad 5)",
"cmd.sketch_spline_fit_point": "Spline gjennom punkter",
"cmd.sketch_spline_insert_point": "Legg til splinepunkt",
"cmd.sketch_spline_reverse": "Reverser splineretning",
"cmd.sketch_spun_profile": "Dreid profil",
"cmd.sketch_toggle_diameter": "Veksle mellom radius / diameter",
"cmd.sketch_toggle_driven": "Veksle referansemål",
@@ -1082,6 +1181,7 @@
"cmd.view.render_dock": "Flytt rendringspanel",
"cmd.view.render_focus": "Rendringsfokusvisning",
"cmd.view_ambient_occlusion": "Ambient occlusion",
"cmd.view_angle_readout": "Visningsvinkler",
"cmd.view_apply": "Gjenopprett navngitt visning",
"cmd.view_back": "Bakvisning",
"cmd.view_bottom": "Bunnvisning",
@@ -1191,6 +1291,8 @@
"dxf.progress.write": "Skriver DXF (5/5)",
"dxf.tolerance": "Kurvetoleranse",
"dxf.tolerance_help": "Bare synlige kropper og viste skisser. Kurver bruker rette segmenter. Mindre toleranse gir finere kurver og bruker mer tid og minne.",
"each rail's curves must be in one sketch": "kurvene til hver føringskurve må ligge i én skisse",
"feature computation ended without a result": "funksjonsberegningen endte uten resultat",
"file.open.drawing_filter": "Design, CAD-filer, meshfiler og tegninger",
"file_load.built": "Bygget så langt: {built} av {total} funksjoner.",
"file_load.converting": "Konverterer volumkropp {done} av {total}",
@@ -1214,6 +1316,7 @@
"help.appearance.assign": "Gir de valgte kroppene, flatene eller komponentene en farge eller et materialutseende, uten å endre vekten deres. Bruk det til å skille deler fra hverandre eller til å forhåndsvise en overflatebehandling.",
"help.attributions": "Opphav og lisenser (ATTRIBUTION.md)",
"help.build": "Versjon {version} · commit {commit} · bygget {date}",
"help.component.finish_edit": "Lagrer delen du redigerer på stedet, går tilbake til sammenstillingen og oppdaterer sammenstillingen fra den. Bruk den når endringene dine i den koblede delen er ferdige.",
"help.connect_note": "Start hver assistent på nytt for å laste inn SolidCraft-serveren. Hver fil ble sikkerhetskopiert som .bak før den ble endret.",
"help.connect_title": "Koble til AI-assistenter",
"help.face.delete": "Fjerner de valgte flatene og lar naboflatene lukke åpningen, eller lar åpningen stå igjen som en flate. Bruk det til å fjerne en avrundet kant, et hull eller en forhøyning som er i veien.",
@@ -1548,14 +1651,34 @@
"no hole": "intet hull",
"no interference": "ingen interferens",
"no joint that moves yet": "ingen bevegelige ledd ennå",
"no motion study yet": "ingen bevegelsesanalyse ennå",
"no picture": "ikke noe bilde",
"no such joint": "finner ikke et slikt ledd",
"none": "ingen",
"not a move": "ikke en flytting",
"not a number": "ikke et tall",
"open a sketch first": "åpne en skisse først",
"parts.automatic_name": "automatisk",
"parts.dimension.across_flats": "Nøkkelvidde",
"parts.dimension.bore": "Boring",
"parts.dimension.d": "Nominell diameter",
"parts.dimension.head_diameter": "Hodediameter",
"parts.dimension.head_height": "Hodehøyde",
"parts.dimension.height": "Høyde",
"parts.dimension.inner": "Innvendig diameter",
"parts.dimension.outer": "Utvendig diameter",
"parts.dimension.pitch": "Stigning",
"parts.dimension.socket": "Sekskantstørrelse",
"parts.dimension.socket_depth": "Sekskantdybde",
"parts.dimension.thickness": "Tykkelse",
"parts.dimension.width": "Bredde",
"parts.family.bearing": "Dypsporkulelager (ytre omriss)",
"parts.family.dowel_pin": "Parallellstift (passtift)",
"parts.family.hex_bolt": "Sekskantbolt / sekskantskrue",
"parts.family.hex_nut": "Sekskantmutter",
"parts.family.set_screw": "Settskrue med innvendig sekskant, flat ende",
"parts.family.socket_head_cap_screw": "Sylinderskrue med innvendig sekskant",
"parts.family.washer": "Underlagsskive",
"pattern.alignment_2": "Justering 2",
"pattern.alignment_hint": "Antallet inkluderer originalen. Senter fordeler kopiene rundt originalen. Speil velger hvilken side som får en ekstra kopi når antallet er et partall.",
"pattern.direction_1": "Retning 1",
@@ -1571,9 +1694,25 @@
"perf.ui_time": "Grensesnitttid",
"perf.viewport": "Visningsområde",
"perf.worst": "verst",
"pick a body or some of its faces": "velg en kropp eller noen av flatene dens",
"pick a hole to seat it, or OK places it at the origin": "velg et hull å plassere den i, eller OK plasserer den ved origo",
"pick a planar face": "velg en plan flate",
"pick a plane or a face as the tool": "velg et plan eller en flate som verktøy",
"pick a size": "velg en størrelse",
"pick edges of a body": "velg kanter på en kropp",
"pick edges of one body": "velg kanter på én kropp",
"pick edges of the holes to remove": "velg kantene på hullene som skal fjernes",
"pick faces of one body": "velg flater på én kropp",
"pick faces to offset": "velg flater som skal forskyves",
"pick lines of one sketch": "velg linjer i én skisse",
"pick one or two vertices, edges or faces": "velg ett eller to hjørner, kanter eller flater",
"pick one plane or face as the tool, or sketch curves": "velg ett plan eller én flate som verktøy, eller skissekurver",
"pick sections in two or more sketches": "velg tverrsnitt i to eller flere skisser",
"pick sketch curves or edges": "velg skissekurver eller kanter",
"pick the face to untrim": "velg flaten du vil fjerne trim fra",
"pick the path": "velg banen",
"pick what to keep": "velg hva som skal beholdes",
"pick what to keep of the other surface": "velg hva som skal beholdes av den andre flaten",
"prefs.add_item": "Legg til elementet",
"prefs.alt_frees_manipulators": "Alt / Option frigjør også manipulatorpilene",
"prefs.anti_aliasing_amd_dx12": "Kantutjevning bruker 1× fordi oppstartskontrollen ikke tegnet noe med multisampling og besto med 1×. Du kan velge en annen innstilling ovenfor. Start på nytt for å ta den i bruk hvis kontrollen består.",
@@ -1655,8 +1794,19 @@
"prefs.layout_note": "Kompakt fester panelene til vinduskantene. Gjennomsiktig modelltre er tilgjengelig i Flytende. Endre siden for assistenten på assistentsiden.",
"prefs.left": "Venstre",
"prefs.left_click": "Venstreklikk",
"prefs.linked_designs": "Koblede design",
"prefs.live_input": "Direkte inndata",
"prefs.live_preview": "Direkte forhåndsvisning",
"prefs.logo": "Logo",
"prefs.logo_accent": "Aksent",
"prefs.logo_theme": "Temaets farger",
"prefs.max_file_size": "Største fil som kan åpnes",
"prefs.max_file_size_design": "Designfiler",
"prefs.max_file_size_flag": "Denne kjøringen startet med --max-file-size {size}, som gjelder til SolidCraft lukkes. Disse innstillingene gjelder ved oppstart uten dette valget.",
"prefs.max_file_size_note": "Standard: 512 MB og 256 MB; høyst 16 GB. Å åpne en fil krever flere ganger filstørrelsen i minne. Et design med en stor import kan trenge en høyere grense for designfiler for å kunne lagres og åpnes igjen.",
"prefs.max_file_size_step": "STEP- og importfiler",
"prefs.max_file_size_web": "Nettappen åpner STEP- og importfiler opptil 512 MB og design opptil 256 MB: en nettleserfane kan ikke romme større filer. Grensene kan økes i skrivebordsappen.",
"prefs.megabytes": "MB",
"prefs.minutes": "minutter",
"prefs.minutes_suffix": " min",
"prefs.mouse": "Mus og styreflate",
@@ -1705,11 +1855,17 @@
"prefs.sketch_selection": "Skisse og valg",
"prefs.sketch_selection_heading": "Skisse og valg",
"prefs.sketch_selection_note": "Hvordan skisser bruker punktfangst, og hvordan klikk velger.",
"prefs.sketch_tab_always": "Behold Skisse-fanen synlig",
"prefs.sketch_tab_always_note": "Skisse-fanen blir stående på båndet, rett etter Modell, også når ingen skisse er åpen. Tegneverktøyene starter da en skisse på det valgte planet eller den valgte flaten, eller på planet du velger deretter, med verktøyet klart i skissen.",
"prefs.sketch_tracking_guides": "Hjelpelinjer for senter og midtpunkt",
"prefs.sketch_tracking_guides_note": "Hold over en sirkels senter eller midtpunktet på en rett kant, og flytt deretter vannrett eller loddrett for å vise en midlertidig prikket hjelpelinje.",
"prefs.sketching": "Skissetegning",
"prefs.snap_drawing_note": "Mens du tegner eller drar et skissepunkt.",
"prefs.source": "Kilde",
"prefs.speed": "Hastighet",
"prefs.stand_up_note": "Når en STEP- eller IGES-fil åpnes med Z opp, tilby en kvart omdreining rundt X.",
"prefs.status_bar": "Statuslinje",
"prefs.status_bar_note": "En tynn linje langs vinduets nedre kant viser hva som redigeres, tilstanden til den åpne skissen i dens farge og designets enheter; et klikk på enhetene endrer dem. Skissens tilstand vises da ikke lenger i hjørnet av visningen.",
"prefs.status_done": "Ferdig",
"prefs.status_error": "Feil",
"prefs.status_selected": "Valgt",
@@ -1717,12 +1873,16 @@
"prefs.supersampling_brief": "Tegner visningen større og skalerer den ned: svært skarpt, og mer krevende.",
"prefs.tab_accent_note": "Aksentfargen følger den aktive fanen",
"prefs.theme_file": "temafil",
"prefs.theme_file_or_paste": "fil eller lim inn",
"prefs.theme_follow": "Følg en temafil",
"prefs.theme_follow_error": "Ikke brukt: {error}. Det siste gyldige temaet forblir aktivt.",
"prefs.theme_follow_launch": "Følges bare denne kjøringen (--theme-file); lastes inn på nytt når et annet verktøy skriver om filen.",
"prefs.theme_follow_note": "Lastes inn på nytt når et annet verktøy skriver om filen. Valg av et tema stopper dette.",
"prefs.theme_follow_off": "Av",
"prefs.theme_follow_stop": "Slutt å følge",
"prefs.theme_import_hint": "Klikk for å velge en temafil, slipp en her, eller trykk Ctrl+V (Cmd+V på Mac) med et kopiert tema",
"prefs.theme_paste": "Lim inn tema",
"prefs.theme_paste_none": "Utklippstavlen inneholder ikke noe SolidCraft-tema",
"prefs.timeline_half_hour": "−30 min",
"prefs.timeline_now": "nå",
"prefs.timeline_quarter_hour": "−15",
@@ -1788,7 +1948,47 @@
"render.aging_upward": "Avsetning på oppadvendte flater",
"render.all_controls_help": "Tett oversikt over alle gjeldende kontroller. Seksjonssidene gir fokusert redigering uten å endre den underliggende forespørselen.",
"render.all_shots_label": "Alle opptak",
"render.appearance_editor.absorption": "Absorpsjon",
"render.appearance_editor.base_colour": "Basefarge",
"render.appearance_editor.body_reason": "Optiske tilordninger gjelder én hel kropp. Velg Kropp for å redigere disse kontrollene.",
"render.appearance_editor.boundary": "Grense",
"render.appearance_editor.box": "Boks",
"render.appearance_editor.choose_image": "Velg bilde…",
"render.appearance_editor.closed": "Lukket volumkropp",
"render.appearance_editor.coat": "Klarlakk",
"render.appearance_editor.cpu_reason": "Krever CPU-strålesporing.",
"render.appearance_editor.glass": "Glass og absorpsjon",
"render.appearance_editor.glass_enable": "Bruk fysisk glass på denne kroppen",
"render.appearance_editor.glass_note": "Lukket glass bruker geometrisk avstand. Det kan ikke brukes på samme kropp som klarlakk eller teksturkart.",
"render.appearance_editor.gpu_reason": "Krever GPU path tracing.",
"render.appearance_editor.ior": "Brytningsindeks (IOR)",
"render.appearance_editor.maps": "Teksturkart",
"render.appearance_editor.normal": "Normal",
"render.appearance_editor.normal_strength": "Normalstyrke",
"render.appearance_editor.origin": "Origo",
"render.appearance_editor.recipe": "Oppskrift",
"render.appearance_editor.reflection_sampling": "Sampling for alle reflekterende kropper",
"render.appearance_editor.reflections": "Refleksjoner",
"render.appearance_editor.remove": "Fjern lagret",
"render.appearance_editor.remove_note": "Fjern den lagrede optikken fra dette opptaket. Geometri og tilordnede stiler beholdes.",
"render.appearance_editor.remove_title": "Fjerne lagret optikk?",
"render.appearance_editor.replace": "Erstatt denne kroppens lagrede optikk",
"render.appearance_editor.revalidate": "Valider denne kroppen på nytt",
"render.appearance_editor.save": "Lagre denne optikken",
"render.appearance_editor.save_reason": "Lagring av denne optikken krever fysisk glass på en gjeldende GPU-kropp. Lakk og lokale bildekart forblir midlertidige.",
"render.appearance_editor.saved": "Lagret med dette opptaket",
"render.appearance_editor.saved_state": "Denne optikken er lagret med dette opptaket.",
"render.appearance_editor.scatter_reason": "Indre spredning krever en lukket volumkropp som grense.",
"render.appearance_editor.temporary": "Denne optikken er midlertidig i denne fanen. Lagre den for å beholde den sammen med opptaket.",
"render.appearance_editor.thickness": "Tykkelse",
"render.appearance_editor.thickness_reason": "Tykkelse gjelder for tynne ark; lukket glass bruker geometrisk avstand.",
"render.appearance_editor.thin": "Tynt ark",
"render.appearance_editor.u_repeat": "U-gjentakelse",
"render.appearance_editor.v_repeat": "V-gjentakelse",
"render.appearance_editor.v_up": "Teksturens V peker oppover",
"render.appearance_editor.weight": "Vekt",
"render.appearance_physics_separate": "Visuelt utseende tildeler ikke tetthet, termiske data eller andre tekniske egenskaper.",
"render.appearances": "Utseender",
"render.availability_native": "Innebygd sammensetning; gjennomgå før bruk",
"render.availability_projection": "Støttet projeksjon; se omfang og utelatelser",
"render.availability_reference": "Referansedata; ikke en automatisk tildeling",
@@ -1843,6 +2043,10 @@
"render.camera_set": "Sett rendringskamera",
"render.camera_target": "Målposisjon · mm",
"render.camera_yaw": "Giring",
"render.category_metal": "Metall",
"render.category_paint": "Maling",
"render.category_plastic": "Plast",
"render.category_wood": "Tre",
"render.chromatic_offset": "Kromatisk kantforskyvning",
"render.clear_stage": "Fjern lagret rendringsdekor",
"render.copy_request": "Kopier JSON",
@@ -1942,6 +2146,7 @@
"render.effects_reset_lens": "Tilbakestill linseeffekter",
"render.effects_seed": "Startverdi for mønster",
"render.emitted_axis": "Emisjonsakse",
"render.environment_tiles_require_raytrace": "Omgivelser krever CPU- eller GPU-strålesporing. Velg metoden i utdataeditoren.",
"render.error_area_samples": "area_samples må være 1, 4, 16 eller 64",
"render.error_binding_allocation": "kunne ikke reservere materialbinding",
"render.error_body_bound": "produktforhåndsvisningen overskrider grensen for lagrede kropper",
@@ -2013,6 +2218,8 @@
"render.flare_strength": "Styrke på linseflekk i høylys",
"render.flare_threshold": "Terskel for linseflekk (scenelineær)",
"render.fold_browser": "Brett sammen modelltreet",
"render.frame_presets": "Forhåndsinnstillinger for bilderute",
"render.frame_presets_pending": "Kommer med opptakseditoren",
"render.gpu_depth": "Maksimal banedybde",
"render.gpu_options_help": "Prøver, dybde, startverdi og eksponering lagres med dette oppsettet. Bare den aktive bildemetoden lagres.",
"render.gpu_path_failed": "GPU-banesporing er ikke tilgjengelig",
@@ -2073,6 +2280,36 @@
"render.inner_half_angle": "Indre halvvinkel",
"render.inspect_scene": "Oppdater sammendraget av den fangede scenen",
"render.inspector": "Inspektør",
"render.inspector_advanced": "Avansert",
"render.inspector_all_users": "Alle {count} som bruker det",
"render.inspector_applies_to": "Gjelder for",
"render.inspector_both_users": "Begge som bruker det",
"render.inspector_chain": "Hvor denne stilen kommer fra",
"render.inspector_colour": "Farge",
"render.inspector_edit": "Rediger",
"render.inspector_finish": "Overflatefinish",
"render.inspector_from_material": "Fra materiale",
"render.inspector_gloss": "Blank",
"render.inspector_material": "Materiale",
"render.inspector_matte": "Matt",
"render.inspector_metal": "Metall",
"render.inspector_opacity": "Opasitet",
"render.inspector_opaque_help": "Overflatefinish og optiske felt bruker ugjennomsiktig PBR (opasitet 1). Lavere opasitet bruker skalar skyggelegging.",
"render.inspector_pick_target": "Velg et mål av denne typen for å inspisere utseendet.",
"render.inspector_remove": "Fjern overstyring",
"render.inspector_replace": "Erstatt…",
"render.inspector_reset": "Tilbakestill",
"render.inspector_satin": "Satin",
"render.inspector_save_library": "Lagre i bibliotek…",
"render.inspector_set_all": "Blandet utseende. Redigering setter stilen på alle valgte mål.",
"render.inspector_soon": "Snart",
"render.inspector_target_limit": "Velg høyst 10000 mål for å inspisere utseende.",
"render.inspector_textures": "Teksturer",
"render.inspector_this_body": "Denne kroppen",
"render.inspector_this_component": "Denne komponenten",
"render.inspector_this_face": "Denne flaten",
"render.inspector_this_target": "Dette målet",
"render.inspector_used_by": "Brukes av {count}",
"render.keep_editing": "Fortsett redigering",
"render.layer_down": "Flytt ned",
"render.layer_enabled": "Lag aktivert",
@@ -2084,8 +2321,12 @@
"render.lens_effects_title": "Linseinspirert og fotografisk",
"render.library_all": "Katalog",
"render.library_all_states": "Alle tilgjengelighetsstatuser",
"render.library_appearance_available": "Tilgjengelig utseende",
"render.library_applicable": "Ressurser som kan brukes",
"render.library_applied": "Ressursprojeksjonen er brukt. Sjekk hele rendringsforespørselen før du starter.",
"render.library_apply_appearance": "Bruk utseende",
"render.library_apply_hdr": "Lagre med denne HDR-omgivelsen",
"render.library_apply_hint": "Klikk for å bruke · dra over på modellen",
"render.library_apply_maps": "Bruk medfølgende GPU-overflatekart",
"render.library_apply_studio": "Lagre medfølgende studioprojeksjon",
"render.library_apply_uniform": "Bruk ensartet, ugjennomsiktig utseende",
@@ -2101,15 +2342,23 @@
"render.library_extent": "Motivets utstrekning",
"render.library_favorite": "Legg til favoritt",
"render.library_favorites": "Favoritter",
"render.library_filters": "Katalog og tilgjengelighet",
"render.library_grid": "Rutenett",
"render.library_hdr": "Innebygde HDR-omgivelser",
"render.library_hdr_help": "Bygger den opprinnelige HDR-en inn i det lagrede oppsettet. Den medfølgende lengdegradsopprinnelsen konverteres eksplisitt; ingen ekstern fil trengs etter lagring.",
"render.library_in_design": "I designet",
"render.library_inspect_only": "Velg for å inspisere; ingenting brukes automatisk. Favoritter og nylige valg hører til denne åpne dokumentøkten.",
"render.library_ior": "IOR",
"render.library_light_concepts": "Lyskonsepter",
"render.library_list": "Liste",
"render.library_maps_help": "Innebygde kart kan lagres med festede kilder og lokal tilordning per forekomst. Midlertidige optiske overstyringer er fortsatt en egen modus.",
"render.library_material_concepts": "Materialkonsepter",
"render.library_metallic": "Metallisk",
"render.library_native_scenes": "Innebygde studioer",
"render.library_next": "Neste side",
"render.library_omitted": "Ikke inkludert",
"render.library_opacity": "Opasitet",
"render.library_other": "Andre utseender",
"render.library_physical_profiles": "Fysiske referanseprofiler",
"render.library_previous": "Forrige side",
"render.library_quality_concepts": "Kvalitetsspesifikasjoner",
@@ -2119,6 +2368,7 @@
"render.library_results": "Samsvarende ressurser",
"render.library_rig_help": "Tilpass én gang med eksplisitt senter og fysisk utstrekning. Komplette rigger inkluderer passive kort; kalibreringshindringer avviser fortsatt. Konservativ klaring mellom lyskilder og kort sjekkes før lagring.",
"render.library_rigs": "Medfølgende lysrigger",
"render.library_roughness": "Ruhet",
"render.library_save_maps": "Lagre kart i dette oppsettet",
"render.library_scene_bounds": "Bruk grensene til den fangede scenen",
"render.library_scenes": "Scenespesifikasjoner",
@@ -2142,6 +2392,7 @@
"render.library_unfavorite": "Fjern favoritt",
"render.library_uniform_ack": "Bruk den ensartede tilnærmingen uten romlige eller avanserte effekter",
"render.library_usable": "Kjørbare projeksjoner",
"render.library_users": "Brukes av",
"render.light_anchor_pending": "Det er ikke støttet ennå å endre lysets koordinatsystem.",
"render.lights_fixed_to": "Lys festet til",
"render.linear_help": "Beholder en RGBA-master med 32-biters flyttall før støyreduksjon, eksponering, tonekartlegging, filtre og bakgrunn. Lineær sRGB/D65; dekningsforhåndsmultiplisert. Ekstra minne tillates eksplisitt. Ingen komprimering eller ekstra pass.",
@@ -2360,6 +2611,7 @@
"render.scene_review": "Avklar og gjennomgå",
"render.scene_review_help": "Gå gjennom hele den innebygde komposisjonen før du bruker den. Ubundne avanserte SCENE-spesifikasjoner erstattes ikke.",
"render.scene_scope_details": "Komposisjonens omfang",
"render.search_appearances": "Søk i {count} utseender",
"render.setup_legacy_environment": "Dette eldre oppsettet bruker utdatert rotasjon eller intensitet for omgivelsen. Disse verdiene beholdes; konverter dem eksplisitt før du lagrer en full bildejobb.",
"render.setup_name": "Oppsettsnavn",
"render.setup_numeric_help": "Lagre registrerer den aktive bildemetoden og alle numeriske innstillinger. Ny innlasting eller valg av et annet oppsett erstatter dette utkastet; gjenopprett kameraet separat.",
@@ -2369,6 +2621,7 @@
"render.sharpening": "Detaljskjerping",
"render.shell_tab_appearance": "RENDRINGSUTSEENDE",
"render.shell_tab_camera": "RENDRINGSKAMERA",
"render.shell_tab_lighting": "BELYSNING",
"render.shell_tab_scene": "RENDRINGSSCENE",
"render.shot_duplicate": "Dupliser lagret opptak",
"render.shot_manager": "Lagrede opptak",
@@ -2378,10 +2631,19 @@
"render.shot_remove_title": "Fjerne lagret opptak?",
"render.shot_rename": "Gi nytt navn til lagret opptak",
"render.shot_saved_only": "Lagre det gjeldende utkastet først. Duplisering og omdøping beholder de nøyaktige lagrede alternativene og ressursreferansene.",
"render.shots_camera_changed": "Kameraet er endret siden dette opptaket ble lagret",
"render.shots_name_help": "Skriv inn et unikt navn på det gjeldende opptaket.",
"render.shots_no_result": "Ingen rendring ennå",
"render.shots_result": "Rendring {number} · {width} × {height}",
"render.shots_revert": "Tilbakestill",
"render.shots_save": "Lagre opptak",
"render.shots_update": "Oppdater opptak",
"render.show_shots_label": "Vis opptaksstripen",
"render.size_4k_uhd": "4K UHD",
"render.size_full_hd": "Full HD",
"render.size_hd": "HD",
"render.size_presets": "Forhåndsinnstillinger for størrelse",
"render.size_presets_pending": "Kommer med utdataeditoren",
"render.sky_ground": "Bakke · lineær RGB",
"render.sky_horizon": "Horisont · lineær RGB",
"render.sky_zenith": "Zenit · lineær RGB",
@@ -2432,8 +2694,10 @@
"render.studio_sky_help": "Original himmelgradient for belysning; ikke en kalibrert atmosfæremodell.",
"render.studio_sun": "Sol",
"render.studio_sun_radius": "Vinkelradius",
"render.studio_tiles_require_gpu": "Studioer krever GPU-banesporing. Velg GPU i utdataeditoren.",
"render.studio_title": "Rendringsstudio",
"render.studio_tone_map": "Tonemapping",
"render.studios_environments": "Studioer og omgivelser",
"render.subsurface_enable": "Indre spredning (tilfeldig vandring)",
"render.subsurface_help": "Bare for lukkede dielektrikum. Spredning og RGB-absorpsjon er separate koeffisienter per mm. Lysbaner spres inne i den faktiske geometrien og brytes eller reflekteres deretter ved grensen. Slå av rett skyggegjennomgang, overflatestøyreduksjon og eksternt volum; bruk ett Fresnel-sjikt. Ikke en diffusjonsshader, et tetthetsrutenett eller et målt materiale. Tette medier kan kreve flere prøver og større banedybde.",
"render.subsurface_rate": "Kollisjonsrate for spredning",
@@ -2519,8 +2783,29 @@
"search.try": "Skriv et kommandonavn, en funksjon i dette designet, eller en parameter. For å kjøre en kommando med verdier, skriv id-en og et JSON-objekt: {example}",
"search.valid": "Gyldig",
"section.off_hint": "Ingen plan valgt: OK slår snittet av.",
"select a direction (an axis, a sketch line or a straight edge) first": "velg en retning (en akse, en skisselinje eller en rett kant) først",
"select a direction (an axis, a sketch line or a straight edge) for the profile to follow": "velg en retning (en akse, en skisselinje eller en rett kant) som profilen skal følge",
"select a point on a component": "velg et punkt på en komponent",
"select a profile, edges or sketch lines first": "velg en profil, kanter eller skisselinjer først",
"select an axis": "velg en akse",
"select an axis, a straight or circular edge, or a cylinder, cone or torus face first": "velg en akse, en rett eller sirkulær kant, eller en sylinder-, kjegle- eller torusflate først",
"select bodies first": "velg kropper først",
"select corner points, or the two lines of one corner": "velg hjørnepunkter, eller de to linjene i ett hjørne",
"select one corner point, or the two lines of one corner": "velg ett hjørnepunkt, eller de to linjene i ett hjørne",
"select sheet edges first": "velg platekanter først",
"select the bend line (a sketch line) first": "velg bøyelinjen (en skisselinje) først",
"select the cam's profile (a sketch profile or the cam's edges)": "velg kammens profil (en skisseprofil eller kammens kanter)",
"select the corner to cut first": "velg hjørnet som skal kappes først",
"select the corners to round first": "velg hjørnene som skal rundes først",
"select the fixed face first (a plate's large face)": "velg den faste flaten først (platens store flate)",
"select the frame sketch's lines first": "velg linjene i rammeskissen først",
"select the path (sketch lines across the edge)": "velg banen (skisselinjer på tvers av kanten)",
"select the rim face first": "velg randflaten først",
"select the sheet edge first": "velg platekanten først",
"select two components": "velg to komponenter",
"set by the sketch's lines": "bestemt av skisselinjene",
"settings.display_quality_note": "Gjelder bare visningen. Design og eksport påvirkes ikke.",
"settings.double_click_components": "Dobbeltklikk på en komponent for å aktivere den, eller for å redigere en koblet komponent på stedet",
"settings.frame_rate_details_note": "Legger til en graf over tidene for de siste bildene med trekantene som er tegnet oppå, et histogram over bildetider og hva GPU-en tegnet. Klikk på bildefrekvensen i visningen for å vise eller skjule dem.",
"settings.frame_rate_note": "Viser bilder per sekund nederst til høyre i visningen.",
"settings.measure_axis_distances": "Vis X/Y/Z-avstander",
@@ -2533,20 +2818,27 @@
"settings.reset_intro": "Disse innstillingene fungerer ikke på denne datamaskinen og ville hindret SolidCraft i å starte, så de ble endret:",
"settings.reset_title": "Innstillinger ble endret",
"settings.supersampling_note": "Tegner visningen i høyere oppløsning og skalerer den ned for å gi jevnere kanter, linjer og rutenett på skjermer under 4K. Det bruker mer GPU-tid: 1,5× gir omtrent 2,25 ganger så mange piksler, og 2× gir 4 ganger så mange.",
"settings.update_changed_links": "Oppdater endrede koblede design når en fane kommer tilbake",
"settings.window_system_details": "Detaljer",
"settings.window_system_note_now": "Gjelder etter en omstart. Nå: {now}.",
"settings.window_system_wayland_limited": "Noen funksjoner er begrenset på native Wayland.",
"sheet.flange_type_base": "Base",
"sheet.flange_type_contour": "Kontur",
"sheet.flange_type_edge": "Kant",
"sheet.no_body": "det finnes ingen platemetallkropp: start en med Flens > Basis, eller gjør en plate om til platemetall med Konverter til platemetall (velg den store, flate siden)",
"shortcut.json_error": "parametere må være JSON: {error}",
"shortcut.no_command": "ingen kommando `{id}`; teksten før JSON må være en kommando-id",
"shortcut.pin": "Fest øverst i S-boksen",
"shortcut.unpin": "Løsne",
"sketch is missing": "skissen mangler",
"sketch.degrees_of_freedom": "{count} frihetsgrader",
"sketch.dof_left": "{count} frihetsgrader igjen",
"sketch.dof_left_one": "{count} frihetsgrad igjen",
"sketch.draw_curves": "Tegn som {linetype}",
"sketch.make_selection": "Konverter utvalget til {linetype}",
"sketch.open_ends": "Åpne {count}",
"sketch.status_conflict": "bindingene er i konflikt",
"sketch.status_fully_constrained": "fullt bundet",
"sketch.untied_ends": "Ubundne {count}",
"sketch_panel.attached": "Festet til {body} / {path}",
"sketch_panel.author": "Skissens forekomst: {path}",
@@ -2573,7 +2865,28 @@
"sketch_text.path_hint": "Velg en kurve i skissen som teksten skal følge: klikk her og deretter på kurven",
"sketch_text.use_font_file": "Bruk skriftfilen {path}",
"solid": "volumkropp",
"status.editing": "Redigerer {name}",
"status.under_constrained": "Underbestemt ({count} frihetsgrader)",
"status.under_constrained_one": "Underbestemt ({count} frihetsgrad)",
"status.units_tip": "Designets enheter. Klikk for å endre dem",
"the angle must be a number": "vinkelen må være et tall",
"the curves must be in one sketch": "kurvene må ligge i én skisse",
"the direction must be an axis, a sketch line or a straight edge": "retningen må være en akse, en skisselinje eller en rett kant",
"the duration must be a number of seconds, 0.001 or more": "varigheten må være et antall sekunder, 0,001 eller mer",
"the line's sketch is gone": "linjens skisse finnes ikke lenger",
"the lines must be in one sketch": "linjene må ligge i én skisse",
"the lines must be in one sketch, not the section's": "linjene må ligge i én skisse, ikke i tverrsnittets",
"the parts library is empty": "delbiblioteket er tomt",
"the parts library is not available": "delbiblioteket er ikke tilgjengelig",
"the path curves must be in one other sketch": "banekurvene må ligge i én annen skisse",
"the path's sketch is gone": "banens skisse finnes ikke lenger",
"the position must be a number": "posisjonen må være et tall",
"the preview failed": "forhåndsvisningen mislyktes",
"the profiles and curves must be in one sketch": "profilene og kurvene må ligge i én skisse",
"the reference face must be on the edges' body": "referanseflaten må ligge på kantenes kropp",
"the scale must be above 0": "skalaen må være større enn 0",
"the section's profiles must be in one sketch": "tverrsnittets profiler må ligge i én skisse",
"the two origins are on the same component": "de to origoene ligger på samme komponent",
"thread.automatic_size": "Automatisk størrelse",
"thread.designation": "Betegnelse",
"thread.direction": "Gjengeretning",
@@ -2613,6 +2926,7 @@
"trace.points": "{count} punkter langs banen",
"trace.stopped_by_contact": "Stoppet ved prøve {sample}: {a} ville trenge gjennom {b}",
"trace.stopped_by_loop": "Stoppet ved prøve {sample}: leddsløyfen gjennom {joint} kan ikke lukkes videre",
"trim by sketch curves, or by one plane or face": "trim med skissekurver, eller med ett plan eller én flate",
"ui.1_degree_of_freedom": "1 frihetsgrad",
"ui.3d_mouse_axes_reassigned": "Aksene tilordnes på nytt i Innstillinger, og det gjelder fortsatt oppå denne kalibreringen.",
"ui.3d_mouse_button": "Knapp {number}",
@@ -3089,6 +3403,8 @@
"ui.design_properties.revision": "Revisjon",
"ui.diameter": "Diameter",
"ui.dimension": "Mål",
"ui.direct_interaction": "Direkte interaksjon",
"ui.direct_interaction_note": "Arbeid direkte på modellen i modellvisningen. Trykk på en plan flate eller en profil og dra: opp ekstruderer, ned i en kropp kutter, og sideveis bevegelse setter trinnstørrelsen",
"ui.direction": "Retning",
"ui.display_quality": "Visningskvalitet",
"ui.display_quality_coarse": "Grov",
@@ -3110,8 +3426,27 @@
"ui.easyprint_uploading": "Laster opp til Printables…",
"ui.edit": "Rediger",
"ui.editJointLimits": "Rediger leddgrenser",
"ui.edit_in_place": "Rediger på stedet",
"ui.edit_in_place_after_turn": "Redigeringen fullføres når agentens tur er over",
"ui.edit_in_place_agent_busy": "Agenten jobber fortsatt i dette designet; prøv igjen når turen er over",
"ui.edit_in_place_already_queued": "En redigering venter allerede på slutten av agentens tur",
"ui.edit_in_place_assembly_closed": "{name} er ikke lenger åpen",
"ui.edit_in_place_banner": "Redigerer i {name}",
"ui.edit_in_place_busy": "Fullfør eller avbryt det som er åpent først",
"ui.edit_in_place_none": "Ingen koblede design redigeres på stedet",
"ui.edit_in_place_not_saved": "Delen har endringer som ikke er lagret; lagre den for å fullføre redigeringen",
"ui.edit_in_place_open_elsewhere": "{name} er åpen i en annen SolidCraft",
"ui.edit_in_place_opens_after_turn": "Delen åpnes når agentens tur er over",
"ui.edit_in_place_refresh_dropped": "Sammenstillingen ble endret mens Oppdater koblinger kjørte; kjør den på nytt",
"ui.edit_in_place_refresh_stopped": "Oppdater koblinger stoppet før den var ferdig",
"ui.edit_in_place_refreshing": "Oppdaterer koblinger i {name}…",
"ui.edit_in_place_save_first": "Lagre sammenstillingen først: de koblede designene finnes ut fra filen",
"ui.edit_in_place_save_waits": "Oppdater koblinger kjører i {name}; lagre den når den er ferdig",
"ui.edit_in_place_saved_elsewhere": "Delen ble lagret i en annen fil; sammenstillingen er fortsatt koblet til {name}",
"ui.edit_in_place_wait": "Vent til det som kjører i designet er ferdig",
"ui.edit_section_analysis": "Rediger snittanalyse",
"ui.ellipse": "Ellipse",
"ui.emboss_wrap_note": "Ortogonale linjeprofiler som tangerer én støttet sylinder, legges automatisk rundt den. Velg en vegg for å velge et mål.",
"ui.end_radius": "Sluttradius",
"ui.end_scale": "Sluttskala",
"ui.entities": "Objekter",
@@ -3183,7 +3518,9 @@
"ui.heading_edit": "REDIGER",
"ui.heading_extrude": "EKSTRUDERING",
"ui.heading_fillet": "AVRUNDING",
"ui.heading_finish_editing": "FULLFØR REDIGERING",
"ui.heading_finish_sketch": "FULLFØR SKISSE",
"ui.heading_form": "FORM",
"ui.heading_front": "FRONT",
"ui.heading_hole": "HULL",
"ui.heading_home": "HJEM",
@@ -3270,6 +3607,8 @@
"ui.intersect": "Snitt",
"ui.isolate": "Isoler",
"ui.join": "Slå sammen",
"ui.joint_play": "▶ Spill av",
"ui.joint_stop": "■ Stopp",
"ui.joints": "Ledd",
"ui.keyboard_shortcuts": "Tastatursnarveier",
"ui.language": "Språk",
@@ -3287,6 +3626,7 @@
"ui.loft": "Loft",
"ui.look_at": "Orienter mot",
"ui.look_at_anim_desc": "Aksevisning, Orienter mot, Hjem og Tilpass",
"ui.major_radius": "Stor radius",
"ui.manipulator_free_alt": "Tillat fri dragging med Alt/Option",
"ui.manipulator_free_alt_tip": "På: Alt/Option eller Ctrl/Cmd på ekstruderings- og manipulatorpiler slår av verdiintervaller. Av: bare Ctrl/Cmd.",
"ui.manufacture": "Produksjon",
@@ -3316,8 +3656,10 @@
"ui.mesh_format_3mf": "3MF",
"ui.mesh_format_3mf_tagged": "3MF med flatemerker",
"ui.mesh_format_stl": "STL",
"ui.minor_radius": "Liten radius",
"ui.mirror": "Speil",
"ui.mirror_line": "Speillinje",
"ui.modifiers_either": "{first} eller {second}",
"ui.move": "Flytt",
"ui.multiple": "Flere",
"ui.named_views": "Navngitte visninger",
@@ -3436,7 +3778,10 @@
"ui.restore_defaults": "Gjenopprett standardinnstillinger",
"ui.reverse_horizontal_pan_direction": "Reverser horisontal panoreringsretning",
"ui.reverse_horizontal_pan_tip": "Horisontal rulling med to fingre og tommelhjulet panorerer i motsatt retning",
"ui.reverse_vertical_pan_direction": "Reverser vertikal panoreringsretning",
"ui.reverse_vertical_pan_tip": "Vertikal rulling med to fingre panorerer i motsatt retning",
"ui.reverse_zoom_direction": "Reverser zoomretning",
"ui.revolutions": "Omdreininger",
"ui.revolve": "Omdreining",
"ui.ribbon_exit": "Avslutt",
"ui.ribbon_labels": "Vis tekst under båndikonene",
@@ -3453,6 +3798,7 @@
"ui.scale": "Skaler",
"ui.second_rail": "Andre føringskurve",
"ui.section_analysis": "Snittanalyse",
"ui.section_size": "Tverrsnittsstørrelse",
"ui.select": "Velg",
"ui.selectOther": "Velg annet…",
"ui.selectTangentChain": "Velg tangentkjede",
@@ -3524,6 +3870,7 @@
"ui.start_page": "Startsiden",
"ui.step_values": "Endre verdier i trinn med piltastene og hjulet",
"ui.step_values_tip": "I et verdifelt du skriver i, endrer pil opp/ned et vanlig tall med 1 (Shift: 10, Alt: 0.1), og det samme gjør musehjulet over feltet. Uttrykk beholdes som skrevet.",
"ui.stitch_tolerance_hint": "stram",
"ui.stretch": "Strekk",
"ui.supersampling": "Supersampling (SSAA)",
"ui.supersampling_adaptive": "Bare når visningen står stille",
@@ -3554,6 +3901,8 @@
"ui.to_object": "Til objekt",
"ui.tool_direction": "Verktøyretning",
"ui.top": "Topp",
"ui.top_diameter": "Toppdiameter",
"ui.top_radius": "Toppradius",
"ui.torus": "Torus",
"ui.total_angle": "Total vinkel",
"ui.trackpad_pan_tip": "Styreplatebevegelser panorerer (av: de zoomer som hjulet); musehjulet zoomer, tommelhjulet panorerer sidelengs, knip zoomer alltid",
@@ -3576,6 +3925,15 @@
"ui.updateToCurrentView": "Oppdater til gjeldende visning",
"ui.upload": "Last opp",
"ui.vertical_constraint": "Vertikal binding",
"ui.view_angles": "Visningsvinkler",
"ui.view_angles_convention": "Grader om oppaksen. Giring 0° er frontvisningen, 90° venstre, −90° høyre, 180° bak; helning 90° er toppvisningen; positiv rull dreier modellen med klokken.",
"ui.view_angles_help": "Viser visningens helning, giring og rull i grader og projeksjonen under orienteringsindikatoren. Klikk på avlesningen for å skrive inn nøyaktige vinkler.",
"ui.view_angles_ortho": "Orto",
"ui.view_angles_persp": "Persp.",
"ui.view_angles_pitch": "Helning",
"ui.view_angles_readout": "Helning {pitch} · Giring {yaw} · Rull {roll}",
"ui.view_angles_roll": "Rull",
"ui.view_angles_yaw": "Giring",
"ui.view_centre": "Visningssenter",
"ui.viewport_theme_follow": "Følg tema",
"ui.visual_shaded_hidden_edges": "Skyggelagt med skjulte kanter",
@@ -3643,7 +4001,11 @@
"update.up_to_date": "SolidCraft er oppdatert.",
"update.update_now": "Oppdater nå",
"update.you_have": "Du har {version}.",
"use a positive nominal diameter and a pitch smaller than it": "bruk en positiv nominell diameter og en stigning som er mindre enn den",
"values must be finite numbers": "verdiene må være endelige tall",
"values must be numbers or expressions": "verdiene må være tall eller uttrykk",
"view.auto_ortho_faces": "Perspektiv ved rotering, ortografisk på flater",
"view.render_inspector": "Rendringsinspektør",
"web.files.insert_drawings_hint": "CAD-filer, mesh, DXF eller SVG inn i det åpne designet",
"web.files.insert_stored_drawings_hint": "Inn i det åpne designet (CAD-filer, mesh, DXF, SVG)",
"web.open_full": "Åpne i SolidCraft",
+252
View File
@@ -1,4 +1,5 @@
{
"(active rule)": "(actieve regel)",
"0 turns it off": "0 schakelt het uit",
"1. Open GitHub draft below, then paste the report copied to your clipboard.": "1. Open hieronder het GitHub-concept en plak het naar het klembord gekopieerde rapport.",
"1. Open GitHub draft below. Your title and report will be filled in.": "1. Open hieronder het GitHub-concept. Uw titel en rapport worden vooraf ingevuld.",
@@ -52,16 +53,20 @@
"Apply your appearance first. Exports assigned appearances and materials created in this design.": "Pas eerst uw uiterlijk toe. Exporteert toegewezen uiterlijken en materialen die in dit ontwerp zijn gemaakt.",
"Arc length": "Booglengte",
"Area": "Oppervlakte",
"As Picked": "Zoals gekozen",
"As-Built": "Zoals gebouwd",
"Assign Rule": "Regel toewijzen",
"Attachment": "Bevestiging",
"BLEND SURFACE": "SAMENVOEGOPPERVLAK",
"BOUNDARY SURFACE": "BEGRENZINGSOPPERVLAK",
"BROWSER": "BROWSER",
"Ball": "Kogel",
"Bar": "Staaf",
"Base Center": "Basismiddelpunt",
"Base Height": "Basishoogte",
"Bend": "Buiging",
"Bend Edges": "Buigranden",
"Bend Line": "Buiglijn",
"Bend Line Position": "Positie van buiglijn",
"Bend Position": "Buigpositie",
"Bend Radius": "Buigstraal",
@@ -73,6 +78,7 @@
"Body (last if none)": "Lichaam (laatste indien geen)",
"Body to split": "Te splitsen lichaam",
"Body, or faces to take out": "Lichaam, of vlakken om te verwijderen",
"Both": "Beide",
"Bottom Left": "Linksonder",
"Bottom Middle": "Midden onder",
"Bottom Right": "Rechtsonder",
@@ -104,6 +110,7 @@
"Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.": "Kies een nieuw vlak. Schetsgeometrie en maatvoering blijven ongewijzigd in de tweedimensionale coördinaten ervan.",
"Choose…": "Kiezen…",
"Circle center": "Middelpunt cirkel",
"Circle or sphere": "Cirkel of bol",
"Circular": "Cirkelvormig",
"Circumference": "Omtrek",
"Clockwise": "Met de klok mee",
@@ -120,6 +127,7 @@
"Config Table": "Configuratietabel",
"Configuration": "Configuratie",
"Configuration rules": "Configuratieregels",
"Constant radius": "Constante straal",
"Contact": "Contact",
"Contact Sets": "Contactsets",
"Contact sets": "Contactsets",
@@ -155,7 +163,9 @@
"Custom": "Aangepast",
"Customize Toolbar": "Werkbalk aanpassen",
"Customize…": "Aanpassen…",
"Cylindrical": "Cilindrisch",
"Cylindrical face": "Cilindrisch vlak",
"Deboss": "Debossen",
"Delete": "Verwijderen",
"Delete Folders and Features…": "Mappen en kenmerken verwijderen…",
"Delete Selected Features…": "Geselecteerde kenmerken verwijderen…",
@@ -190,6 +200,7 @@
"Duration": "Duur",
"Each new shape is also dimensioned from the sketch origin until nothing of it is left free. A shape already attached to the origin or to placed geometry gets none.": "Elke nieuwe vorm krijgt ook maten vanaf de schetsoorsprong tot er niets meer vrij is. Een vorm die al aan de oorsprong of aan geplaatste geometrie vastzit, krijgt er geen.",
"Each shape you draw gets all its dimensions, at the sizes you drew (typed values win). A size the sketch already sets is left out.": "Elke vorm die je tekent krijgt al zijn maten op de getekende grootte (getypte waarden gaan voor). Een maat die de schets al vastlegt, wordt weggelaten.",
"Edge": "Rand",
"Edges": "Randen",
"Edit": "Bewerken",
"Edit Profile Sketch": "Profielschets bewerken",
@@ -198,6 +209,7 @@
"End Radius": "Eindstraal",
"End bulge": "Eindbolling",
"End continuity": "Eindcontinuïteit",
"Equal Distance": "Gelijke afstand",
"Expand": "Uitklappen",
"Expected result (optional)": "Verwacht resultaat (optioneel)",
"Explode": "Exploderen",
@@ -225,6 +237,8 @@
"Faces": "Vlakken",
"Faces / edges / vertices": "Vlakken / randen / hoekpunten",
"Faces to split": "Te splitsen vlakken",
"Faces/Body": "Vlakken/lichaam",
"Factor must be a positive number": "De factor moet een positief getal zijn",
"Fastener": "Bevestigingsmiddel",
"Favorites": "Favorieten",
"Favourite": "Favoriet",
@@ -234,18 +248,25 @@
"Filter…": "Filter…",
"Find in Browser": "Zoeken in browser",
"Finish": "Afwerking",
"First edge": "Eerste rand",
"First plane": "Eerste vlak",
"First point": "Eerste punt",
"Fit and G2 corners require Constant radius.": "Passing en G2-hoeken vereisen Constante straal.",
"Fixed Face": "Vast vlak",
"Flange Gap": "Flensopening",
"Flange thickness": "Flensdikte",
"Flanges": "Flenzen",
"Flat Pattern": "Uitslag",
"Flat size": "Uitgeslagen afmeting",
"Flat size X": "Uitgeslagen afmeting X",
"Flat size Y": "Uitgeslagen afmeting Y",
"Follow camera (Look At sketches)": "De camera volgen (Kijken naar schetsen)",
"Follow the camera (Look At sketches)": "De camera volgen (Kijken naar schetsen)",
"Font": "Lettertype",
"Free Move": "Vrije verplaatsing",
"From": "Vanaf",
"From Bodies": "Uit lichamen",
"From Snap": "Snappen vanaf",
"From edges": "Uit randen",
"Full Length": "Volledige lengte",
"Full thread depth": "Volledige schroefdraaddiepte",
@@ -257,10 +278,13 @@
"Halo under sketch curves": "Halo onder schetscurven",
"Height": "Hoogte",
"Height From": "Hoogte vanaf",
"Hem Gap": "Omzomingsopening",
"Hidden edges": "Verborgen randen",
"History layout": "Geschiedenisindeling",
"History names": "Geschiedenisnamen",
"Hole (optional)": "Gat (optioneel)",
"Hole Depth": "Gatdiepte",
"Hole Diameter": "Gatdiameter",
"Holes": "Gaten",
"Holes (edges on them)": "Gaten (randen erop)",
"Home view": "Beginweergave",
@@ -283,12 +307,16 @@
"Issue title": "Titel van issue",
"Items": "Items",
"JOINTS": "VERBINDINGEN",
"Joint 1": "Verbinding 1",
"Joint 2": "Verbinding 2",
"Joint Origin1": "Verbindingsoorsprong1",
"K Factor": "K-factor",
"Keep (points on it)": "Behouden (punten erop)",
"Keep Tools": "Gereedschappen behouden",
"Keep of the other surface": "Deel van het andere oppervlak behouden",
"Key a joint's value at this step": "Verbindingswaarde als sleutelframe vastleggen op deze stap",
"Keys": "Sleutelframes",
"Knife Edge": "Messcherpe rand",
"L-Angle": "L-profiel",
"LIBRARY": "BIBLIOTHEEK",
"LINKS": "KOPPELINGEN",
@@ -301,9 +329,11 @@
"Letter Spacing": "Letterafstand",
"Library": "Bibliotheek",
"Library file": "Bibliotheekbestand",
"Library response is missing": "Het antwoord van de bibliotheek ontbreekt",
"Limits": "Limieten",
"Line Spacing": "Regelafstand",
"Line Type": "Lijntype",
"Line on plane": "Lijn op vlak",
"Lines": "Lijnen",
"Listing the installed fonts…": "Geïnstalleerde lettertypen ophalen…",
"Load library": "Bibliotheek laden",
@@ -323,11 +353,14 @@
"Material Outside": "Materiaal aan buitenzijde",
"Material copied. Existing body assignments are unchanged.": "Materiaal gekopieerd. Bestaande lichaamstoewijzingen blijven ongewijzigd.",
"Max": "Max",
"Max Thickness": "Maximale dikte",
"Maximum": "Maximum",
"Mesh body": "Meshlichaam",
"Mid Radius": "Middenstraal",
"Middle double-click": "Dubbelklik met middelste knop",
"Min": "Min",
"Min Draft": "Minimale aanzet",
"Min Thickness": "Minimale dikte",
"Minimum": "Minimum",
"Mirror Plane": "Spiegelvlak",
"Mitre": "Verstek",
@@ -347,6 +380,7 @@
"Name": "Naam",
"Name on import": "Naam bij importeren",
"Nearest": "Dichtstbijzijnde",
"Neutral plane": "Neutraal vlak",
"New": "Nieuw",
"New Comp.": "Nieuwe comp.",
"New Sketch": "Nieuwe schets",
@@ -357,6 +391,7 @@
"No matching appearances": "Geen overeenkomende uiterlijken",
"No matching physical materials.": "Geen overeenkomende fysieke materialen.",
"No visible bodies or sketches to export.": "Geen zichtbare lichamen of schetsen om te exporteren.",
"Nominal Radius": "Nominale straal",
"Nominal diameter": "Nominale diameter",
"None": "Geen",
"Not available yet": "Nog niet beschikbaar",
@@ -385,22 +420,28 @@
"Overall length": "Totale lengte",
"Param. Graph": "Param.-grafiek",
"Parameter": "Parameter",
"Part length must be greater than zero and at most 2000 mm": "De onderdeellengte moet groter zijn dan nul en maximaal 2000 mm bedragen",
"Parts": "Onderdelen",
"Pass through": "Doorlaten",
"Path": "Pad",
"Pattern": "Patroon",
"Per Edge": "Per rand",
"Physical material definitions": "Definities van fysieke materialen",
"Pick a part size": "Kies een onderdeelgrootte",
"Pick the tool bodies": "Kies de gereedschapslichamen",
"Picked": "Gekozen",
"Picked point": "Gekozen punt",
"Pin-slot": "Pen-sleuf",
"Pitch": "Spoed",
"Pivot": "Draaipunt",
"Place at the origin instead": "In plaats daarvan op de oorsprong plaatsen",
"Planar": "Vlak",
"Plastic Rules": "Kunststofregels",
"Play the joint's motion in the view": "De beweging van de verbinding in de weergave afspelen",
"Playback": "Afspelen",
"Plot": "Plotten",
"Point on line": "Punt op lijn",
"Point on plane": "Punt op vlak",
"Position": "Positie",
"Position (0 to 1)": "Positie (0 tot 1)",
"Position (G0)": "Positie (G0)",
@@ -413,6 +454,7 @@
"Profiles or curves": "Profielen of curven",
"Profiles/Curves": "Profielen/curven",
"Quantity": "Aantal",
"Quantity must be at least 2": "Het aantal moet minimaal 2 zijn",
"RECENT": "RECENT",
"REFERENCE": "REFERENTIE",
"RULED SURFACE": "REGELOPPERVLAK",
@@ -426,6 +468,8 @@
"Rectangular": "Rechthoekig",
"Rectangular Tube": "Rechthoekige buis",
"Reference face": "Referentievlak",
"Relief Depth": "Vrijsnijdiepte",
"Relief Width": "Vrijsnijbreedte",
"Remove Tools": "Gereedschappen verwijderen",
"Remove condition": "Voorwaarde verwijderen",
"Remove from Folder": "Uit map verwijderen",
@@ -454,6 +498,7 @@
"Restore this studio's tools": "Gereedschappen van deze werkruimte herstellen",
"Result": "Resultaat",
"Retake screenshot": "Schermafbeelding opnieuw maken",
"Reveal Height": "Zichthoogte",
"Reverse": "Omkeren",
"Reverse Direction": "Richting omkeren",
"Revert": "Terugdraaien",
@@ -462,12 +507,15 @@
"Revert Position": "Positie terugzetten",
"Review the report and screenshot before opening GitHub.": "Controleer het rapport en de schermafbeelding voordat u GitHub opent.",
"Review your report on GitHub and click Submit new issue to publish it.": "Controleer uw rapport op GitHub en klik op Submit new issue om het te publiceren.",
"Revolute": "Draaiend",
"Rib Length": "Riblengte",
"Rib Offset": "Riboffset",
"Rib Thickness": "Ribdikte",
"Ribs": "Ribben",
"Right Middle": "Midden rechts",
"Rigid": "Star",
"Rim Edge": "Rand van rand",
"Rim face": "Randvlak",
"Ring of tools on the right button": "Ring van gereedschappen op de rechtermuisknop",
"Roll Back Here": "Hier terugdraaien",
"Roll History Marker Here": "Terugdraaimarkering hier plaatsen",
@@ -492,7 +540,9 @@
"Search": "Zoeken",
"Search commands": "Opdrachten zoeken",
"Search this library": "Deze bibliotheek doorzoeken",
"Second edge": "Tweede rand",
"Second plane": "Tweede vlak",
"Second point": "Tweede punt",
"Section": "Doorsnede",
"Section (optional)": "Doorsnede (optioneel)",
"Section position": "Positie van de doorsnede",
@@ -503,6 +553,15 @@
"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",
"Select edges": "Randen selecteren",
"Select points of the sketch being edited": "Selecteer punten van de schets die je bewerkt",
"Select the centre the pattern turns around first": "Selecteer eerst het middelpunt waaromheen het patroon draait",
"Select the curves to offset first": "Selecteer eerst de curven die je wilt offsetten",
"Select the mirror line": "Selecteer de spiegellijn",
"Select the sketch curves or points to mirror first": "Selecteer eerst de schetscurven of schetspunten die je wilt spiegelen",
"Select the sketch curves or points to move first": "Selecteer eerst de schetscurven of schetspunten die je wilt verplaatsen",
"Select the sketch curves or points to pattern first": "Selecteer eerst de schetscurven of schetspunten waarvan je een patroon wilt maken",
"Select the sketch curves or points to scale first": "Selecteer eerst de schetscurven of schetspunten die je wilt schalen",
"Select the sketch curves or points to transform first": "Selecteer eerst de schetscurven of schetspunten die je wilt transformeren",
"Selection filter": "Selectiefilter",
"Semantic faces share their appearance across instances.": "Semantische vlakken delen hun uiterlijk over alle instanties.",
"Send your report on GitHub": "Uw rapport verzenden via GitHub",
@@ -522,9 +581,12 @@
"Sketch geometry and profiles": "Schetsgeometrie en -profielen",
"Sketch plane has no body-face attachment.": "Het schetsvlak heeft geen bevestiging aan een lichaamsvlak.",
"Sketch points": "Schetspunten",
"Slider": "Schuivend",
"Slot width": "Sleufbreedte",
"Snap to face and edge centres": "Snappen op middelpunten van vlakken en randen",
"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.",
"Spacing 2 must be a non-zero number": "Tussenruimte 2 moet een getal ongelijk aan nul zijn",
"Spacing must be a non-zero number": "De tussenruimte moet een getal ongelijk aan nul zijn",
"Specialise": "Specialiseren",
"Speed": "Snelheid",
"Splitting tool": "Splitsgereedschap",
@@ -535,6 +597,7 @@
"Start angle": "Beginhoek",
"Start bulge": "Beginbolling",
"Start continuity": "Begincontinuïteit",
"Stationary Side": "Vaste zijde",
"Step": "Stap",
"Steps": "Stappen",
"Steps to reproduce (optional)": "Stappen om het te reproduceren (optioneel)",
@@ -549,6 +612,7 @@
"Suppressed": "Onderdrukt",
"Surface": "Oppervlak",
"Surface, or faces to turn over": "Oppervlak of vlakken om om te keren",
"Surfaces": "Oppervlakken",
"Swap Ends": "Uiteinden omwisselen",
"TRIM": "TRIMMEN",
"Tangent": "Tangentieel",
@@ -559,24 +623,34 @@
"Tap drill diameter": "Kerngatdiameter",
"Target": "Doel",
"Target Body": "Doellichaam",
"Target Face (optional)": "Doelvlak (optioneel)",
"Tear": "Scheuren",
"Technical details": "Technische details",
"The dashed sketch is a saved plane guide. Select a new face to repair the attachment.": "De gestreepte schets is een opgeslagen vlakgids. Selecteer een nieuw vlak om de bevestiging te herstellen.",
"The design changed. Reopen Change Part Size for the component.": "Het ontwerp is gewijzigd. Open Onderdeelgrootte wijzigen voor de component opnieuw.",
"The direction must be an axis or a straight sketch curve that lies in the sketch plane": "De richting moet een as of een rechte schetscurve in het schetsvlak zijn",
"The mirror line cannot also be mirrored": "De spiegellijn kan niet ook gespiegeld worden",
"The mirror line must be a straight curve of this sketch, not an origin axis": "De spiegellijn moet een rechte curve van deze schets zijn, geen as van de oorsprong",
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "De opgeslagen auteursoccurrence ontbreekt. Selecteer een geldige auteursplaatsing voordat u opnieuw koppelt.",
"Then": "Dan",
"These features will be deleted:": "Deze kenmerken worden verwijderd:",
"These features will fail:": "Deze kenmerken mislukken:",
"Thickness Outside": "Dikte aan buitenzijde",
"Thickness Variation": "Diktevariatie",
"Thin": "Dun",
"Thin Extrude": "Dunwandige extrusie",
"Third plane": "Derde vlak",
"Third point": "Derde punt",
"This child is shared. Deleting it removes it from every copy of its parent.": "Dit onderliggende item wordt gedeeld. Verwijderen haalt het uit elke kopie van het bovenliggende item.",
"This child is shared. Position changes apply to every copy of its parent.": "Dit onderliggende item wordt gedeeld. Positiewijzigingen gelden voor elke kopie van het bovenliggende item.",
"This face cannot provide a durable attachment. Choose another face or explicitly use a fixed plane.": "Dit vlak kan geen duurzame bevestiging bieden. Kies een ander vlak of gebruik expliciet een vast vlak.",
"This image is not attached to GitHub yet. Copy or save it, then attach it on GitHub.": "Deze afbeelding is nog niet aan GitHub toegevoegd. Kopieer of sla de afbeelding op en voeg die daarna op GitHub toe.",
"Thread depth": "Schroefdraaddiepte",
"Thread diameter": "Schroefdraaddiameter",
"Through point": "Door punt",
"To": "Naar",
"To Offset": "Tot offset",
"To Snap": "Snappen naar",
"To edges": "Tot randen",
"Toe radius": "Teenstraal",
"Tolerance": "Tolerantie",
@@ -590,6 +664,7 @@
"Trimming tool (plane, face or curves)": "Trimgereedschap (vlak, vlak of curven)",
"Turn off section analysis before exporting the view.": "Schakel de doorsnedeanalyse uit voordat u de weergave exporteert.",
"Type": "Type",
"Type a distance or an angle to move by": "Typ een afstand of hoek waarover je wilt verplaatsen",
"UNTRIM": "TRIM OPHEFFEN",
"Unit": "Eenheid",
"Unsuppress": "Onderdrukking opheffen",
@@ -634,6 +709,17 @@
"Z angle": "Z-hoek",
"Z distance": "Z-afstand",
"Zebra": "Zebra",
"`joint` must be an id or a name": "`joint` moet een id of een naam zijn",
"`params` must be an object": "`params` moet een object zijn",
"`values` must be a list": "`values` moet een lijst zijn",
"a chamfer needs a corner of exactly two lines": "een afschuining vereist een hoek van precies twee lijnen",
"a file operation is in progress": "er is een bestandsbewerking bezig",
"a picked curve is no longer in a sketch": "een gekozen curve staat niet meer in een schets",
"a picked curve's sketch has not been solved": "de schets van een gekozen curve is niet opgelost",
"a picked edge has no points": "een gekozen rand heeft geen punten",
"a picked edge is gone": "een gekozen rand bestaat niet meer",
"a picked edge's body is gone": "het lichaam van een gekozen rand bestaat niet meer",
"add a key for a joint first": "voeg eerst een sleutel toe voor een verbinding",
"ai.connected_toast": "SolidCraft is nu beschikbaar in {clients}. Start deze opnieuw op om het te gebruiken (Instellingen > Assistent).",
"ai.prefs.allow_control": "AI-agents laten verbinden (besturingskanaal / MCP)",
"ai.prefs.allow_control_note": "Opent een besturingskanaal alleen op deze computer (127.0.0.1, poort 27878 of de eerstvolgende vrije). Een programma dat verbinding maakt, moet ook een geheim token tonen dat alleen uw gebruikersaccount kan lezen en dat bij elke start opnieuw wordt aangemaakt. Wordt van kracht de volgende keer dat SolidCraft start. Uit: er luistert niets, tenzij u SolidCraft met --control start.",
@@ -674,6 +760,8 @@
"appearance.pbr_opaque": "Ondoorzichtige PBR",
"appearance.roughness": "Ruwheid",
"appearance.surface_response": "Oppervlakrespons",
"at the face": "op het vlak",
"bom.group_children": "Subonderdelen in de rij van hun onderdeel",
"browser.body_name": "Lichaam{number}",
"browser.calibrate_title": "Kalibreren: {name}",
"browser.capture_many": "{count} componenten zijn verplaatst, maar hun posities zijn niet vastgelegd.",
@@ -684,6 +772,8 @@
"browser.move_title": "Verplaatsen: {name}",
"browser.parameters": "Parameters ({count})",
"browser.precision": "Precisie: {precision}",
"browser.sketch_attachment_lost": "{name} (bevestiging kwijt)",
"browser.sketch_editing": "{name} (wordt bewerkt)",
"browser.sketch_name": "Schets{number}",
"browser.units": "Eenheden: {units}",
"cam.flat": "Plat vlak",
@@ -693,7 +783,13 @@
"cam.roller": "Rol",
"cam.roller_pick": "Rol (optioneel)",
"cam.tip_pick": "Punt (optioneel)",
"choose a profile": "kies een profiel",
"choose the cam's and the follower's joints": "kies de verbindingen van de nok en de volger",
"choose the joints to couple": "kies de te koppelen verbindingen",
"click a plane or planar face": "klik op een vlak of een plat vlak",
"click sketch curves": "klik op schetscurven",
"cmd.appearance_assign": "Uiterlijk",
"cmd.appearance_chain": "Uiterlijkenketen",
"cmd.appearance_library": "Uiterlijkbibliotheek",
"cmd.appearance_list": "Uiterlijken weergeven",
"cmd.appearance_paint": "Uiterlijk aanbrengen",
@@ -1031,6 +1127,7 @@
"cmd.sketch_spline_control_point_5": "Spline met controlepunten (graad 5)",
"cmd.sketch_spline_fit_point": "Spline door punten",
"cmd.sketch_spline_insert_point": "Splinepunt toevoegen",
"cmd.sketch_spline_reverse": "Splinerichting omkeren",
"cmd.sketch_spun_profile": "Gedraaid profiel",
"cmd.sketch_toggle_diameter": "Straal/diameter omschakelen",
"cmd.sketch_toggle_driven": "Aangedreven maat omschakelen",
@@ -1191,6 +1288,8 @@
"dxf.progress.write": "DXF schrijven (5/5)",
"dxf.tolerance": "Curvetolerantie",
"dxf.tolerance_help": "Alleen zichtbare lichamen. Curven gebruiken rechte segmenten. Een kleinere tolerantie geeft fijnere curven en kost meer tijd en geheugen.",
"each rail's curves must be in one sketch": "de curven van elke geleider moeten in één schets liggen",
"feature computation ended without a result": "de berekening van het kenmerk eindigde zonder resultaat",
"file.open.drawing_filter": "Ontwerpen, CAD-bestanden, meshes en tekeningen",
"file_load.built": "Tot nu toe opgebouwd: {built} van {total} kenmerken.",
"file_load.converting": "Solid {done} van {total} converteren",
@@ -1249,6 +1348,8 @@
"help.third_party": "Licenties van derden",
"help.timeline.compute_all": "Bouwt elk kenmerk in de tijdlijn opnieuw op vanaf het begin. Gebruik dit om het model te vernieuwen en te zien welke kenmerken mislukken.",
"help.timeline.delete": "Verwijdert de geselecteerde kenmerken uit de tijdlijn; als een schets wordt verwijderd, gaan ook de kenmerken eruit die erop zijn gebouwd. Gebruik dit om een stap te verwijderen die niet meer gewenst is.",
"history.sketch_under_constrained": "Onderbeperkt ({count} vrijheidsgraden)",
"history.sketch_under_constrained_one": "Onderbeperkt ({count} vrijheidsgraad)",
"hole_presets.confirm_delete": "Verwijderen bevestigen",
"hole_presets.delete": "Verwijderen",
"hole_presets.delete_prompt": "Deze persoonlijke voorinstelling verwijderen? Bestaande gaten blijven ongewijzigd.",
@@ -1438,7 +1539,9 @@
"nav.free_orbit_upright.help": "Zijwaarts slepen met de muis draait het model om de verticale wereldas; omhoog en omlaag slepen kantelt zonder limiet over de bovenkant.",
"nav.free_orbit_upright.label": "Om de verticale as draaien",
"nav.fusion.help": "Middenknop slepen pant, Shift+middenknop draait, rechterknop slepen draait",
"nav.hfss.help": "Alt+linkerknop slepen pant, Shift+Alt+linkerknop zoomt, Ctrl+linkerknop slepen op lege ruimte draait",
"nav.inventor.help": "Middenknop slepen pant, Shift+middenknop draait, Ctrl+middenknop zoomt, rechterknop slepen draait",
"nav.maya.help": "Alt+linkerknop slepen draait, Alt+middenknop pant, Alt+rechterknop zoomt; middenknop slepen pant ook",
"nav.openscad.help": "Linkerknop slepen op lege ruimte draait, Shift+linkerknop slepen selecteert met een kader, schetsgeometrie slepen bewerkt die; rechter- of middenknop slepen pant, Shift+rechter- of Shift+middenknop slepen zoomt",
"nav.perspective.sketch": "Schetsbewerking gebruikt een orthografische weergave. Rond de schets af om naar perspectief te schakelen.",
"nav.solidworks.help": "Middenknop slepen draait, Ctrl+middenknop pant, Shift+middenknop zoomt, rechterknop slepen draait",
@@ -1546,12 +1649,34 @@
"no hole": "geen gat",
"no interference": "geen interferentie",
"no joint that moves yet": "nog geen verbinding die beweegt",
"no motion study yet": "nog geen bewegingsstudie",
"no picture": "geen afbeelding",
"no such joint": "deze verbinding bestaat niet",
"none": "geen",
"not a move": "geen verplaatsing",
"not a number": "geen getal",
"open a sketch first": "open eerst een schets",
"parts.automatic_name": "automatisch",
"parts.dimension.across_flats": "Sleutelwijdte",
"parts.dimension.bore": "Boring",
"parts.dimension.d": "Nominale diameter",
"parts.dimension.head_diameter": "Kopdiameter",
"parts.dimension.head_height": "Kophoogte",
"parts.dimension.height": "Hoogte",
"parts.dimension.inner": "Binnendiameter",
"parts.dimension.outer": "Buitendiameter",
"parts.dimension.pitch": "Steek",
"parts.dimension.socket": "Inbusmaat",
"parts.dimension.socket_depth": "Inbusdiepte",
"parts.dimension.thickness": "Dikte",
"parts.dimension.width": "Breedte",
"parts.family.bearing": "Groefkogellager (omhullende)",
"parts.family.dowel_pin": "Parallelpen (paspen)",
"parts.family.hex_bolt": "Zeskantbout / zeskantschroef",
"parts.family.hex_nut": "Zeskantmoer",
"parts.family.set_screw": "Stelschroef met inbus, vlakke punt",
"parts.family.socket_head_cap_screw": "Cilinderkopschroef met inbus",
"parts.family.washer": "Vlakke onderlegring",
"pattern.alignment_2": "Uitlijning 2",
"pattern.alignment_hint": "Aantallen omvatten het origineel. Gecentreerd verdeelt kopieën gelijkmatig; Omkeren kiest de extra zijde bij even totalen.",
"pattern.direction_1": "Richting 1",
@@ -1567,9 +1692,25 @@
"perf.ui_time": "UI-tijd",
"perf.viewport": "Viewport",
"perf.worst": "slechtste",
"pick a body or some of its faces": "kies een lichaam of enkele vlakken ervan",
"pick a hole to seat it, or OK places it at the origin": "kies een gat om het te plaatsen, of OK plaatst het op de oorsprong",
"pick a planar face": "kies een plat vlak",
"pick a plane or a face as the tool": "kies een werkvlak of een vlak als gereedschap",
"pick a size": "kies een maat",
"pick edges of a body": "kies randen van een lichaam",
"pick edges of one body": "kies randen van één lichaam",
"pick edges of the holes to remove": "kies de randen van de gaten die je wilt verwijderen",
"pick faces of one body": "kies vlakken van één lichaam",
"pick faces to offset": "kies de vlakken die je wilt offsetten",
"pick lines of one sketch": "kies lijnen van één schets",
"pick one or two vertices, edges or faces": "kies een of twee hoekpunten, randen of vlakken",
"pick one plane or face as the tool, or sketch curves": "kies één werkvlak of vlak als gereedschap, of schetscurven",
"pick sections in two or more sketches": "kies doorsneden in twee of meer schetsen",
"pick sketch curves or edges": "kies schetscurven of randen",
"pick the face to untrim": "kies het vlak waarvan je de trim wilt opheffen",
"pick the path": "kies het pad",
"pick what to keep": "kies wat je wilt behouden",
"pick what to keep of the other surface": "kies wat je wilt behouden van het andere oppervlak",
"prefs.add_item": "Het item toevoegen",
"prefs.alt_frees_manipulators": "Alt / Option maakt ook de manipulatorpijlen vrij",
"prefs.anti_aliasing_amd_dx12": "Anti-aliasing gebruikt 1×, omdat de opstartcontrole met multisampling niets tekende en bij 1× slaagde. Je kunt hierboven een andere instelling kiezen; start opnieuw op om die toe te passen als de controle slaagt.",
@@ -1653,6 +1794,13 @@
"prefs.left_click": "Linksklik",
"prefs.live_input": "Live invoer",
"prefs.live_preview": "Live voorbeeld",
"prefs.max_file_size": "Maximale bestandsgrootte om te openen",
"prefs.max_file_size_design": "Ontwerpbestanden",
"prefs.max_file_size_flag": "Deze uitvoering is gestart met --max-file-size {size}, dat geldt totdat SolidCraft sluit. Deze instellingen gelden bij het starten zonder die optie.",
"prefs.max_file_size_note": "Standaard: 512 MB en 256 MB; maximaal 16 GB. Een bestand openen vereist meerdere keren de bestandsgrootte aan geheugen. Een ontwerp met een grote import kan een hogere limiet voor ontwerpbestanden nodig hebben om te worden opgeslagen en heropend.",
"prefs.max_file_size_step": "STEP- en importbestanden",
"prefs.max_file_size_web": "De webapp opent STEP- en importbestanden tot 512 MB en ontwerpen tot 256 MB: een browsertabblad kan geen grotere bestanden bevatten. In de desktopapp kunnen deze limieten worden verhoogd.",
"prefs.megabytes": "MB",
"prefs.minutes": "minuten",
"prefs.minutes_suffix": " min",
"prefs.mouse": "Muis en trackpad",
@@ -1701,6 +1849,8 @@
"prefs.sketch_selection": "Schets en selectie",
"prefs.sketch_selection_heading": "Schets en selectie",
"prefs.sketch_selection_note": "Hoe schetsen snappen en hoe klikken selecteren.",
"prefs.sketch_state_icons": "Schetspictogrammen in de kleur van hun staat",
"prefs.sketch_state_icons_note": "In de geschiedenis en de browser krijgt het pictogram van een schets de kleur van zijn staat (onderbeperkt, volledig beperkt of conflicterend) en noemt de tooltip de staat. Uit: elke schets heeft hetzelfde pictogram.",
"prefs.sketch_tab_always": "Schets-tab altijd tonen",
"prefs.sketch_tab_always_note": "De Schets-tab blijft in het lint staan, direct na Model, ook als er geen schets open is. Zijn tekentools beginnen dan een schets op het geselecteerde vlak, of op het vlak dat je daarna kiest, met de tool daarin klaar voor gebruik.",
"prefs.sketching": "Schetsen",
@@ -1708,6 +1858,8 @@
"prefs.source": "Bron",
"prefs.speed": "Snelheid",
"prefs.stand_up_note": "Nadat een STEP- of IGES-bestand met Z omhoog is geopend, een kwartslag om X aanbieden.",
"prefs.status_bar": "Statusbalk",
"prefs.status_bar_note": "Een smalle balk langs de onderrand van het venster toont wat er wordt bewerkt, de staat van de open schets in zijn kleur en de eenheden van het ontwerp; een klik op de eenheden wijzigt ze. De staat van de schets staat dan niet meer in de hoek van de weergave.",
"prefs.status_done": "Gereed",
"prefs.status_error": "Fout",
"prefs.status_selected": "Geselecteerd",
@@ -1787,6 +1939,7 @@
"render.all_controls_help": "Compact overzicht van alle huidige bedieningselementen. Sectiepagina's bieden gerichte bewerking zonder het onderliggende verzoek te wijzigen.",
"render.all_shots_label": "Alle opnamen",
"render.appearance_physics_separate": "Het visuele uiterlijk kent geen dichtheid, thermische gegevens of andere technische eigenschappen toe.",
"render.appearances": "Uiterlijken",
"render.availability_native": "Ingebouwde samenstelling; controleren voor het toepassen",
"render.availability_projection": "Ondersteunde projectie; zie bereik en weglatingen",
"render.availability_reference": "Referentiegegevens; geen automatische toewijzing",
@@ -1841,6 +1994,10 @@
"render.camera_set": "Rendercamera instellen",
"render.camera_target": "Doelpositie · mm",
"render.camera_yaw": "Gieren",
"render.category_metal": "Metaal",
"render.category_paint": "Verf",
"render.category_plastic": "Kunststof",
"render.category_wood": "Hout",
"render.chromatic_offset": "Chromatische randverschuiving",
"render.clear_stage": "Opgeslagen decor verwijderen",
"render.copy_request": "JSON kopiëren",
@@ -1940,6 +2097,7 @@
"render.effects_reset_lens": "Lenseffecten herstellen",
"render.effects_seed": "Patroonseed",
"render.emitted_axis": "Emissieas",
"render.environment_tiles_require_raytrace": "Omgevingen vereisen CPU- of GPU-raytracing. Kies dit in de Uitvoer-editor.",
"render.error_area_samples": "area_samples moet 1, 4, 16 of 64 zijn",
"render.error_binding_allocation": "kon materiaalkoppeling niet reserveren",
"render.error_body_bound": "productvoorbeeld overschrijdt de grens voor vastgelegde lichamen",
@@ -2011,6 +2169,8 @@
"render.flare_strength": "Flare-sterkte voor highlights",
"render.flare_threshold": "Flare-drempel (scènelineair)",
"render.fold_browser": "Browser samenvouwen",
"render.frame_presets": "Kadervoorinstellingen",
"render.frame_presets_pending": "Komt met de Opname-editor",
"render.gpu_depth": "Maximale paddiepte",
"render.gpu_options_help": "Samples, diepte, seed en belichting worden opgeslagen bij deze opstelling. Alleen de actieve beeldmethode wordt opgeslagen.",
"render.gpu_path_failed": "GPU-pathtracer niet beschikbaar",
@@ -2082,8 +2242,12 @@
"render.lens_effects_title": "Lensgeïnspireerd & fotografisch",
"render.library_all": "Catalogus",
"render.library_all_states": "Alle beschikbaarheidsstatussen",
"render.library_appearance_available": "Beschikbaar uiterlijk",
"render.library_applicable": "Toepasbare bronnen",
"render.library_applied": "Projectie van de bron toegepast. Controleer de volledige renderopdracht voordat je start.",
"render.library_apply_appearance": "Uiterlijk toepassen",
"render.library_apply_hdr": "Opslaan met deze HDR-omgeving",
"render.library_apply_hint": "Klik om toe te passen · sleep naar het model",
"render.library_apply_maps": "Ontworpen GPU-oppervlaktetexturen gebruiken",
"render.library_apply_studio": "Ontworpen studioprojectie opslaan",
"render.library_apply_uniform": "Uniform dekkend uiterlijk toepassen",
@@ -2099,15 +2263,23 @@
"render.library_extent": "Omvang van het onderwerp",
"render.library_favorite": "Favoriet toevoegen",
"render.library_favorites": "Favorieten",
"render.library_filters": "Catalogus en beschikbaarheid",
"render.library_grid": "Raster",
"render.library_hdr": "Ingebouwde HDR-omgevingen",
"render.library_hdr_help": "Sluit de oorspronkelijke HDR in de opgeslagen opstelling in. De ontworpen oorsprong van de lengtegraad wordt expliciet omgerekend; na het opslaan is geen extern bestand nodig.",
"render.library_in_design": "In ontwerp",
"render.library_inspect_only": "Selecteer om te inspecteren; er wordt niets automatisch toegepast. Favorieten en recente geschiedenis horen bij deze sessie van het geopende document.",
"render.library_ior": "IOR",
"render.library_light_concepts": "Lichtconcepten",
"render.library_list": "Lijst",
"render.library_maps_help": "Ingebouwde texturen kunnen worden opgeslagen met vastgemaakte bronnen en lokale toewijzing per voorkomen. Tijdelijke optische overschrijvingen blijven een aparte modus.",
"render.library_material_concepts": "Materiaalconcepten",
"render.library_metallic": "Metaalachtig",
"render.library_native_scenes": "Eigen studio's",
"render.library_next": "Volgende pagina",
"render.library_omitted": "Niet inbegrepen",
"render.library_opacity": "Dekking",
"render.library_other": "Overige uiterlijken",
"render.library_physical_profiles": "Fysieke referentieprofielen",
"render.library_previous": "Vorige pagina",
"render.library_quality_concepts": "Kwaliteitsspecificaties",
@@ -2117,6 +2289,7 @@
"render.library_results": "Overeenkomende bronnen",
"render.library_rig_help": "Eenmalig passend maken op een expliciet middelpunt en een fysieke omvang. Volledige rigs omvatten passieve kaarten; rigs die kalibratie vereisen, worden nog steeds geweigerd. Een voorzichtige speling tussen lichtbron en kaart wordt gecontroleerd voordat wordt opgeslagen.",
"render.library_rigs": "Ontworpen lichtrigs",
"render.library_roughness": "Ruwheid",
"render.library_save_maps": "Texturen opslaan in deze opstelling",
"render.library_scene_bounds": "Vastgelegde scènebegrenzing gebruiken",
"render.library_scenes": "Scènespecificaties",
@@ -2140,6 +2313,7 @@
"render.library_unfavorite": "Favoriet verwijderen",
"render.library_uniform_ack": "De uniforme benadering gebruiken zonder ruimtelijke of geavanceerde effecten",
"render.library_usable": "Uitvoerbare projecties",
"render.library_users": "Gebruikt door",
"render.light_anchor_pending": "Het wijzigen van het coördinatensysteem van het licht wordt nog niet ondersteund.",
"render.lights_fixed_to": "Lichten vast aan",
"render.linear_help": "Bewaart een 32-bits float-RGBA-master vóór ruisonderdrukking, belichting, tone mapping, filters en achtergrond. Lineair sRGB/D65; met voorvermenigvuldigde dekking. Extra geheugen wordt expliciet toegestaan. Geen compressie of extra passes.",
@@ -2358,6 +2532,7 @@
"render.scene_review": "Uitwerken en beoordelen",
"render.scene_review_help": "Bekijk de volledige native compositie voordat je toepast. Niet-gekoppelde geavanceerde SCENE-specificaties worden niet door een alternatief vervangen.",
"render.scene_scope_details": "Reikwijdte van de compositie",
"render.search_appearances": "Zoeken in {count} uiterlijken",
"render.setup_legacy_environment": "Deze oudere opstelling gebruikt verouderde omgevingsrotatie of -intensiteit. Die waarden blijven behouden; converteer ze expliciet voordat je een volledige afbeeldingstaak opslaat.",
"render.setup_name": "Naam van opstelling",
"render.setup_numeric_help": "Opslaan legt de actieve afbeeldingsmethode en alle bijbehorende numerieke instellingen vast. Opnieuw laden of een andere opstelling kiezen vervangt dit concept; herstel de camera afzonderlijk.",
@@ -2376,10 +2551,19 @@
"render.shot_remove_title": "Opgeslagen opname verwijderen?",
"render.shot_rename": "Opgeslagen opname hernoemen",
"render.shot_saved_only": "Sla eerst het huidige concept op. Dupliceren en hernoemen behouden de exacte opgeslagen opties en bronverwijzingen.",
"render.shots_camera_changed": "Camera gewijzigd sinds deze opname is opgeslagen",
"render.shots_name_help": "Voer een unieke naam in voor de huidige opname.",
"render.shots_no_result": "Nog geen render",
"render.shots_result": "Render {number} · {width} × {height}",
"render.shots_revert": "Terugdraaien",
"render.shots_save": "Opname opslaan",
"render.shots_update": "Opname bijwerken",
"render.show_shots_label": "Opnamenstrook tonen",
"render.size_4k_uhd": "4K UHD",
"render.size_full_hd": "Full HD",
"render.size_hd": "HD",
"render.size_presets": "Groottevoorinstellingen",
"render.size_presets_pending": "Komt met de Uitvoer-editor",
"render.sky_ground": "Vloer · lineair RGB",
"render.sky_horizon": "Horizon · lineair RGB",
"render.sky_zenith": "Zenit · lineair RGB",
@@ -2430,8 +2614,10 @@
"render.studio_sky_help": "Oorspronkelijk hemelverloop voor de verlichting; geen gekalibreerd atmosfeermodel.",
"render.studio_sun": "Zon",
"render.studio_sun_radius": "Hoekstraal",
"render.studio_tiles_require_gpu": "Studio's vereisen GPU-pathtracing. Kies GPU in de Uitvoer-editor.",
"render.studio_title": "Renderstudio",
"render.studio_tone_map": "Tone mapping",
"render.studios_environments": "Studio's en omgevingen",
"render.subsurface_enable": "Random walk voor inwendige verstrooiing",
"render.subsurface_help": "Alleen voor gesloten diëlektrica. Verstrooiing en RGB-absorptie zijn aparte coëfficiënten per mm. Paden verstrooien binnen de werkelijke geometrie en breken of reflecteren daarna aan de grens. Schakel rechte schaduwtransmissie, ruisonderdrukking van oppervlakken en uitwendig volume uit; gebruik één Fresnel-stratum. Dit is geen diffusieshader, dichtheidsraster of gemeten materiaal. Dichte media kunnen meer samples en een grotere paddiepte nodig hebben.",
"render.subsurface_rate": "Botsingsfrequentie van verstrooiing",
@@ -2517,6 +2703,26 @@
"search.try": "Typ een opdrachtnaam, een kenmerk in dit ontwerp of een parameter. Om een opdracht met waarden uit te voeren, typt u de id en een JSON-object: {example}",
"search.valid": "Geldig",
"section.off_hint": "Geen vlak gekozen: met OK wordt de doorsnede uitgeschakeld.",
"select a direction (an axis, a sketch line or a straight edge) first": "selecteer eerst een richting (een as, een schetslijn of een rechte rand)",
"select a direction (an axis, a sketch line or a straight edge) for the profile to follow": "selecteer een richting (een as, een schetslijn of een rechte rand) die het profiel volgt",
"select a point on a component": "selecteer een punt op een component",
"select a profile, edges or sketch lines first": "selecteer eerst een profiel, randen of schetslijnen",
"select an axis": "selecteer een as",
"select an axis, a straight or circular edge, or a cylinder, cone or torus face first": "selecteer eerst een as, een rechte of cirkelvormige rand, of een cilinder-, kegel- of torusvlak",
"select bodies first": "selecteer eerst lichamen",
"select corner points, or the two lines of one corner": "selecteer hoekpunten, of de twee lijnen van één hoek",
"select one corner point, or the two lines of one corner": "selecteer één hoekpunt, of de twee lijnen van één hoek",
"select sheet edges first": "selecteer eerst plaatranden",
"select the bend line (a sketch line) first": "selecteer eerst de buiglijn (een schetslijn)",
"select the cam's profile (a sketch profile or the cam's edges)": "selecteer het profiel van de nok (een schetsprofiel of de randen van de nok)",
"select the corner to cut first": "selecteer eerst de hoek die je wilt uitsnijden",
"select the corners to round first": "selecteer eerst de hoeken die je wilt afronden",
"select the fixed face first (a plate's large face)": "selecteer eerst het vaste vlak (het grote vlak van een plaat)",
"select the frame sketch's lines first": "selecteer eerst de lijnen van de frameschets",
"select the path (sketch lines across the edge)": "selecteer het pad (schetslijnen langs de rand)",
"select the rim face first": "selecteer eerst het randvlak",
"select the sheet edge first": "selecteer eerst de plaatrand",
"select two components": "selecteer twee componenten",
"set by the sketch's lines": "bepaald door de lijnen van de schets",
"settings.display_quality_note": "Fijnere niveaus tekenen gebogen vlakken en randen vloeiender en kosten meer tijd om te meshen. Alleen de viewport verandert, nooit het ontwerp of de exports.",
"settings.frame_rate_details_note": "Voegt een grafiek toe met de tijden van de laatste frames, met de getekende driehoeken erboven, een histogram van frametijden en wat de GPU heeft getekend. Klik op de framesnelheid in de weergave om ze te tonen of te verbergen.",
@@ -2534,17 +2740,23 @@
"settings.window_system_details": "Details",
"settings.window_system_note_now": "Wordt van kracht na een herstart. Nu: {now}.",
"settings.window_system_wayland_limited": "Sommige functies zijn beperkt op native Wayland.",
"sheet.flange_type_base": "Basis",
"sheet.flange_type_contour": "Contour",
"sheet.flange_type_edge": "Rand",
"sheet.no_body": "er is geen plaatwerklichaam: begin er een met Flens > Basis, of zet een plaat om in plaatwerk met Converteren naar plaatwerk (kies het grote platte vlak ervan)",
"shortcut.json_error": "parameters moeten JSON zijn: {error}",
"shortcut.no_command": "geen opdracht `{id}`; de tekst vóór de JSON moet een opdracht-id zijn",
"shortcut.pin": "Bovenaan het S-vak vastpinnen",
"shortcut.unpin": "Losmaken",
"sketch is missing": "schets ontbreekt",
"sketch.degrees_of_freedom": "{count} vrijheidsgraden",
"sketch.dof_left": "{count} vrijheidsgraden resterend",
"sketch.dof_left_one": "{count} vrijheidsgraad resterend",
"sketch.draw_curves": "Tekenen als {linetype}",
"sketch.make_selection": "De selectie converteren naar {linetype}",
"sketch.open_ends": "{count} open",
"sketch.status_conflict": "beperkingen conflicteren",
"sketch.status_fully_constrained": "volledig beperkt",
"sketch.untied_ends": "Losgemaakt {count}",
"sketch_panel.attached": "Bevestigd aan {body} / {path}",
"sketch_panel.author": "Auteursplaatsing: {path}",
@@ -2571,7 +2783,28 @@
"sketch_text.path_hint": "Een curve van de schets die de tekst volgt: klik hier en daarna op de curve",
"sketch_text.use_font_file": "Lettertypebestand {path} gebruiken",
"solid": "solid",
"status.editing": "{name} wordt bewerkt",
"status.under_constrained": "Onderbeperkt ({count} vrijheidsgraden)",
"status.under_constrained_one": "Onderbeperkt ({count} vrijheidsgraad)",
"status.units_tip": "De eenheden van het ontwerp. Klik om ze te wijzigen",
"the angle must be a number": "de hoek moet een getal zijn",
"the curves must be in one sketch": "de curven moeten in één schets staan",
"the direction must be an axis, a sketch line or a straight edge": "de richting moet een as, een schetslijn of een rechte rand zijn",
"the duration must be a number of seconds, 0.001 or more": "de duur moet een aantal seconden zijn, 0.001 of meer",
"the line's sketch is gone": "de schets van de lijn bestaat niet meer",
"the lines must be in one sketch": "de lijnen moeten in één schets staan",
"the lines must be in one sketch, not the section's": "de lijnen moeten in één schets staan, niet in die van de doorsnede",
"the parts library is empty": "de onderdelenbibliotheek is leeg",
"the parts library is not available": "de onderdelenbibliotheek is niet beschikbaar",
"the path curves must be in one other sketch": "de padcurven moeten in één andere schets staan",
"the path's sketch is gone": "de schets van het pad bestaat niet meer",
"the position must be a number": "de positie moet een getal zijn",
"the preview failed": "het voorbeeld is mislukt",
"the profiles and curves must be in one sketch": "de profielen en curven moeten in één schets staan",
"the reference face must be on the edges' body": "het referentievlak moet op het lichaam van de randen liggen",
"the scale must be above 0": "de schaal moet groter zijn dan 0",
"the section's profiles must be in one sketch": "de profielen van de doorsnede moeten in één schets staan",
"the two origins are on the same component": "de twee oorsprongen liggen op hetzelfde component",
"thread.automatic_size": "Automatische grootte",
"thread.designation": "Aanduiding",
"thread.direction": "Richting van schroefdraad",
@@ -2611,6 +2844,7 @@
"trace.points": "{count} punten langs het pad",
"trace.stopped_by_contact": "Eindigt bij monster {sample}: {a} zou door {b} heen gaan",
"trace.stopped_by_loop": "Eindigt bij monster {sample}: de lus via {joint} kan niet verder sluiten",
"trim by sketch curves, or by one plane or face": "trimmen met schetscurven, of met één werkvlak of vlak",
"ui.1_degree_of_freedom": "1 vrijheidsgraad",
"ui.3d_mouse_axes_reassigned": "Assen worden opnieuw toegewezen in Voorkeuren, en dat blijft gelden bovenop deze kalibratie.",
"ui.3d_mouse_button": "Knop {number}",
@@ -3087,6 +3321,8 @@
"ui.design_properties.revision": "Revisie",
"ui.diameter": "Diameter",
"ui.dimension": "Maat",
"ui.direct_interaction": "Directe interactie",
"ui.direct_interaction_note": "Werk direct aan het model in de viewport. Druk op een plat vlak of een profiel en trek: omhoog extrudeert, omlaag in een lichaam maakt een uitsnede, en zijwaartse beweging bepaalt de stapgrootte",
"ui.direction": "Richting",
"ui.display_quality": "Weergavekwaliteit",
"ui.display_quality_coarse": "Grof",
@@ -3110,6 +3346,7 @@
"ui.editJointLimits": "Verbindingslimieten bewerken",
"ui.edit_section_analysis": "Doorsnedeanalyse bewerken",
"ui.ellipse": "Ellips",
"ui.emboss_wrap_note": "Orthogonale lijnprofielen die raken aan één ondersteunde cilinder worden automatisch omwikkeld. Kies een wand om een doel te kiezen.",
"ui.end_radius": "Eindstraal",
"ui.end_scale": "Eindschaal",
"ui.entities": "Elementen",
@@ -3182,6 +3419,7 @@
"ui.heading_extrude": "EXTRUDEREN",
"ui.heading_fillet": "AFRONDING",
"ui.heading_finish_sketch": "SCHETS VOLTOOIEN",
"ui.heading_form": "VORM",
"ui.heading_front": "VOOR",
"ui.heading_hole": "GAT",
"ui.heading_home": "HOME",
@@ -3268,6 +3506,8 @@
"ui.intersect": "Snijden",
"ui.isolate": "Isoleren",
"ui.join": "Samenvoegen",
"ui.joint_play": "▶ Afspelen",
"ui.joint_stop": "■ Stoppen",
"ui.joints": "Verbindingen",
"ui.keyboard_shortcuts": "Sneltoetsen",
"ui.language": "Taal",
@@ -3285,6 +3525,7 @@
"ui.loft": "Loft",
"ui.look_at": "Kijken naar",
"ui.look_at_anim_desc": "Aanzicht op as, Kijken naar, Home en Passend",
"ui.major_radius": "Grote straal",
"ui.manipulator_free_alt": "Manipulator vrij slepen ook met Alt/Option",
"ui.manipulator_free_alt_tip": "Aan: Alt/Option of Ctrl/Cmd op extrusie-/gizmopijlen schakelt waardesnap uit. Uit: alleen Ctrl/Cmd.",
"ui.manufacture": "Fabricage",
@@ -3314,8 +3555,10 @@
"ui.mesh_format_3mf": "3MF",
"ui.mesh_format_3mf_tagged": "3MF met vlaktags",
"ui.mesh_format_stl": "STL",
"ui.minor_radius": "Kleine straal",
"ui.mirror": "Spiegelen",
"ui.mirror_line": "Spiegellijn",
"ui.modifiers_either": "{first} of {second}",
"ui.move": "Verplaatsen",
"ui.multiple": "Meerdere",
"ui.named_views": "Benoemde weergaven",
@@ -3372,6 +3615,7 @@
"ui.pinToShortcuts": "Vastpinnen aan sneltoetsen",
"ui.pinToToolbar": "Vastpinnen aan werkbalk",
"ui.pipe": "Buis",
"ui.pitch": "Steek",
"ui.placement": "Plaatsing",
"ui.placement_handles": "Plaatsingsgrepen",
"ui.placement_handles_tip": "Grepen op het voorbeeld van een Rustvlak, Nok of Snapverbinding: sleep de markering om hem over het model te verplaatsen, draai hem aan de ring",
@@ -3434,6 +3678,7 @@
"ui.reverse_horizontal_pan_direction": "Horizontale pan-richting omkeren",
"ui.reverse_horizontal_pan_tip": "Horizontaal scrollen met twee vingers en het duimwiel pannen de andere kant op",
"ui.reverse_zoom_direction": "Zoomrichting omkeren",
"ui.revolutions": "Omwentelingen",
"ui.revolve": "Revolve",
"ui.ribbon_exit": "Afsluiten",
"ui.ribbon_labels": "Tekst onder lintpictogrammen tonen",
@@ -3450,6 +3695,7 @@
"ui.scale": "Schalen",
"ui.second_rail": "Tweede geleider",
"ui.section_analysis": "Doorsnedeanalyse",
"ui.section_size": "Doorsnedegrootte",
"ui.select": "Selecteren",
"ui.selectOther": "Overige selecteren…",
"ui.selectTangentChain": "Tangentiële keten selecteren",
@@ -3521,6 +3767,7 @@
"ui.start_page": "Startpagina",
"ui.step_values": "Waarden stapsgewijs wijzigen met de pijltoetsen en het wiel",
"ui.step_values_tip": "In een waardeveld waarin je typt, wijzigen Omhoog/Omlaag een gewoon getal met 1 (Shift: 10, Alt: 0,1), net als het muiswiel boven het veld. Expressies blijven zoals getypt.",
"ui.stitch_tolerance_hint": "strak",
"ui.stretch": "Rekken",
"ui.supersampling": "Supersampling (SSAA)",
"ui.supersampling_adaptive": "Alleen als de weergave stilstaat",
@@ -3551,6 +3798,8 @@
"ui.to_object": "Tot object",
"ui.tool_direction": "Gereedschapsrichting",
"ui.top": "Boven",
"ui.top_diameter": "Bovendiameter",
"ui.top_radius": "Bovenstraal",
"ui.torus": "Torus",
"ui.total_angle": "Totale hoek",
"ui.trackpad_pan_tip": "Trackpadgebaren pannen (uit: ze zoomen zoals het wiel); muiswiel zoomt, duimwiel pant zijwaarts, knijpen zoomt altijd",
@@ -3640,6 +3889,9 @@
"update.up_to_date": "SolidCraft is up-to-date.",
"update.update_now": "Nu bijwerken",
"update.you_have": "U hebt {version}.",
"use a positive nominal diameter and a pitch smaller than it": "gebruik een positieve nominale diameter en een kleinere steek",
"values must be finite numbers": "waarden moeten eindige getallen zijn",
"values must be numbers or expressions": "waarden moeten getallen of expressies zijn",
"view.auto_ortho_faces": "Perspectief tijdens roteren, orthogonaal op vlakken",
"web.files.insert_drawings_hint": "CAD-bestanden, meshes, DXF of SVG in het geopende ontwerp",
"web.files.insert_stored_drawings_hint": "In het geopende ontwerp (CAD-bestanden, meshes, DXF, SVG)",
+9
View File
@@ -479,6 +479,7 @@
"appearance.pbr_opaque": "Nieprzezroczysty PBR",
"appearance.roughness": "Chropowatość",
"appearance.surface_response": "Reakcja powierzchni",
"at the face": "przy ścianie",
"browser.body_name": "Bryła{number}",
"browser.calibrate_title": "Kalibruj: {name}",
"browser.capture_many": "Przesunięto komponenty ({count}), ale ich położenia nie zostały zapamiętane.",
@@ -779,6 +780,7 @@
"cmd.sketch_spline_control_point": "Splajn z punktów kontrolnych",
"cmd.sketch_spline_control_point_5": "Splajn z punktów kontrolnych (stopień 5)",
"cmd.sketch_spline_fit_point": "Splajn przez punkty",
"cmd.sketch_spline_reverse": "Odwróć kierunek splajnu",
"cmd.sketch_spun_profile": "Profil obrotowy",
"cmd.sketch_toggle_driven": "Przełącz wymiar sterowany",
"cmd.sketch_trim_path": "Przytnij wzdłuż ścieżki",
@@ -1136,6 +1138,13 @@
"prefs.left_click": "Kliknięcie lewym",
"prefs.live_input": "Bieżące wejście",
"prefs.live_preview": "Podgląd na żywo",
"prefs.max_file_size": "Maksymalny rozmiar otwieranego pliku",
"prefs.max_file_size_design": "Pliki projektów",
"prefs.max_file_size_flag": "To uruchomienie używa --max-file-size {size} aż do zamknięcia SolidCraft. Te ustawienia obowiązują przy uruchomieniu bez tej opcji.",
"prefs.max_file_size_note": "Domyślnie: 512 MB i 256 MB; maksymalnie 16 GB. Otwarcie pliku wymaga pamięci kilkakrotnie większej od jego rozmiaru. Projekt z dużym importem może wymagać wyższego limitu pliku projektu, aby go zapisać i ponownie otworzyć.",
"prefs.max_file_size_step": "Pliki STEP i importu",
"prefs.max_file_size_web": "Aplikacja internetowa otwiera pliki STEP i importu do 512 MB oraz projekty do 256 MB: karta przeglądarki nie pomieści większych plików. W aplikacji komputerowej można zwiększyć te limity.",
"prefs.megabytes": "MB",
"prefs.minutes": "minut",
"prefs.minutes_suffix": " min",
"prefs.mouse": "Mysz i gładzik",
+9
View File
@@ -582,6 +582,7 @@
"appearance.pbr_opaque": "PBR opaco",
"appearance.roughness": "Rugosidade",
"appearance.surface_response": "Resposta da superfície",
"at the face": "na face",
"browser.body_name": "Corpo{number}",
"browser.calibrate_title": "Calibrar: {name}",
"browser.capture_many": "{count} componentes foram movidos, mas suas posições não foram capturadas.",
@@ -885,6 +886,7 @@
"cmd.sketch_spline_control_point": "Spline por pontos de controle",
"cmd.sketch_spline_control_point_5": "Spline por pontos de controle (grau 5)",
"cmd.sketch_spline_fit_point": "Spline por pontos de ajuste",
"cmd.sketch_spline_reverse": "Inverter direção do spline",
"cmd.sketch_spun_profile": "Perfil de revolução",
"cmd.sketch_toggle_driven": "Alternar cota conduzida",
"cmd.sketch_trim_path": "Aparar ao longo do caminho",
@@ -1259,6 +1261,13 @@
"prefs.left_click": "Clique esquerdo",
"prefs.live_input": "Entrada ao vivo",
"prefs.live_preview": "Visualização ao vivo",
"prefs.max_file_size": "Tamanho máximo de arquivo a abrir",
"prefs.max_file_size_design": "Arquivos de projeto",
"prefs.max_file_size_flag": "Esta execução foi iniciada com --max-file-size {size}, que vale até fechar o SolidCraft. Estas configurações se aplicam ao iniciar sem essa opção.",
"prefs.max_file_size_note": "Padrões: 512 MB e 256 MB; máximo de 16 GB. Abrir um arquivo exige várias vezes seu tamanho em memória. Um projeto que mantém uma importação grande pode precisar de um limite maior para ser salvo e reaberto.",
"prefs.max_file_size_step": "Arquivos STEP e de importação",
"prefs.max_file_size_web": "O aplicativo web abre arquivos STEP e de importação de até 512 MB e projetos de até 256 MB: uma aba do navegador não comporta arquivos maiores. O aplicativo para desktop permite aumentar esses limites.",
"prefs.megabytes": "MB",
"prefs.minutes": "minutos",
"prefs.minutes_suffix": " min",
"prefs.mouse": "Mouse e trackpad",
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
File diff suppressed because it is too large Load Diff
+169
View File
@@ -181,6 +181,8 @@
"Designs you open or save appear here.": "Các thiết kế bạn mở hoặc lưu sẽ xuất hiện tại đây.",
"Determined by To Object. Choose Angle to edit the angular extent.": "Được xác định theo Đến đối tượng (To Object). Chọn Góc để chỉnh sửa góc mở rộng.",
"Diameter": "Đường kính",
"Did this assembly open the wrong way up?": "Cụm lắp ráp này có bị mở sai hướng không?",
"Did this part open the wrong way up?": "Chi tiết này có bị mở sai hướng không?",
"Dimension new shapes": "Ghi kích thước cho hình mới",
"Direction": "Hướng",
"Discard": "Hủy bỏ thay đổi",
@@ -593,6 +595,7 @@
"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 assembly up?": "Dựng đứng cụm lắp ráp này lên?",
"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",
@@ -760,6 +763,7 @@
"appearance.pbr_opaque": "PBR không trong suốt",
"appearance.roughness": "Độ nhám bề mặt",
"appearance.surface_response": "Phản hồi bề mặt",
"at the face": "tại bề mặt",
"bom.group_children": "Chi tiết con trong hàng của chi tiết cha",
"browser.body_name": "Thân khối{number}",
"browser.calibrate_title": "Hiệu chuẩn: {name}",
@@ -768,6 +772,7 @@
"browser.capture_one": "Một chi tiết đã được di chuyển nhưng vị trí chưa được ghi lại.",
"browser.component_name": "Cụm chi tiết{number}",
"browser.edit_canvas_title": "Sửa ảnh nền Canvas: {name}",
"browser.edit_point_cloud_title": "Chỉnh sửa đám mây điểm: {name}",
"browser.move_title": "Di chuyển: {name}",
"browser.parameters": "Bảng tham số ({count})",
"browser.precision": "Độ chính xác: {precision}",
@@ -995,6 +1000,10 @@
"cmd.plastic_rest": "Vấu đỡ nhựa (Rest)",
"cmd.plastic_rules": "Danh sách quy tắc nhựa kỹ thuật",
"cmd.plastic_snap_fit": "Khớp gài bấm (Snap Fit)",
"cmd.pointcloud_delete": "Xóa đám mây điểm",
"cmd.pointcloud_edit": "Chỉnh sửa đám mây điểm",
"cmd.pointcloud_insert": "Chèn đám mây điểm",
"cmd.pointcloud_list": "Đám mây điểm",
"cmd.render_aging_describe": "Lão hóa bề mặt phân lớp",
"cmd.render_backends": "Kết xuất hình ảnh Sauends",
"cmd.render_batch": "Kết xuất mọi cảnh chụp",
@@ -1017,15 +1026,20 @@
"cmd.render_library_surface_prepare": "Chuẩn bị bản đồ bề mặt đã tạo",
"cmd.render_library_surface_unbind": "Xóa liên kết bề mặt",
"cmd.render_library_targets": "Tìm mục tiêu trong thư viện kết xuất",
"cmd.render_lights_add": "Thêm đèn kết xuất",
"cmd.render_lights_solo": "Chỉ hiện một đèn",
"cmd.render_optics_bind": "Lưu liên kết quang học",
"cmd.render_optics_unbind": "Xóa liên kết quang học",
"cmd.render_output_size": "Đặt kích thước đầu ra kết xuất",
"cmd.render_preflight": "Kiểm tra yêu cầu kết xuất",
"cmd.render_preset_apply": "Áp dụng studio gốc",
"cmd.render_preset_resolve": "Xem xét studio gốc",
"cmd.render_quality_prepare": "Xem xét định mức lấy mẫu cố định",
"cmd.render_result_close": "Đóng ảnh kết xuất",
"cmd.render_result_copy": "Sao chép ảnh kết xuất",
"cmd.render_result_pin": "Ghim kết quả kết xuất đang hiển thị",
"cmd.render_result_regrade": "Chỉnh màu lại bản gốc tuyến tính (Linear Master)",
"cmd.render_result_view": "Xem ảnh kết xuất",
"cmd.render_scene": "Kiểm tra Kết xuất hình ảnh Khung cảnh",
"cmd.render_settings_apply": "Áp dụng Kết xuất hình ảnh Góc nhìn máy quay",
"cmd.render_settings_capture_hdr": "Lưu cài đặt kết xuất kèm HDR",
@@ -1128,6 +1142,7 @@
"cmd.sketch_spline_control_point_5": "Đường cong Spline qua điểm điều khiển (Bậc 5)",
"cmd.sketch_spline_fit_point": "Đường cong Spline qua các điểm",
"cmd.sketch_spline_insert_point": "Thêm điểm vào đường cong Spline",
"cmd.sketch_spline_reverse": "Đảo ngược hướng đường cong Spline",
"cmd.sketch_spun_profile": "Biên dạng xoay tròn (Spun Profile)",
"cmd.sketch_toggle_diameter": "Chuyển đổi bán kính / đường kính",
"cmd.sketch_toggle_driven": "Chuyển đổi kích thước tham chiếu (Driven Dimension)",
@@ -1255,6 +1270,7 @@
"dialog.dimension_insert_hint": "Nhấp vào một kích thước điều khiển khác để chèn tên của nó",
"dialog.dimension_name": "Kích thước: {name}",
"dialog.edit_title": "CHỈNH SỬA {title}",
"dialog.no_shown_body": "Không có thân khối đang hiển thị để thay đổi: hãy hiển thị một thân khối, hoặc chọn “Tạo mới Thân khối”",
"dialog.select": "↖ Chọn",
"dialog.selected_count": "↖ {count} đã chọn",
"dialog.selection_guidance": "Chọn một mục đầu vào để thay đổi. Nhấp vào đối tượng đã chọn để bỏ chọn; Ctrl/Cmd-nhấp để bật/tắt chọn. Xem trước kết quả, sau đó nhấn OK.",
@@ -1325,6 +1341,7 @@
"help.mit": "Giấy phép MIT",
"help.notice": "NOTICE",
"help.parameters.change": "Mở bảng các giá trị có tên và biểu thức điều khiển kích thước của thiết kế. Dùng để sửa một kích thước ở một chỗ và mọi tính năng dùng nó sẽ cập nhật theo.",
"help.pointcloud.insert": "Chèn bản quét PLY, XYZ hoặc PTS dưới dạng đám mây điểm bên cạnh mô hình. Dùng làm tham chiếu khi dựng mô hình từ dữ liệu đo.",
"help.solid.align": "Di chuyển các khối đã chọn để một điểm, mặt hoặc mặt phẳng trên chúng gặp một điểm, mặt hoặc mặt phẳng ở nơi khác. Dùng để đặt chi tiết áp vào chi tiết khác mà không cần đo khoảng cách.",
"help.solid.chamfer": "Vát các cạnh đã chọn theo khoảng cách bạn thiết lập. Dùng để triệt tiêu cạnh sắc hoặc giúp lắp chốt, bu lông dễ dàng hơn.",
"help.solid.combine": "Hợp nhất các khối, cắt trừ khỏi khối đích, hoặc chỉ giữ lại phần thể tích giao nhau, đồng thời có thể giữ lại các khối công cụ. Dùng để gộp chi tiết, dùng hình này cắt hình kia hoặc tìm vị trí hai khối giao nhau.",
@@ -1350,6 +1367,8 @@
"help.third_party": "Giấy phép của bên thứ ba",
"help.timeline.compute_all": "Dựng lại mọi tính năng trong lịch sử từ đầu. Dùng để làm mới mô hình và xem tính năng nào bị lỗi.",
"help.timeline.delete": "Xóa các tính năng đã chọn khỏi lịch sử; xóa một bản phác thảo cũng xóa các tính năng dựng từ nó. Dùng để bỏ một bước không còn cần.",
"history.sketch_under_constrained": "Thiếu ràng buộc ({count} bậc tự do)",
"history.sketch_under_constrained_one": "Thiếu ràng buộc ({count} bậc tự do)",
"hole_presets.confirm_delete": "Xác nhận xóa",
"hole_presets.delete": "Xóa",
"hole_presets.delete_prompt": "Xóa mẫu thiết lập sẵn cá nhân này? Các lỗ ren/khoan hiện có sẽ không thay đổi.",
@@ -1717,6 +1736,8 @@
"prefs.anti_aliasing_note": "Làm mịn các cạnh răng cưa.",
"prefs.applies_on_restart": "Áp dụng sau khi khởi động lại",
"prefs.assistant": "Trợ lý",
"prefs.auto_operation": "Tự động chọn thao tác",
"prefs.auto_operation_note": "Cửa sổ tính năng chọn “Nối liền”, “Tạo mới Thân khối” hoặc “Cắt” theo những gì vật liệu mới chạm vào, cho đến khi bạn tự chọn. Tắt: cửa sổ giữ nguyên thao tác lúc mở.",
"prefs.axes": "Các trục tọa độ",
"prefs.axis": "Trục tọa độ",
"prefs.bodies_folders": "Thư mục thân khối",
@@ -1798,6 +1819,13 @@
"prefs.logo": "Logo",
"prefs.logo_accent": "Màu nhấn",
"prefs.logo_theme": "Màu của chủ đề",
"prefs.max_file_size": "Kích thước tệp tối đa để mở",
"prefs.max_file_size_design": "Tệp thiết kế",
"prefs.max_file_size_flag": "Lần chạy này bắt đầu với --max-file-size {size}, có hiệu lực đến khi đóng SolidCraft. Các cài đặt này áp dụng khi khởi động không có tùy chọn đó.",
"prefs.max_file_size_note": "Mặc định: 512 MB và 256 MB; tối đa 16 GB. Mở tệp cần lượng bộ nhớ gấp nhiều lần kích thước tệp. Thiết kế giữ dữ liệu nhập lớn có thể cần giới hạn tệp thiết kế cao hơn để lưu và mở lại.",
"prefs.max_file_size_step": "Tệp STEP và tệp nhập",
"prefs.max_file_size_web": "Ứng dụng web mở tệp STEP và tệp nhập tối đa 512 MB, thiết kế tối đa 256 MB: thẻ trình duyệt không chứa được tệp lớn hơn. Ứng dụng máy tính cho phép tăng các giới hạn này.",
"prefs.megabytes": "MB",
"prefs.minutes": "phút",
"prefs.minutes_suffix": " phút",
"prefs.mouse": "Chuột và bàn di cảm ứng",
@@ -1846,6 +1874,8 @@
"prefs.sketch_selection": "Phác thảo & chọn đối tượng",
"prefs.sketch_selection_heading": "Phác thảo và lựa chọn",
"prefs.sketch_selection_note": "Cách phác thảo bắt dính và cách nhấp chuột chọn đối tượng.",
"prefs.sketch_state_icons": "Biểu tượng bản phác theo màu trạng thái",
"prefs.sketch_state_icons_note": "Trong Lịch sử và trình duyệt, biểu tượng của bản phác lấy màu trạng thái của nó (thiếu ràng buộc, ràng buộc đủ hoặc xung đột) và chú thích nêu trạng thái. Tắt: mọi bản phác có cùng biểu tượng.",
"prefs.sketch_tab_always": "Luôn hiện thẻ Bản phác",
"prefs.sketch_tab_always_note": "Thẻ Bản phác vẫn nằm trên thanh công cụ, ngay sau Mô hình, khi không có bản phác nào đang mở. Khi đó các công cụ vẽ của nó bắt đầu một bản phác trên mặt phẳng hoặc mặt đã chọn, hoặc trên mặt phẳng bạn chọn tiếp theo, với công cụ sẵn sàng trong đó.",
"prefs.sketch_tracking_guides": "Đường căn chỉnh tâm và trung điểm",
@@ -1855,6 +1885,13 @@
"prefs.source": "Nguồn",
"prefs.speed": "Tốc độ",
"prefs.stand_up_note": "Sau khi mở tệp STEP hoặc IGES với trục Z hướng lên, hỏi xem có muốn xoay một phần tư vòng quanh trục X không.",
"prefs.stand_up_unknown": "Với tệp từ chương trình không xác định",
"prefs.stand_up_unknown.ask": "Hỏi trong hộp thoại",
"prefs.stand_up_unknown.note": "Hiện ghi chú",
"prefs.stand_up_unknown.off": "Không gợi ý",
"prefs.stand_up_unknown_note": "Cách gợi ý khi phần đầu tệp không nêu chương trình nào được biết là lưu với trục Y hướng lên. Ghi chú chỉ hiện khi tệp có vẻ đang nằm ngửa; “Hỏi trong hộp thoại” sẽ hỏi với mọi tệp như vậy, như trước đây.",
"prefs.status_bar": "Thanh trạng thái",
"prefs.status_bar_note": "Một thanh mảnh dọc cạnh dưới cửa sổ hiển thị thứ đang được chỉnh sửa, trạng thái của bản phác đang mở theo màu của nó và đơn vị của thiết kế; nhấp vào đơn vị để đổi. Khi đó trạng thái bản phác không còn hiện ở góc khung nhìn.",
"prefs.status_done": "Hoàn thành",
"prefs.status_error": "Lỗi",
"prefs.status_selected": "Đã chọn",
@@ -1937,6 +1974,45 @@
"render.aging_upward": "Lắng đọng trên bề mặt hướng lên",
"render.all_controls_help": "Xem chi tiết toàn bộ các điều khiển hiện tại. Các trang chuyên mục cho phép chỉnh sửa tập trung mà không làm thay đổi yêu cầu cơ bản bên dưới.",
"render.all_shots_label": "Mọi cảnh chụp",
"render.appearance_editor.absorption": "Hấp thụ",
"render.appearance_editor.base_colour": "Màu cơ bản",
"render.appearance_editor.body_reason": "Liên kết quang học nhắm vào một thân hoàn chỉnh. Hãy chọn Thân để chỉnh các điều khiển này.",
"render.appearance_editor.boundary": "Biên",
"render.appearance_editor.box": "Hộp",
"render.appearance_editor.choose_image": "Chọn ảnh…",
"render.appearance_editor.closed": "Khối đặc kín",
"render.appearance_editor.coat": "Lớp phủ trong",
"render.appearance_editor.cpu_reason": "Cần dò tia bằng CPU.",
"render.appearance_editor.glass": "Kính và hấp thụ",
"render.appearance_editor.glass_enable": "Dùng kính vật lý trên thân này",
"render.appearance_editor.glass_note": "Kính kín dùng khoảng cách hình học. Không thể dùng chung thân với lớp phủ trong hoặc bản đồ.",
"render.appearance_editor.gpu_reason": "Cần dò đường đi của tia bằng GPU.",
"render.appearance_editor.ior": "Chiết suất",
"render.appearance_editor.maps": "Bản đồ",
"render.appearance_editor.normal": "Pháp tuyến",
"render.appearance_editor.normal_strength": "Cường độ pháp tuyến",
"render.appearance_editor.origin": "Gốc",
"render.appearance_editor.recipe": "Công thức",
"render.appearance_editor.reflection_sampling": "Lấy mẫu cho mọi thân phản chiếu",
"render.appearance_editor.reflections": "Phản xạ",
"render.appearance_editor.remove": "Gỡ bản đã lưu",
"render.appearance_editor.remove_note": "Gỡ thuộc tính quang học đã lưu khỏi cảnh chụp này. Hình học và diện mạo đã gán được giữ lại.",
"render.appearance_editor.remove_title": "Gỡ thuộc tính quang học đã lưu?",
"render.appearance_editor.replace": "Thay thuộc tính quang học đã lưu của thân này",
"render.appearance_editor.revalidate": "Kiểm tra lại thân này",
"render.appearance_editor.save": "Lưu các thuộc tính quang học này",
"render.appearance_editor.save_reason": "Lưu các thuộc tính quang học này cần kính vật lý trên một thân GPU hiện hành. Lớp phủ và bản đồ ảnh cục bộ vẫn tạm thời.",
"render.appearance_editor.saved": "Lưu cùng cảnh chụp này",
"render.appearance_editor.saved_state": "Các thuộc tính quang học này được lưu cùng cảnh chụp này.",
"render.appearance_editor.scatter_reason": "Tán xạ bên trong cần biên là khối đặc kín.",
"render.appearance_editor.temporary": "Các thuộc tính quang học này chỉ tạm thời trên tab này. Hãy lưu để giữ cùng cảnh chụp.",
"render.appearance_editor.thickness": "Độ dày",
"render.appearance_editor.thickness_reason": "Độ dày áp dụng cho tấm mỏng; kính kín dùng khoảng cách hình học.",
"render.appearance_editor.thin": "Tấm mỏng",
"render.appearance_editor.u_repeat": "Lặp U",
"render.appearance_editor.v_repeat": "Lặp V",
"render.appearance_editor.v_up": "Trục V của kết cấu hướng lên",
"render.appearance_editor.weight": "Trọng số",
"render.appearance_physics_separate": "Diện mạo thị giác không gán khối lượng riêng, dữ liệu nhiệt hay các đặc tính kỹ thuật khác.",
"render.appearances": "Diện mạo",
"render.availability_native": "Tổ hợp gốc; hãy xem xét trước khi áp dụng",
@@ -1997,6 +2073,9 @@
"render.category_paint": "Sơn",
"render.category_plastic": "Nhựa",
"render.category_wood": "Gỗ",
"render.check_opaque": "Độ trong suốt: chỉ bề mặt đục; hỗ trợ alpha của phông nền.",
"render.check_sorted_alpha": "Độ trong suốt: xấp xỉ alpha có sắp thứ tự.",
"render.check_transmission": "Độ trong suốt: truyền sáng vật lý.",
"render.chromatic_offset": "Độ lệch sắc sai viền",
"render.clear_stage": "Xóa sân khấu đã lưu",
"render.copy_request": "Sao chép JSON",
@@ -2230,6 +2309,36 @@
"render.inner_half_angle": "Nửa góc trong",
"render.inspect_scene": "Làm mới tóm tắt cảnh đã ghi nhận",
"render.inspector": "Bảng thuộc tính",
"render.inspector_advanced": "Nâng cao",
"render.inspector_all_users": "Cả {count} người dùng",
"render.inspector_applies_to": "Áp dụng cho",
"render.inspector_both_users": "Cả hai người dùng",
"render.inspector_chain": "Diện mạo này đến từ đâu",
"render.inspector_colour": "Màu",
"render.inspector_edit": "Sửa",
"render.inspector_finish": "Hoàn thiện",
"render.inspector_from_material": "Từ vật liệu",
"render.inspector_gloss": "Bóng",
"render.inspector_material": "Vật liệu",
"render.inspector_matte": "Mờ",
"render.inspector_metal": "Kim loại",
"render.inspector_opacity": "Độ đục",
"render.inspector_opaque_help": "Các trường hoàn thiện và quang học dùng PBR đục (độ đục 1). Độ đục thấp hơn dùng tô bóng vô hướng.",
"render.inspector_pick_target": "Chọn một đích thuộc loại này để xem diện mạo của nó.",
"render.inspector_remove": "Gỡ ghi đè",
"render.inspector_replace": "Thay…",
"render.inspector_reset": "Đặt lại",
"render.inspector_satin": "Satin",
"render.inspector_save_library": "Lưu vào thư viện…",
"render.inspector_set_all": "Diện mạo hỗn hợp. Chỉnh sửa sẽ đặt diện mạo cho mọi đích đã chọn.",
"render.inspector_soon": "Sắp có",
"render.inspector_target_limit": "Chọn tối đa 10000 đích để xem diện mạo.",
"render.inspector_textures": "Kết cấu",
"render.inspector_this_body": "Thân này",
"render.inspector_this_component": "Thành phần này",
"render.inspector_this_face": "Mặt này",
"render.inspector_this_target": "Đích này",
"render.inspector_used_by": "Được {count} dùng",
"render.keep_editing": "Tiếp tục chỉnh sửa",
"render.layer_down": "Chuyển xuống",
"render.layer_enabled": "Bật lớp này",
@@ -2396,12 +2505,34 @@
"render.optics_saved": "Các mục tiêu quang học đã lưu",
"render.optics_saved_help": "Định danh chính xác về định nghĩa, tính năng và thực thể được duy trì sau khi mở lại ngoại tuyến. Từ chối các mục tiêu bị thiếu/thay thế/ẩn; vật liệu kỹ thuật không đổi. Hãy lưu hoặc hủy các chỉnh sửa số trước khi xóa mục tiêu đã lưu.",
"render.outer_half_angle": "Nửa góc ngoài",
"render.output_advanced": "Nâng cao",
"render.output_backend_ready": "Sẵn sàng",
"render.output_check": "Kiểm tra",
"render.output_check_help": "Chạy Kiểm tra để xem lại yêu cầu này mà không bắt đầu kết xuất.",
"render.output_check_summary": "Kiểm tra · Ghi chú: {count}",
"render.output_copy": "Sao chép",
"render.output_destination": "Đích",
"render.output_destination_help": "Chọn tệp đích khi xuất ảnh đã hoàn tất.",
"render.output_dimensions": "Kích thước",
"render.output_effects": "Hiệu ứng",
"render.output_export": "Xuất PNG…",
"render.output_exr_unavailable": "Thiết lập đầu ra EXR không khả dụng ở đây. Xuất EXR tuyến tính cần ảnh GPU đã hoàn tất với bản gốc tuyến tính được giữ lại.",
"render.output_fit": "Vừa khít",
"render.output_format": "Định dạng",
"render.output_format_status": "PNG là hình ảnh hiển thị hoàn chỉnh chính xác. EXR tùy chọn trên GPU giữ lại suất bức xạ FLOAT32 tuyến tính cảnh thô trước các bộ lọc hiển thị. Xuất không bao giờ thay thế bằng bộ nhớ đệm xem trước hoặc tác vụ khác.",
"render.output_image": "Ảnh",
"render.output_largest": "Tối đa {width} × {height}",
"render.output_lock_ratio": "Khóa tỷ lệ khung hình",
"render.output_over_budget": "Hình ảnh yêu cầu vượt quá giới hạn tài nguyên đã chọn. Hãy chủ động chọn kích thước hoặc cấu hình tương thích; không tự động hạ cấp chất lượng.",
"render.output_presets_help": "Định dạng, chất lượng và danh sách Kiểm tra nằm trong trình chỉnh Đầu ra.",
"render.output_quality": "Chất lượng",
"render.output_quality_waiting": "đang chờ hợp đồng chất lượng",
"render.output_ratio_refused": "Kích thước liên kết vượt quá giới hạn của phương thức này. Kích thước cũ được giữ lại; hãy mở khóa tỷ lệ hoặc chọn giá trị nhỏ hơn.",
"render.output_ready": "Sẵn sàng kết xuất",
"render.output_size_ok": "Kích thước ổn · {width} × {height} px",
"render.output_swap_orientation": "Hoán đổi chiều rộng và chiều cao",
"render.output_tools_inspector": "Chỉnh kích thước và định dạng trong bảng thuộc tính Đầu ra.",
"render.output_view_image": "Xem ảnh",
"render.page_aging": "Lão hóa và bám bẩn",
"render.page_all": "Tất cả điều khiển",
"render.page_appearance": "Diện mạo",
@@ -2462,10 +2593,18 @@
"render.property_unknown": "không rõ",
"render.quality_ack": "Xác nhận phép chiếu định mức cố định và các mục lược bỏ",
"render.quality_apply": "Sử dụng định mức lấy mẫu cố định này",
"render.quality_balanced": "Cân bằng",
"render.quality_custom": "Tùy chỉnh",
"render.quality_draft": "Bản nháp",
"render.quality_high": "Cao",
"render.quality_projection_help": "Các thiết lập sẵn Q bao gồm các yêu cầu tương lai. Hành động này chỉ chiếu định mức mẫu PNG cố định được hỗ trợ và lược bỏ rõ ràng việc dừng thích ứng, đầu ra tuyến tính thô và các giới hạn truyền dẫn độc lập.",
"render.quality_review": "Xem xét định mức lấy mẫu",
"render.queue_pending": "Không có hàng đợi sản xuất tự động hay bộ kết xuất hoạt hình trong giao diện này. Ảnh tĩnh đã hoàn thành vẫn có thể xuất riêng lẻ.",
"render.radial_assign": "Gán diện mạo",
"render.radial_compare": "So sánh",
"render.radial_distortion": "Độ méo hướng tâm",
"render.radial_fit": "Vừa sản phẩm",
"render.radial_save": "Lưu cảnh chụp",
"render.regrade_action": "Chỉnh màu lại bản gốc thô (không khử nhiễu)",
"render.regrade_help": "Tạo một ảnh PNG riêng biệt từ bản gốc thô đã hoàn thành được chọn, sử dụng các điều khiển trình chiếu này. Không dò lại tia, không đổi kích thước/camera/ánh sáng đã ghi nhận, và không ghi đè tệp EXR. Bộ khử nhiễu không áp dụng lại do bản gốc thô không giữ dữ liệu dẫn hướng. Bản phái sinh vẫn có thể xuất qua cùng điều khiển tác vụ.",
"render.regrade_label": "Chỉnh lại màu",
@@ -2474,6 +2613,7 @@
"render.replace_label": "Thay",
"render.replace_saved_surface": "Thay thế liên kết đã lưu hiện có cho mục tiêu này",
"render.restore_camera": "Khôi phục camera đã lưu",
"render.result_actual": "100%",
"render.result_actual_help": "Một pixel hình ảnh tương ứng một pixel màn hình hiển thị.",
"render.result_actual_size": "100%",
"render.result_capture": "Phiên bản đã ghi nhận",
@@ -2541,6 +2681,7 @@
"render.sharpening": "Làm sắc nét chi tiết",
"render.shell_tab_appearance": "DIỆN MẠO KẾT XUẤT",
"render.shell_tab_camera": "MÁY ẢNH KẾT XUẤT",
"render.shell_tab_lighting": "ÁNH SÁNG",
"render.shell_tab_scene": "CẢNH KẾT XUẤT",
"render.shot_duplicate": "Nhân bản khung hình đã lưu",
"render.shot_manager": "Khung hình đã lưu",
@@ -2558,11 +2699,15 @@
"render.shots_save": "Lưu cảnh chụp",
"render.shots_update": "Cập nhật cảnh chụp",
"render.show_shots_label": "Hiện dải cảnh chụp",
"render.size_4k": "4K",
"render.size_4k_uhd": "4K UHD",
"render.size_custom": "Tùy chỉnh…",
"render.size_full_hd": "Full HD",
"render.size_hd": "HD",
"render.size_presets": "Cỡ đặt sẵn",
"render.size_presets_pending": "Sẽ có cùng trình chỉnh Đầu ra",
"render.size_preview": "Xem trước",
"render.size_square": "Vuông",
"render.sky_ground": "Mặt đất · RGB tuyến tính",
"render.sky_horizon": "Đường chân trời · RGB tuyến tính",
"render.sky_zenith": "Thiên đỉnh · RGB tuyến tính",
@@ -2575,6 +2720,9 @@
"render.stage_output": "Đầu ra",
"render.stage_panel_count": "Các tấm panel đã lưu",
"render.stage_preview_note": "Xem trước tích hợp · ánh sáng trực tiếp",
"render.state_completed": "Ảnh đã hoàn tất",
"render.state_in_progress": "Ảnh đang tạo",
"render.state_model": "Xem trước mô hình",
"render.strip_label": "Dải",
"render.studio_add_area": "Thêm nguồn sáng vùng",
"render.studio_ambient": "Suất bức xạ môi trường không đổi",
@@ -2670,6 +2818,7 @@
"render.volume_max": "Ranh giới tối đa · mm thế giới",
"render.volume_min": "Ranh giới tối thiểu · mm thế giới",
"render.volume_title": "Thể tích đồng nhất có giới hạn",
"render.wait_add_light": "chưa hỗ trợ: tạo đèn có tên",
"render.wait_batch": "chưa hỗ trợ: hàng đợi kết xuất cảnh chụp đã lưu",
"render.wait_compare": "chưa hỗ trợ: so sánh ảnh được giữ lại",
"render.wait_paint": "chưa hỗ trợ: chọn sản phẩm và tô diện mạo",
@@ -2784,6 +2933,10 @@
"sketch_text.path_hint": "Đường cong phác thảo mà văn bản chạy theo: nhấp vào đây, sau đó chọn đường cong",
"sketch_text.use_font_file": "Sử dụng tệp phông chữ {path}",
"solid": "khối đặc",
"status.editing": "Đang sửa {name}",
"status.under_constrained": "Thiếu ràng buộc ({count} bậc tự do)",
"status.under_constrained_one": "Thiếu ràng buộc ({count} bậc tự do)",
"status.units_tip": "Đơn vị của thiết kế. Nhấp để đổi",
"the angle must be a number": "góc phải là một số",
"the curves must be in one sketch": "các đường cong phải nằm trong cùng một phác thảo",
"the direction must be an axis, a sketch line or a straight edge": "hướng phải là một trục, một đường thẳng phác thảo hoặc một cạnh thẳng",
@@ -2822,8 +2975,10 @@
"timeline.move_to_group": "Chuyển tính năng vào thư mục",
"timeline.rename_group": "Đổi tên thư mục tính năng",
"timeline.reorder_many": "Sắp xếp lại các tính năng",
"timeline.suppress_features": "Bỏ kích hoạt các tính năng",
"timeline.suppress_folders_tip": "Bỏ kích hoạt hoặc kích hoạt lại mọi tính năng trong các thư mục và các thư mục lồng bên trong",
"timeline.ungroup": "Xóa thư mục tính năng",
"timeline.unsuppress_features": "Kích hoạt lại các tính năng",
"to the face": "tới bề mặt",
"to the next face": "tới bề mặt tiếp theo",
"toolbar.customize_note": "Thẻ {tab}. Đánh dấu một lệnh để tạo nút trên thanh công cụ. Bạn cũng có thể kéo một hàng trong danh sách thả xuống của bảng lên thanh công cụ, kéo một nút ra khỏi thanh công cụ để gỡ bỏ, hoặc dùng biểu tượng ghim trên hàng trong danh sách thả xuống.",
@@ -3234,6 +3389,8 @@
"ui.cancel": "Hủy bỏ",
"ui.canvases": "Các ảnh nền Canvas",
"ui.center": "Tâm",
"ui.center_arc_follows_cursor": "Cung tròn xác định bởi tâm và điểm theo hướng con trỏ",
"ui.center_arc_follows_cursor_tip": "Cung tròn xác định bởi tâm và điểm chạy theo chiều hoặc ngược chiều kim đồng hồ, tùy theo hướng con trỏ đi quanh tâm. Tắt: luôn ngược chiều kim đồng hồ từ điểm đầu.",
"ui.centerline": "Đường tâm",
"ui.chain": "Chuỗi",
"ui.chamfer": "Vát mép",
@@ -3646,6 +3803,15 @@
"ui.plugins_skipped": "Không thể đọc {count} thư mục.",
"ui.plugins_version": "{version}",
"ui.point": "Điểm",
"ui.point_cloud_auto_range": "Phạm vi tự động",
"ui.point_cloud_color_by": "Tô màu theo",
"ui.point_cloud_file_colors": "Màu trong tệp",
"ui.point_cloud_height_high": "Cao nhất",
"ui.point_cloud_height_low": "Thấp nhất",
"ui.point_cloud_no_file_colors": "Tệp không có màu",
"ui.point_cloud_single_color": "Một màu",
"ui.point_cloud_size": "Kích thước điểm",
"ui.point_cloud_units": "Đơn vị của tệp",
"ui.point_under_the_cursor": "Điểm dưới con trỏ",
"ui.points": "Các điểm",
"ui.polygon": "Đa giác",
@@ -3899,11 +4065,13 @@
"update.installed": "SolidCraft {version} đã được cài đặt. Hãy khởi động lại để sử dụng.",
"update.installer_started": "Trình cài đặt sẽ bắt đầu khi SolidCraft đóng.",
"update.later": "Nhắc tôi sau",
"update.managed": "Có bản cập nhật: hãy cập nhật SolidCraft trong {manager}",
"update.no_notes": "Bản phát hành này không có ghi chú.",
"update.package_hint": "Bản sao này được cài từ {kind}. Hãy tải về gói mới, sau đó cài đặt bằng lệnh:",
"update.prefs_dev": "Đây là bản dựng phát triển ({version}): không tự động kiểm tra bản cập nhật.",
"update.prefs_flatpak": "Flatpak quản lý cập nhật bản sao này ({version}): hãy chạy {command} hoặc dùng trung tâm phần mềm của bạn. SolidCraft không tự kiểm tra.",
"update.prefs_label": "Bản cập nhật",
"update.prefs_managed": "Đây là SolidCraft {version}, do {manager} quản lý. Lần kiểm tra chỉ hỏi GitHub về bản phát hành mới nhất và không gửi gì khác; hãy cập nhật SolidCraft trong {manager}.",
"update.prefs_note": "Đây là SolidCraft {version} ({kind}). Quá trình kiểm tra gửi yêu cầu đến GitHub để tìm bản mới nhất và không gửi thông tin nào khác.",
"update.prefs_web": "Tải lại trang để nhận ứng dụng web mới nhất. Nếu mạng không khả dụng hoặc bản cập nhật không thể tải về kịp thời gian, phiên bản đã lưu sẽ mở thay thế; quá trình tải xuống đang diễn ra sẽ tiếp tục cho lần tải lại tiếp theo.",
"update.release_page": "Mở trang phát hành",
@@ -3920,6 +4088,7 @@
"values must be finite numbers": "các giá trị phải là số hữu hạn",
"values must be numbers or expressions": "các giá trị phải là số hoặc biểu thức",
"view.auto_ortho_faces": "Phối cảnh khi xoay quỹ đạo, trực giao khi nhìn trực diện mặt",
"view.render_inspector": "Bảng thuộc tính kết xuất",
"web.files.insert_drawings_hint": "Tệp CAD, lưới đa giác, DXF hoặc SVG vào thiết kế đang mở",
"web.files.insert_stored_drawings_hint": "Vào thiết kế đang mở (tệp CAD, lưới đa giác, DXF, SVG)",
"web.open_full": "Mở trong SolidCraft",
+16
View File
@@ -757,6 +757,7 @@
"appearance.pbr_opaque": "不透明 PBR",
"appearance.roughness": "粗糙度",
"appearance.surface_response": "表面反應",
"at the face": "在面上",
"bom.group_children": "子零件併入所屬零件的列",
"browser.body_name": "實體{number}",
"browser.calibrate_title": "校準:{name}",
@@ -1088,6 +1089,7 @@
"cmd.sketch_spline_control_point_5": "控制點雲形線(5 次)",
"cmd.sketch_spline_fit_point": "擬合點雲形線",
"cmd.sketch_spline_insert_point": "新增雲形線點",
"cmd.sketch_spline_reverse": "反向雲形線方向",
"cmd.sketch_spun_profile": "旋轉輪廓",
"cmd.sketch_toggle_diameter": "切換半徑 / 直徑",
"cmd.sketch_toggle_driven": "切換從動尺寸",
@@ -1743,6 +1745,13 @@
"prefs.left_click": "左鍵點擊",
"prefs.live_input": "即時輸入",
"prefs.live_preview": "即時預覽",
"prefs.max_file_size": "可開啟的最大檔案",
"prefs.max_file_size_design": "設計檔案",
"prefs.max_file_size_flag": "本次啟動指定了 --max-file-size {size},此值在關閉 SolidCraft 前有效。不帶此選項啟動時才會套用這些設定。",
"prefs.max_file_size_note": "預設值:512 MB 和 256 MB;上限為 16 GB。開啟檔案需要數倍於檔案大小的記憶體。保留大型匯入資料的設計可能需要提高設計檔案上限,才能儲存並重新開啟。",
"prefs.max_file_size_step": "STEP 和匯入檔案",
"prefs.max_file_size_web": "網頁版可開啟最大 512 MB 的 STEP 和匯入檔案,以及最大 256 MB 的設計:瀏覽器分頁無法容納更大的檔案。桌面版可提高這些上限。",
"prefs.megabytes": "MB",
"prefs.minutes": "分鐘",
"prefs.minutes_suffix": " min",
"prefs.mouse": "滑鼠與觸控板",
@@ -2464,6 +2473,13 @@
"render.shot_remove_title": "移除已儲存的鏡頭?",
"render.shot_rename": "重新命名已儲存的鏡頭",
"render.shot_saved_only": "請先儲存目前的草稿。複製與重新命名會保留確切的已儲存選項與資源參照。",
"render.shots_camera_changed": "儲存此鏡頭後相機已變更",
"render.shots_name_help": "請為目前的鏡頭輸入唯一的名稱。",
"render.shots_no_result": "尚無彩現",
"render.shots_result": "彩現 {number} · {width} × {height}",
"render.shots_revert": "還原",
"render.shots_save": "儲存鏡頭",
"render.shots_update": "更新鏡頭",
"render.show_shots_label": "顯示鏡頭列",
"render.size_4k_uhd": "4K UHD",
"render.size_full_hd": "Full HD",
+169
View File
@@ -181,6 +181,8 @@
"Designs you open or save appear here.": "你打开或保存的设计会显示在此处。",
"Determined by To Object. Choose Angle to edit the angular extent.": "由“到对象”确定。选择“角度”可编辑角度范围。",
"Diameter": "直径",
"Did this assembly open the wrong way up?": "此装配是否以错误的方向打开?",
"Did this part open the wrong way up?": "此零件是否以错误的方向打开?",
"Dimension new shapes": "为新图形标注尺寸",
"Direction": "方向",
"Discard": "放弃",
@@ -593,6 +595,7 @@
"Square": "正方形",
"Stand it up": "将其立起",
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "将其立起:绕 X 轴旋转四分之一周,作为一个可撤销的移动特征。",
"Stand this assembly up?": "立起此装配?",
"Stand this part up?": "立起此零件?",
"Start angle": "起始角度",
"Start bulge": "起始鼓度",
@@ -760,6 +763,7 @@
"appearance.pbr_opaque": "不透明 PBR",
"appearance.roughness": "粗糙度",
"appearance.surface_response": "表面响应",
"at the face": "在面上",
"bom.group_children": "子零件并入所属零件的行",
"browser.body_name": "实体{number}",
"browser.calibrate_title": "校准:{name}",
@@ -768,6 +772,7 @@
"browser.capture_one": "一个组件已移动,但尚未捕获其位置。",
"browser.component_name": "组件{number}",
"browser.edit_canvas_title": "编辑画布:{name}",
"browser.edit_point_cloud_title": "编辑点云: {name}",
"browser.move_title": "移动:{name}",
"browser.parameters": "参数 ({count})",
"browser.precision": "精度: {precision}",
@@ -995,6 +1000,10 @@
"cmd.plastic_rest": "垫块",
"cmd.plastic_rules": "列出塑料规则",
"cmd.plastic_snap_fit": "卡扣",
"cmd.pointcloud_delete": "删除点云",
"cmd.pointcloud_edit": "编辑点云",
"cmd.pointcloud_insert": "插入点云",
"cmd.pointcloud_list": "点云",
"cmd.render_aging_describe": "分层表面老化",
"cmd.render_backends": "渲染后端",
"cmd.render_batch": "渲染所有镜头",
@@ -1017,15 +1026,20 @@
"cmd.render_library_surface_prepare": "准备已制作的表面贴图",
"cmd.render_library_surface_unbind": "移除表面绑定",
"cmd.render_library_targets": "查找渲染库目标",
"cmd.render_lights_add": "添加灯光",
"cmd.render_lights_solo": "单独显示灯光",
"cmd.render_optics_bind": "保存光学绑定",
"cmd.render_optics_unbind": "移除光学绑定",
"cmd.render_output_size": "设置渲染输出尺寸",
"cmd.render_preflight": "检查渲染请求",
"cmd.render_preset_apply": "应用原生工作室",
"cmd.render_preset_resolve": "审阅原生工作室",
"cmd.render_quality_prepare": "审阅固定采样预算",
"cmd.render_result_close": "关闭渲染图像",
"cmd.render_result_copy": "复制渲染图像",
"cmd.render_result_pin": "保留当前显示的渲染结果",
"cmd.render_result_regrade": "重新调整线性母版",
"cmd.render_result_view": "查看渲染图像",
"cmd.render_scene": "检查渲染场景",
"cmd.render_settings_apply": "应用渲染相机",
"cmd.render_settings_capture_hdr": "保存渲染设置及 HDR",
@@ -1128,6 +1142,7 @@
"cmd.sketch_spline_control_point_5": "控制点样条曲线(5 次)",
"cmd.sketch_spline_fit_point": "拟合点样条曲线",
"cmd.sketch_spline_insert_point": "添加样条曲线点",
"cmd.sketch_spline_reverse": "反向样条曲线方向",
"cmd.sketch_spun_profile": "旋转轮廓",
"cmd.sketch_toggle_diameter": "切换半径 / 直径",
"cmd.sketch_toggle_driven": "切换被驱动尺寸",
@@ -1255,6 +1270,7 @@
"dialog.dimension_insert_hint": "单击其他驱动尺寸以插入其名称",
"dialog.dimension_name": "尺寸:{name}",
"dialog.edit_title": "编辑 {title}",
"dialog.no_shown_body": "没有可更改的显示实体:请显示一个实体,或选择新建实体",
"dialog.select": "↖ 选择",
"dialog.selected_count": "↖ 已选择 {count} 个",
"dialog.selection_guidance": "选择输入项进行修改。点击已选对象可取消选择;按住 Ctrl/Cmd 点击也可切换选择。检查预览后,再按“确定”。",
@@ -1325,6 +1341,7 @@
"help.mit": "MIT 许可证",
"help.notice": "NOTICE",
"help.parameters.change": "打开驱动设计尺寸的命名数值和表达式表。用于在一处修改尺寸,让所有引用它的特征随之更新。",
"help.pointcloud.insert": "将 PLY、XYZ 或 PTS 扫描作为点云插入到模型旁。根据测量数据建模时,可将其用作参考。",
"help.solid.align": "移动所选实体,使其上的点、面或平面与别处的点、面或平面对齐。用于把一个零件贴靠到另一个零件上,而无需测量距离。",
"help.solid.chamfer": "用指定的距离在所选边上切出一个平直的斜面。用于去除锐边,或使销钉、螺钉更容易插入。",
"help.solid.combine": "将多个实体合并、从目标实体中切除,或只保留它们共有的体积,还可以保留工具实体。用于合并零件、用一个形体切割另一个形体,或找出两个实体重叠的位置。",
@@ -1350,6 +1367,8 @@
"help.third_party": "第三方许可证",
"help.timeline.compute_all": "从头重新计算时间线中的所有特征。用于刷新模型,并查看哪些特征失败。",
"help.timeline.delete": "从时间线中删除所选特征;删除草图时,由它生成的特征也会一起删除。用于去掉不再需要的步骤。",
"history.sketch_under_constrained": "欠约束({count} 个自由度)",
"history.sketch_under_constrained_one": "欠约束({count} 个自由度)",
"hole_presets.confirm_delete": "确认删除",
"hole_presets.delete": "删除",
"hole_presets.delete_prompt": "删除此个人预设?现有孔不会改变。",
@@ -1717,6 +1736,8 @@
"prefs.anti_aliasing_note": "平滑锯齿边缘。",
"prefs.applies_on_restart": "重启后生效",
"prefs.assistant": "助手",
"prefs.auto_operation": "自动选择操作",
"prefs.auto_operation_note": "特征窗口根据新材料接触的对象选择连接、新建实体或剪切,直到您手动选择。关闭:保持窗口打开时的操作。",
"prefs.axes": "坐标轴",
"prefs.axis": "轴",
"prefs.bodies_folders": "实体文件夹",
@@ -1798,6 +1819,13 @@
"prefs.logo": "标志",
"prefs.logo_accent": "强调色",
"prefs.logo_theme": "主题颜色",
"prefs.max_file_size": "可打开的最大文件",
"prefs.max_file_size_design": "设计文件",
"prefs.max_file_size_flag": "本次启动指定了 --max-file-size {size},该值在关闭 SolidCraft 前有效。不带此选项启动时才会应用这些设置。",
"prefs.max_file_size_note": "默认值:512 MB 和 256 MB;上限为 16 GB。打开文件需要数倍于文件大小的内存。保留大型导入数据的设计可能需要提高设计文件上限,才能保存并重新打开。",
"prefs.max_file_size_step": "STEP 和导入文件",
"prefs.max_file_size_web": "网页版可打开最大 512 MB 的 STEP 和导入文件,以及最大 256 MB 的设计:浏览器标签页无法容纳更大的文件。桌面版可提高这些上限。",
"prefs.megabytes": "MB",
"prefs.minutes": "分钟",
"prefs.minutes_suffix": " min",
"prefs.mouse": "鼠标与触控板",
@@ -1846,6 +1874,8 @@
"prefs.sketch_selection": "草图与选择",
"prefs.sketch_selection_heading": "草图和选择",
"prefs.sketch_selection_note": "草图如何捕捉,以及单击如何选择。",
"prefs.sketch_state_icons": "以草图状态的颜色显示草图图标",
"prefs.sketch_state_icons_note": "在历史和浏览器中,草图图标采用其状态(欠约束、完全约束或冲突)的颜色,工具提示会说明状态。关闭:所有草图使用相同的图标。",
"prefs.sketch_tab_always": "始终显示草图选项卡",
"prefs.sketch_tab_always_note": "未打开草图时,草图选项卡也保留在功能区中紧随模型之后。其中的绘图工具会先在所选平面或面上(否则在接下来选取的平面上)创建草图,并在其中准备好该工具。",
"prefs.sketch_tracking_guides": "圆心和中点对齐辅助线",
@@ -1855,6 +1885,13 @@
"prefs.source": "来源",
"prefs.speed": "速度",
"prefs.stand_up_note": "以 Z 轴向上打开 STEP 或 IGES 文件后,询问是否绕 X 轴旋转 90°。",
"prefs.stand_up_unknown": "来自未知程序的文件",
"prefs.stand_up_unknown.ask": "在对话框中询问",
"prefs.stand_up_unknown.note": "显示提示",
"prefs.stand_up_unknown.off": "不提供",
"prefs.stand_up_unknown_note": "当文件头未注明已知以 Y 轴向上保存的程序时,如何提供立起选项。仅当文件看起来平躺时才显示提示;“在对话框中询问”会像以前一样对每个此类文件进行询问。",
"prefs.status_bar": "状态栏",
"prefs.status_bar_note": "窗口底边的一条细栏显示正在编辑的对象、打开的草图的状态(以其颜色显示)以及设计的单位;单击单位即可更改。草图的状态随后不再显示在视图的角落。",
"prefs.status_done": "完成",
"prefs.status_error": "错误",
"prefs.status_selected": "已选择",
@@ -1937,6 +1974,45 @@
"render.aging_upward": "向上表面沉积",
"render.all_controls_help": "集中显示当前所有控件;各分页可针对相应任务编辑,不改变底层请求。",
"render.all_shots_label": "所有镜头",
"render.appearance_editor.absorption": "吸收",
"render.appearance_editor.base_colour": "基础颜色",
"render.appearance_editor.body_reason": "光学绑定针对单个完整实体。选择实体后可编辑这些控件。",
"render.appearance_editor.boundary": "边界",
"render.appearance_editor.box": "长方体",
"render.appearance_editor.choose_image": "选择图像…",
"render.appearance_editor.closed": "封闭实体",
"render.appearance_editor.coat": "清漆涂层",
"render.appearance_editor.cpu_reason": "需要 CPU 光线追踪。",
"render.appearance_editor.glass": "玻璃与吸收",
"render.appearance_editor.glass_enable": "对此实体使用物理玻璃",
"render.appearance_editor.glass_note": "封闭玻璃使用几何距离。同一实体不能同时使用清漆或贴图。",
"render.appearance_editor.gpu_reason": "需要 GPU 路径追踪。",
"render.appearance_editor.ior": "IOR",
"render.appearance_editor.maps": "贴图",
"render.appearance_editor.normal": "法线",
"render.appearance_editor.normal_strength": "法线强度",
"render.appearance_editor.origin": "原点",
"render.appearance_editor.recipe": "配方",
"render.appearance_editor.reflection_sampling": "所有反射实体的采样",
"render.appearance_editor.reflections": "反射",
"render.appearance_editor.remove": "移除已保存设置",
"render.appearance_editor.remove_note": "从此镜头中移除已保存的光学设置。几何形状和已分配的外观保持不变。",
"render.appearance_editor.remove_title": "移除已保存的光学设置?",
"render.appearance_editor.replace": "替换此实体已保存的光学设置",
"render.appearance_editor.revalidate": "重新验证此实体",
"render.appearance_editor.save": "保存这些光学设置",
"render.appearance_editor.save_reason": "保存需要当前 GPU 实体上的物理玻璃。清漆和本地图像贴图仍为临时设置。",
"render.appearance_editor.saved": "随此镜头保存",
"render.appearance_editor.saved_state": "这些光学设置已随此镜头保存。",
"render.appearance_editor.scatter_reason": "内部散射需要封闭实体边界。",
"render.appearance_editor.temporary": "这些光学设置仅在此标签页临时生效。保存后可随镜头保留。",
"render.appearance_editor.thickness": "厚度",
"render.appearance_editor.thickness_reason": "厚度适用于薄片;封闭玻璃使用几何距离。",
"render.appearance_editor.thin": "薄片",
"render.appearance_editor.u_repeat": "U 重复",
"render.appearance_editor.v_repeat": "V 重复",
"render.appearance_editor.v_up": "纹理 V 轴朝上",
"render.appearance_editor.weight": "权重",
"render.appearance_physics_separate": "视觉外观不会分配密度、热学数据或其他工程属性。",
"render.appearances": "外观",
"render.availability_native": "原生组合;应用前请审阅",
@@ -1997,6 +2073,9 @@
"render.category_paint": "涂漆",
"render.category_plastic": "塑料",
"render.category_wood": "木材",
"render.check_opaque": "透明度:仅不透明表面;支持背景 Alpha。",
"render.check_sorted_alpha": "透明度:排序 Alpha 近似。",
"render.check_transmission": "透明度:物理透射。",
"render.chromatic_offset": "边缘色差偏移",
"render.clear_stage": "移除已保存的舞台",
"render.copy_request": "复制 JSON",
@@ -2230,6 +2309,36 @@
"render.inner_half_angle": "内半角",
"render.inspect_scene": "刷新捕获的场景摘要",
"render.inspector": "检查器",
"render.inspector_advanced": "高级",
"render.inspector_all_users": "全部 {count} 个使用者",
"render.inspector_applies_to": "应用范围",
"render.inspector_both_users": "两个使用对象",
"render.inspector_chain": "此外观的来源",
"render.inspector_colour": "颜色",
"render.inspector_edit": "编辑",
"render.inspector_finish": "表面处理",
"render.inspector_from_material": "来自材质",
"render.inspector_gloss": "光泽",
"render.inspector_material": "材质",
"render.inspector_matte": "哑光",
"render.inspector_metal": "金属",
"render.inspector_opacity": "不透明度",
"render.inspector_opaque_help": "表面处理和光学字段使用不透明 PBR(不透明度为 1)。降低不透明度会使用标量着色。",
"render.inspector_pick_target": "拾取此类型的目标以检查其外观。",
"render.inspector_remove": "移除覆盖",
"render.inspector_replace": "替换…",
"render.inspector_reset": "重置",
"render.inspector_satin": "缎面",
"render.inspector_save_library": "保存到库…",
"render.inspector_set_all": "外观混合。编辑会将外观设置到所有选定目标。",
"render.inspector_soon": "即将推出",
"render.inspector_target_limit": "最多选择 10000 个目标以检查外观。",
"render.inspector_textures": "纹理",
"render.inspector_this_body": "此实体",
"render.inspector_this_component": "此组件",
"render.inspector_this_face": "此面",
"render.inspector_this_target": "此目标",
"render.inspector_used_by": "由 {count} 个对象使用",
"render.keep_editing": "继续编辑",
"render.layer_down": "下移",
"render.layer_enabled": "启用此层",
@@ -2396,12 +2505,34 @@
"render.optics_saved": "已保存光学目标",
"render.optics_saved_help": "定义、特征及实例的确切标识在离线重开后保留。拒绝缺失、替换或隐藏的目标;工程材料保持不变。移除目标前先保存或放弃数值编辑。",
"render.outer_half_angle": "外半角",
"render.output_advanced": "高级",
"render.output_backend_ready": "就绪",
"render.output_check": "检查",
"render.output_check_help": "运行检查可审查此请求,而不启动渲染。",
"render.output_check_summary": "检查 · {count} 条提示",
"render.output_copy": "复制",
"render.output_destination": "目标位置",
"render.output_destination_help": "导出已完成的图像时选择目标文件。",
"render.output_dimensions": "尺寸",
"render.output_effects": "效果",
"render.output_export": "导出 PNG…",
"render.output_exr_unavailable": "此处无法设置 EXR 输出。线性 EXR 导出需要保留线性母版的已完成 GPU 图像。",
"render.output_fit": "适合窗口",
"render.output_format": "格式",
"render.output_format_status": "PNG 是完成的显示图像。可选 GPU EXR 保留显示滤镜前的原始线性 FLOAT32 辐亮度;导出不会替换成预览缓存或其他任务。",
"render.output_image": "图像",
"render.output_largest": "{width} × {height} 最大",
"render.output_lock_ratio": "锁定宽高比",
"render.output_over_budget": "请求图像超出所选资源范围;请明确选择兼容尺寸或配置,不会自动降级。",
"render.output_presets_help": "格式、质量和检查列表位于输出编辑器中。",
"render.output_quality": "质量",
"render.output_quality_waiting": "等待质量约定",
"render.output_ratio_refused": "关联尺寸超出此方式的上限;保留原尺寸。请解锁宽高比或选择较小值。",
"render.output_ready": "已就绪,可渲染",
"render.output_size_ok": "尺寸正常 · {width} × {height} px",
"render.output_swap_orientation": "交换宽度和高度",
"render.output_tools_inspector": "请在输出检查器中编辑尺寸和格式。",
"render.output_view_image": "查看图像",
"render.page_aging": "老化与沉积",
"render.page_all": "全部控件",
"render.page_appearance": "外观",
@@ -2462,10 +2593,18 @@
"render.property_unknown": "未知",
"render.quality_ack": "确认固定预算映射和省略项",
"render.quality_apply": "使用此固定采样预算",
"render.quality_balanced": "均衡",
"render.quality_custom": "自定义",
"render.quality_draft": "草稿",
"render.quality_high": "高",
"render.quality_projection_help": "Q 预设包含未来需求。此操作仅映射受支持的固定样本 PNG 预算,明确省略自适应停止、原始线性输出和独立透射限制。",
"render.quality_review": "审阅采样预算",
"render.queue_pending": "此界面没有自动生产队列或动画渲染器;已完成的静止图像仍可单独导出。",
"render.radial_assign": "指定外观",
"render.radial_compare": "比较",
"render.radial_distortion": "径向畸变",
"render.radial_fit": "适配产品",
"render.radial_save": "保存镜头",
"render.regrade_action": "重新调整原始母版(不降噪)",
"render.regrade_help": "根据所选已完成原始母版和这些显示设置创建独立 PNG。不追踪光线,不改变已捕获的尺寸、相机或灯光,也不覆盖 EXR。原始母版不保留降噪辅助数据,因此不重新降噪。可通过相同任务控件导出派生结果。",
"render.regrade_label": "重新调色",
@@ -2474,6 +2613,7 @@
"render.replace_label": "替换",
"render.replace_saved_surface": "替换此目标已有的保存绑定",
"render.restore_camera": "恢复已保存相机",
"render.result_actual": "100%",
"render.result_actual_help": "一个图像像素对应一个屏幕像素。",
"render.result_actual_size": "100%",
"render.result_capture": "捕获版本",
@@ -2541,6 +2681,7 @@
"render.sharpening": "细节锐化",
"render.shell_tab_appearance": "外观",
"render.shell_tab_camera": "相机",
"render.shell_tab_lighting": "灯光",
"render.shell_tab_scene": "场景",
"render.shot_duplicate": "复制已保存镜头",
"render.shot_manager": "已保存镜头",
@@ -2558,11 +2699,15 @@
"render.shots_save": "保存镜头",
"render.shots_update": "更新镜头",
"render.show_shots_label": "显示镜头条",
"render.size_4k": "4K",
"render.size_4k_uhd": "4K 超高清",
"render.size_custom": "自定义…",
"render.size_full_hd": "全高清",
"render.size_hd": "HD",
"render.size_presets": "尺寸预设",
"render.size_presets_pending": "随输出编辑器推出",
"render.size_preview": "预览",
"render.size_square": "正方形",
"render.sky_ground": "地面 · 线性 RGB",
"render.sky_horizon": "地平线 · 线性 RGB",
"render.sky_zenith": "天顶 · 线性 RGB",
@@ -2575,6 +2720,9 @@
"render.stage_output": "输出",
"render.stage_panel_count": "已保存面板",
"render.stage_preview_note": "集成预览 · 直接光照",
"render.state_completed": "已完成图像",
"render.state_in_progress": "图像生成中",
"render.state_model": "模型预览",
"render.strip_label": "镜头条",
"render.studio_add_area": "添加面光源",
"render.studio_ambient": "恒定环境辐亮度",
@@ -2670,6 +2818,7 @@
"render.volume_max": "最大边界 · 世界坐标 mm",
"render.volume_min": "最小边界 · 世界坐标 mm",
"render.volume_title": "有界均匀体积",
"render.wait_add_light": "尚不支持:创建命名灯光",
"render.wait_batch": "暂不支持:已保存镜头渲染队列",
"render.wait_compare": "暂不支持:保留图像比较",
"render.wait_paint": "暂不支持:产品拾取和外观涂绘",
@@ -2784,6 +2933,10 @@
"sketch_text.path_hint": "文字所跟随的草图曲线:先单击此处,再选择曲线",
"sketch_text.use_font_file": "使用字体文件 {path}",
"solid": "实心",
"status.editing": "正在编辑 {name}",
"status.under_constrained": "欠约束({count} 个自由度)",
"status.under_constrained_one": "欠约束({count} 个自由度)",
"status.units_tip": "设计的单位。单击可更改",
"the angle must be a number": "角度必须是数字",
"the curves must be in one sketch": "曲线必须位于同一个草图中",
"the direction must be an axis, a sketch line or a straight edge": "方向必须是轴、草图直线或直边",
@@ -2822,8 +2975,10 @@
"timeline.move_to_group": "将特征移至文件夹",
"timeline.rename_group": "重命名特征文件夹",
"timeline.reorder_many": "特征重新排序",
"timeline.suppress_features": "抑制特征",
"timeline.suppress_folders_tip": "抑制或取消抑制文件夹及其嵌套文件夹中的所有特征",
"timeline.ungroup": "移除特征文件夹",
"timeline.unsuppress_features": "取消抑制特征",
"to the face": "到面",
"to the next face": "到下一个面",
"toolbar.customize_note": "{tab} 选项卡。勾选命令可将其按钮添加到工具栏。您也可以将面板下拉菜单中的某一行拖到工具栏,将按钮拖出工具栏以移除,或使用下拉菜单行中的固定图标。",
@@ -3234,6 +3389,8 @@
"ui.cancel": "取消",
"ui.canvases": "画布",
"ui.center": "中心",
"ui.center_arc_follows_cursor": "中心点圆弧跟随光标方向",
"ui.center_arc_follows_cursor_tip": "中心点圆弧按光标绕中心移动的方向沿顺时针或逆时针绘制。关闭:始终从起点逆时针绘制。",
"ui.centerline": "中心线",
"ui.chain": "链",
"ui.chamfer": "倒角",
@@ -3646,6 +3803,15 @@
"ui.plugins_skipped": "有 {count} 个文件夹无法读取。",
"ui.plugins_version": "{version}",
"ui.point": "点",
"ui.point_cloud_auto_range": "自动范围",
"ui.point_cloud_color_by": "着色方式",
"ui.point_cloud_file_colors": "文件颜色",
"ui.point_cloud_height_high": "最高高度",
"ui.point_cloud_height_low": "最低高度",
"ui.point_cloud_no_file_colors": "文件中没有颜色",
"ui.point_cloud_single_color": "单色",
"ui.point_cloud_size": "点大小",
"ui.point_cloud_units": "文件单位",
"ui.point_under_the_cursor": "光标下的点",
"ui.points": "点",
"ui.polygon": "多边形",
@@ -3899,11 +4065,13 @@
"update.installed": "SolidCraft {version} 已安装。重新启动后生效。",
"update.installer_started": "SolidCraft 关闭后将启动安装程序。",
"update.later": "稍后提醒",
"update.managed": "有可用更新:请在 {manager} 中更新 SolidCraft",
"update.no_notes": "此版本没有发布说明。",
"update.package_hint": "此副本来自 {kind}。下载新软件包后用以下命令安装:",
"update.prefs_dev": "这是开发版本({version}),不检查更新。",
"update.prefs_flatpak": "此副本({version})由 Flatpak 更新:运行 {command} 或使用软件中心。SolidCraft 不会自行检查。",
"update.prefs_label": "更新",
"update.prefs_managed": "这是由 {manager} 管理的 SolidCraft {version}。检查时只向 GitHub 询问最新版本,不会发送其他任何内容;请在 {manager} 中更新 SolidCraft。",
"update.prefs_note": "当前为 SolidCraft {version}({kind})。检查时只向 GitHub 查询最新版本,不发送其他任何数据。",
"update.prefs_web": "重新加载页面以获取最新的网页应用。如果网络不可用或更新无法及时下载完成,则打开已保存的版本;正在进行的下载会继续,供下次重新加载时使用。",
"update.release_page": "打开发布页面",
@@ -3920,6 +4088,7 @@
"values must be finite numbers": "值必须是有限数字",
"values must be numbers or expressions": "值必须是数字或表达式",
"view.auto_ortho_faces": "旋转时透视,正对表面时正交",
"view.render_inspector": "渲染检查器",
"web.files.insert_drawings_hint": "将 CAD 文件、网格、DXF 或 SVG 插入当前设计",
"web.files.insert_stored_drawings_hint": "插入当前设计(CAD 文件、网格、DXF、SVG)",
"web.open_full": "在 SolidCraft 中打开",
+36
View File
@@ -653,3 +653,39 @@ fn fs_img(i: IOut) -> @location(0) vec4<f32> {
fn fs_line_all(i: LOut) -> @location(0) vec4<f32> {
return out_color(i.c);
}
// Point clouds: each point is a round dot a few pixels across, facing the screen, depth-tested
// and written like the model's faces, so the model hides the points behind it and they hide it.
// The colour's alpha carries the dot's size (eighths of a pixel).
struct POut {
@builtin(position) pos: vec4<f32>,
@location(0) c: vec4<f32>,
@location(1) wp: vec3<f32>,
@location(2) at: vec2<f32>,
};
@vertex
fn vs_point(@builtin(vertex_index) vi: u32, @location(0) p: vec3<f32>, @location(1) c: vec4<f32>) -> POut {
var xs = array<f32, 6>(-1.0, 1.0, 1.0, -1.0, 1.0, -1.0);
var ys = array<f32, 6>(-1.0, -1.0, 1.0, -1.0, 1.0, 1.0);
let at = vec2<f32>(xs[vi], ys[vi]);
let size = max(c.a * 255.0 / 8.0, 1.0);
let centre = u.vp * vec4<f32>(p, 1.0);
let half = u.screen.xy * 0.5;
var o: POut;
o.pos = vec4<f32>(centre.xy + at * size * 0.5 / half * centre.w, centre.z, centre.w);
// Behind the eye: nothing.
if (near_z(centre) < 0.0) { o.pos = vec4<f32>(0.0, 0.0, 0.0, -1.0); }
o.c = vec4<f32>(c.rgb, 1.0);
o.wp = p;
// Dots under two pixels are squares: a disc that small loses its centre pixel.
o.at = at * select(1.0, 0.0, size < 2.0);
return o;
}
@fragment
fn fs_point(i: POut) -> @location(0) vec4<f32> {
if (clipped(i.wp) || dot(i.at, i.at) > 1.0) { discard; }
return out_color(i.c);
}
+5
View File
@@ -2727,6 +2727,9 @@ pub struct Document {
/// Reference images on planes (Insert Canvas).
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub canvases: Vec<crate::canvas::Canvas>,
/// Scanned points kept as a reference (Insert Point Cloud).
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub point_clouds: Vec<crate::point_cloud::PointCloud>,
/// Comments of feature-input parameters (by name).
#[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")]
pub param_comments: std::collections::BTreeMap<String, String>,
@@ -2932,6 +2935,7 @@ impl Document {
.map(|g| g.id)
.chain(self.timeline_groups.iter().map(|g| g.id))
.chain(self.canvases.iter().map(|c| c.id))
.chain(self.point_clouds.iter().map(|c| c.id))
.chain(a.joints.iter().map(|j| j.id))
.chain(a.contact_sets.iter().map(|c| c.id))
.chain(a.studies.iter().map(|s| s.id))
@@ -2979,6 +2983,7 @@ impl Document {
appearances: Default::default(),
favorites: Default::default(),
canvases: Vec::new(),
point_clouds: Vec::new(),
param_comments: Default::default(),
browser_groups: Vec::new(),
timeline_groups: Vec::new(),
+23 -11
View File
@@ -1865,6 +1865,14 @@ fn placement_digest(doc: &Document, f: &Feature, st: Option<&ModelState>) -> u64
reads.placed_body(st, tool);
}
}
// A pattern of bodies copies them along its directions or about its axis in its own
// component's frame, so it follows where each body it copies is placed relative to that
// frame: copies of a root block, patterned in a component, move with the component.
FeatureKind::Pattern { bodies, .. } if !bodies.is_empty() => {
for b in bodies {
reads.placed_body(st, &b.0);
}
}
// Joins, cuts and intersects meet their targets where those are placed (#3383).
kind if drawn_tools_change_bodies(kind) => reads.drawn_targets(st, f),
FeatureKind::Sketch { plane, sketch } => {
@@ -2721,10 +2729,22 @@ fn note_join_apart(before: &ModelState, after: &ModelState, f: &Feature, warning
if operation != Operation::Join {
return;
}
for (name, n) in join_pieces_added(before, after) {
append_warning(
warning,
format!("the joined piece doesn't touch `{name}`, so `{name}` is now in {n} separate pieces (New Body makes it a body of its own)"),
);
}
}
/// The bodies a Join from `before` to `after` left in more separate pieces than it found, with
/// their piece counts now: empty when the joined material made one body with what it touched
/// (or bridged pieces). A lid closing a cavity adds a void, not a piece.
pub fn join_pieces_added(before: &ModelState, after: &ModelState) -> Vec<(String, usize)> {
let pieces = |b: &Body| b.lumps().map_or(1, |l| l.len());
// Pieces of the bodies the join changed or absorbed before it, and of those it kept after.
let (mut had, mut have) = (0usize, 0usize);
let mut grown: Vec<(&str, usize)> = Vec::new();
let mut grown: Vec<(String, usize)> = Vec::new();
for was in &before.bodies {
match after.body(&was.name) {
Some(now) if now.body.same_shape(&was.body) => {}
@@ -2733,21 +2753,13 @@ fn note_join_apart(before: &ModelState, after: &ModelState, f: &Feature, warning
had += old;
have += new;
if new > old {
grown.push((now.name.as_str(), new));
grown.push((now.name.clone(), new));
}
}
None => had += pieces(&was.body),
}
}
if have <= had {
return;
}
for (name, n) in grown {
append_warning(
warning,
format!("the joined piece doesn't touch `{name}`, so `{name}` is now in {n} separate pieces (New Body makes it a body of its own)"),
);
}
if have <= had { Vec::new() } else { grown }
}
/// Add tool bodies to the model according to the operation.
+38 -2
View File
@@ -352,6 +352,14 @@ fn lex_spans(s: &str) -> Result<Vec<(Tok, usize, usize)>, DocError> {
{
i = k + 1;
}
// A value typed as a definition, `name = expression` (#4071), is worth its expression: the
// name is what it defines, not part of it (`Document::define_inline` stores it apart).
if i == 0
&& definition(s).is_some()
&& let Some(eq) = s.find('=')
{
i = cs.iter().position(|(b, _)| *b > eq).unwrap_or(cs.len());
}
while let Some(&(_, c)) = cs.get(i) {
if c.is_whitespace() {
i += 1;
@@ -565,6 +573,10 @@ pub fn rename_reference(s: &str, old: &str, new: &str) -> String {
/// Is the expression just a number (with an optional unit), like `20`, `20 mm` or `-1.5 in`?
pub fn is_literal(s: &str) -> bool {
// A definition names its value: it is not just a number, whatever it defines (#4071).
if definition(s).is_some() {
return false;
}
let Ok(toks) = lex(s) else { return false };
let toks: &[Tok] = match toks.first() {
Some(Tok::Op('-' | '+')) => toks.get(1..).unwrap_or_default(),
@@ -592,8 +604,8 @@ fn is_name_shaped(s: &str) -> bool {
/// A value typed as a definition, `name = expression` (or `#name = expression`, #2371): the name
/// without the `#` and the text after the first `=`, both trimmed (the text may be empty). `None` for anything else, including text whose
/// left side is not shaped like a name (`2 = 3`), which stays an ordinary expression and fails
/// to parse as before. The dimension input defines a user parameter this way (#2916); other
/// value inputs can take the same form later.
/// to parse as before. Every value input defines a user parameter this way (#2916, #4071): until
/// `Document::define_inline` stores the parameter, the text evaluates as its expression.
pub fn definition(s: &str) -> Option<(&str, &str)> {
let (name, rhs) = s.split_once('=')?;
let name = name.trim();
@@ -979,6 +991,9 @@ pub fn eval_with(s: &str, lookup: &dyn Fn(&str) -> Result<Value, DocError>) -> R
pub fn eval_with_defaults(s: &str, lookup: &dyn Fn(&str) -> Result<Value, DocError>, defaults: Defaults) -> Result<Value, DocError> {
let (toks, spans): (Vec<Tok>, Vec<(usize, usize)>) = lex_spans(s)?.into_iter().map(|(t, a, b)| (t, (a, b))).unzip();
if toks.is_empty() {
if let Some((name, _)) = definition(s) {
return Err(DocError::Expr(format!("`{name} =` needs a value after the `=`")));
}
return Err(DocError::Expr("empty expression".into()));
}
let mut p = Parser { toks, spans, pos: 0, lookup, depth: 0, defaults };
@@ -1470,6 +1485,27 @@ mod tests {
}
}
/// Until the design stores it, a definition is worth its expression (#4071): the name is
/// not a reference, it is never a plain number, and it renames like its expression.
#[test]
fn a_definition_evaluates_as_its_expression() {
let look = |n: &str| if n == "a" { Ok(Value::length(4.0)) } else { Err(DocError::Expr(format!("unknown parameter `{n}`"))) };
let ev = |s: &str| eval_with(s, &look).and_then(|v| v.to_kind(Kind::Length));
assert_eq!(ev("w = 20").ok(), Some(20.0));
assert_eq!(ev("#w = a * 2").ok(), Some(8.0));
assert_eq!(ev("szerokość=1 in").ok(), Some(25.4));
assert_eq!(references("w = a + b"), ["a", "b"]);
assert!(references("w = 3").is_empty());
assert_eq!(rename_reference("w = a * 2", "a", "len"), "w = len * 2");
assert_eq!(rename_reference("a = a * 2", "a", "len"), "a = len * 2");
assert!(!is_literal("w = 20") && !is_bare_number("w = 20"));
assert!(ev("w = w").unwrap_err().to_string().contains("unknown parameter `w`"));
assert!(ev("w =").unwrap_err().to_string().contains("`w =` needs a value after the `=`"));
for bad in ["w = = 3", "2 = 3", "w == 3", "w = 3 = 4", "= 5 ="] {
assert!(ev(bad).is_err(), "{bad}");
}
}
/// A reserved name is refused by saying what kind of word it is.
#[test]
fn a_reserved_name_is_refused_by_its_kind() {
+9 -10
View File
@@ -716,6 +716,7 @@ pub fn read(s: &str) -> Result<Document> {
{
return Err(DocError::Invalid("document too large".into()));
}
crate::point_cloud::check_all(&d.point_clouds)?;
let feature_ids: std::collections::BTreeSet<_> = d.features.iter().map(|feature| feature.id).collect();
let mut group_ids = std::collections::BTreeSet::new();
let mut grouped_features = std::collections::BTreeSet::new();
@@ -1493,16 +1494,14 @@ mod tests {
file["format_pending"] = serde_json::json!(["not-a-change-anyone-made"]);
file["features"] = serde_json::json!([{"type": "emboss", "face": {"body": "Body", "at": [0, 0, 0], "a_newer_key": true}}]);
let error = read(&file.to_string()).unwrap_err().to_string();
if PENDING.is_empty() {
assert!(error.contains("emboss face"), "a list beside a folded format means nothing: {error}");
} else {
assert_eq!(
error,
format!(
"this design was saved by a newer SolidCraft (file format {FORMAT} with `not-a-change-anyone-made`, which this version doesn't know): update SolidCraft to open it"
)
);
}
// With changes pending, FORMAT is made of them; once they are folded it is the folded
// format, which is made of changes too. Either way an unknown change is a newer build's.
assert_eq!(
error,
format!(
"this design was saved by a newer SolidCraft (file format {FORMAT} with `not-a-change-anyone-made`, which this version doesn't know): update SolidCraft to open it"
)
);
}
/// Drawings follow the same rules from their own first format: folded formats follow on from 1
@@ -0,0 +1,5 @@
//! A design can hold point clouds (`point_clouds`: scanned points kept as a reference, #5151),
//! their points and colours a binary part of the design file (base64 in a plain JSON design), with
//! how they are coloured (`color_by`, `color`, `height_range`).
//! Older designs need no transformation: they have none. The last release refuses a design
//! saved with this (the newer format number).
+202
View File
@@ -0,0 +1,202 @@
//! Values typed as definitions, `name = expression` (#4071): any value input, typed or sent as
//! a command's parameter, can name its value. Until the design stores it, such a text evaluates
//! as its expression (`expr::definition`), so a command reads and checks it like any other value;
//! [`Document::define_inline`] then makes the user parameter `name` with that expression and
//! leaves `name` where the text was.
use serde::Serialize;
use crate::expr::{self, Kind};
use crate::params::ExprRole;
use crate::walk::Walk;
use crate::{DocError, Document, FeatureKind, Result};
/// A user parameter a value typed as `name = expression` defined.
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct Defined {
pub name: String,
pub expression: String,
pub unit: String,
}
/// How the place a definition was typed in reads a bare number, which the parameter keeps.
#[derive(Clone, Copy)]
enum Reads<'a> {
/// A typed value of this kind: lengths in the design's units, angles in degrees.
Typed(Kind),
/// A model parameter's (a dimension's) own unit.
Unit(&'a str),
/// A primitive's placement coordinate: millimetres whatever the design's units.
Millimetres,
/// A value of no fixed kind: the parameter takes its value's unit.
Inferred,
}
/// A definition found: the name, its expression and its unit (`None`: its value's own).
struct Pending {
name: String,
expression: String,
unit: Option<String>,
}
/// The definitions found so far: each slot's text becomes the name, the definition is kept.
struct Found {
units: String,
defs: Vec<Pending>,
problem: Option<String>,
}
impl Found {
/// If `text` is a definition, note it and leave the name in its place.
fn take(&mut self, text: &mut String, reads: Reads) {
let Some((name, rhs)) = expr::definition(text) else { return };
if rhs.is_empty() {
self.problem.get_or_insert_with(|| format!("`{name} =` needs a value after the `=`"));
return;
}
let (expression, unit) = match reads {
Reads::Typed(Kind::Length) => {
let units = expr::length_unit_or_mm(&self.units);
(with_unit(rhs, units), Some(units.to_string()))
}
Reads::Typed(Kind::Angle) => (rhs.to_string(), Some("deg".to_string())),
Reads::Typed(Kind::Unitless) => (rhs.to_string(), Some(String::new())),
Reads::Unit(u) => (rhs.to_string(), Some(u.to_string())),
Reads::Millimetres => (rhs.to_string(), Some("mm".to_string())),
Reads::Inferred => (rhs.to_string(), None),
};
let name = name.to_string();
match self.defs.iter().find(|d| d.name == name) {
// The same definition in two places (one typed value a command stores twice).
Some(d) if d.expression == expression && d.unit == unit => {}
Some(_) => {
self.problem.get_or_insert_with(|| format!("`{name}` is defined twice with different values"));
return;
}
None => self.defs.push(Pending { name: name.clone(), expression, unit }),
}
*text = name;
}
/// [`Found::take`] for an expression field.
fn take_expr(&mut self, e: &mut expr::Expr, reads: Reads) {
if expr::definition(e.as_str()).is_none() {
return;
}
let mut text = e.as_str().to_string();
self.take(&mut text, reads);
*e = text.into();
}
}
/// A bare number in the design's length unit, as a typed length reads it.
fn with_unit(rhs: &str, units: &str) -> String {
if units != "mm" && expr::is_bare_number(rhs) { format!("{} {units}", rhs.trim()) } else { rhs.to_string() }
}
impl Document {
/// Store every value typed as `name = expression`: the user parameter `name` with that
/// expression (a bare number read as the value would read it, in that unit), and `name`
/// where the text was. A name that is taken or reserved is refused, as is a definition where
/// none can be made: in a parameter's own expression or a configuration cell. All or nothing:
/// on an error the design is unchanged.
pub fn define_inline(&mut self) -> Result<Vec<Defined>> {
// Where a definition is refused: a parameter's expression defines nothing else (the
// parameters command renames the parameter instead), nor does a configuration row.
if let Some(p) = self.params.iter().find(|p| !p.model && expr::definition(&p.expr).is_some()) {
let other = expr::definition(&p.expr).map(|(n, _)| n).unwrap_or_default();
return Err(DocError::Invalid(format!(
"the expression of `{}` cannot define another parameter `{other}`: add `{other}` as a parameter of its own",
p.name
)));
}
// Only a parameter's cells are expressions (a text parameter's are texts).
let numeric: Vec<bool> =
self.configs.columns.iter().map(|c| matches!(c, crate::config::Column::Param(n) if self.text_param(n).is_none())).collect();
let mut cells = self.configs.rows.iter().flat_map(|r| r.cells.iter().zip(&numeric).filter(|(_, n)| **n).map(|(c, _)| c));
if let Some(other) = cells.find_map(|c| expr::definition(c).map(|(n, _)| n)) {
return Err(DocError::Invalid(format!("a configuration cell cannot define the parameter `{other}`: add it in Parameters first")));
}
let mut next = self.clone();
let mut found = Found { units: next.units.clone(), defs: Vec::new(), problem: None };
for p in next.params.iter_mut().filter(|p| p.model) {
let unit = p.unit.clone();
found.take(&mut p.expr, Reads::Unit(&unit));
}
for f in &mut next.features {
f.kind.walk_exprs_mut(&mut |(_, e, kind, role)| {
let reads = if role == ExprRole::Placement { Reads::Millimetres } else { Reads::Typed(kind) };
found.take_expr(e, reads);
});
if let FeatureKind::Sketch { sketch, .. } = &mut f.kind {
for p in &mut sketch.patterns {
if let Some(e) = p.count_expression.as_mut() {
found.take(e, Reads::Typed(Kind::Unitless));
}
for (key, e) in &mut p.expressions {
let kind = match key.as_str() {
"count" | "count2" => Kind::Unitless,
"angle" => Kind::Angle,
_ => Kind::Length,
};
found.take(e, Reads::Typed(kind));
}
}
for link in &mut sketch.links {
if let solidcraft_sketch::LinkSource::Text { exprs, .. } = &mut link.source {
for e in [&mut exprs.x, &mut exprs.y, &mut exprs.height].into_iter().flatten() {
found.take(e, Reads::Typed(Kind::Length));
}
if let Some(e) = exprs.angle.as_mut() {
found.take(e, Reads::Typed(Kind::Angle));
}
}
}
}
}
for e in next.assembly.exprs_mut() {
found.take(e, Reads::Inferred);
}
for r in &mut next.sheet.rules {
let kinds = r.fields().map(|(_, _, k)| k);
for ((_, e), k) in r.fields_mut().into_iter().zip(kinds) {
found.take(e, Reads::Typed(k));
}
}
for r in &mut next.plastic.rules {
let kinds = r.fields().map(|(_, _, k)| k);
for ((_, e), k) in r.fields_mut().into_iter().zip(kinds) {
found.take(e, Reads::Typed(k));
}
}
for c in &mut next.canvases {
c.plane.walk_exprs_mut(&mut |(_, e, kind, _)| found.take_expr(e, Reads::Typed(kind)));
}
if let Some(problem) = found.problem {
return Err(DocError::Invalid(problem));
}
if found.defs.is_empty() {
return Ok(Vec::new());
}
for d in &found.defs {
if let Some(problem) = Self::param_name_problem(&d.name) {
return Err(DocError::Invalid(problem));
}
if self.name_taken(&d.name) {
return Err(DocError::Invalid(format!(
"there is already a parameter `{}`: type `{}` to use it, or change it in Parameters",
d.name, d.name
)));
}
}
let mut out = Vec::with_capacity(found.defs.len());
for d in found.defs {
next.add_user_param(&d.name, &d.expression, d.unit.as_deref(), None)
.map_err(|e| DocError::Invalid(format!("`{} = {}`: {e}", d.name, d.expression)))?;
let unit = next.param(&d.name).map(|p| p.unit.clone()).unwrap_or_default();
out.push(Defined { name: d.name, expression: d.expression, unit });
}
*self = next;
Ok(out)
}
}
+7 -4
View File
@@ -36,7 +36,9 @@ pub mod frame;
mod history_names;
pub mod hole_reference;
pub mod ids;
mod inline_params;
pub use format_offsets::whole_profile_selection_from_value;
pub use inline_params::Defined;
pub mod joints;
mod lineage;
pub mod metadata;
@@ -50,6 +52,7 @@ pub mod persistent_name;
pub mod physical_material;
mod piece_names;
pub mod pipe_thread_catalog;
pub mod point_cloud;
mod points;
mod profile_refs;
pub mod progress;
@@ -77,10 +80,10 @@ pub use assembly::{
pub use document::*;
pub use eval::{
EdgeRef, FeatureResult, Model, ModelBody, ModelState, PlacedBodies, RevolveExtent, SavedModel, SolvedSketch, ThreadInfo, curve_pick_error,
edge_names_for, edge_refs, face_point_names, face_point_state, hole_direction_at, import_body_names, map_wire, metric_thread_sizes,
object_height, parse_metric_thread, pattern_matrices, revolve_extent, revolve_extent_with_convention, select_profiles, sketch_hole_direction,
sketch_line_plane, state_in_frame, state_in_frame_at, suggested_thread, tangent_face_chain, through_all_distance, world_state,
world_state_reusing,
edge_names_for, edge_refs, face_point_names, face_point_state, hole_direction_at, import_body_names, join_pieces_added, map_wire,
metric_thread_sizes, object_height, parse_metric_thread, pattern_matrices, revolve_extent, revolve_extent_with_convention, select_profiles,
sketch_hole_direction, sketch_line_plane, state_in_frame, state_in_frame_at, suggested_thread, tangent_face_chain, through_all_distance,
world_state, world_state_reusing,
};
pub use eval::{EvalCache, KeptResults};
pub use expr::Expr;
+6
View File
@@ -225,6 +225,12 @@ impl Document {
/// A typed length that is a bare number means the design's units: it gets that unit written
/// in (feature inputs are stored in mm otherwise). Anything else is returned unchanged.
pub fn with_design_unit(&self, e: &str, kind: Kind) -> String {
// A definition's value reads as the value would (#4071): `w = 2` is `w = 2 in`.
if let Some((name, rhs)) = expr::definition(e)
&& expr::is_bare_number(rhs)
{
return format!("{name} = {}", self.with_design_unit(rhs, kind));
}
let u = self.units.trim();
let bare = expr::is_bare_number(e);
if kind == Kind::Length && bare && !u.is_empty() && u != "mm" && expr::unit_info(u).is_some_and(|(k, _)| k == Kind::Length) {
+2
View File
@@ -136,6 +136,7 @@ fn same_geometry_inputs(a: &Document, b: &Document) -> bool {
browser_order,
named_views,
visibility,
point_clouds,
} = a;
(Arc::ptr_eq(render, &b.render) || *render == b.render)
&& *format == b.format
@@ -170,6 +171,7 @@ fn same_geometry_inputs(a: &Document, b: &Document) -> bool {
&& *browser_order == b.browser_order
&& *named_views == b.named_views
&& *visibility == b.visibility
&& *point_clouds == b.point_clouds
}
/// Compare feature definitions without traversing immutable imported payloads. A replacement
+501
View File
@@ -0,0 +1,501 @@
//! Point clouds: scanned points (a PLY or XYZ file) kept in the design as a reference to model
//! from (#5151). They are not bodies: nothing is built from them, they are drawn as points.
//! The points travel inside the design file (a binary part of the container, base64 in plain
//! JSON), so it reopens without the original file. They are single precision, relative to the
//! cloud's own `origin` (double precision), so a scan far from the design's origin keeps its
//! detail.
use std::sync::Arc;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use solidcraft_geom::Vec3;
use crate::canvas::{base64_decode, base64_encode};
use crate::{DocError, IDENTITY, Mat, Result};
/// Most points one cloud may hold.
pub const MAX_POINTS: usize = 20_000_000;
/// Largest point coordinate (mm) from the cloud's origin, and largest origin coordinate.
pub const MAX_COORD: f64 = 1e9;
/// Point sizes on screen (pixels).
pub const POINT_SIZE: std::ops::RangeInclusive<f64> = 0.5..=16.0;
/// Length units a point cloud file may be in, and millimetres per unit.
pub const UNITS: [(&str, f64); 5] = [("mm", 1.0), ("cm", 10.0), ("m", 1000.0), ("in", 25.4), ("ft", 304.8)];
/// Millimetres per `unit` (one of [`UNITS`], any case).
pub fn unit_scale(unit: &str) -> Option<f64> {
UNITS.iter().find(|(u, _)| u.eq_ignore_ascii_case(unit)).map(|(_, k)| *k)
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct PointCloud {
pub id: u64,
pub name: String,
/// The file it was read from (its name only).
#[serde(default)]
pub file: String,
/// Where the points' own frame sits in the cloud's placement (mm).
pub origin: Vec3,
/// The points from `origin` (mm).
pub points: Points,
/// The file's colour of each point, when it has them.
#[serde(default, skip_serializing_if = "Colors::is_empty")]
pub colors: Colors,
/// Points the file had (more than `points` when the cloud was thinned as it came in).
#[serde(default)]
pub source_points: u64,
/// The file's length unit (one of [`UNITS`]): the points were scaled from it to millimetres.
#[serde(default = "mm")]
pub units: String,
/// Where the cloud sits in the design: a rigid move of its points.
#[serde(default = "identity", skip_serializing_if = "is_identity")]
pub transform: Mat,
/// Size of a point on screen (pixels).
#[serde(default = "two")]
pub point_size: f64,
/// How the points are coloured.
#[serde(default, skip_serializing_if = "ColorBy::is_file")]
pub color_by: ColorBy,
/// The colour of every point with [`ColorBy::Single`] (straight sRGB, 0..1; `None`: [`PLAIN`]).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub color: Option<[f32; 3]>,
/// The heights (design Z, mm) the [`ColorBy::Height`] ramp runs between, lowest first;
/// `None`: worked out from the points ([`PointCloud::auto_height_range`]).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub height_range: Option<[f64; 2]>,
#[serde(default = "yes")]
pub visible: bool,
}
/// How a cloud's points are coloured.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ColorBy {
/// The file's colour of each point ([`PLAIN`] when it has none).
#[default]
File,
/// One colour for every point ([`PointCloud::color`]).
Single,
/// By height in the design: blue low, through cyan, green and yellow, to red high.
Height,
}
impl ColorBy {
fn is_file(&self) -> bool {
*self == ColorBy::File
}
/// From its name (`file`, `single`, `height`).
pub fn parse(s: &str) -> Option<ColorBy> {
match s {
"file" => Some(ColorBy::File),
"single" => Some(ColorBy::Single),
"height" => Some(ColorBy::Height),
_ => None,
}
}
pub fn name(self) -> &'static str {
match self {
ColorBy::File => "file",
ColorBy::Single => "single",
ColorBy::Height => "height",
}
}
}
/// The colour of points that have no other (straight sRGB, 0..1).
pub const PLAIN: [f32; 3] = [0.59, 0.63, 0.69];
/// Points sampled for the automatic height range.
const HEIGHT_SAMPLES: usize = 200_000;
/// The share of points left below and above the automatic height range, so a few stray points
/// don't flatten the ramp.
const HEIGHT_TAIL: f64 = 0.02;
/// A colour of the height ramp at `t` (0: low, 1: high): blue, cyan, green, yellow, red.
pub fn height_ramp(t: f64) -> [u8; 3] {
const STOPS: [[f64; 3]; 5] = [[40.0, 70.0, 200.0], [30.0, 170.0, 230.0], [60.0, 200.0, 90.0], [240.0, 220.0, 60.0], [230.0, 70.0, 50.0]];
let k = if t.is_finite() { t.clamp(0.0, 1.0) } else { 0.0 } * (STOPS.len() - 1) as f64;
let i = (k as usize).min(STOPS.len() - 2);
let f = k - i as f64;
let (a, b) = (STOPS[i], STOPS[i + 1]);
std::array::from_fn(|c| (a[c] * (1.0 - f) + b[c] * f).round().clamp(0.0, 255.0) as u8)
}
/// A colour channel (0..1) as a byte.
pub fn byte(v: f32) -> u8 {
(v.clamp(0.0, 1.0) * 255.0).round() as u8
}
fn identity() -> Mat {
IDENTITY
}
fn is_identity(m: &Mat) -> bool {
*m == IDENTITY
}
fn mm() -> String {
"mm".into()
}
fn two() -> f64 {
2.0
}
fn yes() -> bool {
true
}
impl PointCloud {
pub fn len(&self) -> usize {
self.points.len()
}
pub fn is_empty(&self) -> bool {
self.points.len() == 0
}
/// The `i`th point in the design (mm).
pub fn world_point(&self, i: usize) -> Option<Vec3> {
let p = self.points.get(i)?;
let local = self.origin + Vec3::new(f64::from(p[0]), f64::from(p[1]), f64::from(p[2]));
Some(crate::apply_point(&self.transform, local))
}
/// The corners of the box around the points, in the cloud's placement (mm), or `None` for
/// an empty cloud.
pub fn local_bounds(&self) -> Option<(Vec3, Vec3)> {
let (lo, hi) = self.points.bounds()?;
let v = |p: [f32; 3]| self.origin + Vec3::new(f64::from(p[0]), f64::from(p[1]), f64::from(p[2]));
Some((v(lo), v(hi)))
}
/// The design Z (mm) of a point from the cloud's origin.
fn world_z(&self, p: [f32; 3]) -> f64 {
let m = &self.transform;
let l = [self.origin.x + f64::from(p[0]), self.origin.y + f64::from(p[1]), self.origin.z + f64::from(p[2])];
m[0][2] * l[0] + m[1][2] * l[1] + m[2][2] * l[2] + m[3][2]
}
/// The heights (design Z, mm) holding all but the lowest and highest 2 % of the points (an
/// even sample of them), or `None` for an empty cloud. A flat cloud gets a range 1 mm high.
pub fn auto_height_range(&self) -> Option<[f64; 2]> {
let n = self.points.len();
if n == 0 {
return None;
}
let step = n.div_ceil(HEIGHT_SAMPLES).max(1);
let mut z: Vec<f64> = (0..n).step_by(step).filter_map(|i| self.points.get(i)).map(|p| self.world_z(p)).collect();
z.sort_by(f64::total_cmp);
let last = z.len().checked_sub(1)?;
let at = |q: f64| z.get((last as f64 * q).round() as usize).copied();
let (lo, hi) = (at(HEIGHT_TAIL)?, at(1.0 - HEIGHT_TAIL)?);
Some(if hi - lo > 1e-9 { [lo, hi] } else { [lo - 0.5, lo + 0.5] })
}
/// The heights the height ramp runs between now: the cloud's own, else the automatic ones.
pub fn height_range_now(&self) -> Option<[f64; 2]> {
self.height_range.or_else(|| self.auto_height_range())
}
/// The colour of each point (by index) as the cloud is shown.
pub fn shade(&self) -> impl Fn(usize) -> [u8; 3] + '_ {
let single = self.color.unwrap_or(PLAIN).map(byte);
let plain = PLAIN.map(byte);
let range = if self.color_by == ColorBy::Height { self.height_range_now() } else { None };
move |i| match self.color_by {
ColorBy::File => self.colors.get(i).unwrap_or(plain),
ColorBy::Single => single,
ColorBy::Height => match (range, self.points.get(i)) {
(Some([lo, hi]), Some(p)) => height_ramp((self.world_z(p) - lo) / (hi - lo)),
_ => plain,
},
}
}
/// The corners of the box around the points, placed in the design (mm).
pub fn world_corners(&self) -> Option<[Vec3; 8]> {
let (lo, hi) = self.local_bounds()?;
Some(std::array::from_fn(|i| {
let q = Vec3::new(if i & 1 == 0 { lo.x } else { hi.x }, if i & 2 == 0 { lo.y } else { hi.y }, if i & 4 == 0 { lo.z } else { hi.z });
crate::apply_point(&self.transform, q)
}))
}
/// The cloud is whole: as many colours as points, finite coordinates within range, a size
/// on screen in range and a rigid placement.
pub fn check(&self) -> Result<()> {
let bad = |what: &str| Err(DocError::Invalid(format!("point cloud `{}`: {what}", self.name)));
if self.points.len() > MAX_POINTS {
return bad("too many points");
}
if !self.colors.is_empty() && self.colors.len() != self.points.len() {
return bad("its colours do not match its points");
}
if ![self.origin.x, self.origin.y, self.origin.z].iter().all(|v| v.is_finite() && v.abs() <= MAX_COORD) {
return bad("its origin is out of range");
}
if self.points.values().iter().any(|v| !v.is_finite() || f64::from(v.abs()) > MAX_COORD) {
return bad("a point is out of range");
}
if unit_scale(&self.units).is_none() {
return bad("its units are not mm, cm, m, in or ft");
}
if !(self.point_size.is_finite() && POINT_SIZE.contains(&self.point_size)) {
return bad("its point size is out of range");
}
if self.color.is_some_and(|c| c.iter().any(|v| !(0.0..=1.0).contains(v))) {
return bad("its colour is out of range");
}
if self.height_range.is_some_and(|[lo, hi]| !(lo.is_finite() && hi.is_finite() && lo < hi && lo.abs() <= MAX_COORD && hi.abs() <= MAX_COORD))
{
return bad("its height range is not two heights, lowest first");
}
if !rigid(&self.transform) {
return bad("its placement is not a rigid move");
}
Ok(())
}
}
/// A rotation and a translation within range (no scale, shear or mirror).
pub fn rigid(m: &Mat) -> bool {
let column = |i: usize| Vec3::new(m[i][0], m[i][1], m[i][2]);
let (x, y, z) = (column(0), column(1), column(2));
m.iter().flatten().all(|v| v.is_finite())
&& m[3][..3].iter().all(|v| v.abs() <= MAX_COORD)
&& [x, y, z].iter().all(|axis| (axis.dot(*axis) - 1.0).abs() < 1e-6)
&& x.dot(y).abs() < 1e-6
&& x.dot(z).abs() < 1e-6
&& y.dot(z).abs() < 1e-6
&& x.cross(y).dot(z) > 0.0
&& [m[0][3], m[1][3], m[2][3]].iter().all(|v| v.abs() < 1e-9)
&& (m[3][3] - 1.0).abs() < 1e-9
}
/// Corners of the box around some points.
type Corners = Option<([f32; 3], [f32; 3])>;
/// x, y, z per point (single precision, little-endian when stored). Shared, so undo states and
/// copies of the design don't copy the points; the box around them is worked out once.
#[derive(Clone, Debug, Default)]
pub struct Points {
values: Arc<[f32]>,
bounds: Arc<std::sync::OnceLock<Corners>>,
}
impl Points {
pub fn new(values: Arc<[f32]>) -> Points {
Points { values, bounds: Default::default() }
}
/// x, y, z per point.
pub fn values(&self) -> &Arc<[f32]> {
&self.values
}
pub fn len(&self) -> usize {
self.values.len() / 3
}
pub fn is_empty(&self) -> bool {
self.values.len() < 3
}
pub fn get(&self, i: usize) -> Option<[f32; 3]> {
let s = self.values.get(i.checked_mul(3)?..i.checked_mul(3)?.checked_add(3)?)?;
Some([*s.first()?, *s.get(1)?, *s.get(2)?])
}
pub fn iter(&self) -> impl Iterator<Item = [f32; 3]> + '_ {
self.values.as_chunks::<3>().0.iter().copied()
}
/// The lowest and highest x, y and z, or `None` without points.
pub fn bounds(&self) -> Corners {
*self.bounds.get_or_init(|| {
let mut it = self.iter();
let first = it.next()?;
Some(it.fold((first, first), |(mut lo, mut hi), p| {
for k in 0..3 {
lo[k] = lo[k].min(p[k]);
hi[k] = hi[k].max(p[k]);
}
(lo, hi)
}))
})
}
/// The points as stored: little-endian f32, x, y, z per point.
pub fn to_bytes(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(self.values.len() * 4);
for v in self.values.iter() {
out.extend_from_slice(&v.to_le_bytes());
}
out
}
/// The points from stored bytes; `None` unless whole points of finite coordinates.
pub fn from_bytes(b: &[u8]) -> Option<Points> {
if !b.len().is_multiple_of(12) || b.len() / 12 > MAX_POINTS {
return None;
}
let v: Vec<f32> = b.as_chunks::<4>().0.iter().map(|c| f32::from_le_bytes(*c)).collect();
v.iter().all(|x| x.is_finite()).then(|| Points::new(v.into()))
}
}
/// The same points: the same shared storage, or equal values.
impl PartialEq for Points {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.values, &other.values) || self.values == other.values
}
}
impl Serialize for Points {
fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
s.serialize_str(&base64_encode(&self.to_bytes()))
}
}
impl<'de> Deserialize<'de> for Points {
fn deserialize<D: Deserializer<'de>>(d: D) -> std::result::Result<Self, D::Error> {
let s = String::deserialize(d)?;
if s.len() > (MAX_POINTS * 12).div_ceil(3) * 4 + 16 {
return Err(serde::de::Error::custom("too many points"));
}
let b = base64_decode(&s).ok_or_else(|| serde::de::Error::custom("points are not base64"))?;
Points::from_bytes(&b).ok_or_else(|| serde::de::Error::custom("points are not whole, finite x, y, z"))
}
}
/// r, g, b per point (bytes).
#[derive(Clone, Debug, Default)]
pub struct Colors(pub Arc<[u8]>);
impl Colors {
pub fn len(&self) -> usize {
self.0.len() / 3
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn get(&self, i: usize) -> Option<[u8; 3]> {
let s = self.0.get(i.checked_mul(3)?..i.checked_mul(3)?.checked_add(3)?)?;
Some([*s.first()?, *s.get(1)?, *s.get(2)?])
}
pub fn from_bytes(b: &[u8]) -> Option<Colors> {
(b.len().is_multiple_of(3) && b.len() / 3 <= MAX_POINTS).then(|| Colors(b.into()))
}
}
impl PartialEq for Colors {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.0, &other.0) || self.0 == other.0
}
}
impl Serialize for Colors {
fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
s.serialize_str(&base64_encode(&self.0))
}
}
impl<'de> Deserialize<'de> for Colors {
fn deserialize<D: Deserializer<'de>>(d: D) -> std::result::Result<Self, D::Error> {
let s = String::deserialize(d)?;
if s.len() > (MAX_POINTS * 3).div_ceil(3) * 4 + 16 {
return Err(serde::de::Error::custom("too many colours"));
}
let b = base64_decode(&s).ok_or_else(|| serde::de::Error::custom("colours are not base64"))?;
Colors::from_bytes(&b).ok_or_else(|| serde::de::Error::custom("colours are not whole r, g, b"))
}
}
/// Every cloud of a design is whole ([`PointCloud::check`]) and has its own id.
pub fn check_all(clouds: &[PointCloud]) -> Result<()> {
let mut ids = std::collections::BTreeSet::new();
for c in clouds {
c.check()?;
if !ids.insert(c.id) {
return Err(DocError::Invalid(format!("two point clouds have the id {}", c.id)));
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
fn cloud(points: Vec<f32>, colors: Vec<u8>) -> PointCloud {
PointCloud {
id: 7,
name: "Scan".into(),
file: "scan.ply".into(),
origin: Vec3::new(1000.0, 0.0, 0.0),
points: Points::new(points.into()),
colors: Colors(colors.into()),
source_points: 2,
units: "m".into(),
transform: IDENTITY,
point_size: 2.0,
color_by: ColorBy::File,
color: None,
height_range: None,
visible: true,
}
}
#[test]
fn points_round_trip_through_json_and_stay_shared() {
let c = cloud(vec![0.0, 1.0, 2.0, -3.5, 4.25, 1e6], vec![255, 0, 10, 1, 2, 3]);
let json = serde_json::to_string(&c).unwrap();
let back: PointCloud = serde_json::from_str(&json).unwrap();
assert_eq!(back, c);
assert_eq!(back.points.get(1), Some([-3.5, 4.25, 1e6]));
assert_eq!(back.colors.get(0), Some([255, 0, 10]));
assert_eq!(back.world_point(0), Some(Vec3::new(1000.0, 1.0, 2.0)));
assert_eq!(back.points.get(2), None);
let copy = c.clone();
assert!(Arc::ptr_eq(copy.points.values(), c.points.values()), "a copy shares the points");
let (lo, hi) = c.local_bounds().unwrap();
assert_eq!((lo, hi), (Vec3::new(996.5, 1.0, 2.0), Vec3::new(1000.0, 4.25, 1e6)));
check_all(&[c]).unwrap();
assert_eq!((unit_scale("M"), unit_scale("yd")), (Some(1000.0), None));
}
#[test]
fn damaged_clouds_are_refused() {
// Not whole points, not finite, not base64.
for points in ["AAAA", "AADAfwAAwH8AAMB/", "!!!!"] {
let json = serde_json::to_string(&cloud(vec![0.0; 3], Vec::new())).unwrap().replace("\"AAAAAAAAAAAAAAAA\"", &format!("\"{points}\""));
assert!(serde_json::from_str::<PointCloud>(&json).is_err(), "{points}");
}
assert!(cloud(vec![0.0; 6], vec![1, 2, 3]).check().is_err(), "one colour for two points");
let mut c = cloud(vec![0.0; 3], Vec::new());
c.point_size = 100.0;
assert!(c.check().is_err());
let mut c = cloud(vec![0.0; 3], Vec::new());
c.transform[0][0] = 2.0;
assert!(c.check().is_err(), "a scale is not a placement");
let c = cloud(vec![0.0; 3], Vec::new());
assert!(check_all(&[c.clone(), c]).is_err(), "two clouds with one id");
for range in [[5.0, 5.0], [9.0, 1.0], [f64::NAN, 1.0], [0.0, 1e12]] {
let mut c = cloud(vec![0.0; 3], Vec::new());
c.height_range = Some(range);
assert!(c.check().is_err(), "{range:?}");
}
}
#[test]
fn points_are_coloured_by_the_file_one_colour_or_their_height() {
// Heights 0..=100 mm, and a stray point far above the rest.
let mut values: Vec<f32> = (0..=100).flat_map(|z| [0.0, 0.0, z as f32]).collect();
values.extend([0.0, 0.0, 1000.0]);
let colors: Vec<u8> = (0..102).flat_map(|i| [i as u8, 7, 9]).collect();
let mut c = cloud(values, colors);
c.origin = Vec3::ZERO;
assert_eq!(c.shade()(3), [3, 7, 9], "the file's colours");
c.colors = Colors::default();
assert_eq!(c.shade()(3), PLAIN.map(byte), "none: plain");
c.color_by = ColorBy::Single;
c.color = Some([1.0, 0.5, 0.0]);
assert_eq!(c.shade()(50), [255, 128, 0]);
// The stray point doesn't stretch the automatic range.
c.color_by = ColorBy::Height;
// 102 points: the 2nd and the 99th (of 0…101) once sorted.
assert_eq!(c.auto_height_range(), Some([2.0, 99.0]));
{
let shade = c.shade();
assert_eq!((shade(0), shade(100), shade(101)), (height_ramp(0.0), height_ramp(1.0), height_ramp(1.0)));
assert_eq!(shade(50), height_ramp(48.0 / 97.0));
}
// A range of its own; moved up 100 mm, everything is above it.
c.height_range = Some([0.0, 50.0]);
assert_eq!(c.shade()(25), height_ramp(0.5));
c.transform[3][2] = 100.0;
assert_eq!(c.shade()(0), height_ramp(1.0));
assert_eq!((height_ramp(0.0), height_ramp(1.0), height_ramp(f64::NAN)), ([40, 70, 200], [230, 70, 50], [40, 70, 200]));
let back: PointCloud = serde_json::from_str(&serde_json::to_string(&c).unwrap()).unwrap();
assert_eq!(back, c);
}
}
+35
View File
@@ -309,6 +309,41 @@ fn exact_edges(body: &crate::ModelBody, plane: &Plane, edges: &[usize]) -> Vec<O
/// `edges` of `body` projected on `plane`: each exactly ([`exact_edges`]) or fitted to its
/// polyline in `mesh`.
fn projected_edges(body: &crate::ModelBody, mesh: &Mesh, plane: &Plane, edges: &[usize]) -> Vec<LinkGeom> {
let mut out = projected_edges_apart(body, mesh, plane, edges);
meet_at_shared_ends(&mut out, order::join_tolerance(mesh));
out
}
/// Where curve ends lie within `gap` of each other, moves them onto the first of them. A fitted
/// round's edges end a few 1e-5 mm short of their neighbours (#8284); the sketch only shares a
/// point between ends 1e-7 apart, and without it the outline has no closed profile.
fn meet_at_shared_ends(geom: &mut [LinkGeom], gap: f64) {
let mut seen: Vec<Vec2> = Vec::new();
let mut snap = |p: &mut Vec2| match seen.iter().find(|q| q.dist(*p) <= gap) {
Some(q) => *p = *q,
None => seen.push(*p),
};
for g in geom {
match g {
LinkGeom::Line(a, b) | LinkGeom::Arc { a, b, .. } | LinkGeom::Conic { a, b, .. } => {
snap(a);
snap(b);
}
LinkGeom::Spline { pts, periodic: false, .. } => {
if let Some(first) = pts.first_mut() {
snap(first);
}
if let Some(last) = pts.last_mut() {
snap(last);
}
}
_ => {}
}
}
}
/// Each of `edges` projected on its own ([`projected_edges`] without the ends brought together).
fn projected_edges_apart(body: &crate::ModelBody, mesh: &Mesh, plane: &Plane, edges: &[usize]) -> Vec<LinkGeom> {
let mut out = Vec::new();
for (e, exact) in edges.iter().zip(exact_edges(body, plane, edges)) {
match exact {
+73 -5
View File
@@ -9,6 +9,13 @@ use crate::{DocError, Result};
type Key = (i64, i64, i64);
/// How far apart two edge ends of `mesh` may be and still meet. A round's fitted edges end a few
/// 1e-5 mm short of their neighbours, far more than the tolerance that ranks equal areas (#8284).
pub(super) fn join_tolerance(mesh: &Mesh) -> f64 {
let diagonal = mesh.bounds().diagonal();
(diagonal * 1e-6).max(1e-9)
}
/// Boundary of the picked face as drawn: a closed surface cut into pieces (a cylinder or a
/// round's torus in halves) projects as the whole patch, since its seams are hidden.
pub(super) fn face_edges(mesh: &Mesh, face: u32, plane: &Plane) -> Result<Vec<usize>> {
@@ -53,12 +60,13 @@ fn boundary_edges(mesh: &Mesh, mut edges: Vec<usize>, faces: &BTreeSet<usize>, p
let origin = bounds.min;
let local_origin = plane.to_local(origin);
let tol = (bounds.diagonal() * 1e-8).max(1e-9);
if !tol.is_finite() {
let join = join_tolerance(mesh);
if !tol.is_finite() || !join.is_finite() {
return Err(DocError::Invalid("face projection has invalid bounds".into()));
}
let key = |p: Vec3| -> Key {
let p = p - origin;
((p.x / tol).round() as i64, (p.y / tol).round() as i64, (p.z / tol).round() as i64)
((p.x / join).round() as i64, (p.y / join).round() as i64, (p.z / join).round() as i64)
};
if edges.iter().any(|e| !mesh.edges.get(*e).is_some_and(|p| p.len() >= 2 && p.iter().all(|p| p.is_finite()))) {
return Err(DocError::Invalid("face projection has an invalid boundary edge".into()));
@@ -71,6 +79,9 @@ fn boundary_edges(mesh: &Mesh, mut edges: Vec<usize>, faces: &BTreeSet<usize>, p
let b: Aabb3 = pts.iter().copied().collect();
(key(b.min), key(b.max))
};
// A round that dies away at a corner leaves an edge from a point to itself: it bounds nothing,
// and left in, it reads as a branch where the real edges meet.
edges.retain(|e| mesh.edges.get(*e).is_some_and(|p| p.len() > 2 || p.first().zip(p.last()).is_some_and(|(a, b)| a.dist(*b) > join)));
edges.sort_by_key(|e| geometry_key(*e));
let mut at: BTreeMap<Key, Vec<usize>> = BTreeMap::new();
for e in &edges {
@@ -107,7 +118,7 @@ fn boundary_edges(mesh: &Mesh, mut edges: Vec<usize>, faces: &BTreeSet<usize>, p
*slot = true;
}
boundary.push(edge);
if current.dist(start) <= tol {
if current.dist(start) <= join {
break;
}
// Neighbouring quantization cells cover points on either side of a rounding
@@ -121,7 +132,7 @@ fn boundary_edges(mesh: &Mesh, mut edges: Vec<usize>, faces: &BTreeSet<usize>, p
if let Some(list) = at.get(&cell) {
incident.extend(list.iter().copied().filter(|e| {
mesh.edges.get(*e).is_some_and(|p| {
p.first().is_some_and(|a| a.dist(current) <= tol) || p.last().is_some_and(|b| b.dist(current) <= tol)
p.first().is_some_and(|a| a.dist(current) <= join) || p.last().is_some_and(|b| b.dist(current) <= join)
})
}));
}
@@ -148,7 +159,7 @@ fn boundary_edges(mesh: &Mesh, mut edges: Vec<usize>, faces: &BTreeSet<usize>, p
edge = *next;
}
boundary.sort_by_key(|e| geometry_key(*e));
let closed = current.dist(start) <= tol;
let closed = current.dist(start) <= join;
if !closed || !twice_area.is_finite() {
return Err(DocError::Invalid("face projection has an open or invalid boundary".into()));
}
@@ -379,6 +390,63 @@ mod tests {
}
}
/// A 100 mm square whose edges each end `gap` short of the next one's start.
fn square_with_gaps(gap: f64) -> Mesh {
let corners = [Vec3::new(0., 0., 0.), Vec3::new(100., 0., 0.), Vec3::new(100., 100., 0.), Vec3::new(0., 100., 0.)];
let mut mesh = Mesh::default();
mesh.positions.extend(corners);
for i in 0..4 {
let (a, b) = (corners[i], corners[(i + 1) % 4]);
let back = (a - b).normalized().unwrap() * gap;
mesh.edges.push(vec![a, b + back]);
mesh.edge_faces.push(vec![0]);
}
mesh
}
#[test]
fn edges_of_a_round_that_end_a_hair_short_of_their_neighbours_still_close() {
// A fitted round's edges end a few 1e-5 mm from the vertex they share (#8284).
let mesh = square_with_gaps(3e-5);
assert_eq!(face_edges(&mesh, 0, &Plane::XY).unwrap().len(), 4);
// A real gap is still an open boundary.
let open = square_with_gaps(1e-2);
assert!(face_edges(&open, 0, &Plane::XY).unwrap_err().to_string().contains("open"));
}
#[test]
fn an_edge_from_a_point_to_itself_is_not_a_branch() {
// Where a round dies away at a corner, the mesh keeps a zero-length edge there (#8284).
let mut mesh = square_with_gaps(0.);
for corner in mesh.positions.clone() {
mesh.edges.push(vec![corner, corner]);
mesh.edge_faces.push(vec![0, 1]);
}
assert_eq!(face_edges(&mesh, 0, &Plane::XY).unwrap().len(), 4);
}
#[test]
fn curve_ends_a_hair_apart_become_one_point_and_distant_ones_stay() {
use solidcraft_geom::Vec2;
use solidcraft_sketch::LinkGeom;
let mut geom = vec![
LinkGeom::Line(Vec2::new(0., 0.), Vec2::new(10., 0.)),
LinkGeom::Spline {
pts: vec![Vec2::new(10. + 3e-5, 2e-5), Vec2::new(11., 1.), Vec2::new(12., 5.)],
control: true,
degree: 3,
periodic: false,
},
LinkGeom::Line(Vec2::new(12., 5. - 1e-5), Vec2::new(0., 0.1)),
];
super::super::meet_at_shared_ends(&mut geom, 1e-4);
assert_eq!(
geom[1],
LinkGeom::Spline { pts: vec![Vec2::new(10., 0.), Vec2::new(11., 1.), Vec2::new(12., 5.)], control: true, degree: 3, periodic: false }
);
assert_eq!(geom[2], LinkGeom::Line(Vec2::new(12., 5.), Vec2::new(0., 0.1)));
}
#[test]
fn incomplete_and_branched_boundaries_are_reported_without_partial_geometry() {
let mut open = rectangles();
+8 -3
View File
@@ -47,7 +47,7 @@ pub static COMMANDS: &[CommandSpec] = &[
or joints?: [joint id or name] (their glyphs; they still constrain and move parts); \
or features?: [feature id or name] (the bodies they made; path?: [occurrence ids] for only that placement's); \
or folder?: bodies|sketches|construction|origin|joints|all (all of it; showing also shows its browser folders and groups, and `all` takes in the occurrence hides too); \
or tree_folders?: [browser folder or group key: c0/bodies, o:4/sketches, c0/g7, c0/joints, c0/canvases] (the folder or group itself: its items keep their own state); \
or tree_folders?: [browser folder or group key: c0/bodies, o:4/sketches, c0/g7, c0/joints, c0/canvases, c0/point_clouds] (the folder or group itself: its items keep their own state); \
or paths?: [[occurrence ids]] (exact placements and descendants), isolate?: bool (requires paths and visible:true). Saved with the design and undone like any other change (the camera is not)",
),
CommandSpec::new("browser.expand_all", "Expand All", expand_all).icon("folder").noundo().params(
@@ -258,8 +258,9 @@ fn tree_folder_key(k: &str) -> bool {
let Some((scope, last)) = k.rsplit_once('/') else { return false };
let scope_ok =
scope == "c0" || scope.strip_prefix("o:").is_some_and(|ids| ids.split('/').count() <= 64 && ids.split('/').all(|i| i.parse::<u64>().is_ok()));
let last_ok =
FOLDERS.contains(&last) || ["joints", "canvases"].contains(&last) || last.strip_prefix('g').is_some_and(|g| g.parse::<u64>().is_ok());
let last_ok = FOLDERS.contains(&last)
|| ["joints", "canvases", "point_clouds"].contains(&last)
|| last.strip_prefix('g').is_some_and(|g| g.parse::<u64>().is_ok());
k.len() <= 2048 && scope_ok && last_ok
}
@@ -556,6 +557,9 @@ fn collect_component_keys(s: &Session, prefix: &str, component: u64, root_extras
if !s.doc.canvases.is_empty() {
keys.open.insert("canvases".into());
}
if !s.doc.point_clouds.is_empty() {
keys.open.insert("point_clouds".into());
}
if !s.doc.assembly.joints.is_empty() {
keys.open.insert("c0/joints".into());
}
@@ -1029,6 +1033,7 @@ fn redefine_sketch(s: &mut Session, p: &Value) -> Result<Value> {
&& super::sketch::face::anchor(&plane)
.is_none_or(|next| next.target != anchor.target || next.component != anchor.component || next.path != anchor.path)
{
// design-only: Redefine Sketch Plane edits the design's sketch feature.
let sketch = trial.doc_mut().sketch_mut(id)?;
if let Some(mut visible) = visible_geometry {
let constraints: std::collections::BTreeMap<_, _> =
+5 -2
View File
@@ -96,7 +96,7 @@ fn clearance(s: &mut Session, p: &Value) -> Result<Value> {
// Mesh bodies are not measured. A mesh body named in `bodies` or `against` is left out of the
// pairs and listed as unchecked, rather than refused as "no solid body".
let named = |key: &str| -> Result<Vec<usize>> {
let names = string_list(p, key);
let names = string_list(cmd, p, key)?;
let mut out = Vec::new();
for n in &names {
let Some(i) = solid.iter().position(|b| &b.name == n) else {
@@ -259,7 +259,10 @@ fn clearance(s: &mut Session, p: &Value) -> Result<Value> {
let (mut overlaps, mut near) = (Vec::new(), Vec::new());
// The mesh bodies the call was about (every one without `bodies`, else those named in
// `bodies` or `against`) come first, so "nothing found" is not read as "no overlap".
let asked: Vec<String> = ["bodies", "against"].iter().flat_map(|k| string_list(p, k)).collect();
let mut asked: Vec<String> = Vec::new();
for k in ["bodies", "against"] {
asked.extend(string_list(cmd, p, k)?);
}
let mut unchecked: Vec<Value> = mesh_bodies
.iter()
.filter(|b| p.get("bodies").is_none_or(Value::is_null) || asked.contains(&b.name))
+3 -5
View File
@@ -8,7 +8,7 @@ use serde_json::{Value, json};
use solidcraft_doc::{FeatureKind, PlaneRef};
use super::CommandSpec;
use crate::params::{bad, string_list, vec3};
use crate::params::{bad, name_or_id_list, vec3};
use crate::{Result, Session};
pub static COMMANDS: &[CommandSpec] = &[
@@ -22,10 +22,8 @@ pub static COMMANDS: &[CommandSpec] = &[
fn copy(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "timeline.copy";
let mut keys = string_list(p, "features");
if let Some(Value::Array(a)) = p.get("features") {
keys.extend(a.iter().filter_map(Value::as_u64).map(|n| n.to_string()));
}
// The features are named or numbered, and both kinds can sit in one list.
let keys = name_or_id_list(cmd, p, "features")?;
if keys.is_empty() || keys.len() > 1000 {
return Err(bad(cmd, "`features` must list 1…1000 features"));
}
+3 -3
View File
@@ -381,7 +381,7 @@ fn move_bodies(s: &mut Session, p: &Value) -> Result<Value> {
fn copy_bodies(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "component.copy_bodies";
let to = if p.get("component").is_some() { component(s, p, "component", cmd)? } else { s.active_component };
let names = string_list(p, "bodies");
let names = string_list(cmd, p, "bodies")?;
if names.is_empty() || names.len() > 1000 {
return Err(bad(cmd, "`bodies` must list 1…1000 bodies"));
}
@@ -407,7 +407,7 @@ fn copy_bodies(s: &mut Session, p: &Value) -> Result<Value> {
// The copy is made where its body is, so it starts out in the same place.
scratch.active_component = from;
scratch.active_occurrence_path = None;
let mut made = string_list(&scratch.execute("solid.move", &json!({"bodies": bodies, "copy": true}))?, "created_bodies");
let mut made = string_list(cmd, &scratch.execute("solid.move", &json!({"bodies": bodies, "copy": true}))?, "created_bodies")?;
// Each copy is its body's name numbered as all copies are (`Cube (2)`, `Cube (2)(2)`): the
// move names a copy of `Cube (2)` `Cube (2) (2)`.
if made.len() == bodies.len() {
@@ -450,7 +450,7 @@ pub(crate) fn transfer_constructor_result(s: &mut Session, p: &Value) -> Result<
fn move_sketches(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "component.move_sketches";
let to = component(s, p, "component", cmd)?;
let keys = string_list(p, "sketches");
let keys = string_list(cmd, p, "sketches")?;
let mut ids = Vec::new();
for k in &keys {
match s.doc.find_feature(k) {
+3 -2
View File
@@ -163,8 +163,9 @@ fn read_link(file: &Path, own: Option<&str>) -> Result<Vec<u8>> {
}
// A network location only when its server is allowed, a device never: judged from the text
// before anything is read (on Windows, reading `\\host\share\x` connects to the host).
solidcraft_io::vfs::read_design_path(&shown, own, solidcraft_io::MAX_DESIGN_BYTES).map_err(|e| {
let e = e.to_string();
let design = solidcraft_io::limits::Kind::Design;
solidcraft_io::vfs::read_design_path(&shown, own, solidcraft_io::limits::max_design_bytes()).map_err(|e| {
let e = solidcraft_io::limits::explain(design, e).to_string();
EngineError::Other(if e.starts_with(&shown) { e } else { format!("{shown}: {e}") })
})
}
+1 -1
View File
@@ -34,7 +34,7 @@ fn body_names(s: &Session, p: &Value, cmd: &str, allow_boundary: bool) -> Result
if p.get("bodies").and_then(Value::as_array).is_none_or(|names| names.iter().any(|name| !name.is_string())) {
return Err(bad(cmd, "bodies must be an array of body names"));
}
let names = string_list(p, "bodies");
let names = string_list(cmd, p, "bodies")?;
if names.is_empty() || names.len() > 1000 || names.iter().collect::<BTreeSet<_>>().len() != names.len() {
return Err(bad(cmd, "bodies must list 1..1000 distinct existing definition bodies"));
}
+3 -3
View File
@@ -129,16 +129,16 @@ fn add_column(s: &mut Session, c: Column, cmd: &str) -> Result<()> {
fn configure(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "config.configure";
for n in string_list(p, "params") {
for n in string_list(cmd, p, "params")? {
let c = column_of(s, &json!({ "param": n }), cmd)?;
add_column(s, c, cmd)?;
}
for n in string_list(p, "features") {
for n in string_list(cmd, p, "features")? {
let c = column_of(s, &json!({ "feature": n }), cmd)?;
add_column(s, c, cmd)?;
}
for key in ["materials", "appearances"] {
for n in string_list(p, key) {
for n in string_list(cmd, p, key)? {
let field = if key == "materials" { "material" } else { "appearance" };
let c = column_of(s, &json!({ field: n }), cmd)?;
add_column(s, c, cmd)?;
+8 -4
View File
@@ -1,19 +1,19 @@
//! Delete for everything that can be selected, in one undo step: sketch entities (while
//! sketching), timeline features and sketches (with the features that depend on them),
//! construction planes and axes, bodies (a Remove feature from here on in the timeline), occurrences and
//! components, canvases and joints. `dry_run` reports what would go and what would fail without
//! components, canvases, point clouds and joints. `dry_run` reports what would go and what would fail without
//! changing anything, so the UI can ask first.
use serde_json::{Value, json};
use solidcraft_doc::FeatureKind;
use solidcraft_geom::Vec3;
use super::CommandSpec;
use super::{CommandSpec, sketch_store};
use crate::params::{bad, bool_};
use crate::{EngineError, Result, Sel, Session};
pub static COMMANDS: &[CommandSpec] = &[CommandSpec::new("selection.delete", "Delete", delete).icon("delete").params(
"items?: [selection items]; components?, occurrences?, canvases?, joints?: [ids or names]; dry_run?: bool → deleted, deleted_joints, would_fail, skipped (one undo step)",
"items?: [selection items]; components?, occurrences?, canvases?, point_clouds?, joints?: [ids or names]; dry_run?: bool → deleted, deleted_joints, would_fail, skipped (one undo step)",
)];
/// Run another command's body inside this one (so the whole delete is one undo step).
@@ -119,7 +119,7 @@ fn apply(s: &mut Session, p: &Value) -> Result<Value> {
// whole text, so no counters are left behind.
Sel::Profile { sketch, index } if s.active_sketch == Some(*sketch) => {
let st = s.model.state();
let (Some(ss), Ok(sk)) = (st.sketch(*sketch), s.doc.sketch(*sketch)) else {
let (Some(ss), Ok(sk)) = (st.sketch(*sketch), sketch_store::read(&s.doc, *sketch)) else {
skip("nothing around that profile can be deleted".into());
continue;
};
@@ -212,6 +212,10 @@ fn apply(s: &mut Session, p: &Value) -> Result<Value> {
sub(s, "canvas.delete", json!({ "canvas": k }))?;
done = true;
}
for k in keys(p, "point_clouds") {
sub(s, "pointcloud.delete", json!({ "cloud": k }))?;
done = true;
}
if !done {
return Err(bad(cmd, skipped.first().cloned().unwrap_or_else(|| "nothing to delete".into())));
}
+3 -5
View File
@@ -6,7 +6,7 @@ use serde_json::{Value, json};
use solidcraft_doc::{Feature, FeatureKind};
use super::CommandSpec;
use crate::params::{bad, bool_, num, str_, string_list};
use crate::params::{bad, bool_, name_or_id_list, num, str_};
use crate::{EngineError, Result, Sel, Session, Snapshot};
pub static COMMANDS: &[CommandSpec] = &[
@@ -174,10 +174,8 @@ fn feature_id(s: &Session, v: Option<&Value>, cmd: &str) -> Result<u64> {
fn delete(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "timeline.delete";
let mut keys = string_list(p, "features");
if let Some(Value::Array(a)) = p.get("features") {
keys.extend(a.iter().filter_map(Value::as_u64).map(|n| n.to_string()));
}
// The features are named or numbered, and both kinds can sit in one list.
let mut keys = name_or_id_list(cmd, p, "features")?;
if let Some(f) = p.get("feature") {
keys.push(match f {
Value::Number(n) => n.to_string(),
+64 -55
View File
@@ -10,7 +10,7 @@ use solidcraft_doc::{Dir, FacePick, Position};
use solidcraft_geom::Vec3;
use super::{CommandSpec, not_in_sketch};
use crate::params::{bad, bool_, expr, expr_problem, req_expr, str_, string_list, vec2, vec3};
use crate::params::{UNKNOWN_COMMAND, bad, bool_, expr, expr_problem, req_expr, str_, string_list, string_list_or_default, vec2, vec3};
use crate::schema::features::{
BoundingSolidParams, BoxParams, ConeParams, CylinderParams, ExtrudeParams, FeatureResult, PipeParams, ScaleParams, SphereParams, TorusParams,
};
@@ -273,7 +273,10 @@ fn auto_operation_in(s: &Session, p: &Value, world_faces: bool) -> Option<&'stat
// assembly component considers every body; otherwise keep the active scope.
// Explicit participants override either default.
// A component with no body of its own works on those of the components inside it (#3223).
let parts = string_list(p, "participants");
// The participants only pick the scope here; the feature itself refuses a list of the
// wrong type (`add_feature`), so a wrong type cannot make this cut anything. This preview
// cannot fail, so it reads the list leniently.
let parts = string_list_or_default(p, "participants");
let implicit_all = grouped.is_some() && !st.bodies.iter().any(|body| s.doc.model_body_component(body) == s.active_component);
let mut bodies: Vec<_> = if !parts.is_empty() {
st.bodies.iter().filter(|b| parts.contains(&b.name)).collect()
@@ -343,7 +346,7 @@ pub(super) fn feature_sketch(s: &Session, p: &Value, cmd: &str) -> Result<u64> {
_ => Err(bad(cmd, format!("`{key}` is not a sketch"))),
};
}
if let Some(a) = s.active_sketch {
if let Some(a) = super::sketch_store::active(s) {
return Ok(a);
}
let limit = s.doc.marker.unwrap_or(usize::MAX);
@@ -608,12 +611,15 @@ pub(crate) fn profiles(p: &Value, cmd: &str) -> Result<ProfileSel> {
for x in a {
let ids: Vec<String> = match x {
Value::Array(ids) => ids.iter().filter_map(|i| i.as_str().map(str::to_string)).collect(),
Value::Object(o) => {
if let Some(c) = o.get("curves") {
string_list(&json!({"c": c}), "c")
} else if let Some(ls) = o.get("loops").and_then(Value::as_array) {
Value::Object(_) => {
if x.get("curves").is_some() {
string_list(cmd, x, "curves")?
} else if let Some(ls) = x.get("loops").and_then(Value::as_array) {
let outer = ls.iter().find(|l| l.get("outer").and_then(Value::as_bool).unwrap_or(true)).or(ls.first());
outer.map(|l| string_list(l, "curves")).unwrap_or_default()
match outer {
Some(l) => string_list(cmd, l, "curves")?,
None => Vec::new(),
}
} else {
Vec::new()
}
@@ -716,8 +722,8 @@ fn add_feature_in(s: &mut Session, p: &Value, kind: FeatureKind, world: bool) ->
kind,
FeatureKind::Box { .. } | FeatureKind::Cylinder { .. } | FeatureKind::Sphere { .. } | FeatureKind::Torus { .. } | FeatureKind::Cone { .. }
);
let (participants, key) = match string_list(p, "participants") {
v if v.is_empty() && primitive => (string_list(p, "targets"), "targets"),
let (participants, key) = match string_list(UNKNOWN_COMMAND, p, "participants")? {
v if v.is_empty() && primitive => (string_list(UNKNOWN_COMMAND, p, "targets")?, "targets"),
v => (v, "participants"),
};
if participants.len() > 10_000 || participants.iter().any(|b| b != "*" && s.model.state().body(b).is_none()) {
@@ -757,7 +763,7 @@ fn add_feature_in(s: &mut Session, p: &Value, kind: FeatureKind, world: bool) ->
{
f.component = comp;
}
let mut names = string_list(p, "body_names");
let mut names = string_list(UNKNOWN_COMMAND, p, "body_names")?;
if let Some(b) = str_(p, "body_name") {
names.insert(0, b.to_string());
}
@@ -857,9 +863,9 @@ fn extrude(s: &mut Session, p: &Value) -> Result<Value> {
Some(o) if o.eq_ignore_ascii_case("auto") => Operation::parse(auto_operation_in(s, p, false).unwrap_or("new")).unwrap_or(Operation::NewBody),
_ => operation(p, cmd)?,
};
let mut targets = string_list(p, "targets");
let mut targets = string_list(cmd, p, "targets")?;
let mut p = std::borrow::Cow::Borrowed(p);
if operation == Operation::Join && targets.is_empty() && string_list(&p, "participants").is_empty() {
if operation == Operation::Join && targets.is_empty() && string_list(cmd, &p, "participants")?.is_empty() {
// A face's outward extension joins that body, and so does a profile sketched on a body's
// face. Assembly neighbours can touch only an edge of the tool: trying to join them
// first fails, or absorbs unrelated parts (#226). A profile drawn past the face can lie
@@ -870,11 +876,12 @@ fn extrude(s: &mut Session, p: &Value) -> Result<Value> {
Some(body) => Some(body),
None => face_body_reached(s, sketch)?,
});
} else if let Some(parts) = cut_scope(s, &p, operation, sketch, face_body)? {
} else if let Some(parts) = cut_scope(s, &p, operation, sketch, face_body, cmd)? {
p.to_mut()["participants"] = json!(parts);
}
if matches!(operation, Operation::Cut | Operation::Intersect) && targets.is_empty() && string_list(&p, "participants").is_empty() {
targets = shown_scope(s);
if matches!(operation, Operation::Cut | Operation::Intersect) && targets.is_empty() && string_list(cmd, &p, "participants")?.is_empty() {
// With every body hidden, every body, as before.
targets = shown_scope(s).unwrap_or_default();
}
let kind = FeatureKind::Extrude {
sketch: SketchId(sketch),
@@ -896,10 +903,10 @@ fn extrude(s: &mut Session, p: &Value) -> Result<Value> {
/// none and that body is in a component inside the active one (#5679): the active component's
/// own bodies, as by default, and that body. `None` when the default already reaches it: it is
/// one of the active component's, or a component with no body of its own cuts every body.
fn cut_scope(s: &Session, p: &Value, operation: Operation, sketch: u64, face_body: Option<String>) -> Result<Option<Vec<String>>> {
fn cut_scope(s: &Session, p: &Value, operation: Operation, sketch: u64, face_body: Option<String>, cmd: &str) -> Result<Option<Vec<String>>> {
if !matches!(operation, Operation::Cut | Operation::Intersect)
|| !string_list(p, "targets").is_empty()
|| !string_list(p, "participants").is_empty()
|| !string_list(cmd, p, "targets")?.is_empty()
|| !string_list(cmd, p, "participants")?.is_empty()
{
return Ok(None);
}
@@ -1020,17 +1027,6 @@ fn face_body_reached(s: &Session, sketch: u64) -> Result<Option<String>> {
Ok(Some(body.name.clone()))
}
/// A cut or intersect that names no bodies leaves hidden bodies alone, as hiding a body to keep
/// it out of a cut is the usual way to (#3961): while some of the active component's bodies
/// are hidden, its shown ones, stored as the feature's targets so showing the others again
/// later changes nothing. Empty (every body, as before) when none is hidden, or all are.
fn shown_scope(s: &Session) -> Vec<String> {
let st = s.model.state();
let scope = active_scope(s, &st);
let shown: Vec<String> = scope.iter().filter(|b| s.body_visible(&b.name)).map(|b| b.name.clone()).collect();
if shown.len() == scope.len() { Vec::new() } else { shown }
}
/// The body whose face `sketch` is attached to (on the face itself, not offset from it), when
/// it is one the active component works on ([`active_scope`]).
fn sketch_face_body(s: &Session, sketch: u64) -> Option<String> {
@@ -1045,9 +1041,9 @@ fn sketch_face_body(s: &Session, sketch: u64) -> Option<String> {
/// sketch that is on one), as for an extrude, also in a component inside the active one, moved
/// or turned (#5679; refused as [`face_body_reached`] says). Explicit `targets` or
/// `participants` keep theirs.
fn face_join_targets(s: &Session, p: &Value, operation: Operation, sketches: &[u64]) -> Result<Vec<BodyName>> {
let mut targets = string_list(p, "targets");
if operation == Operation::Join && targets.is_empty() && string_list(p, "participants").is_empty() {
fn face_join_targets(s: &Session, p: &Value, operation: Operation, sketches: &[u64], cmd: &str) -> Result<Vec<BodyName>> {
let mut targets = string_list(cmd, p, "targets")?;
if operation == Operation::Join && targets.is_empty() && string_list(cmd, p, "participants")?.is_empty() {
for sketch in sketches {
if let Some(body) = face_body_reached(s, *sketch)? {
targets.push(body);
@@ -1058,6 +1054,19 @@ fn face_join_targets(s: &Session, p: &Value, operation: Operation, sketches: &[u
Ok(targets.into_iter().map(BodyName).collect())
}
/// The bodies a Join, Cut or Intersect that names none may change, less the hidden ones, when
/// some of them are hidden: stored as the feature's `targets`, so a hidden body is left alone
/// (#3961, #6901) and showing it again later doesn't change the model. None when every one is
/// shown; empty when none is.
pub fn shown_scope(s: &Session) -> Option<Vec<String>> {
let st = s.model.state();
let scope = active_scope(s, &st);
if scope.iter().all(|b| s.body_visible(&b.name)) {
return None;
}
Some(scope.into_iter().filter(|b| s.body_visible(&b.name)).map(|b| b.name.clone()).collect())
}
/// The bodies a feature of the active component works on when it names none: the component's
/// own. A component with no body of its own (the root of an opened STEP assembly, whose parts
/// are components inside it) works on the bodies of the components inside it whose geometry is
@@ -1090,7 +1099,7 @@ fn active_scope<'a>(s: &Session, st: &'a solidcraft_doc::ModelState) -> Vec<&'a
fn thin_param(s: &Session, p: &Value, sketch: u64, cmd: &str) -> Result<ThinWall> {
let st = s.model.state();
let ss = st.sketch(sketch).ok_or_else(|| bad(cmd, "the sketch is not evaluated"))?;
let mut curves = string_list(p, "curves");
let mut curves = string_list(cmd, p, "curves")?;
if curves.is_empty() {
curves =
ss.sketch.curves.iter().filter(|c| !c.construction && !c.centerline && !c.infinite && c.link.is_none()).map(|c| c.id.clone()).collect();
@@ -1223,11 +1232,11 @@ fn revolve(s: &mut Session, p: &Value) -> Result<Value> {
Some(value) => serde_json::from_value(value.clone()).map_err(|_| bad(cmd, "`convention` must be sketch_normal or legacy_axis_start"))?,
};
let operation = operation(p, cmd)?;
let targets = face_join_targets(s, p, operation, &[sketch])?;
let targets = face_join_targets(s, p, operation, &[sketch], cmd)?;
// From a profile on a body's face in a component inside the active one, a cut changes that
// body too, as an extrude's does (#5679).
let mut p = std::borrow::Cow::Borrowed(p);
if let Some(parts) = cut_scope(s, &p, operation, sketch, None)? {
if let Some(parts) = cut_scope(s, &p, operation, sketch, None, cmd)? {
p.to_mut()["participants"] = json!(parts);
}
let kind = FeatureKind::Revolve {
@@ -1752,7 +1761,7 @@ fn chamfer(s: &mut Session, p: &Value) -> Result<Value> {
fn combine(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "solid.combine";
let target = str_(p, "target").ok_or_else(|| bad(cmd, "`target` must be a body name"))?.to_string();
let tools = string_list(p, "tools");
let tools = string_list(cmd, p, "tools")?;
if tools.is_empty() {
return Err(bad(cmd, "`tools` must list bodies"));
}
@@ -1833,7 +1842,7 @@ fn move_placements(s: &mut Session, p: &Value, plan: crate::frames::PlacementMov
fn move_body_feature(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "solid.move";
let bodies = string_list(p, "bodies");
let bodies = string_list(cmd, p, "bodies")?;
if bodies.is_empty() {
return Err(bad(cmd, "`bodies` must list bodies"));
}
@@ -1894,7 +1903,7 @@ fn copy_keeps_appearance(s: &mut Session, reply: &Value, sources: &[String], cmd
}
fn source_features(s: &Session, p: &Value, cmd: &str) -> Result<Vec<solidcraft_doc::FeatureId>> {
let names = string_list(p, "features");
let names = string_list(cmd, p, "features")?;
if names.is_empty() {
return Err(bad(cmd, "`features` must list features to copy"));
}
@@ -1952,7 +1961,7 @@ fn finish_pattern(
pattern: solidcraft_doc::PatternKind,
bodies: Vec<String>,
) -> Result<Value> {
let comps = string_list(p, "components");
let comps = string_list(UNKNOWN_COMMAND, p, "components")?;
if comps.is_empty() {
return add_feature(s, p, FeatureKind::Pattern { features, pattern, bodies: bodies.into_iter().map(BodyName).collect() });
}
@@ -1972,10 +1981,10 @@ fn finish_pattern(
/// What a pattern copies: features (replayed), bodies (`bodies: [names]`), or components
/// (`components: [names]`, placed again as occurrences).
fn pattern_sources(s: &Session, p: &Value, cmd: &str) -> Result<(Vec<solidcraft_doc::FeatureId>, Vec<String>)> {
if !string_list(p, "components").is_empty() {
if !string_list(cmd, p, "components")?.is_empty() {
return Ok((Vec::new(), Vec::new()));
}
let bodies = string_list(p, "bodies");
let bodies = string_list(cmd, p, "bodies")?;
if bodies.is_empty() {
return Ok((source_features(s, p, cmd)?, Vec::new()));
}
@@ -2042,7 +2051,7 @@ fn pattern_path(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "solid.pattern.path";
let (features, bodies) = pattern_sources(s, p, cmd)?;
let path_sketch = sketch_id(s, p.get("path_sketch").or_else(|| p.get("sketch")), cmd, "path_sketch")?;
let path = string_list(p, "path");
let path = string_list(cmd, p, "path")?;
if path.is_empty() || path.len() > 1000 {
return Err(bad(cmd, "`path` must list 1…1000 curve ids"));
}
@@ -2066,7 +2075,7 @@ fn pattern_path(s: &mut Session, p: &Value) -> Result<Value> {
fn mirror(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "solid.mirror";
let bodies = string_list(p, "bodies");
let bodies = string_list(cmd, p, "bodies")?;
let features = if bodies.is_empty() { source_features(s, p, cmd)? } else { Vec::new() };
let plane = plane_param(s, p.get("plane"), cmd)?;
let combine = bool_(p, "combine").unwrap_or(false);
@@ -2413,7 +2422,7 @@ fn hole(s: &mut Session, p: &Value) -> Result<Value> {
let sketch = feature_sketch(s, p, cmd)?;
// No points named: every sketch point (standalone points and circle centres, not
// line or arc ends, not the origin).
let ids = if p.get("points").is_some() { string_list(p, "points") } else { sketch_hole_points(s, sketch) };
let ids = if p.get("points").is_some() { string_list(cmd, p, "points")? } else { sketch_hole_points(s, sketch) };
if ids.is_empty() || ids.len() > 10_000 {
return Err(bad(cmd, "`points` must list 1…10000 sketch point ids"));
}
@@ -2568,7 +2577,7 @@ fn hole(s: &mut Session, p: &Value) -> Result<Value> {
None => None,
};
let mut p = p.clone();
let targets = string_list(&p, "targets");
let targets = string_list(cmd, &p, "targets")?;
if p.get("participants").is_none() {
// The bodies to drill, as `targets` (like an extrude's): its participants.
if !targets.is_empty() {
@@ -2754,14 +2763,14 @@ fn sweep(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "solid.sweep";
let sketch = feature_sketch(s, p, cmd)?;
let path_sketch = sketch_id(s, p.get("path_sketch"), cmd, "path_sketch")?;
let path = string_list(p, "path");
let path = string_list(cmd, p, "path")?;
if path.is_empty() {
return Err(bad(cmd, "`path` must list curve ids"));
}
let (orientation, twist, scale) = sweep_options(s, p, cmd)?;
let (rails, rail_fit) = sweep_rails(s, p, cmd, path_sketch)?;
let operation = operation(p, cmd)?;
let targets = face_join_targets(s, p, operation, &[sketch])?;
let targets = face_join_targets(s, p, operation, &[sketch], cmd)?;
add_feature(
s,
p,
@@ -2863,7 +2872,7 @@ pub(super) fn guide_rails(s: &Session, p: &Value, cmd: &str, what: &str, default
(None, None) => return Err(bad(cmd, "each rail needs the `sketch` its curves are in")),
(Some(_), _) => sketch_id(s, rail.get("sketch"), cmd, "rails[].sketch")?,
};
let curves = string_list(rail, "curves");
let curves = string_list(cmd, rail, "curves")?;
if curves.is_empty() || curves.len() > 1000 {
return Err(bad(cmd, "each rail's `curves` must list 1…1000 curve ids in order"));
}
@@ -2899,8 +2908,8 @@ fn loft(s: &mut Session, p: &Value) -> Result<Value> {
let (rails, rail_fit) = guide_rails(s, p, cmd, "loft", None)?;
let operation = operation(p, cmd)?;
let sketches: Vec<u64> = sections.iter().map(|sec| sec.sketch.0).collect();
let mut targets = face_join_targets(s, p, operation, &sketches)?;
if operation == Operation::Join && string_list(p, "targets").is_empty() && string_list(p, "participants").is_empty() {
let mut targets = face_join_targets(s, p, operation, &sketches, cmd)?;
if operation == Operation::Join && string_list(cmd, p, "targets")?.is_empty() && string_list(cmd, p, "participants")?.is_empty() {
let st = s.model.state();
let candidates: Vec<_> = st
.bodies
@@ -2960,7 +2969,7 @@ fn thread(s: &mut Session, p: &Value) -> Result<Value> {
fn pipe(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "solid.pipe";
let path_sketch = sketch_id(s, p.get("path_sketch"), cmd, "path_sketch")?;
let path = string_list(p, "path");
let path = string_list(cmd, p, "path")?;
if path.is_empty() {
return Err(bad(cmd, "`path` must list curve ids"));
}
@@ -2979,7 +2988,7 @@ fn pipe(s: &mut Session, p: &Value) -> Result<Value> {
diameter,
wall,
operation: operation(p, cmd)?,
targets: string_list(p, "targets").into_iter().map(BodyName).collect(),
targets: string_list(cmd, p, "targets")?.into_iter().map(BodyName).collect(),
},
)
}
@@ -2988,12 +2997,12 @@ fn bounding_solid(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "solid.bounding_solid";
let margin = expr(p, "margin").unwrap_or_else(|| "0".into());
check_expr(s, &margin, Kind::Length, cmd, "margin")?;
add_feature(s, p, FeatureKind::BoundingSolid { bodies: string_list(p, "bodies").into_iter().map(BodyName).collect(), margin })
add_feature(s, p, FeatureKind::BoundingSolid { bodies: string_list(cmd, p, "bodies")?.into_iter().map(BodyName).collect(), margin })
}
fn scale(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "solid.scale";
let bodies = string_list(p, "bodies");
let bodies = string_list(cmd, p, "bodies")?;
if bodies.is_empty() {
return Err(bad(cmd, "`bodies` must list bodies"));
}
+6 -6
View File
@@ -128,13 +128,13 @@ fn coil(s: &mut Session, p: &Value) -> Result<Value> {
start_angle,
clockwise,
operation,
targets: string_list(p, "targets").into_iter().map(BodyName).collect(),
targets: string_list(cmd, p, "targets")?.into_iter().map(BodyName).collect(),
},
)
}
fn curves_param(p: &Value, cmd: &str) -> Result<Vec<String>> {
let c = string_list(p, "curves");
let c = string_list(cmd, p, "curves")?;
if c.is_empty() || c.len() > 1000 {
return Err(bad(cmd, "`curves` must list 1…1000 sketch curve ids"));
}
@@ -185,7 +185,7 @@ fn emboss(s: &mut Session, p: &Value) -> Result<Value> {
Some("deboss") => true,
Some(m) => return Err(bad(cmd, format!("unknown mode `{m}` (emboss or deboss)"))),
};
let mut targets = string_list(p, "targets");
let mut targets = string_list(cmd, p, "targets")?;
let face = if let Some(value) = p.get("face") {
let local = vec3(value).ok_or_else(|| bad(cmd, "`face` must be a finite point [x,y,z]"))?;
let at = solidcraft_doc::apply_point(&s.active_component_frame(), local);
@@ -400,7 +400,7 @@ fn replace_face(s: &mut Session, p: &Value) -> Result<Value> {
fn align(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "solid.align";
let bodies = string_list(p, "bodies");
let bodies = string_list(cmd, p, "bodies")?;
if bodies.is_empty() {
return Err(bad(cmd, "`bodies` must list the bodies to move"));
}
@@ -489,7 +489,7 @@ fn align(s: &mut Session, p: &Value) -> Result<Value> {
fn remove(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "solid.remove";
let bodies = string_list(p, "bodies");
let bodies = string_list(cmd, p, "bodies")?;
if bodies.is_empty() {
return Err(bad(cmd, "`bodies` must list bodies"));
}
@@ -541,7 +541,7 @@ pub(super) fn snap_point(s: &Session, kind: &str, at: Vec3) -> Option<Vec3> {
fn boundary_fill(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "solid.boundary_fill";
let tools = string_list(p, "tools");
let tools = string_list(cmd, p, "tools")?;
let st = s.model.state();
if tools.is_empty() || tools.len() > 64 || tools.iter().any(|t| st.body(t).is_none()) {
return Err(bad(cmd, "`tools` must list 1…64 bodies"));
+31 -12
View File
@@ -27,7 +27,7 @@ pub static COMMANDS: &[CommandSpec] = &[
.icon("open")
.key("Ctrl+O")
.noundo()
.params("path: .sc design (or a .solidcraft design from an earlier release), .dxf/.svg drawing (opens as a new, unsaved design with an XY sketch, using sketch.insert_dxf/sketch.insert_svg units and defaults), or a .step/.stp/.igs/.fcstd/.3dm/.3mf/.stl/.obj/.glb/.gltf file (STEP, IGES, FreeCAD, Rhino, 3MF, STL, OBJ or glTF; opens as a new design holding the file as a component named after it, as file.insert_step and file.insert_mesh make); up?: z|y for a STEP, IGES, FreeCAD, Rhino or glTF file (see file.insert_step and file.insert_mesh); names?: auto|solid|product|instance for a STEP, IGES, FreeCAD or Rhino file (see file.insert_step); read_only?: bool (a design: saving to its own file is refused, file.save_as keeps the changes elsewhere) → open_elsewhere: {host, user, app, opened} when another SolidCraft has the design open (its lock file is fresh)"),
.params("path: .sc design (or a .solidcraft design from an earlier release), .dxf/.svg drawing (opens as a new, unsaved design with an XY sketch, using sketch.insert_dxf/sketch.insert_svg units and defaults), or a .step/.stp/.igs/.fcstd/.3dm/.3mf/.stl/.obj/.glb/.gltf file (STEP, IGES, FreeCAD, Rhino, 3MF, STL, OBJ or glTF; opens as a new design holding the file as a component named after it, as file.insert_step and file.insert_mesh make), or a .ply/.xyz/.pts point cloud (opens as a new design holding it, as pointcloud.insert makes; its `units` and `max_points` as there); up?: z|y for a STEP, IGES, FreeCAD, Rhino or glTF file (see file.insert_step and file.insert_mesh); names?: auto|solid|product|instance for a STEP, IGES, FreeCAD or Rhino file (see file.insert_step); read_only?: bool (a design: saving to its own file is refused, file.save_as keeps the changes elsewhere) → open_elsewhere: {host, user, app, opened} when another SolidCraft has the design open (its lock file is fresh)"),
CommandSpec::new("file.insert_step", "Insert STEP", insert_step)
.at("SOLID", "INSERT")
.icon("import")
@@ -35,7 +35,7 @@ pub static COMMANDS: &[CommandSpec] = &[
CommandSpec::new("file.insert_mesh", "Insert Mesh", insert_mesh)
.at("SOLID", "INSERT")
.icon("import")
.params("path: .3mf, .stl, .obj, .glb or .gltf file (its meshes join the design as mesh bodies in a new component named after the file → component, component_name, occurrence; a glTF file's node tree becomes nested components held as one rigid group until component.unlock, a mesh placed again another occurrence, its materials the bodies' appearances, and its metres millimetres); name?; up?: z|y for a glTF file (default y: glTF has Y up, so the import's component is turned a quarter turn about X, Y becoming Z → stand_up: its occurrence; z: the file's axes are kept)"),
.params("path: .3mf, .stl, .obj, .ply (with faces), .glb or .gltf file (its meshes join the design as mesh bodies in a new component named after the file → component, component_name, occurrence; a glTF file's node tree becomes nested components held as one rigid group until component.unlock, a mesh placed again another occurrence, its materials the bodies' appearances, and its metres millimetres); name?; up?: z|y for a glTF file (default y: glTF has Y up, so the import's component is turned a quarter turn about X, Y becoming Z → stand_up: its occurrence; z: the file's axes are kept)"),
CommandSpec::new("file.save", "Save", save)
.icon("save")
.key("Ctrl+S")
@@ -117,7 +117,7 @@ fn step_options(p: &Value, cmd: &str) -> Result<StepOptions> {
/// Read a STEP file as an Import feature.
fn read_step(path: &str, names: solidcraft_io::StepNames) -> Result<solidcraft_io::StepFeature> {
let bytes = solidcraft_io::vfs::read(path, solidcraft_io::MAX_STEP_BYTES).map_err(|e| EngineError::Other(format!("{path}: {e}")))?;
let bytes = solidcraft_io::limits::read(path, solidcraft_io::limits::Kind::Step).map_err(|e| EngineError::Other(format!("{path}: {e}")))?;
solidcraft_io::step_import_feature_named(&bytes, path, names).map_err(|e| EngineError::Other(format!("{path}: {e}")))
}
@@ -240,8 +240,8 @@ fn mesh_up(p: &Value, path: &str, cmd: &str) -> Result<Option<bool>> {
fn insert_mesh(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "file.insert_mesh";
let path = path_arg(p, cmd)?;
if !solidcraft_io::is_mesh_path(path) {
return Err(bad(cmd, "only 3MF, STL, OBJ and glTF files (.3mf, .stl, .obj, .glb, .gltf) can be inserted as meshes"));
if !solidcraft_io::is_mesh_path(path) && !solidcraft_io::is_ply_path(path) {
return Err(bad(cmd, "only 3MF, STL, OBJ, PLY and glTF files (.3mf, .stl, .obj, .ply, .glb, .gltf) can be inserted as meshes"));
}
let up = mesh_up(p, path, cmd)?;
add_mesh(s, path, str_(p, "name"), up)
@@ -290,6 +290,24 @@ fn open(s: &mut Session, p: &Value) -> Result<Value> {
out["errors"] = json!(s.model.results.iter().filter(|r| r.error.is_some()).count());
return Ok(out);
}
if solidcraft_io::is_point_cloud_path(path) {
// A new, unsaved design holding the points.
let stem = std::path::Path::new(path).file_stem().map(|x| x.to_string_lossy().to_string()).unwrap_or_else(|| "Untitled".into());
let mut fresh = Session::new(Document::new(&stem));
let mut q = serde_json::Map::new();
q.insert("path".into(), json!(path));
for k in ["units", "max_points"] {
if let Some(v) = p.get(k) {
q.insert(k.into(), v.clone());
}
}
let r = fresh.execute("pointcloud.insert", &Value::Object(q))?;
fresh.undo.clear();
s.replace_design(fresh);
return Ok(
json!({"path": path, "features": 0, "errors": 0, "cloud": r["cloud"], "points": r["points"], "source_points": r["source_points"], "bounds": r["bounds"], "warnings": r["warnings"]}),
);
}
if solidcraft_io::is_step_path(path) || solidcraft_io::is_mesh_path(path) {
// A new, unsaved design holding the file's bodies.
let stem = std::path::Path::new(path).file_stem().map(|x| x.to_string_lossy().to_string()).unwrap_or_else(|| "Untitled".into());
@@ -624,13 +642,14 @@ fn export_to(s: &Session, path: &str, format: Format, bodies: &[String], quality
}
fn export(s: &mut Session, p: &Value) -> Result<Value> {
let path = path_arg(p, "file.export")?;
let cmd = "file.export";
let path = path_arg(p, cmd)?;
let format = Format::from_name(str_(p, "format").unwrap_or(path))?;
if format == Format::Urdf {
return export_urdf(s, path, p);
}
let quality = mesh_quality(p, "file.export")?;
export_to(s, path, format, &string_list(p, "bodies"), quality)
let quality = mesh_quality(p, cmd)?;
export_to(s, path, format, &string_list(cmd, p, "bodies")?, quality)
}
/// The OBJ export: the mesh file and the material library beside it, with the colours of the
@@ -663,7 +682,7 @@ fn export_obj(s: &Session, path: &str, bodies: &[String], quality: solidcraft_io
/// anything is written; the meshes go first and the `.urdf` last.
fn export_urdf(s: &Session, path: &str, p: &Value) -> Result<Value> {
let cmd = "file.export";
if !string_list(p, "bodies").is_empty() {
if !string_list(cmd, p, "bodies")?.is_empty() {
return Err(bad(cmd, "urdf exports the whole assembly; omit bodies"));
}
let positive = |k: &str| match p.get(k) {
@@ -749,7 +768,7 @@ fn save_mesh(s: &mut Session, p: &Value) -> Result<Value> {
cmd,
kind: crate::slicers::TargetKind::Slicer,
spec,
bodies: string_list(p, "bodies"),
bodies: string_list(cmd, p, "bodies")?,
format,
quality,
dest: send::Dest::Path(path),
@@ -758,7 +777,7 @@ fn save_mesh(s: &mut Session, p: &Value) -> Result<Value> {
},
);
}
export_to(s, path, format, &string_list(p, "bodies"), quality)
export_to(s, path, format, &string_list(cmd, p, "bodies")?, quality)
}
/// The browser build can't start a program.
@@ -791,7 +810,7 @@ fn send_to_slicer(s: &mut Session, p: &Value) -> Result<Value> {
cmd,
kind: crate::slicers::TargetKind::Slicer,
spec,
bodies: string_list(p, "bodies"),
bodies: string_list(cmd, p, "bodies")?,
format,
quality: Default::default(),
dest: send::Dest::Folder,
+1 -1
View File
@@ -56,7 +56,7 @@ pub(super) fn open_in_easyprint(s: &mut Session, p: &Value) -> Result<Value> {
if !bool_(p, "consent").unwrap_or(false) {
return Err(bad(cmd, "this uploads the model to Printables' servers: ask the user, then pass `consent: true`"));
}
let Exported { bytes, ext, fell_back, .. } = export_bodies(s, cmd, &string_list(p, "bodies"), format)?;
let Exported { bytes, ext, fell_back, .. } = export_bodies(s, cmd, &string_list(cmd, p, "bodies")?, format)?;
if bytes.len() > MAX_BYTES {
let mb = bytes.len().div_ceil(1_000_000);
return Err(bad(cmd, format!("the model is {mb} MB and EasyPrint takes {} MB at most: send fewer bodies", MAX_BYTES / 1_000_000)));
+1 -1
View File
@@ -188,7 +188,7 @@ fn member(s: &mut Session, p: &Value) -> Result<Value> {
let lines = match p.get("lines") {
None | Some(Value::Null) => Vec::new(),
Some(Value::Array(a)) if !a.is_empty() && a.len() <= frame::MAX_MEMBERS && a.iter().all(Value::is_string) => {
let lines = string_list(p, "lines");
let lines = string_list(cmd, p, "lines")?;
let mut seen = std::collections::HashSet::new();
if let Some(twice) = lines.iter().find(|l| !seen.insert(l.as_str())) {
return Err(bad(cmd, format!("line `{twice}` is listed twice")));
+7 -11
View File
@@ -107,10 +107,11 @@ pub fn measure_json(b: &solidcraft_doc::ModelBody) -> Value {
}
fn measure(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "inspect.measure";
if let Some(items) = p.get("items") {
return super::measure_sel::measure_items(s, items);
}
let want = string_list(p, "bodies");
let want = string_list(cmd, p, "bodies")?;
let st = s.world_state();
let selected: Vec<_> = st.bodies.iter().filter(|b| want.is_empty() || want.contains(&b.name)).collect();
// Each body is measured on its own: on all cores, in body order.
@@ -131,7 +132,7 @@ fn measure(s: &mut Session, p: &Value) -> Result<Value> {
}
}
if !want.is_empty() && bodies.len() != want.len() {
return Err(bad("inspect.measure", "unknown body name"));
return Err(bad(cmd, "unknown body name"));
}
// Sheet metal keeps the edges where bends meet flat faces (so bends stay visible): count its
// B-rep faces as they are, without merging coplanar neighbours.
@@ -251,12 +252,7 @@ fn sketch_json(s: &Session, id: u64) -> Option<Value> {
}
fn sketch_inspect(s: &mut Session, p: &Value) -> Result<Value> {
let id = match p.get("sketch") {
Some(Value::Number(n)) => s.doc.find_feature(&n.to_string()).map(|f| f.id),
Some(Value::String(x)) => s.doc.find_feature(x).map(|f| f.id),
_ => s.active_sketch,
}
.ok_or_else(|| bad("sketch.inspect", "no such sketch (and none is active)"))?;
let id = super::sketch_store::target(s, p, "sketch.inspect")?;
sketch_json(s, id).ok_or_else(|| EngineError::Other("that sketch is not evaluated (rolled back or not a sketch)".into()))
}
@@ -450,7 +446,7 @@ fn model_threads(s: &mut Session, _p: &Value) -> Result<Value> {
fn physical_material(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "material.assign";
let bodies = string_list(p, "bodies");
let bodies = string_list(cmd, p, "bodies")?;
let material = str_(p, "material").ok_or_else(|| bad(cmd, "`material` must be a material name"))?;
let canonical = s.doc.material_name(material).ok_or_else(|| bad(cmd, format!("unknown material `{material}` (see material.list)")))?;
let st = s.model.state();
@@ -486,7 +482,7 @@ pub(super) fn appearance(s: &mut Session, p: &Value) -> Result<Value> {
use solidcraft_doc::appearance::FaceLook;
let cmd = "appearance.assign";
let look = appearance_look(cmd, p)?;
let bodies = string_list(p, "bodies");
let bodies = string_list(cmd, p, "bodies")?;
let st = s.model.state();
if bodies.iter().any(|b| st.body(b).is_none()) {
return Err(bad(cmd, "`bodies` must list existing bodies"));
@@ -508,7 +504,7 @@ pub(super) fn appearance(s: &mut Session, p: &Value) -> Result<Value> {
}
}
let mut comps: Vec<u64> = Vec::new();
for c in string_list(p, "components") {
for c in string_list(cmd, p, "components")? {
let id = s
.doc
.find_component(&c)
+1 -1
View File
@@ -73,7 +73,7 @@ pub(super) fn interference(s: &mut Session, p: &Value) -> Result<Value> {
},
};
let budget = std::time::Duration::from_secs_f64(budget_s);
let want = string_list(p, "bodies");
let want = string_list(cmd, p, "bodies")?;
let st = s.world_state();
let (meshes_left, bodies): (Vec<&solidcraft_doc::ModelBody>, Vec<&solidcraft_doc::ModelBody>) =
st.bodies.iter().filter(|b| want.is_empty() || want.contains(&b.name)).partition(|b| b.body.is_mesh());
+125
View File
@@ -0,0 +1,125 @@
//! What a feature's material does when it is joined: the test the feature windows use to choose
//! Join, New Body or Cut by themselves (#6901). It runs the Join itself, so it agrees with the
//! result OK makes, and with the warning a Join apart gets (#4434).
use serde_json::Value;
use crate::Session;
/// The most commands a feature window previews at once.
const MAX_COMMANDS: usize = 64;
/// What a feature's material makes, joined to the bodies it may join.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Addition {
/// It would be a separate piece (or there is no body to join): a New Body.
Apart,
/// It makes one body with what it touches: a Join.
Joins,
/// It adds no material: it lies inside a body (a Cut, where the window offers one).
Inside,
}
/// What `cmds` (a feature window's commands, with operation New Body or Join) make when run with
/// Join (see [`crate::Ahead::addition`]).
pub fn addition(s: &Session, cmds: &[(String, Value)], inside: bool) -> Option<Addition> {
s.ahead().addition(cmds, inside).0
}
impl crate::Ahead {
/// What `cmds` (a feature window's commands, with operation New Body or Join) make when run
/// with Join, and, when they join, the Join evaluated (OK takes it rather than computing it
/// again, [`crate::Session::prime`]). `inside`: tell material lying inside a body
/// ([`Addition::Inside`]) from material that joins. None when they have no such operation
/// (Combine, a Cut chosen by hand, a feature without one), the model has no body, or the Join
/// fails.
///
/// Material that touches no body can't be fused to one: that is found from the material alone
/// and a contact test, without the Join (a boolean that takes seconds on a large body).
pub fn addition(self, cmds: &[(String, Value)], inside: bool) -> (Option<Addition>, Option<crate::Evaluated>) {
let s = self.0;
let before = s.model.state();
if cmds.is_empty() || cmds.len() > MAX_COMMANDS || before.bodies.is_empty() {
return (None, None);
}
let (Some(alone), Some(joined)) = (with_operation(cmds, "new"), with_operation(cmds, "join")) else { return (None, None) };
let Ok(tools) = s.preview(&alone) else { return (None, None) };
let tools: Vec<&solidcraft_doc::ModelBody> = tools.after.bodies.iter().filter(|b| tools.before.body(&b.name).is_none()).collect();
// Every body the Join could take part in (its scope is narrower): none touched, none joined.
let named: Vec<String> = joined
.iter()
.flat_map(|(_, p)| {
let p = p.get("params").filter(|_| p.get("command").is_some()).unwrap_or(p);
crate::params::string_list_or_default(p, "targets").into_iter().chain(crate::params::string_list_or_default(p, "participants"))
})
.collect();
let candidates: Vec<&solidcraft_doc::ModelBody> = before
.bodies
.iter()
.filter(|b| named.is_empty() || named.iter().any(|n| n == "*" || *n == b.name))
.filter(|b| !b.body.is_mesh())
.collect();
if !tools.is_empty() && !tools.iter().any(|t| candidates.iter().any(|b| t.body.meets(&b.body).unwrap_or(true))) {
return (Some(Addition::Apart), None);
}
let Ok((after, _)) = s.preview_in_place(&joined, None) else { return (None, None) };
let state = after.model.state();
// A Join with nothing to join makes a new body.
if state.bodies.iter().any(|b| before.body(&b.name).is_none()) || !solidcraft_doc::join_pieces_added(&before, &state).is_empty() {
return (Some(Addition::Apart), None);
}
if inside && adds_nothing(&before, &state, &tools) {
return (Some(Addition::Inside), None);
}
after.world_state().warm_display();
(Some(Addition::Joins), Some(crate::Evaluated(after.model)))
}
}
/// Whether the Join from `before` to `after` added (next to) no volume: its material lies inside
/// the bodies. Display meshes are close enough for that, against the material's own volume;
/// measuring a large body exactly takes seconds.
fn adds_nothing(before: &solidcraft_doc::ModelState, after: &solidcraft_doc::ModelState, tools: &[&solidcraft_doc::ModelBody]) -> bool {
let (mut had, mut have) = (0.0, 0.0);
for was in &before.bodies {
match after.body(&was.name) {
Some(now) if now.body.same_shape(&was.body) => {}
Some(now) => {
had += mesh_volume(&was.mesh());
have += mesh_volume(&now.mesh());
}
None => had += mesh_volume(&was.mesh()),
}
}
let material: f64 = tools.iter().map(|t| mesh_volume(&t.mesh())).sum();
material > 0.0 && (have - had) < material * 0.01
}
/// The volume a closed mesh encloses.
fn mesh_volume(m: &solidcraft_geom::Mesh) -> f64 {
m.triangles.iter().filter_map(|t| m.tri(t)).map(|[a, b, c]| a.dot(b.cross(c)) / 6.0).sum::<f64>().abs()
}
/// `cmds` with their New Body or Join operation set to `op`; None when none has one.
fn with_operation(cmds: &[(String, Value)], op: &str) -> Option<Vec<(String, Value)>> {
let mut out = cmds.to_vec();
let mut any = false;
for (command, p) in &mut out {
let inner = p.get("command").and_then(Value::as_str).unwrap_or(command.as_str());
if inner == "solid.combine" {
return None;
}
let fields = if p.get("command").is_some() { p.get_mut("params")? } else { p };
if let Some(o) = fields.get_mut("operation")
&& o.as_str().is_some_and(|o| o == "new" || o == "join")
{
*o = Value::from(op);
any = true;
}
}
any.then_some(out)
}
#[cfg(test)]
#[path = "join_pieces_tests.rs"]
mod tests;
+176
View File
@@ -0,0 +1,176 @@
//! The Join/New Body test agrees with what the Join makes: one body, or a body in pieces.
use serde_json::{Value, json};
use super::{Addition, addition};
use crate::Session;
fn run(s: &mut Session, id: &str, params: Value) {
if let Err(e) = s.execute(id, &params) {
panic!("{id} {params}: {e}");
}
}
/// Sketch `circles` (centre, radius) and `rects` (corners) on a plane at height `z`.
fn sketch(s: &mut Session, z: f64, name: &str, circles: &[([f64; 2], f64)], rects: &[([f64; 2], [f64; 2])]) {
run(s, "sketch.create", json!({"plane": {"origin": [0, 0, z], "x_dir": [1, 0, 0], "y_dir": [0, 1, 0]}, "name": name}));
for (c, r) in circles {
run(s, "sketch.circle.center", json!({"center": c, "radius": r}));
}
for (p0, p1) in rects {
run(s, "sketch.rectangle.two_point", json!({"p0": p0, "p1": p1}));
}
run(s, "sketch.finish", json!({}));
}
fn extrude(sketch: &str, point: [f64; 2], distance: f64) -> Vec<(String, Value)> {
vec![("solid.extrude".to_string(), json!({"sketch": sketch, "profiles": [{"point": point}], "distance": distance, "operation": "new"}))]
}
/// The pieces of every body after running `cmds` as a Join for real.
fn pieces_after_join(s: &Session, cmds: &[(String, Value)]) -> Vec<usize> {
let mut t = s.scratch();
for (id, p) in cmds {
let mut p = p.clone();
p["operation"] = json!("join");
run(&mut t, id, p);
}
t.model.state().bodies.iter().map(|b| b.body.lumps().map_or(1, |l| l.len())).collect()
}
/// The verdict (does the material join?), checked against the Join's own result: one body in one
/// piece, or not.
fn verdict(label: &str, s: &Session, cmds: &[(String, Value)]) -> bool {
let one = addition(s, cmds, false).unwrap_or_else(|| panic!("{label}: no verdict")) != Addition::Apart;
let before: Vec<usize> = s.model.state().bodies.iter().map(|b| b.body.lumps().map_or(1, |l| l.len())).collect();
let after = pieces_after_join(s, cmds);
// Joined: no new body, and no body in more pieces than before (a bridge leaves fewer).
let joined = after.len() == before.len() && after.iter().zip(&before).all(|(a, b)| a <= b);
assert_eq!(one, joined, "{label}: verdict {one}, pieces {before:?} -> {after:?}");
one
}
/// A 40 × 40 × 10 block centred on the origin with a 5 mm radius hole through it.
fn holed_block() -> Session {
let mut s = Session::default();
run(&mut s, "solid.box", json!({"length": 40, "width": 40, "height": 10, "corner": [-20, -20, 0], "body_name": "Base"}));
sketch(&mut s, 0.0, "Hole", &[([0.0, 0.0], 5.0)], &[]);
run(&mut s, "solid.extrude", json!({"sketch": "Hole", "profiles": [{"point": [0, 0]}], "distance": 10, "operation": "cut"}));
s
}
fn cube() -> Session {
let mut s = Session::default();
run(&mut s, "solid.box", json!({"length": 10, "width": 10, "height": 10, "body_name": "Base"}));
s
}
#[test]
fn material_on_a_body_away_from_its_centre_joins() {
// A ring round a hole: its centre is over the hole, its material on the face.
let mut s = holed_block();
sketch(&mut s, 10.0, "Ring", &[([0.0, 0.0], 5.0), ([0.0, 0.0], 10.0)], &[]);
assert!(verdict("ring round a hole", &s, &extrude("Ring", [7.5, 0.0], 5.0)));
// A pin filling the bore: it meets the body only on the bore's round wall.
sketch(&mut s, 0.0, "Pin", &[([0.0, 0.0], 5.0)], &[]);
assert!(verdict("pin filling the bore", &s, &extrude("Pin", [0.0, 0.0], 10.0)));
// A block against a side wall, all of it or half of it, and one standing past an edge.
let mut s = cube();
sketch(&mut s, 0.0, "Side", &[], &[([10.0, 0.0], [20.0, 10.0]), ([10.0, 15.0], [20.0, 25.0])]);
assert!(verdict("block against a side wall", &s, &extrude("Side", [15.0, 5.0], 10.0)));
sketch(&mut s, 0.0, "Half", &[], &[([10.0, 5.0], [20.0, 15.0])]);
assert!(verdict("block half against a side wall", &s, &extrude("Half", [15.0, 10.0], 5.0)));
sketch(&mut s, 10.0, "Ledge", &[], &[([8.0, 0.0], [30.0, 10.0])]);
assert!(verdict("block past an edge", &s, &extrude("Ledge", [20.0, 5.0], 5.0)));
}
#[test]
fn material_apart_stays_a_new_body() {
let mut s = holed_block();
// A pin in the bore with room round it: inside the block's box, touching nothing.
sketch(&mut s, 0.0, "Thin", &[([0.0, 0.0], 4.0)], &[]);
assert!(!verdict("pin clear in the bore", &s, &extrude("Thin", [0.0, 0.0], 10.0)));
let mut s = cube();
sketch(&mut s, 0.0, "Far", &[], &[([30.0, 0.0], [40.0, 10.0])]);
assert!(!verdict("block away from the cube", &s, &extrude("Far", [35.0, 5.0], 10.0)));
sketch(&mut s, 0.0, "Gap", &[], &[([10.1, 0.0], [20.0, 10.0])]);
assert!(!verdict("block 0.1 mm off a side wall", &s, &extrude("Gap", [15.0, 5.0], 10.0)));
}
#[test]
fn hairline_gaps_follow_the_join() {
// Material closer than the contact test's tolerance: whatever the Join makes of it, the
// verdict says the same (`verdict` checks).
let mut s = cube();
for (i, gap) in [0.001, 0.01].into_iter().enumerate() {
let name = format!("Gap{i}");
sketch(&mut s, 0.0, &name, &[], &[([10.0 + gap, 0.0], [20.0, 10.0])]);
verdict(&format!("block {gap} mm off a side wall"), &s, &extrude(&name, [15.0, 5.0], 10.0));
}
let mut s = holed_block();
sketch(&mut s, 0.0, "Close", &[([0.0, 0.0], 4.99)], &[]);
verdict("pin 0.01 mm clear in the bore", &s, &extrude("Close", [0.0, 0.0], 10.0));
}
#[test]
fn a_bridge_between_pieces_joins_them() {
// Two pieces of one body (a Join apart, kept with its warning) and a block across them.
let mut s = cube();
run(&mut s, "solid.box", json!({"length": 10, "width": 10, "height": 10, "corner": [20, 0, 0], "operation": "join"}));
assert_eq!(s.model.state().bodies.len(), 1);
sketch(&mut s, 10.0, "Bridge", &[], &[([5.0, 0.0], [25.0, 10.0])]);
assert!(verdict("bridge", &s, &extrude("Bridge", [15.0, 5.0], 5.0)));
}
#[test]
fn material_inside_a_body_adds_nothing() {
let s = cube();
let box_at = |corner: [f64; 3], size: f64| {
vec![("solid.box".to_string(), json!({"length": size, "width": size, "height": size, "corner": corner, "operation": "new"}))]
};
assert_eq!(addition(&s, &box_at([2.0, 2.0, 2.0], 4.0), true), Some(Addition::Inside), "a box inside the cube");
assert_eq!(addition(&s, &box_at([2.0, 2.0, 6.0], 4.0), true), Some(Addition::Inside), "a box sunk flush with the top");
assert_eq!(addition(&s, &box_at([2.0, 2.0, 8.0], 4.0), true), Some(Addition::Joins), "a box standing out of the top");
assert_eq!(addition(&s, &box_at([2.0, 2.0, 10.0], 4.0), true), Some(Addition::Joins), "a box on the top");
assert_eq!(addition(&s, &box_at([20.0, 2.0, 2.0], 4.0), true), Some(Addition::Apart), "a box apart");
// A revolve whose volume sits inside the cube: a ring round its middle, inside its walls.
let mut s = cube();
run(&mut s, "sketch.create", json!({"plane": {"origin": [0, 5, 0], "x_dir": [1, 0, 0], "y_dir": [0, 0, 1]}, "name": "Section"}));
run(&mut s, "sketch.rectangle.two_point", json!({"p0": [6, 4], "p1": [7, 6]}));
run(&mut s, "sketch.finish", json!({}));
let ring = |x: f64| {
vec![(
"solid.revolve".to_string(),
json!({"sketch": "Section", "profiles": [0], "axis": {"origin": [x, 5, 0], "dir": [0, 0, 1]}, "angle": "360 deg", "operation": "new"}),
)]
};
assert_eq!(addition(&s, &ring(5.0), true), Some(Addition::Inside), "a ring inside the cube");
}
#[test]
fn no_verdict_without_an_operation_or_a_body() {
let s = Session::default();
assert_eq!(addition(&s, &[("solid.box".into(), json!({"length": 1, "width": 1, "height": 1, "operation": "new"}))], false), None);
let s = cube();
assert_eq!(addition(&s, &[("solid.fillet".into(), json!({"edges": [[10, 0, 5]], "radius": 1}))], false), None);
assert_eq!(addition(&s, &[("solid.combine".into(), json!({"target": "Base", "tools": [], "operation": "join"}))], false), None);
// A cut keeps its operation: nothing to choose.
assert_eq!(addition(&s, &[("solid.box".into(), json!({"length": 1, "width": 1, "height": 1, "operation": "cut"}))], false), None);
// A primitive placed apart, whichever of New Body and Join it has.
let apart = json!({"length": 5, "width": 5, "height": 5, "corner": [20, 0, 0], "operation": "join"});
assert_eq!(addition(&s, &[("solid.box".into(), apart)], false), Some(Addition::Apart));
}
#[test]
fn hidden_bodies_are_out_of_scope() {
let mut s = cube();
assert_eq!(crate::shown_scope(&s), None, "all shown: nothing to narrow");
run(&mut s, "solid.box", json!({"length": 10, "width": 10, "height": 10, "corner": [30, 0, 0], "operation": "new", "body_name": "Other"}));
run(&mut s, "browser.visibility", json!({"items": ["Base"], "visible": false}));
assert_eq!(crate::shown_scope(&s), Some(vec!["Other".to_string()]));
// A block on the hidden cube, with the shown bodies as its targets: apart from all of them.
let on_hidden = json!({"length": 4, "width": 4, "height": 4, "corner": [2, 2, 10], "operation": "new", "targets": ["Other"]});
assert_eq!(addition(&s, &[("solid.box".into(), on_hidden)], false), Some(Addition::Apart));
run(&mut s, "browser.visibility", json!({"items": ["Other"], "visible": false}));
assert_eq!(crate::shown_scope(&s), Some(Vec::new()), "nothing shown");
}
+1
View File
@@ -184,6 +184,7 @@ fn face_ref(doc: &Document, name: &str) -> Value {
// A side swept from a sketch curve (a later profile's too, `side:s<k>.<curve>`).
if let Base::Made { role: Role::Side { curve, .. }, .. } = &face.base {
let found = f.kind.input_sketches().into_iter().find_map(|id| {
// design-only: a joint names a curve of one of the design's sketches.
let sk = doc.sketch(id).ok()?;
let c = sk.curves.iter().find(|c| c.id == *curve)?;
let curve_type = serde_json::to_value(&c.kind).ok().and_then(|k| k.get("type").cloned()).unwrap_or(Value::Null);
+1 -1
View File
@@ -150,7 +150,7 @@ fn sketch_of<'a>(
if let Some(id) = sketch {
return st.sketch(id);
}
if let Some(ss) = s.active_sketch.and_then(|id| st.sketch(id)).filter(|ss| has(&ss.sketch)) {
if let Some(ss) = super::sketch_store::active(s).and_then(|id| st.sketch(id)).filter(|ss| has(&ss.sketch)) {
return Some(ss);
}
st.sketches.iter().rev().map(|ss| &**ss).find(|ss| has(&ss.sketch))
+12 -2
View File
@@ -35,10 +35,12 @@ mod edit;
mod face;
mod features;
mod features_more;
mod join_pieces;
pub(crate) use face::{FaceRefForm, face_ref_form};
pub(crate) use features::profiles as feature_profiles;
pub(crate) use features::{EdgeRefForm, axis_dir_form, edge_ref_form, sketch_key};
pub use features::{auto_operation, extrude_reach};
pub use features::{auto_operation, extrude_reach, shown_scope};
pub use join_pieces::{Addition, addition};
pub use sketch::dimension_roles;
pub use sketch_import::drawing_import_command;
pub use sketch_modify::{extend_arc_preview, extend_preview, trim_piece};
@@ -62,6 +64,7 @@ mod mesh;
mod metadata;
pub(crate) mod motion;
mod parts;
mod point_cloud;
mod script;
mod section;
pub(crate) use measure_sel::{measure_items, shape_of};
@@ -92,6 +95,7 @@ mod sketch_import;
mod sketch_inspect;
mod sketch_modify;
mod sketch_project;
mod sketch_store;
mod surface;
mod surface_analysis;
mod surface_boundary;
@@ -216,7 +220,12 @@ impl CommandSpec {
/// undo step and the evaluation).
pub(crate) fn run_nested(&self, s: &mut Session, p: &Value) -> Result<Value> {
(self.enabled)(s).map_err(|why| crate::EngineError::Disabled(self.id.to_string(), why))?;
(self.run)(s, p)
let r = (self.run)(s, p);
// A parameter error that did not name the command names it here (see `Session::execute`).
match r {
Err(crate::EngineError::BadParams { cmd, msg }) if cmd.is_empty() => Err(crate::EngineError::BadParams { cmd: self.id.to_string(), msg }),
other => other,
}
}
pub fn info(&self, s: &Session) -> CommandInfo {
let e = (self.enabled)(s);
@@ -279,6 +288,7 @@ pub fn command_specs() -> Vec<&'static CommandSpec> {
v.extend(sketch_ends::COMMANDS.iter());
v.extend(sketch_canvas::COMMANDS.iter());
v.extend(canvas_sketch::COMMANDS.iter());
v.extend(point_cloud::COMMANDS.iter());
v.extend(sketch_3d::COMMANDS.iter());
v.extend(surface_analysis::COMMANDS.iter());
v.extend(surface_boundary::COMMANDS.iter());
+4
View File
@@ -254,6 +254,7 @@ fn seeded(s: &Session, t: &MotionTrace) -> Session {
pub(super) fn remember(s: &mut Session, t: &MotionTrace) {
let key = inputs(s, t).map_err(|e| e.to_string());
if let Ok(key) = &key
// design-only: a motion trace's wire is in one of the design's sketches.
&& let Some(points) = s.doc.sketch(t.sketch).ok().and_then(|sk| sk.wires.iter().find(|w| w.id == t.wire)).map(|w| w.fit.clone())
{
remember_points(s, t.sketch, key.clone(), points);
@@ -330,6 +331,7 @@ pub(crate) fn refresh(s: &mut Session) {
let unchanged = s.motion_traces.entries.get(&t.sketch).is_some_and(|old| old.inputs == key);
if unchanged
&& cached.as_ref().is_none_or(|points| {
// design-only: a motion trace's wire is in one of the design's sketches.
s.doc.sketch(t.sketch).ok().and_then(|sk| sk.wires.iter().find(|w| w.id == t.wire)).is_some_and(|w| same_points(&w.fit, points))
})
{
@@ -352,9 +354,11 @@ pub(crate) fn refresh(s: &mut Session) {
remember_points(s, t.sketch, key.clone(), points.clone());
}
let error = match result {
// design-only: a motion trace's wire is in one of the design's sketches.
Ok(points) => match s.doc.sketch(t.sketch).ok().and_then(|sk| sk.wires.iter().find(|w| w.id == t.wire)) {
Some(wire) if same_points(&wire.fit, &points) => None,
Some(_) => {
// design-only: a motion trace's wire is in one of the design's sketches.
if let Ok(sk) = s.doc_mut().sketch_mut(t.sketch)
&& let Some(wire) = sk.wires.iter_mut().find(|w| w.id == t.wire)
{
+26
View File
@@ -1481,3 +1481,29 @@ fn join_extrude_to_a_body_of_another_component_leaves_that_body_alone() {
assert!((volume("Shell") - 8000.0).abs() < 1e-3, "the shell is unchanged: {}", volume("Shell"));
assert!((volume("Connector") - (pi * 36.0 * 10.0 + pi * 16.0 * 10.0)).abs() < 1.0, "the connector took the boss: {}", volume("Connector"));
}
/// A pattern of bodies made in a component copies them about its own component's axis, so
/// moving that component's occurrence moves the copies of a body that lives elsewhere. The model
/// kept between edits must show them where a fresh evaluation does: a root block patterned four
/// times about the Z axis of component Part, then Part moved.
#[test]
fn a_pattern_of_another_components_body_follows_its_own_component_when_moved() {
let boxes = |st: &solidcraft_doc::ModelState| -> Vec<String> {
st.bodies
.iter()
.map(|b| {
let bb = b.body.outline_bounds();
format!("{} [{:.6} {:.6} {:.6}]..[{:.6} {:.6} {:.6}]", b.name, bb.min.x, bb.min.y, bb.min.z, bb.max.x, bb.max.y, bb.max.z)
})
.collect()
};
let mut s = Session::default();
run(&mut s, "solid.box", json!({"corner": [-4, -27, 0], "length": 20, "width": 13, "height": 4}));
created(&mut s, "Part");
run(&mut s, "solid.pattern.circular", json!({"axis": "Z", "bodies": ["Body1"], "count": 4}));
run(&mut s, "occurrence.move", json!({"occurrence": "Part:1", "translate": [6, -14, -3]}));
let mut fresh = solidcraft_doc::Model::new();
fresh.no_cache = true;
fresh.evaluate(&s.doc);
assert_eq!(boxes(&s.model.state()), boxes(&fresh.state()), "the kept model against a fresh evaluation");
}
+55 -10
View File
@@ -7,7 +7,7 @@ use solidcraft_doc::FeatureKind;
use solidcraft_doc::expr::{self, Kind};
use solidcraft_sketch::{Analysis, ConstraintKind, CurveKind, Hold, Sketch, solve_with};
use super::CommandSpec;
use super::{CommandSpec, sketch_store};
use crate::params::{bad, bool_, str_};
use crate::{EngineError, Result, Session};
@@ -15,7 +15,8 @@ pub static COMMANDS: &[CommandSpec] = &[
CommandSpec::new("parameters.change", "Change Parameters", change_param).at("SOLID", "MODIFY").icon("params").params(
"name, expression (number or text), unit?: mm|cm|m|in|ft|deg|rad|\"\"|text (new parameters default to their value's unit; \
a text parameter's expression is its text, quotes round it optional: sketch texts' words are built from it), comment?, \
favorite?: bool, new_name?: rename (references follow); or delete: name; or changes: {name: expression, …} (existing \
favorite?: bool, new_name?: rename (references follow; an expression `new = value` renames to `new` and sets value, \
or sets `new` when the expression is just `new`); or delete: name; or changes: {name: expression, …} (existing \
parameters, checked together and rebuilt once, all or nothing) → changed?: {name: value} (with changes), recomputed: features rebuilt, \
restored: features whose earlier result for the same inputs was reused (e.g. a value set back), switched?: [{feature, name, suppressed}] \
(the configuration rules that switch a feature's suppression and read a parameter whose value changed run in their order, as on \
@@ -87,6 +88,11 @@ pub(super) fn known(s: &Session, name: &str) -> bool {
s.doc.param(name).is_some() || s.doc.text_param(name).is_some() || s.doc.features.iter().any(|f| f.param_names.iter().any(|n| n == name))
}
/// Whether `name` is (or, with this unit, becomes) a text parameter, whose expression is text.
fn is_text(s: &Session, name: &str, unit: Option<&str>) -> bool {
s.doc.text_param(name).is_some() || unit.is_some_and(solidcraft_doc::text_params::is_text_unit)
}
fn change_param(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "parameters.change";
let solved = s.solved_sketches();
@@ -106,13 +112,41 @@ fn change_param(s: &mut Session, p: &Value) -> Result<Value> {
name = new.trim().to_string();
}
let has_expr = p.get("expression").is_some() || p.get("value").is_some();
let e = if has_expr {
let mut e = if has_expr {
Some(expr_arg(p, "expression").or_else(|| expr_arg(p, "value")).ok_or_else(|| bad(cmd, "`expression` must be a number or text"))?)
} else if out.get("renamed").is_some() || p.get("favorite").is_some() || p.get("comment").is_some() {
None
} else {
return Err(bad(cmd, "`expression` must be a number or text"));
};
// `name = expression` names the value being changed (#4071): the parameter (a dimension's
// `d7`, a feature input's) is renamed, references following, and takes the expression. A
// text parameter's text is only text.
if let Some((new, rhs)) = e.as_deref().filter(|_| !is_text(s, &name, str_(p, "unit"))).and_then(expr::definition) {
if out.get("renamed").is_some() {
return Err(bad(cmd, format!("`{new} = …` renames `{name}` already: give `new_name` or `name = expression`, not both")));
}
if rhs.is_empty() {
return Err(bad(cmd, format!("`{new} =` needs a value after the `=`")));
}
let (new, rhs) = (new.to_string(), rhs.to_string());
// A value that is just a parameter's name changes that parameter: a dimension made as
// `w = 20` shows `w`, and `w = 30` typed over it sets `w`.
let refers_to = s.doc.all_param_exprs().into_iter().find(|(n, _, _)| *n == name).map(|(_, e, _)| e);
if refers_to.as_deref().map(str::trim) == Some(new.as_str()) && s.doc.param(&new).is_some() {
out["changed_through"] = json!(name);
name = new.clone();
}
if new != name {
if known(s, &new) {
return Err(bad(cmd, format!("there is already a parameter `{new}`: type `{new}` to use it, or change it in Parameters")));
}
let n = s.doc_mut().rename_param(&name, &new)?;
out["renamed"] = json!({"from": name, "to": new, "references_updated": n});
name = new;
}
e = Some(rhs);
}
let (unit, comment) = (str_(p, "unit"), str_(p, "comment"));
let before = s.doc.param_values().0;
match &e {
@@ -189,7 +223,7 @@ fn keep_dimension_start(s: &mut Session, name: &str) {
}
let vals = s.doc.evaluation_values();
for (id, start) in targets {
let Ok(stored) = s.doc.sketch(id) else { continue };
let Ok(stored) = sketch_store::read(&s.doc, id) else { continue };
let mut trial = stored.clone();
if s.doc.apply_dimension_values(&vals, &mut trial).is_err() {
continue;
@@ -199,12 +233,13 @@ fn keep_dimension_start(s: &mut Session, name: &str) {
if !solve_with(&mut trial, &hold, Some(&reference), Analysis::StatusOnly, None).keeps_orientation() {
continue;
}
if let Ok(stored) = s.doc_mut().sketch_mut(id)
&& stored.points.len() == trial.points.len()
{
for (p, q) in stored.points.iter_mut().zip(&trial.points) {
if stored.points.len() == trial.points.len() {
// Only the points move: the stored dimensions keep their own values.
let mut kept = stored.clone();
for (p, q) in kept.points.iter_mut().zip(&trial.points) {
p.pos = q.pos;
}
let _ = sketch_store::write(s.doc_mut(), id, kept);
}
}
}
@@ -220,6 +255,10 @@ fn change_many(s: &mut Session, changes: &Value, solved: &crate::SketchValues) -
let mut list = Vec::with_capacity(map.len());
for (name, e) in map {
let e = expr_arg(&json!({"e": e}), "e").ok_or_else(|| bad(cmd, format!("`changes.{name}` must be a number or text")))?;
// Renaming is one parameter at a time (#4071).
if let Some((new, _)) = expr::definition(&e).filter(|_| !is_text(s, name.trim(), None)) {
return Err(bad(cmd, format!("`changes.{name}`: `{new} = …` would rename `{name}`; change it on its own to rename it")));
}
list.push((name.trim().to_string(), e));
}
let before = s.doc.param_values().0;
@@ -435,7 +474,9 @@ fn evaluate(s: &mut Session, p: &Value) -> Result<Value> {
Some(k) => v.to_kind_in(k, defaults).map(|x| (x, k)),
None => v.kind().map(|k| (v.v, k)).ok_or_else(|| solidcraft_doc::DocError::Expr(format!("the result is {}", v.describe()))),
});
Ok(match r {
// `name = expression` is worth its expression, and names what it would define (#4071).
let defines = expr::definition(&e).map(|(n, _)| n);
let mut out = match r {
Ok((v, k)) => {
let value = match k {
Kind::Length => expr::Value::length(v),
@@ -450,7 +491,11 @@ fn evaluate(s: &mut Session, p: &Value) -> Result<Value> {
json!({"ok": true, "value": v, "kind": k, "display_value": shown, "references": refs})
}
Err(err) => json!({"ok": false, "error": err.to_string().trim_start_matches("expression: ").to_string(), "references": refs}),
})
};
if let Some(name) = defines {
out["defines"] = json!(name);
}
Ok(out)
}
fn complete(s: &mut Session, p: &Value) -> Result<Value> {
+157
View File
@@ -753,3 +753,160 @@ fn flipped_offset_sketch_plane_follows_its_parameter() {
run(&mut s, "parameters.change", json!({"name": "d", "expression": "50 mm"}));
assert!((volume(&mut s) - 400.0 * 50.0).abs() < 1e-6, "{}", volume(&mut s));
}
/// A value typed as `name = expression` in any value input defines the user parameter `name`
/// and leaves the input using it (#4071): a feature's distance, an angle, a count, a sheet metal
/// rule and a canvas offset. The parameter takes the input's unit, the design's for a bare length.
#[test]
fn any_value_input_can_define_a_parameter() {
let mut s = Session::default();
block(&mut s);
let r = run(&mut s, "solid.extrude", json!({"sketch": "Base", "distance": "depth = 15", "operation": "new", "body_name": "Second"}));
assert_eq!(r["defined"], "depth", "{r}");
assert_eq!(r["definitions"], json!([{"name": "depth", "expression": "15", "unit": "mm"}]), "{r}");
let l = run(&mut s, "parameters.list", json!({}));
assert_eq!((row(&l, "depth")["source"].as_str(), row(&l, "depth")["expression"].as_str()), (Some("user"), Some("15")), "{l}");
// The input shows the name afterwards, and follows the parameter.
let input = l["parameters"].as_array().unwrap().iter().find(|r| r["feature"] == "Extrude2" && r["input"] == "Distance").unwrap().clone();
assert_eq!(input["expression"], "depth", "{input}");
assert!(rel(volume(&mut s), 40.0 * 30.0 * 10.0 + 40.0 * 30.0 * 15.0) < 1e-9);
run(&mut s, "parameters.change", json!({"name": "depth", "expression": "20 mm"}));
assert!(rel(volume(&mut s), 40.0 * 30.0 * 10.0 + 40.0 * 30.0 * 20.0) < 1e-9);
// One undo removes the feature and its parameter together.
run(&mut s, "edit.undo", json!({}));
run(&mut s, "edit.undo", json!({}));
assert!(s.doc.param("depth").is_none());
assert!(s.doc.features.iter().all(|f| f.name != "Extrude2"));
run(&mut s, "edit.redo", json!({}));
assert!(s.doc.param("depth").is_some());
// An angle input's parameter is in degrees; `#name` works as in a dimension.
let r = run(&mut s, "solid.extrude", json!({"sketch": "Base", "distance": 5, "taper": "#draft = 2", "operation": "new"}));
assert_eq!(r["definitions"][0], json!({"name": "draft", "expression": "2", "unit": "deg"}), "{r}");
// Sheet metal rules and a canvas offset take one too.
run(&mut s, "sheet.manage_rules", json!({"name": "Steel", "thickness": "gauge = 2", "active": true}));
assert_eq!((s.doc.sheet.rules[0].thickness.as_str(), s.doc.param("gauge").map(|p| p.expr.as_str())), ("gauge", Some("2")));
let mut png = b"\x89PNG\r\n\x1a\n\0\0\0\rIHDR".to_vec();
png.extend([0, 0, 0, 4, 0, 0, 0, 2, 8, 6, 0, 0, 0, 0, 0, 0, 0]);
run(&mut s, "canvas.insert", json!({"data": solidcraft_doc::canvas::base64_encode(&png), "plane": "XY", "offset": "lift = 15"}));
match &s.doc.canvases[0].plane {
solidcraft_doc::PlaneRef::Offset { distance, .. } => assert_eq!(distance.as_ref(), "lift"),
other => panic!("{other:?}"),
}
assert_eq!(s.doc.param("lift").map(|p| (p.expr.as_str(), p.unit.as_str())), Some(("15", "mm")));
}
/// In an inch design a bare number defines a parameter in inches, as the bare value would read;
/// a value with a unit keeps it.
#[test]
fn a_defined_length_takes_the_design_unit() {
let mut s = Session::default();
run(&mut s, "document.units", json!({"units": "in"}));
let r = run(&mut s, "solid.box", json!({"length": "l = 2", "width": "w = 25.4 mm", "height": 1}));
assert_eq!(
r["definitions"],
json!([{"name": "l", "expression": "2 in", "unit": "in"}, {"name": "w", "expression": "25.4 mm", "unit": "in"}]),
"{r}"
);
assert!(rel(volume(&mut s), 50.8 * 25.4 * 25.4) < 1e-9);
}
/// A definition that cannot be made fails the command and leaves the design as it was: a taken,
/// reserved or malformed name, no value, a value that names itself, two different values for one
/// name, and a parameter's or configuration cell's own expression.
#[test]
fn a_refused_definition_changes_nothing() {
let mut s = Session::default();
block(&mut s);
let undo = s.undo.len();
let features = s.doc.features.len();
let refused = [
(json!({"distance": "len = 5"}), "there is already a parameter `len`: type `len` to use it"),
(json!({"distance": "mm = 5"}), "`mm` is a unit name"),
(json!({"distance": "pi = 5"}), "`pi` is a constant"),
(json!({"distance": "w ="}), "`w =` needs a value after the `=`"),
(json!({"distance": "w = "}), "needs a value"),
(json!({"distance": "w = w + 1"}), "w"),
(json!({"distance": "w = = 3"}), "unexpected `=`"),
(json!({"distance": "= 5 ="}), ""),
(json!({"distance": "w = 5", "distance2": "w = 6"}), "`w` is defined twice"),
];
for (p, message) in refused {
let mut q = json!({"sketch": "Base", "operation": "new"});
for (k, v) in p.as_object().unwrap() {
q[k] = v.clone();
}
let e = err(&mut s, "solid.extrude", q.clone());
assert!(e.contains(message), "{q}: {e}");
assert_eq!((s.undo.len(), s.doc.features.len()), (undo, features), "{q}: a refused definition changed the design");
assert!(s.doc.param("w").is_none(), "{q}");
}
// The same definition twice is one parameter.
let r = run(&mut s, "solid.extrude", json!({"sketch": "Base", "operation": "new", "distance": "w = 5", "distance2": "w = 5"}));
assert_eq!(r["definitions"].as_array().map(Vec::len), Some(1), "{r}");
run(&mut s, "edit.undo", json!({}));
// A parameter's own expression or a configuration cell defines nothing.
let e = err(&mut s, "parameters.add", json!({"name": "a", "expression": "b = 5"}));
assert!(e.contains("cannot define another parameter `b`"), "{e}");
assert!(s.doc.param("a").is_none() && s.doc.param("b").is_none());
let e = err(&mut s, "parameters.change", json!({"changes": {"len": "b = 5"}}));
assert!(e.contains("change it on its own to rename it"), "{e}");
// Text that only looks like a definition, where no value is read, is left alone.
let r = run(&mut s, "parameters.add", json!({"name": "label", "expression": "a = b", "unit": "text"}));
assert!(r.get("defined").is_none(), "{r}");
}
/// Changing a parameter to `name = expression` names it (#4071): the dimension's or feature
/// input's own parameter is renamed, references follow, and it takes the expression; no other
/// parameter is made. One undo takes both back.
#[test]
fn changing_a_parameter_to_a_definition_renames_it() {
let mut s = Session::default();
block(&mut s);
let l = run(&mut s, "parameters.list", json!({"filter": "feature"}));
let d = l["parameters"].as_array().unwrap().iter().find(|r| r["input"] == "Distance").unwrap()["name"].as_str().unwrap().to_string();
let count = run(&mut s, "parameters.list", json!({}))["count"].clone();
let r = run(&mut s, "parameters.change", json!({"name": d, "expression": "height = 12"}));
assert_eq!((r["name"].as_str(), r["renamed"]["from"].as_str()), (Some("height"), Some(d.as_str())), "{r}");
assert_eq!(expr_of(&mut s, "height"), "12");
assert!(rel(volume(&mut s), 40.0 * 30.0 * 12.0) < 1e-9);
assert_eq!(run(&mut s, "parameters.list", json!({}))["count"], count, "no extra parameter");
// A dimension's parameter too; whatever refers to it follows.
let width =
s.doc.sketch(s.doc.features[0].id).unwrap().constraints.iter().find_map(|c| c.param.clone().filter(|p| expr_of_now(&s, p) == "40")).unwrap();
run(&mut s, "solid.box", json!({"length": width.clone(), "width": 5, "height": 5, "corner": [100, 0, 0]}));
run(&mut s, "parameters.change", json!({"name": width, "expression": "wide = 50"}));
assert_eq!(expr_of(&mut s, "wide"), "50");
assert!(rel(volume(&mut s), 50.0 * 30.0 * 12.0 + 50.0 * 25.0) < 1e-9);
run(&mut s, "edit.undo", json!({}));
assert!(s.doc.param("wide").is_none());
assert_eq!(expr_of_now(&s, &width), "40");
// Its own name is just a change; a taken name is refused.
run(&mut s, "parameters.change", json!({"name": "height", "expression": "height = 14"}));
assert_eq!(expr_of(&mut s, "height"), "14");
let e = err(&mut s, "parameters.change", json!({"name": "height", "expression": "len = 3"}));
assert!(e.contains("there is already a parameter `len`"), "{e}");
let e = err(&mut s, "parameters.change", json!({"name": "height", "new_name": "h", "expression": "x = 3"}));
assert!(e.contains("not both"), "{e}");
}
fn expr_of_now(s: &Session, name: &str) -> String {
s.doc.param(name).map(|p| p.expr.clone()).unwrap_or_default()
}
/// A dimension made as `w = 20` shows `w`; typing `w = 30` over it sets `w` rather than being
/// refused as a taken name (#4071).
#[test]
fn a_definition_of_the_parameter_a_value_names_changes_it() {
let mut s = Session::default();
block(&mut s);
run(&mut s, "solid.box", json!({"length": "w = 20", "width": 5, "height": 5, "corner": [100, 0, 0]}));
let l = run(&mut s, "parameters.list", json!({"filter": "feature"}));
let d = l["parameters"].as_array().unwrap().iter().find(|r| r["expression"] == "w").unwrap()["name"].as_str().unwrap().to_string();
let r = run(&mut s, "parameters.change", json!({"name": d, "expression": "w = 30"}));
assert_eq!((r["name"].as_str(), r["changed_through"].as_str()), (Some("w"), Some(d.as_str())), "{r}");
assert_eq!((expr_of(&mut s, "w"), expr_of(&mut s, &d)), ("30".to_string(), "w".to_string()));
let r = run(&mut s, "expr.evaluate", json!({"expression": "q = w * 2", "kind": "length"}));
assert_eq!((r["ok"].as_bool(), r["value"].as_f64(), r["defines"].as_str()), (Some(true), Some(60.0), Some("q")), "{r}");
}
+39 -5
View File
@@ -272,10 +272,7 @@ fn revolve(s: &mut Session, pts: &[(f64, f64)], op: &str) -> Result<()> {
/// Fix every point and curve of the sketch being drawn: a standard part's outline is its size, so
/// its sketches are fully defined and a check of the design finds nothing left free in them.
fn fix_all(s: &mut Session) -> Result<()> {
let sketch = s.active_sketch.and_then(|id| s.doc.features.iter().find(|f| f.id == id)).and_then(|f| match &f.kind {
solidcraft_doc::FeatureKind::Sketch { sketch, .. } => Some(sketch),
_ => None,
});
let sketch = super::sketch_store::active(s).and_then(|id| super::sketch_store::read(&s.doc, id).ok());
let entities: Vec<String> =
sketch.map(|sk| sk.points.iter().map(|p| p.id.clone()).chain(sk.curves.iter().map(|c| c.id.clone())).collect()).unwrap_or_default();
if !entities.is_empty() {
@@ -335,7 +332,7 @@ fn build(s: &mut Session, sh: &Shape) -> Result<()> {
fn library(_s: &mut Session, p: &Value) -> Result<Value> {
let fam = |id: &str| -> Value {
let sizes: Vec<Value> = match id {
let mut sizes: Vec<Value> = match id {
"socket_head_cap_screw" => SHCS.iter().map(|r| json!({"size": r.0, "d": metric(r.0).map(|m| m.0), "pitch": metric(r.0).map(|m| m.1), "head_diameter": r.1, "head_height": r.2, "socket": r.3, "socket_depth": r.4})).collect(),
"hex_bolt" => HEX_BOLT.iter().map(|r| json!({"size": r.0, "d": metric(r.0).map(|m| m.0), "pitch": metric(r.0).map(|m| m.1), "across_flats": r.1, "head_height": r.2})).collect(),
"hex_nut" => HEX_NUT.iter().map(|r| json!({"size": r.0, "d": metric(r.0).map(|m| m.0), "pitch": metric(r.0).map(|m| m.1), "across_flats": r.1, "height": r.2})).collect(),
@@ -347,6 +344,15 @@ fn library(_s: &mut Session, p: &Value) -> Result<Value> {
};
let (_, name, standard) = FAMILIES.iter().find(|f| f.0 == id).copied().unwrap_or(("", "", ""));
let lengths = matches!(id, "socket_head_cap_screw" | "hex_bolt" | "set_screw" | "dowel_pin").then_some(LENGTHS.to_vec());
if lengths.is_some() {
for row in &mut sizes {
let Some(size) = row.get("size").and_then(Value::as_str).map(str::to_string) else { continue };
let Some(l) = shape(id, &size, None).ok().as_ref().and_then(shape_len) else { continue };
if let Value::Object(m) = row {
m.insert("default_length".into(), json!(l));
}
}
}
json!({"family": id, "name": name, "standard": standard, "sizes": sizes, "lengths": lengths})
};
match str_(p, "family") {
@@ -599,6 +605,34 @@ mod tests {
assert!(b.execute("parts.insert", &json!({"family": "hex_nut", "size": "M7"})).is_err());
}
/// The Length list of a dialog opens on the length an insert gets when none is asked for,
/// so the library reports that length for every size of every family that has lengths. The
/// rule differs by family, so the reported value is the one the part is actually built to.
#[test]
fn the_library_reports_the_default_length_of_every_size() {
let all = run(&mut Session::default(), "parts.library", json!({}));
let families = all["families"].as_array().cloned().unwrap_or_default();
assert!(!families.is_empty(), "{all}");
for fam in families {
let id = fam["family"].as_str().unwrap_or_default().to_string();
let standard: Vec<f64> = fam["lengths"].as_array().map(|a| a.iter().filter_map(Value::as_f64).collect()).unwrap_or_default();
for row in fam["sizes"].as_array().cloned().unwrap_or_default() {
let size = row["size"].as_str().unwrap_or_default().to_string();
let reported = row["default_length"].as_f64();
if standard.is_empty() {
assert!(reported.is_none(), "{id} {size} has no lengths of its own but reports a default");
continue;
}
// What an insert that asks for no length is actually built to.
let mut s = Session::default();
let out = run(&mut s, "parts.insert", json!({"family": id, "size": size}));
let built = s.doc.parts.get(&out["component"].as_u64().unwrap_or(0)).and_then(|p| p.length);
assert_eq!(reported, built, "{id} {size}: reported default is not what an insert builds");
assert!(standard.iter().any(|l| Some(*l) == reported), "{id} {size}: {reported:?} is not one of the standard lengths");
}
}
}
/// #2091: the second and larger ISO sizes, M2, M2.5, M14, M24 and M30, are in the screw, bolt,
/// nut and washer tables (they stopped at M3 and M20).
#[test]
+1 -1
View File
@@ -120,7 +120,7 @@ fn manage_rules(s: &mut Session, p: &Value) -> Result<Value> {
fn assign_rule(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "plastic.assign_rule";
let bodies = string_list(p, "bodies");
let bodies = string_list(cmd, p, "bodies")?;
if bodies.is_empty() || bodies.len() > 10_000 {
return Err(bad(cmd, "`bodies` must list bodies"));
}
+446
View File
@@ -0,0 +1,446 @@
//! Point clouds (#5151): scanned points (PLY, XYZ or PTS files) kept in the design as a reference
//! to model from. They are drawn as points and never become bodies.
use serde_json::{Value, json};
use solidcraft_doc::Mat;
use solidcraft_doc::point_cloud::{ColorBy, Colors, MAX_COORD, MAX_POINTS, POINT_SIZE, PointCloud, Points, UNITS, unit_scale};
use super::CommandSpec;
use crate::params::{bad, bool_, str_};
use crate::{EngineError, Result, Session};
pub static COMMANDS: &[CommandSpec] = &[
CommandSpec::new("pointcloud.insert", "Insert Point Cloud", insert)
.at("SOLID", "INSERT")
.icon("import")
.params("path: .ply, .xyz or .pts file (its points join the design as a point cloud: drawn as points, never a body; a PLY's red, green and blue are its colours) → cloud, name, points, source_points, bounds, warnings; name?; units?: mm|cm|m|in|ft (the file's length unit, default mm); max_points?: most points kept (default 5000000, at most 20000000; a larger file is thinned evenly); center?: bool (move the cloud so the middle of its base sits at the origin); point_size?: pixels (0.5…16, default 2); color_by?: file|single|height (the file's colours, the default; one colour; or a ramp by height, blue low to red high); color?: [r, g, b] 0…1 (the single colour; given alone it sets color_by single, null sets color_by file); height_range?: [low, high] design Z in mm for the height ramp, or null to work it out from the points (all but the lowest and highest 2 %)"),
CommandSpec::new("pointcloud.edit", "Edit Point Cloud", edit)
.icon("edit")
.params("cloud: id or name; name?; visible?; point_size?: pixels (0.5…16); color_by?: file|single|height (the file's colours, the default; one colour; or a ramp by height, blue low to red high); color?: [r, g, b] 0…1 (the single colour; given alone it sets color_by single, null sets color_by file); height_range?: [low, high] design Z in mm for the height ramp, or null to work it out from the points (all but the lowest and highest 2 %); units?: mm|cm|m|in|ft (the file's length unit: the points are scaled from the unit they came in as); matrix?: [12 or 16 numbers] (where the cloud sits: a rigid row-major 3x4 or 4x4 matrix, as occurrence.move takes it); tolerance? → cloud"),
CommandSpec::new("pointcloud.delete", "Delete Point Cloud", delete).icon("delete").params("cloud: id or name → deleted"),
CommandSpec::new("pointcloud.list", "Point Clouds", list)
.icon("list")
.noundo()
.doc_only()
.params("→ clouds: [{cloud, name, file, points, source_points, units, visible, point_size, color_by, color, colored, height_range (the ramp's now, its own or worked out), height_auto, bounds: [[x,y,z], [x,y,z]] in the design, matrix}]"),
];
fn units_of(p: &Value, cmd: &str) -> Result<Option<(&'static str, f64)>> {
match p.get("units") {
None | Some(Value::Null) => Ok(None),
Some(Value::String(u)) => UNITS
.iter()
.find(|(name, _)| name.eq_ignore_ascii_case(u))
.map(|(name, k)| Some((*name, *k)))
.ok_or_else(|| bad(cmd, format!("`units` must be one of mm, cm, m, in, ft (not `{u}`)"))),
Some(_) => Err(bad(cmd, "`units` must be one of mm, cm, m, in, ft")),
}
}
fn point_size(p: &Value, cmd: &str) -> Result<Option<f64>> {
match p.get("point_size") {
None | Some(Value::Null) => Ok(None),
Some(v) => v
.as_f64()
.filter(|s| s.is_finite() && POINT_SIZE.contains(s))
.map(Some)
.ok_or_else(|| bad(cmd, format!("`point_size` must be a number of pixels from {} to {}", POINT_SIZE.start(), POINT_SIZE.end()))),
}
}
/// `color`: absent (`None`), null for the file's colours (`Some(None)`), or [r, g, b] 0…1.
fn color(p: &Value, cmd: &str) -> Result<Option<Option<[f32; 3]>>> {
match p.get("color") {
None => Ok(None),
Some(Value::Null) => Ok(Some(None)),
Some(Value::Array(a)) if a.len() == 3 => {
let c: Option<Vec<f32>> = a.iter().map(|v| v.as_f64().filter(|x| (0.0..=1.0).contains(x)).map(|x| x as f32)).collect();
match c.as_deref() {
Some(&[r, g, b]) => Ok(Some(Some([r, g, b]))),
_ => Err(bad(cmd, "`color` must be [r, g, b] with each from 0 to 1")),
}
}
Some(_) => Err(bad(cmd, "`color` must be [r, g, b] with each from 0 to 1, or null")),
}
}
/// `color_by`: absent (`None`), or file, single or height.
fn color_by(p: &Value, cmd: &str) -> Result<Option<ColorBy>> {
match p.get("color_by") {
None | Some(Value::Null) => Ok(None),
Some(v) => v.as_str().and_then(ColorBy::parse).map(Some).ok_or_else(|| bad(cmd, "`color_by` must be file, single or height")),
}
}
/// `height_range`: absent (`None`), null for the automatic one (`Some(None)`), or [low, high].
fn height_range(p: &Value, cmd: &str) -> Result<Option<Option<[f64; 2]>>> {
let refuse = || bad(cmd, "`height_range` must be [low, high] heights in mm, lowest first, or null");
match p.get("height_range") {
None => Ok(None),
Some(Value::Null) => Ok(Some(None)),
Some(Value::Array(a)) => match a.iter().map(Value::as_f64).collect::<Option<Vec<f64>>>().as_deref() {
Some(&[lo, hi]) if lo.is_finite() && hi.is_finite() && lo < hi && lo.abs() <= MAX_COORD && hi.abs() <= MAX_COORD => {
Ok(Some(Some([lo, hi])))
}
_ => Err(refuse()),
},
Some(_) => Err(refuse()),
}
}
/// Apply `color_by`, `color` and `height_range`: a colour given alone means one colour, and
/// `color: null` the file's.
fn set_colors(c: &mut PointCloud, by: Option<ColorBy>, color: Option<Option<[f32; 3]>>, range: Option<Option<[f64; 2]>>) {
if let Some(k) = color {
c.color = k.or(c.color);
c.color_by = if k.is_some() { ColorBy::Single } else { ColorBy::File };
}
if let Some(b) = by {
c.color_by = b;
}
if let Some(r) = range {
c.height_range = r;
}
}
/// The cloud's placement as `matrix` takes it: 12 numbers, row by row.
fn matrix_rows(m: &Mat) -> Vec<f64> {
(0..3).flat_map(|row| (0..4).map(move |col| m[col][row])).collect()
}
fn bounds(c: &PointCloud) -> Value {
let Some(corners) = c.world_corners() else { return Value::Null };
// The box around the corners in the design.
let mut a = [f64::INFINITY; 3];
let mut b = [f64::NEG_INFINITY; 3];
for w in corners {
for (k, v) in [w.x, w.y, w.z].into_iter().enumerate() {
a[k] = a[k].min(v);
b[k] = b[k].max(v);
}
}
json!([a, b])
}
fn insert(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "pointcloud.insert";
let path = str_(p, "path").filter(|x| !x.trim().is_empty() && x.len() < 4096).ok_or_else(|| bad(cmd, "`path` is required"))?;
if !solidcraft_io::is_point_cloud_path(path) {
return Err(bad(cmd, "only PLY, XYZ and PTS files (.ply, .xyz, .pts) can be inserted as point clouds"));
}
let (units, scale) = units_of(p, cmd)?.unwrap_or(("mm", 1.0));
let max_points = match p.get("max_points") {
None | Some(Value::Null) => solidcraft_io::DEFAULT_MAX_POINTS,
Some(v) => v
.as_u64()
.filter(|n| (1..=MAX_POINTS as u64).contains(n))
.map(|n| n as usize)
.ok_or_else(|| bad(cmd, format!("`max_points` must be a whole number from 1 to {MAX_POINTS}")))?,
};
let size = point_size(p, cmd)?.unwrap_or(2.0);
let colors = (color_by(p, cmd)?, color(p, cmd)?, height_range(p, cmd)?);
let center = bool_(p, "center").unwrap_or(false);
let bytes = solidcraft_io::vfs::read(path, solidcraft_io::MAX_POINT_CLOUD_BYTES).map_err(|e| EngineError::Other(format!("{path}: {e}")))?;
let read = solidcraft_io::read_point_cloud(&bytes, path, scale, max_points).map_err(|e| EngineError::Other(format!("{path}: {e}")))?;
let mut warnings = read.warnings;
if solidcraft_io::is_ply_path(path)
&& let Ok((_, faces)) = solidcraft_io::ply_counts(&bytes)
&& faces > 0
{
warnings.push(format!("the file also has {faces} faces: Insert Mesh (file.insert_mesh) reads it as a mesh"));
}
drop(bytes);
let file = std::path::Path::new(path).file_name().map(|n| n.to_string_lossy().to_string()).unwrap_or_else(|| path.to_string());
let stem = std::path::Path::new(path).file_stem().map(|n| n.to_string_lossy().trim().to_string()).filter(|n| !n.is_empty());
let taken = |n: &str| s.doc.point_clouds.iter().any(|c| c.name == n);
let name = str_(p, "name").filter(|n| !n.trim().is_empty()).map(str::to_string).unwrap_or_else(|| {
let base = stem.unwrap_or_else(|| "Point Cloud".into());
if !taken(&base) { base } else { (2..=s.doc.point_clouds.len() + 2).map(|k| format!("{base} ({k})")).find(|n| !taken(n)).unwrap_or(base) }
});
let mut cloud = PointCloud {
id: 0,
name: name.clone(),
file,
origin: read.origin,
points: Points::new(read.points.into()),
colors: Colors(read.colors.into()),
source_points: read.source_points,
units: units.into(),
transform: solidcraft_doc::IDENTITY,
point_size: size,
color_by: ColorBy::File,
color: None,
height_range: None,
visible: true,
};
set_colors(&mut cloud, colors.0, colors.1, colors.2);
if center && let Some((lo, hi)) = cloud.local_bounds() {
cloud.transform[3] = [-(lo.x + hi.x) / 2.0, -(lo.y + hi.y) / 2.0, -lo.z, 1.0];
}
cloud.check()?;
let doc = s.doc_mut();
let id = doc.allocate_id()?;
cloud.id = id;
let out = json!({
"cloud": id,
"name": name,
"points": cloud.len(),
"source_points": cloud.source_points,
"bounds": bounds(&cloud),
"warnings": warnings,
});
doc.point_clouds.push(cloud);
Ok(out)
}
fn find(s: &Session, p: &Value, cmd: &str) -> Result<usize> {
let key = match p.get("cloud") {
Some(Value::Number(n)) => n.to_string(),
Some(Value::String(x)) => x.clone(),
_ => return Err(bad(cmd, "`cloud` must be an id or a name")),
};
s.doc.point_clouds.iter().position(|c| c.id.to_string() == key || c.name == key).ok_or_else(|| bad(cmd, format!("no point cloud `{key}`")))
}
fn edit(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "pointcloud.edit";
let i = find(s, p, cmd)?;
let size = point_size(p, cmd)?;
let colors = (color_by(p, cmd)?, color(p, cmd)?, height_range(p, cmd)?);
let units = units_of(p, cmd)?;
let placement = match p.get("matrix") {
None | Some(Value::Null) => None,
Some(m) => Some(super::component::matrix::rigid(m, super::component::matrix::tolerance(p, cmd)?, cmd)?),
};
let name = str_(p, "name").map(str::trim).filter(|n| !n.is_empty()).map(str::to_string);
if let Some(n) = &name
&& s.doc.point_clouds.iter().enumerate().any(|(k, c)| k != i && &c.name == n)
{
return Err(bad(cmd, format!("another point cloud is named `{n}`")));
}
let mut c = s.doc.point_clouds.get(i).cloned().ok_or_else(|| bad(cmd, "point cloud"))?;
if let Some((unit, k)) = units {
// The points came in as `c.units`: scale them to what they are in `unit`.
let from = unit_scale(&c.units).unwrap_or(1.0);
let f = k / from;
if f != 1.0 {
c.origin = c.origin * f;
c.points = Points::new(c.points.values().iter().map(|v| (f64::from(*v) * f) as f32).collect());
}
c.units = unit.into();
}
if let Some(m) = placement {
c.transform = m;
}
if let Some(v) = size {
c.point_size = v;
}
set_colors(&mut c, colors.0, colors.1, colors.2);
if let Some(v) = bool_(p, "visible") {
c.visible = v;
}
if let Some(n) = name {
c.name = n;
}
// A scale that puts a point out of range is refused, leaving the cloud as it was.
c.check()?;
let id = c.id;
let slot = s.doc_mut().point_clouds.get_mut(i).ok_or_else(|| bad(cmd, "point cloud"))?;
*slot = c;
Ok(json!({"cloud": id}))
}
fn delete(s: &mut Session, p: &Value) -> Result<Value> {
let i = find(s, p, "pointcloud.delete")?;
let c = s.doc_mut().point_clouds.remove(i);
Ok(json!({"deleted": c.id}))
}
fn list(s: &mut Session, _: &Value) -> Result<Value> {
let clouds: Vec<Value> = s
.doc
.point_clouds
.iter()
.map(|c| {
json!({
"cloud": c.id,
"name": c.name,
"file": c.file,
"points": c.len(),
"source_points": c.source_points,
"units": c.units,
"visible": c.visible,
"point_size": c.point_size,
"color_by": c.color_by.name(),
"color": c.color,
"colored": !c.colors.is_empty(),
"height_range": c.height_range_now(),
"height_auto": c.height_range.is_none(),
"bounds": bounds(c),
"matrix": matrix_rows(&c.transform),
})
})
.collect();
Ok(json!({"clouds": clouds}))
}
#[cfg(test)]
mod tests {
use serde_json::json;
use solidcraft_doc::point_cloud::ColorBy;
use crate::Session;
fn ply(dir: &std::path::Path, name: &str, points: &[[f32; 3]], colors: bool) -> String {
let mut b = format!(
"ply\nformat binary_little_endian 1.0\ncomment a test scan\nelement vertex {}\nproperty float x\nproperty float y\nproperty float z\n",
points.len()
);
if colors {
b.push_str("property uchar red\nproperty uchar green\nproperty uchar blue\n");
}
b.push_str("end_header\n");
let mut b = b.into_bytes();
for (i, p) in points.iter().enumerate() {
for v in p {
b.extend(v.to_le_bytes());
}
if colors {
b.extend([i as u8, 100, 200]);
}
}
let path = dir.join(name);
std::fs::write(&path, b).unwrap();
path.to_string_lossy().to_string()
}
fn dir(test: &str) -> std::path::PathBuf {
let d = std::env::temp_dir().join(format!("solidcraft-pointcloud-{test}-{}", std::process::id()));
std::fs::create_dir_all(&d).unwrap();
d
}
#[test]
fn insert_a_scan_in_metres_edit_it_and_save_it_with_the_design() {
let d = dir("insert");
let path = ply(&d, "room.ply", &[[0.0, 0.0, 0.0], [2.0, 0.0, 0.0], [2.0, 3.0, 1.5], [10.0, 20.0, 2.5]], true);
let mut s = Session::default();
let r = s.execute("pointcloud.insert", &json!({"path": path, "units": "m"})).unwrap();
assert_eq!((r["name"].as_str(), r["points"].as_u64(), r["source_points"].as_u64()), (Some("room"), Some(4), Some(4)));
assert_eq!(r["bounds"], json!([[0.0, 0.0, 0.0], [10000.0, 20000.0, 2500.0]]));
let id = r["cloud"].as_u64().unwrap();
let c = &s.doc.point_clouds[0];
assert_eq!(c.colors.get(2), Some([2, 100, 200]));
assert_eq!(c.world_point(2).map(|p| [p.x, p.y, p.z]), Some([2000.0, 3000.0, 1500.0]));
// The file was in millimetres after all; then moved 100 mm along X.
s.execute("pointcloud.edit", &json!({"cloud": "room", "units": "mm", "point_size": 4, "color": [1, 0.5, 0]})).unwrap();
s.execute("pointcloud.edit", &json!({"cloud": id, "matrix": [1, 0, 0, 100, 0, 1, 0, 0, 0, 0, 1, 0]})).unwrap();
let c = &s.doc.point_clouds[0];
assert_eq!(c.world_point(3).map(|p| [p.x, p.y, p.z]), Some([110.0, 20.0, 2.5]));
assert_eq!((c.point_size, c.color, c.color_by), (4.0, Some([1.0, 0.5, 0.0]), ColorBy::Single));
// By height, its own range, then the automatic one again; back to the file's colours.
s.execute("pointcloud.edit", &json!({"cloud": id, "color_by": "height", "height_range": [0, 10]})).unwrap();
let listed = s.execute("pointcloud.list", &json!({})).unwrap();
assert_eq!((listed["clouds"][0]["color_by"].as_str(), listed["clouds"][0]["height_range"].clone()), (Some("height"), json!([0.0, 10.0])));
s.execute("pointcloud.edit", &json!({"cloud": id, "height_range": null})).unwrap();
let listed = s.execute("pointcloud.list", &json!({})).unwrap();
assert_eq!(listed["clouds"][0]["height_auto"], true);
assert_eq!(s.doc.point_clouds[0].color_by, ColorBy::Height);
s.execute("pointcloud.edit", &json!({"cloud": id, "color": null})).unwrap();
assert_eq!(s.doc.point_clouds[0].color_by, ColorBy::File);
// The single colour was kept meanwhile.
s.execute("pointcloud.edit", &json!({"cloud": id, "color_by": "single"})).unwrap();
assert_eq!(s.doc.point_clouds[0].color, Some([1.0, 0.5, 0.0]));
let listed = s.execute("pointcloud.list", &json!({})).unwrap();
assert_eq!(listed["clouds"][0]["units"], "mm");
assert_eq!(listed["clouds"][0]["matrix"][3], 100.0);
// Undo-able; saved and read back with the design file, points and all.
s.execute("pointcloud.edit", &json!({"cloud": id, "visible": false})).unwrap();
s.execute("edit.undo", &json!({})).unwrap();
assert!(s.doc.point_clouds[0].visible);
let back = solidcraft_doc::Document::from_json(&s.doc.to_json()).unwrap();
assert_eq!(back.point_clouds, s.doc.point_clouds);
let file = solidcraft_io::write_design_container(&s.doc).unwrap();
let opened = solidcraft_io::read_design(&file).unwrap();
assert_eq!(opened.point_clouds, s.doc.point_clouds);
s.execute("pointcloud.delete", &json!({"cloud": id})).unwrap();
assert!(s.doc.point_clouds.is_empty());
let _ = std::fs::remove_dir_all(d);
}
#[test]
fn a_large_scan_is_thinned_and_centred() {
let d = dir("thin");
let points: Vec<[f32; 3]> = (0..100).map(|i| [i as f32, 1000.0, 50.0 + (i % 2) as f32]).collect();
let path = ply(&d, "line.ply", &points, false);
let mut s = Session::default();
let r = s.execute("pointcloud.insert", &json!({"path": path, "max_points": 10, "center": true, "name": "Line"})).unwrap();
assert_eq!((r["points"].as_u64(), r["source_points"].as_u64()), (Some(10), Some(100)));
assert_eq!(r["warnings"].as_array().map(Vec::len), Some(1), "{r}");
// The middle of its base at the origin.
assert_eq!(r["bounds"], json!([[-45.0, 0.0, 0.0], [45.0, 0.0, 0.0]]));
assert!(s.doc.point_clouds[0].colors.is_empty());
// A second insert of the same file gets a name of its own.
let r = s.execute("pointcloud.insert", &json!({"path": path})).unwrap();
assert_eq!(r["name"], "line");
let r = s.execute("pointcloud.insert", &json!({"path": path})).unwrap();
assert_eq!(r["name"], "line (2)");
let _ = std::fs::remove_dir_all(d);
}
#[test]
fn hostile_point_cloud_params_never_panic() {
let d = dir("hostile");
let path = ply(&d, "p.ply", &[[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]], false);
let bad = d.join("bad.ply");
std::fs::write(
&bad,
b"ply\nformat binary_little_endian 1.0\nelement vertex 99999999999\nproperty float x\nproperty float y\nproperty float z\nend_header\n",
)
.unwrap();
let mut s = Session::default();
for p in [
json!({}),
json!({"path": ""}),
json!({"path": d.join("missing.ply").to_string_lossy()}),
json!({"path": bad.to_string_lossy()}),
json!({"path": d.join("a.stl").to_string_lossy()}),
json!({"path": path, "units": "parsec"}),
json!({"path": path, "units": 3}),
json!({"path": path, "max_points": 0}),
json!({"path": path, "max_points": -5}),
json!({"path": path, "max_points": 1e30}),
json!({"path": path, "point_size": 1e9}),
json!({"path": path, "point_size": "big"}),
json!({"path": path, "color": [2, 0, 0]}),
json!({"path": path, "color": "red"}),
json!({"path": path, "color_by": "rainbow"}),
json!({"path": path, "height_range": [5, 1]}),
json!({"path": path, "height_range": [0, "x"]}),
json!({"path": path, "height_range": [0, 1e12]}),
] {
assert!(s.execute("pointcloud.insert", &p).is_err(), "{p}");
}
assert!(s.doc.point_clouds.is_empty());
let id = s.execute("pointcloud.insert", &json!({"path": path})).unwrap()["cloud"].as_u64().unwrap();
for p in [
json!({}),
json!({"cloud": 999}),
json!({"cloud": [1]}),
json!({"cloud": id, "matrix": [2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0]}),
json!({"cloud": id, "matrix": "x"}),
json!({"cloud": id, "units": "km"}),
json!({"cloud": id, "point_size": -1}),
] {
assert!(s.execute("pointcloud.edit", &p).is_err(), "{p}");
}
assert!(s.execute("pointcloud.delete", &json!({"cloud": "nope"})).is_err());
// Scaled out of range: refused, and the cloud is as it was.
let far = ply(&d, "far.ply", &[[2e6, 0.0, 0.0], [2e6, 1.0, 0.0]], false);
let far = s.execute("pointcloud.insert", &json!({"path": far})).unwrap()["cloud"].as_u64().unwrap();
let before = s.doc.point_clouds.clone();
assert!(s.execute("pointcloud.edit", &json!({"cloud": far, "units": "m"})).is_err());
assert_eq!(s.doc.point_clouds, before);
let _ = std::fs::remove_dir_all(d);
}
}
+3 -1
View File
@@ -95,12 +95,14 @@ CommandSpec::new("render.settings.capture_hdr", "Save Render Settings with HDR",
.params("name, set: typed CPU/GPU setup patch with hdr/environment unset; path: scoped HDR; expected_sha256?: digest returned by render.hdr.inspect (changed sources refuse); rotation_rad?:0, intensity?:1; samples?:1/4/16/64 for CPU only (default16); assume_linear_srgb?:false for GPU only. Captures immutable bytes and saves resource plus setup atomically in one undo, without a host; returns name/setup/resource metadata, never source bytes"),
CommandSpec::new("render.preflight", "Check Render Request", jobs::preflight).icon("inspect").noundo().doc_only()
.params("setup?: saved name OR settings?: full RenderSetup. A typed job selects raster/cpu_raytrace/gpu_path and its numerical options plus embedded HDR before dispatch; only replace may accompany it. Without job, the legacy runtime controls below remain available. See docs/render-settings.md. integrator?: raster (default) or raytrace; progressive?: false (registered GPU path, CPU integrator/LED options are mutually exclusive); samples?: 1..4096; max_depth?: 1..16; seed?: u32 for progressive; raytrace?: bounded runtime progressive/primary_samples/lights/punctual_lights/area_lights/area_samples/seed/exposure_ev/shadows/reflections/anisotropic_faces/emissions/surface_effects/dielectrics/environment options, only with raytrace. CPU raytrace.budget?: compact (legacy default), studio or workstation; see docs/render-cpu-resource-profiles.md for explicit image/geometry/memory/work limits. CPU raytrace.progressive?: {samples_per_pixel:1..profile limit, seed:u32}; requires primary_samples:1 and selected reflections.samples:1; no area/LED, anisotropy or dielectrics in this initial mode. See docs/render-cpu-progressive.md; environment?: {path: scoped HDR, expected_sha256?: inspected digest, rotation_radians: 0, intensity: 1, samples: 16} OR {resource: saved HDR digest, rotation_radians, intensity, samples}; embedded resources cannot combine with path/hash; explicit sRGB/D65 HDR primaries required. Opaque CPU preview with selected mirror/rough-specular reflections and certified planar anisotropy. HDR with rough reflections or anisotropy requires fully metallic selected bodies and all effective faces; unsupported combinations refuse. No MIS or GI. CPU runtime dielectrics support only certified thin_sheet/closed_box boundaries with explicit IOR and Beer absorption; see docs/render-raytrace-dielectrics.md. CPU emissions are visible radiance only; surface_effects change roughness/shading normals without geometry changes. led_strip?: continuous world-mm LED rectangle recipe; led_samples?: 1/4/16/64 for the whole strip, default16. At most8directional+8punctual+4rectangle/LED sources; shared area N. See docs/render-light-union.md. transparent_background?: false; replace?: false. With progressive:true, raytrace instead accepts GPU dielectrics/clear_coats/textures; environment instead accepts tagged hdr/embedded_hdr/sky with rotation_rad/intensity; file hdr supports expected_sha256, embedded_hdr uses a document resource digest. See docs/render-hdr-resources.md. GPU lens, studio lighting, tone_map, denoise, bounded finishing/backdrop and linear_exr retention are also available. GPU adaptive?: {pilot_samples:16..256,min_samples:16..samples,relative_error:0..1,absolute_error:0.000001..1000000,coverage_error:0.001..0.25} allocates independent production budgets; pilots are discarded, not a certified error guarantee. transmission_samples?:1|2|4 stratifies the first Fresnel branch with extra path cost. volume?:{min_mm:[x,y,z],max_mm:[x,y,z],extinction_per_mm:0..1000,scattering_albedo:[r,g,b],anisotropy:-0.95..0.95} is one finite homogeneous world-mm medium; active media require explicit lighting/environment and refuse glass, denoise and multiple transmission strata. These fields also persist in GPU job.options. See docs/render-materials/TRANSPORT-MASTER-BATCH-2026-10-23.md; see docs/render-transmission.md, render-environment.md, render-runtime-textures.md, render-thin-lens.md, render-studio-lighting.md and render-denoise.md. Optional CPU raytracing requires a raytrace-cpu build and native host, checked before scene capture. Stale geometry is rejected. See docs/render-jobs.md"),
CommandSpec::new("render.start", "Render Image", jobs::start).noundo().doc_only().at("RENDER", "IMAGE").icon("render_image")
CommandSpec::new("render.start", "Render Image", jobs::start).key("R").noundo().doc_only().at("RENDER", "IMAGE").icon("render_image")
.params("Same request as render.preflight. Captures an immutable product scene and queues a bounded image job. Returns its session-scoped job identity, captured scene_generation and progress. Does not change the design. See docs/render-jobs.md"),
CommandSpec::new("render.status", "Render Progress", jobs::status).icon("list").noundo().doc_only()
.params("job: {session, serial, generation}. Non-blocking progress, error, optional CPU preview metadata {frame_sequence,samples_per_pixel,frame}, and completed frame metadata, optional sampling allocation/work counts and retained-master presentation provenance; no image bytes and no rendering in this call. Preview is separate from the exportable final image."),
CommandSpec::new("render.cancel", "Cancel Render", jobs::cancel).icon("delete").noundo().doc_only()
.params("job: {session, serial, generation}. Requests cooperative cancellation; completed jobs remain completed. Late cancelled frames cannot be published."),
CommandSpec::new("render.result.copy", "Copy Render Image", jobs::copy).icon("copy").noundo().doc_only()
.params("job: completed JobId. Validates the retained PNG for app clipboard copying (max 4096 per side, 4194304 pixels); headless returns frame metadata only. Never copies a provisional image or starts a job."),
CommandSpec::new("render.export", "Export Render Image", jobs::export).icon("export").noundo().doc_only()
.params("job: {session, serial, generation}, path: destination matching format?: png (default) or exr, overwrite?: false. EXR requires an opt-in raw linear master retained by that completed GPU job; never converts PNG pixels. Exports a retained completed image atomically; an existing destination is preserved on failure. See docs/render-jobs.md"),
CommandSpec::new("render.backends", "Render Backends", backends).icon("list")
+19
View File
@@ -1197,3 +1197,22 @@ fn a_setup_saved_with_exposure_renders_and_names_the_controls_it_leaves_out() {
let id: JobId = serde_json::from_value(started["job"].clone()).unwrap();
assert_eq!(s.execute("render.status", &json!({"job":id})).unwrap()["progress"]["phase"], "completed");
}
#[test]
fn output_viewer_copy_validates_completed_job_without_mutation_or_pixel_payloads() {
let (mut s, host) = session();
let job = start(&mut s);
assert!(s.execute("render.result.copy", &json!({"job":job})).is_err());
finish(&host);
let document = s.doc.clone();
let before = (s.revision, s.undo.len(), s.redo.len(), host.admitted_bytes());
let copied = s.execute("render.result.copy", &json!({"job":job})).unwrap();
assert_eq!(copied["frame"]["job"], json!(job));
assert_eq!(copied["frame"]["width"], 16);
assert!(copied.get("png").is_none());
for params in [json!({}), json!(null), json!({"job":job,"extra":true}), json!({"job":{"session":0,"serial":0,"generation":0}})] {
assert!(s.execute("render.result.copy", &params).is_err());
}
assert!(Arc::ptr_eq(&document, &s.doc));
assert_eq!(before, (s.revision, s.undo.len(), s.redo.len(), host.admitted_bytes()));
}
+16
View File
@@ -706,6 +706,22 @@ pub(super) fn pin(s: &mut Session, params: &Value) -> Result<Value> {
Ok(json!({"frame":s.render_jobs.pin(input.job).map_err(job_error)?}))
}
pub(super) fn copy(s: &mut Session, params: &Value) -> Result<Value> {
let input: Job = serde_json::from_value(params.clone()).map_err(|e| bad("render.result.copy", e.to_string()))?;
let frame = s
.render_jobs
.handle(input.job)
.map_err(job_error)?
.frame()
.map_err(job_error)?
.ok_or_else(|| bad("render.result.copy", "render has no completed image"))?;
let size = (u64::from(frame.info.width), u64::from(frame.info.height));
if solidcraft_render::RenderLimits::default().fit(size.0, size.1, None) != Some(size) {
return Err(bad("render.result.copy", "clipboard image exceeds 4096 per side or 4194304 pixels; export the PNG instead"));
}
Ok(json!({"frame":frame.info}))
}
pub(super) fn cancel(s: &mut Session, params: &Value) -> Result<Value> {
let input: Job = serde_json::from_value(params.clone()).map_err(|e| bad("render.cancel", e.to_string()))?;
let status = s.render_jobs.status(input.job).map_err(job_error)?;
+95 -1
View File
@@ -1,5 +1,8 @@
//! Planned Render interactions, with explicit non-mutating refusals.
use super::CommandSpec;
use crate::{Result, Session, params::bad};
use serde::Deserialize;
use serde_json::{Value, json};
macro_rules! waiting {
($id:literal, $label:literal, $tab:literal, $panel:literal, $icon:literal, $reason:literal) => {
@@ -15,6 +18,23 @@ macro_rules! waiting {
}
pub static COMMANDS: &[CommandSpec] = &[
CommandSpec::new("render.output.size", "Set Render Output Size", output_size)
.icon("settings")
.noundo()
.doc_only()
.params("width, height: 1..16384; custom?: bool. Validated Render output draft request; headless echo only, no design edits or job."),
CommandSpec::new("render.result.view", "View Render Image", result_view)
.icon("render_image")
.noundo()
.doc_only()
.params("view: model|image|fit|actual. Validated result canvas request; headless echo only, never starts a job."),
CommandSpec::new("render.result.close", "Close Render Image", close_result)
.icon("close")
.key("Escape")
.noundo()
.doc_only()
.params("No parameters. Return to Model Preview; headless echo only, never cancels a job."),
waiting!("render.lights.add", "Add Light", "RENDER SCENE", "LIGHTS", "render_scene", "named light creation"),
waiting!("appearance.paint", "Paint Appearance", "RENDER APPEARANCE", "APPEARANCE", "appearance", "product picking and appearance painting"),
waiting!("appearance.pick_up", "Pick Up Appearance", "RENDER APPEARANCE", "APPEARANCE", "select", "product picking and appearance sampling"),
waiting!("render.lights.solo", "Solo Light", "RENDER SCENE", "LIGHTS", "render_scene", "light visibility and solo controls"),
@@ -26,6 +46,7 @@ pub static COMMANDS: &[CommandSpec] = &[
#[test]
fn render_workspace_planned_commands_refuse_without_mutation() {
use serde_json::{Value, json};
assert!(super::find_command("appearance.replace").is_some_and(|command| command.planned.is_none()));
let mut session = crate::Session::default();
session.execute("render.settings.set", &json!({"name":"Studio","set":{"width":800}})).unwrap();
session.execute("render.settings.set", &json!({"name":"Studio","set":{"width":900}})).unwrap();
@@ -33,7 +54,9 @@ fn render_workspace_planned_commands_refuse_without_mutation() {
let document = session.doc.clone();
let before = (session.undo.len(), session.redo.len(), session.revision, session.evaluations, session.is_dirty());
for command in COMMANDS {
assert!(command.planned.is_some());
if command.planned.is_none() {
continue;
}
assert!(!command.info(&session).enabled);
for params in [json!({}), Value::Null, json!([]), json!({"target":1e300,"name":"hostile"})] {
let error = session.execute(command.id, &params).unwrap_err().to_string();
@@ -43,3 +66,74 @@ fn render_workspace_planned_commands_refuse_without_mutation() {
}
}
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct OutputSize {
width: u32,
height: u32,
#[serde(default)]
custom: bool,
}
fn output_size(_: &mut Session, params: &Value) -> Result<Value> {
let input: OutputSize = serde_json::from_value(params.clone()).map_err(|e| bad("render.output.size", e.to_string()))?;
if !(1..=16384).contains(&input.width) || !(1..=16384).contains(&input.height) {
return Err(bad("render.output.size", "dimensions must be in 1..16384"));
}
let mut reply = json!({"width":input.width,"height":input.height});
if input.custom {
reply["custom"] = json!(true);
}
Ok(reply)
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct ResultView {
view: String,
}
fn result_view(_: &mut Session, params: &Value) -> Result<Value> {
let input: ResultView = serde_json::from_value(params.clone()).map_err(|e| bad("render.result.view", e.to_string()))?;
if !matches!(input.view.as_str(), "model" | "image" | "fit" | "actual") {
return Err(bad("render.result.view", "view must be model, image, fit or actual"));
}
Ok(json!({"view":input.view}))
}
fn close_result(_: &mut Session, params: &Value) -> Result<Value> {
if !params.as_object().is_some_and(|p| p.is_empty()) {
return Err(bad("render.result.close", "expected no parameters"));
}
Ok(json!({"view":"model"}))
}
#[cfg(test)]
#[test]
fn output_viewer_requests_are_validated_headless_echoes() {
let mut s = Session::default();
let document = s.doc.clone();
let before = (s.revision, s.undo.len(), s.evaluations);
for size in [json!({"width":1280,"height":720}), json!({"width":1920,"height":1080})] {
assert_eq!(s.execute("render.output.size", &size).unwrap(), size);
}
let custom = json!({"width":1920,"height":1080,"custom":true});
assert_eq!(s.execute("render.output.size", &custom).unwrap(), custom);
assert!(s.execute("render.output.size", &json!({"width":1920,"height":1080,"custom":"yes"})).is_err());
for view in ["model", "image", "fit", "actual"] {
let request = json!({"view":view});
assert_eq!(s.execute("render.result.view", &request).unwrap(), request);
}
assert_eq!(s.execute("render.result.close", &json!({})).unwrap(), json!({"view":"model"}));
for request in
[json!(null), json!({}), json!({"width":0,"height":1}), json!({"width":u64::MAX,"height":1}), json!({"width":1,"height":1,"extra":true})]
{
assert!(s.execute("render.output.size", &request).is_err());
}
for request in [json!(null), json!({"view":"unknown"}), json!({"view":"image","extra":true})] {
assert!(s.execute("render.result.view", &request).is_err());
// Session normalizes null parameters to an empty object for no-argument commands.
if request.is_object() {
assert!(s.execute("render.result.close", &request).is_err());
}
}
assert!(std::sync::Arc::ptr_eq(&document, &s.doc));
assert_eq!(before, (s.revision, s.undo.len(), s.evaluations));
}
+1
View File
@@ -197,6 +197,7 @@ pub(crate) fn script_steps(doc: &Document) -> Result<(Vec<Value>, Vec<String>)>
(doc.appearances.is_empty(), "appearances (colours of bodies and faces)"),
(doc.configs.is_empty(), "configurations"),
(doc.canvases.is_empty(), "canvases (reference images)"),
(doc.point_clouds.is_empty(), "point clouds"),
(doc.named_views.is_empty(), "named views"),
(doc.browser_groups.is_empty() && doc.browser_order.is_empty(), "Browser folders and order"),
(doc.timeline_groups.is_empty(), "timeline folders"),
+2 -2
View File
@@ -242,7 +242,7 @@ fn flange(s: &mut Session, p: &Value) -> Result<Value> {
}
"contour" => {
let sketch = sketch_id(s, p.get("sketch"), cmd, "sketch")?;
let curves = string_list(p, "curves");
let curves = string_list(cmd, p, "curves")?;
if curves.is_empty() || curves.len() > 1000 {
return Err(bad(cmd, "`curves` must list the open chain's lines"));
}
@@ -464,7 +464,7 @@ fn path_flange(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sheet.path_flange";
let edge = p.get("edge").and_then(vec3).ok_or_else(|| bad(cmd, "`edge` must be a point [x, y, z] on a sheet edge"))?;
let sketch = sketch_id(s, p.get("sketch"), cmd, "sketch")?;
let curves = string_list(p, "curves");
let curves = string_list(cmd, p, "curves")?;
if curves.is_empty() || curves.len() > sheet::MAX_PATH_LINES {
return Err(bad(cmd, format!("`curves` must list the path's lines (1…{})", sheet::MAX_PATH_LINES)));
}
+73 -86
View File
@@ -15,6 +15,7 @@ mod dimensioned;
mod drag;
#[path = "sketch_face.rs"]
pub(super) mod face;
use super::sketch_store;
pub use dimensioned::dimension_roles;
use dimensioned::draw as draw_dimensioned;
use drag::drag_entity;
@@ -99,7 +100,7 @@ pub static COMMANDS: &[CommandSpec] = &[
.at("SKETCH", "CREATE")
.icon("arc_center")
.enabled(in_sketch)
.params(concat!("center, start, end (counter-clockwise from start) | center, start, sweep: signed degrees; construction?: bool", connect_doc!(), created_doc!())),
.params(concat!("center, start, end (counter-clockwise from start; clockwise?: bool for clockwise) | center, start, sweep: signed degrees; construction?: bool", connect_doc!(), created_doc!())),
CommandSpec::new("sketch.polygon.inscribed", "Inscribed Polygon", polygon_inscribed)
.at("SKETCH", "CREATE")
.icon("polygon")
@@ -127,7 +128,7 @@ pub static COMMANDS: &[CommandSpec] = &[
.params(concat!("p0, p1: overall ends, width", connect_doc!(), created_doc!())),
CommandSpec::new("sketch.point", "Point", draw_point).at("SKETCH", "CREATE").icon("point").enabled(in_sketch).params(concat!("point: [x,y] (each a number or an expression over the design's parameters: the point is then fixed where it comes to and follows them) or a snap: \"mid:<line>\" (held at the line's midpoint), \"on:<curve>:x,y\" (on the curve near x,y), {vertex: [x,y,z]} or {on_edge: [x,y,z]} of the model; id?", connect_doc!(), created_doc!())),
CommandSpec::new("sketch.dimension", "Sketch Dimension", dimension).at("SKETCH", "CREATE").icon("dimension").key("D").enabled(in_sketch).params(
"entities: [refs] (a line midpoint is \"mid:line_id\"), type?: auto|distance|horizontal|distance_x|vertical|distance_y|length|radius|diameter|angle, value?: number or expression (default: current; a bare number is in the design's units, e.g. 2 means 2 in in an inch design; give a unit such as \"50 mm\" to override), or `name = expression` (e.g. \"width = 40\"; also `#name = expression`), which adds the user parameter `name` with that expression and dimensions with `name` (refused when the name is taken or reserved; the reply's `defined` names it); text_at?: [x, y]. Two points or a line off the axes: with auto, text_at across from them is the distance between them, above or below them horizontal, beside them vertical. Two midpoints: placing text above or below their bounding box measures horizontally, beside it vertically. Two lines: the angle between them (acute, or 180° − it when the value is nearer that) unless text_at picks a sector. A line and a circle or arc: the distance from the line to its edge (the gap, or how far the edge reaches past a line that cuts it; 0 is tangent); dimension the centre point for the distance to the centre. A point and a circle or arc: the distance from the point to its edge (or how far within it a point inside lies). Two circles or arcs: the gap between their edges (or how far they overlap when they cross, or the narrowest gap between them when one lies inside the other), or the centre-to-centre distance when they are the two caps of a slot. An arc and one of its end points: the arc's length (type arc_length does it from the arc alone). A dimension the sketch already determines is refused (naming what determines it); if_redundant?: \"driven\" places it as a driven (reference) dimension instead. scale_sketch?: bool (the dimension tool sends true): when this is the sketch's first length dimension and nothing in the sketch fixes a size (no other driving length dimension, nothing fixed or projected), the whole sketch scales about its origin to the value instead of distorting. Returns measured in unit (mm or deg), and scaled: the factor when the sketch was scaled",
"entities: [refs] (a line midpoint is \"mid:line_id\"), type?: auto|distance|horizontal|distance_x|vertical|distance_y|length|radius|diameter|angle, value?: number or expression (default: current; a bare number is in the design's units, e.g. 2 means 2 in in an inch design; give a unit such as \"50 mm\" to override), or `name = expression` (e.g. \"width = 40\"; also `#name = expression`), which adds the user parameter `name` with that expression and dimensions with `name` (refused when the name is taken or reserved; the reply's `defined` names it); text_at?: [x, y]. Two points, two midpoints or a line off the axes: with auto, text_at inside the box they span (as opposite corners) or in its outer corners is the distance between them, above or below the box horizontal, beside it vertical. Two lines: the angle between them (acute, or 180° − it when the value is nearer that) unless text_at picks a sector. A line and a circle or arc: the distance from the line to its edge (the gap, or how far the edge reaches past a line that cuts it; 0 is tangent); dimension the centre point for the distance to the centre. A point and a circle or arc: the distance from the point to its edge (or how far within it a point inside lies). Two circles or arcs: the gap between their edges (or how far they overlap when they cross, or the narrowest gap between them when one lies inside the other), or the centre-to-centre distance when they are the two caps of a slot. An arc and one of its end points: the arc's length (type arc_length does it from the arc alone). A dimension the sketch already determines is refused (naming what determines it); if_redundant?: \"driven\" places it as a driven (reference) dimension instead. scale_sketch?: bool (the dimension tool sends true): when this is the sketch's first length dimension and nothing in the sketch fixes a size (no other driving length dimension, nothing fixed or projected), the whole sketch scales about its origin to the value instead of distorting. Returns measured in unit (mm or deg), and scaled: the factor when the sketch was scaled",
),
CommandSpec::new("sketch.constraint.horizontal_vertical", "Horizontal/Vertical", c_horizontal_vertical)
.at("SKETCH", "CONSTRAINTS")
@@ -219,23 +220,6 @@ pub static COMMANDS: &[CommandSpec] = &[
// ---------------------------------------------------------------------------------------------
// Helpers
/// Resolve the sketch a command acts on: `sketch` param (id or name) or the active sketch.
pub(super) fn target_sketch(s: &Session, p: &Value, cmd: &str) -> Result<u64> {
if let Some(v) = p.get("sketch") {
let key = match v {
Value::Number(n) => n.to_string(),
Value::String(x) => x.clone(),
_ => return Err(bad(cmd, "`sketch` must be an id or a name")),
};
let f = s.doc.find_feature(&key).ok_or_else(|| bad(cmd, format!("no sketch `{key}`")))?;
if !matches!(f.kind, FeatureKind::Sketch { .. }) {
return Err(bad(cmd, format!("`{key}` is not a sketch")));
}
return Ok(f.id);
}
s.active_sketch.ok_or_else(|| EngineError::Other("no sketch is being edited".into()))
}
/// Edit a sketch: clone it, apply `f`, solve with the document's dimension values and store
/// it. With `strict`, an edit that makes the constraints unsolvable is rejected.
pub(super) fn edit<T>(
@@ -245,7 +229,7 @@ pub(super) fn edit<T>(
strict: bool,
f: impl FnOnce(&mut Sketch, &mut solidcraft_doc::Document) -> Result<T>,
) -> Result<(T, Value)> {
let id = target_sketch(s, p, cmd)?;
let id = sketch_store::target(s, p, cmd)?;
let (out, rep) = edit_solved(s, id, strict, Analysis::Freedom, |sk, doc| Ok((f(sk, doc)?, Left::Unsolved)))?;
Ok((out, rep.map(|rep| report_json(&rep)).unwrap_or(Value::Null)))
}
@@ -289,7 +273,7 @@ pub(super) fn edit_solved<T>(
f: impl FnOnce(&mut Sketch, &mut solidcraft_doc::Document) -> Result<(T, Left)>,
) -> Result<(T, Option<SolveReport>)> {
let mut doc = (*s.doc).clone();
let mut sk = doc.sketch(id)?.clone();
let mut sk = sketch_store::read(&doc, id)?.clone();
if sk.patterns.iter().any(|p| p.count_expression.is_some() || !p.expressions.is_empty()) {
doc.apply_pattern_values(&doc.param_values().0, &mut sk)?;
}
@@ -336,8 +320,8 @@ pub(super) fn edit_solved<T>(
Some(settle(strict, rep, &mut sk, unsolved)?)
}
};
sk.validate_periodic_splines()?;
*doc.sketch_mut(id)? = sk;
sketch_store::checked(&sk)?;
sketch_store::write(&mut doc, id, sk)?;
*s.doc_mut() = doc;
Ok((out, rep))
}
@@ -882,7 +866,7 @@ fn entry_point(s: &Session) -> SketchEntry {
/// and Undo is the way back.
fn cancel_sketch(s: &mut Session, _p: &Value) -> Result<Value> {
let cmd = "sketch.cancel";
let id = s.active_sketch.ok_or_else(|| bad(cmd, "no sketch is being edited"))?;
let id = sketch_store::active(s).ok_or_else(|| bad(cmd, "no sketch is being edited"))?;
let entry = s.sketch_entry.clone().filter(|entry| entry.sketch == id);
let current = entry.filter(|entry| {
let top = entry.undo_len.checked_sub(1).and_then(|i| s.undo.get(i)).map(|snap| &snap.doc);
@@ -930,7 +914,7 @@ fn draw_line(s: &mut Session, p: &Value) -> Result<Value> {
}
let closed = bool_(p, "closed").unwrap_or(false);
let infer = bool_(p, "infer").unwrap_or(false);
let ids = string_list(p, "ids");
let ids = string_list(cmd, p, "ids")?;
let ((curves, cons), info) = draw(s, p, cmd, |sk, doc| {
let args: Vec<PArg> = list.iter().map(|v| parg(sk, doc, v, cmd)).collect::<Result<_>>()?;
let n = args.len();
@@ -1400,7 +1384,31 @@ fn arc_center(s: &mut Session, p: &Value) -> Result<Value> {
return Ok((ids_of(sk, &[ci]), Vec::new()));
}
let b = req_parg(sk, doc, p, "end", cmd)?;
let ci = sk.add_arc(c.pos(), a.pos(), b.pos(), [c.idx(), a.idx(), b.idx()], id.as_deref())?;
// Clockwise from start to end (#6645): stored counter-clockwise from the end and reversed,
// as a negative sweep is. The start keeps the radius and `<id>.start`.
let ci = if bool_(p, "clockwise").unwrap_or(false) {
let r = a.pos().dist(c.pos());
let end = c.pos() + (b.pos() - c.pos()).normalized().ok_or_else(|| bad(cmd, "`end` must not be on `center`"))? * r;
let cid = match &id {
Some(i) => i.clone(),
None => sk.fresh("a")?,
};
let ai = match a.idx() {
Some(i) => i,
None => sk.add_point(a.pos(), Some(&format!("{cid}.start")))?,
};
let bi = match b.idx() {
Some(i) => i,
None => sk.add_point(end, Some(&format!("{cid}.end")))?,
};
let ci = sk.add_arc(c.pos(), end, a.pos(), [c.idx(), Some(bi), Some(ai)], Some(&cid))?;
if let Some(cu) = sk.curves.get_mut(ci) {
cu.reversed = true;
}
ci
} else {
sk.add_arc(c.pos(), a.pos(), b.pos(), [c.idx(), a.idx(), b.idx()], id.as_deref())?
};
mark_construction(sk, &[ci], p);
Ok((ids_of(sk, &[ci]), Vec::new()))
})?;
@@ -1554,7 +1562,12 @@ fn draw_point(s: &mut Session, p: &Value) -> Result<Value> {
// end refuses it, rather than answered as if a point had been made.
let made = str_(p, "point")
.filter(|_| p.get("point") != Some(&given))
.filter(|id| target_sketch(s, p, cmd).ok().and_then(|sid| s.doc.sketch(sid).ok()).is_some_and(|sk| sk.resolve_point(id).is_some()))
.filter(|id| {
sketch_store::target(s, p, cmd)
.ok()
.and_then(|sid| sketch_store::read(&s.doc, sid).ok())
.is_some_and(|sk| sk.resolve_point(id).is_some())
})
.map(str::to_string);
let Some(pid) = made else {
return Err(bad(
@@ -1776,7 +1789,7 @@ pub(super) fn redundancy(
/// The constraints that already determine constraint `cid` of the target sketch, as
/// "id (Name)".
fn determined_by(s: &Session, p: &Value, cmd: &str, cid: &str) -> Vec<String> {
let Some(sk) = target_sketch(s, p, cmd).ok().and_then(|id| s.doc.sketch(id).ok()) else { return Vec::new() };
let Some(sk) = sketch_store::target(s, p, cmd).ok().and_then(|id| sketch_store::read(&s.doc, id).ok()) else { return Vec::new() };
let Some(ci) = sk.constraints.iter().position(|c| c.id == cid) else { return Vec::new() };
solidcraft_sketch::determined_by(sk, ci)
.into_iter()
@@ -1911,7 +1924,7 @@ fn c_coincident(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.constraint.coincident";
let p = &super::sketch_project::project_operands(s, p, cmd, &["a", "b"])?;
// Already tied (e.g. connected when it was drawn there): nothing to add.
let sk = s.doc.sketch(target_sketch(s, p, cmd)?)?;
let sk = sketch_store::read(&s.doc, sketch_store::target(s, p, cmd)?)?;
if let Some((a, b)) = two_round(sk, p) {
return coincident_circles(s, p, cmd, a, b);
}
@@ -2039,7 +2052,7 @@ fn c_equal(s: &mut Session, p: &Value) -> Result<Value> {
/// can, so a dimensioned curve keeps its size and the others take it, whatever the order.
fn equal_curves(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.constraint.equal";
let ids = string_list(p, "curves");
let ids = string_list(cmd, p, "curves")?;
if !(2..=200).contains(&ids.len()) {
return Err(bad(cmd, "`curves` must list 2 to 200 lines or circles/arcs"));
}
@@ -2136,7 +2149,7 @@ fn c_midpoint(s: &mut Session, p: &Value) -> Result<Value> {
/// moves when the point can't.
fn line_stays(s: &Session, p: &Value, cmd: &str) -> Value {
let swapped = (|| {
let sk = s.doc.sketch(target_sketch(s, p, cmd).ok()?).ok()?;
let sk = sketch_store::read(&s.doc, sketch_store::target(s, p, cmd).ok()?).ok()?;
let (a, b) = (p.get("a")?.as_str()?, p.get("b")?.as_str()?);
let line = sk.curve_index(b).filter(|c| matches!(sk.curves.get(*c).map(|c| &c.kind), Some(CurveKind::Line { .. })));
(sk.curve_index(a).is_none() && sk.resolve_point(a).is_some() && line.is_some()).then(|| {
@@ -2309,7 +2322,7 @@ fn round_center(sk: &Sketch, c: usize) -> Option<usize> {
fn c_fix(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.constraint.fix";
let want = bool_(p, "fixed");
let mut refs = string_list(p, "entities");
let mut refs = string_list(cmd, p, "entities")?;
if let Some(e) = str_(p, "entity") {
refs.insert(0, e.to_string());
}
@@ -2374,7 +2387,7 @@ pub(super) fn line_pts(sk: &Sketch, l: usize) -> Option<(Vec2, Vec2)> {
fn dimension(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.dimension";
let ents = string_list(p, "entities");
let ents = string_list(cmd, p, "entities")?;
if ents.is_empty() || ents.len() > 2 {
return Err(bad(cmd, "`entities` must list one or two sketch entities"));
}
@@ -2481,12 +2494,7 @@ fn dimension(s: &mut Session, p: &Value) -> Result<Value> {
}
((Some(a), None), Some((Some(b), None))) => {
let (pa, pb) = (sk.point(a).unwrap_or_default(), sk.point(b).unwrap_or_default());
let orientation = if ents.iter().all(|id| id.starts_with("mid:")) {
midpoint_orientation(&ty, pa, pb, text_at)
} else {
auto_orientation(&ty, pa, pb, text_at)
};
match orientation {
match auto_orientation(&ty, pa, pb, text_at) {
"horizontal" => (ConstraintKind::DistanceX { p: a, q: b, value: (pb.x - pa.x).abs() }, (pb.x - pa.x).abs()),
"vertical" => (ConstraintKind::DistanceY { p: a, q: b, value: (pb.y - pa.y).abs() }, (pb.y - pa.y).abs()),
_ => (ConstraintKind::Distance { p: a, q: b, value: pa.dist(pb) }, pa.dist(pb)),
@@ -2716,54 +2724,33 @@ fn measured(is_angle: bool, v: f64) -> (f64, &'static str) {
/// What the dimension just added (the sketch's last constraint) measures on the solved sketch.
fn solved_measure(s: &Session, p: &Value, cmd: &str) -> Option<(f64, &'static str)> {
let sk = s.doc.sketch(target_sketch(s, p, cmd).ok()?).ok()?;
let sk = sketch_store::read(&s.doc, sketch_store::target(s, p, cmd).ok()?).ok()?;
let k = &sk.constraints.last()?.kind;
Some(measured(k.is_angle(), solidcraft_sketch::measure_dimension(sk, k)?))
}
/// The type of a dimension between `a` and `b` (two points or a line's ends). `auto` takes it
/// from where the text is placed: across from the two (between the perpendiculars through
/// them) the distance between them; past that, above or below them the horizontal distance,
/// beside them the vertical one (#257). With no text, or when the two already line up with
/// an axis, `auto` stays the distance between them; other types are returned unchanged.
/// The type of a dimension between `a` and `b` (two points, a line's ends or two line
/// midpoints). `auto` takes it from where the text is placed relative to the box the two span
/// as opposite corners: inside the box the distance between them; above or below it the
/// horizontal distance, beside it the vertical one; in the corners outside it the distance
/// again (#7295). Every type keeps a zone the size of the box's side whatever the slope, and
/// the box's edges are the thresholds. With no text, or when the two already line up with an
/// axis, `auto` stays the distance between them; other types are returned unchanged.
fn auto_orientation(ty: &str, a: Vec2, b: Vec2, text_at: Option<Vec2>) -> &str {
let Some(t) = text_at.filter(|_| ty == "auto") else { return ty };
let d = b - a;
let len = d.len();
let along = (t - a).dot(d) / len;
if !t.x.is_finite() || !t.y.is_finite() || d.x.abs() <= 1e-9 * len || d.y.abs() <= 1e-9 * len {
return ty;
}
let between = |v: f64, p: f64, q: f64| p.min(q) <= v && v <= p.max(q);
if d.x.abs() <= 1e-9 * len || d.y.abs() <= 1e-9 * len || !along.is_finite() || (0.0..=len).contains(&along) {
ty
} else if between(t.x, a.x, b.x) {
"horizontal"
} else if between(t.y, a.y, b.y) {
"vertical"
} else {
ty
match (between(t.x, a.x, b.x), between(t.y, a.y, b.y)) {
(true, false) => "horizontal",
(false, true) => "vertical",
_ => ty,
}
}
/// Midpoint pairs choose an axis as soon as the text passes their bounding box.
/// Keeping the aligned sector inside that box avoids requiring a long pointer move
/// for shallow diagonals; explicit types and already aligned points retain their behavior.
fn midpoint_orientation(ty: &str, a: Vec2, b: Vec2, text_at: Option<Vec2>) -> &str {
let d = b - a;
if ty == "auto"
&& d.x.abs() > 1e-9 * d.len()
&& d.y.abs() > 1e-9 * d.len()
&& let Some(t) = text_at
{
let between = |v: f64, p: f64, q: f64| p.min(q) <= v && v <= p.max(q);
if between(t.x, a.x, b.x) && !between(t.y, a.y, b.y) {
return "horizontal";
}
if between(t.y, a.y, b.y) && !between(t.x, a.x, b.x) {
return "vertical";
}
}
auto_orientation(ty, a, b, text_at)
}
/// Keep a dimension's text where it was placed (`at`, sketch coordinates), relative to the
/// dimension so it follows the geometry.
fn place_text(sk: &mut Sketch, id: &str, at: Option<Vec2>) {
@@ -2932,7 +2919,7 @@ fn line_ends(sk: &Sketch, l: usize) -> (usize, usize) {
fn construction(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.construction";
let ids = string_list(p, "curves");
let ids = string_list(cmd, p, "curves")?;
if ids.is_empty() {
return Err(bad(cmd, "`curves` must list curve ids"));
}
@@ -2966,7 +2953,7 @@ fn construction(s: &mut Session, p: &Value) -> Result<Value> {
fn sketch_delete(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.delete";
let ids = string_list(p, "entities");
let ids = string_list(cmd, p, "entities")?;
if ids.is_empty() {
return Err(bad(cmd, "`entities` must list sketch entity ids"));
}
@@ -3051,7 +3038,7 @@ fn sketch_delete(s: &mut Session, p: &Value) -> Result<Value> {
fn move_point(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.move_point";
let to = req_vec2(cmd, p, "to")?;
let id = target_sketch(s, p, cmd)?;
let id = sketch_store::target(s, p, cmd)?;
let state = s.model.result(id).map(|r| r.state.clone());
let last = state.as_ref().and_then(|state| state.sketch(id));
let (_, rep) = edit_solved(s, id, false, Analysis::Freedom, |sk, _| {
@@ -3077,7 +3064,7 @@ fn drag_frame(s: &mut Session, p: &Value, live: bool) -> Result<(Value, Option<c
None | Some(Value::Null) => to,
Some(v) => vec2(v).ok_or_else(|| bad(cmd, "`from` must be a point [x, y]"))?,
};
let id = target_sketch(s, p, cmd)?;
let id = sketch_store::target(s, p, cmd)?;
// The sketch's report as last evaluated: a live frame keeps its degrees of freedom.
let state = s.model.result(id).map(|r| r.state.clone());
let last = state.as_ref().and_then(|state| state.sketch(id));
@@ -3114,7 +3101,7 @@ fn drag_frame(s: &mut Session, p: &Value, live: bool) -> Result<(Value, Option<c
})?;
// Lines retain the grabbed material point through rotation. Other entities report the
// average displacement of their moving points, as before.
let sk = s.doc.sketch(id)?;
let sk = sketch_store::read(&s.doc, id)?;
let at = d.at(sk, from);
let rep = rep.or_else(|| last.map(|ss| SolveReport { status: solidcraft_sketch::SolveStatus::Solved, failing: Vec::new(), ..ss.report.clone() }));
let out = json!({"sketch": rep.as_ref().map(report_json), "reached": d.reached, "at": [at.x, at.y], "retry": d.retry});
@@ -3126,7 +3113,7 @@ fn drag_frame(s: &mut Session, p: &Value, live: bool) -> Result<(Value, Option<c
/// evaluate first.
fn drag_end(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.drag_end";
let id = target_sketch(s, p, cmd)?;
let id = sketch_store::target(s, p, cmd)?;
let start = s.doc.clone();
let mut constraint = Value::Null;
if let Some(c) = p.get("constraint").filter(|c| !c.is_null()) {
@@ -3189,8 +3176,8 @@ fn sketch_options(s: &mut Session, p: &Value) -> Result<Value> {
}
let set = |k: &str| bool_(p, k);
if keys.iter().all(|k| set(k).is_none()) {
let id = target_sketch(s, p, cmd)?;
return Ok(json!({"options": options_json(&s.doc.sketch(id)?.view)}));
let id = sketch_store::target(s, p, cmd)?;
return Ok(json!({"options": options_json(&sketch_store::read(&s.doc, id)?.view)}));
}
let (v, info) = edit(s, p, cmd, false, |sk, _| {
let v = &mut sk.view;
@@ -3216,14 +3203,14 @@ fn sketch_options(s: &mut Session, p: &Value) -> Result<Value> {
fn chain_cmd(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.chain";
let id = target_sketch(s, p, cmd)?;
let sk = s.doc.sketch(id)?;
let id = sketch_store::target(s, p, cmd)?;
let sk = sketch_store::read(&s.doc, id)?;
let c = curve_ref(sk, p.get("curve"), cmd, "curve")?;
Ok(json!({"curves": ids_of(sk, &sk.chain(c))}))
}
fn solve_cmd(s: &mut Session, p: &Value) -> Result<Value> {
let id = target_sketch(s, p, "sketch.solve")?;
let id = sketch_store::target(s, p, "sketch.solve")?;
let st = s.model.state();
let ss = st.sketch(id).ok_or_else(|| EngineError::Other("the sketch has not been evaluated".into()))?;
Ok(json!({"status": ss.report.status, "dof": ss.report.dof, "failing": ss.report.failing, "profiles": ss.profiles.len()}))
+5 -4
View File
@@ -7,7 +7,8 @@ use serde_json::{Value, json};
use solidcraft_geom::Vec3;
use solidcraft_sketch::WireSpace;
use super::sketch::{edit, target_sketch};
use super::sketch::edit;
use super::sketch_store;
use super::{CommandSpec, in_sketch};
use crate::params::{bad, req_vec3, vec3};
use crate::{Result, Session};
@@ -53,8 +54,8 @@ fn needs_3d(sk: &solidcraft_sketch::Sketch, cmd: &str) -> Result<()> {
/// The 3D Arc button starts an interactive tool, so it is only offered while a 3D sketch is
/// being edited; otherwise the tool could only fail on its third click.
fn in_sketch_3d(s: &Session) -> std::result::Result<(), String> {
let id = s.active_sketch.ok_or("no sketch is being edited (Create Sketch or Edit Sketch first)")?;
match s.doc.sketch(id) {
let id = sketch_store::active(s).ok_or("no sketch is being edited (Create Sketch or Edit Sketch first)")?;
match sketch_store::read(&s.doc, id) {
Ok(sk) if sk.view.three_d => Ok(()),
Ok(_) => Err("turn on 3D Sketch (sketch.options sketch_3d: true) to draw 3D arcs".into()),
Err(_) => Err("no sketch is being edited (Create Sketch or Edit Sketch first)".into()),
@@ -64,7 +65,7 @@ fn in_sketch_3d(s: &Session) -> std::result::Result<(), String> {
/// World command inputs belong to the target sketch's owner, which may differ from the
/// active component when a script supplies an explicit sketch.
fn inverse_owner_frame(s: &Session, p: &Value, cmd: &str) -> Result<solidcraft_doc::Mat> {
let id = target_sketch(s, p, cmd)?;
let id = sketch_store::target(s, p, cmd)?;
let owner = s.doc.feature(id).map(|f| f.component).ok_or_else(|| bad(cmd, "target sketch is missing"))?;
if owner != 0 && owner != s.active_component {
if s.doc.occurrences.len() > 100_000 {
+11 -10
View File
@@ -11,7 +11,8 @@ use serde_json::{Value, json};
use solidcraft_geom::{Aabb2, Vec2};
use solidcraft_sketch::{ConstraintKind, CurveKind, LinkKind, LinkSource, Sketch};
use super::sketch::{edit, target_sketch};
use super::sketch::edit;
use super::sketch_store;
use super::{CommandSpec, in_sketch};
use crate::params::{bad, string_list, vec2};
use crate::{Result, Sel, Session};
@@ -71,17 +72,17 @@ pub struct SketchClip {
}
/// Curve and point ids named by `entities`, or the selected sketch geometry.
fn chosen(s: &Session, p: &Value) -> Vec<String> {
fn chosen(s: &Session, p: &Value, cmd: &str) -> Result<Vec<String>> {
if p.get("entities").is_some() {
return string_list(p, "entities");
return string_list(cmd, p, "entities");
}
s.selection
Ok(s.selection
.iter()
.filter_map(|sel| match sel {
Sel::SketchCurve { id, .. } | Sel::SketchPoint { id } => Some(id.clone()),
_ => None,
})
.collect()
.collect())
}
fn text_of(sk: &Sketch, ci: usize) -> Option<&str> {
@@ -91,12 +92,12 @@ fn text_of(sk: &Sketch, ci: usize) -> Option<&str> {
fn copy(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.copy";
let id = target_sketch(s, p, cmd)?;
let ids = chosen(s, p);
let id = sketch_store::target(s, p, cmd)?;
let ids = chosen(s, p, cmd)?;
if ids.is_empty() {
return Err(bad(cmd, "select the sketch curves or points to copy"));
}
let sk = s.doc.sketch(id)?;
let sk = sketch_store::read(&s.doc, id)?;
let (mut cs, mut ps, mut texts): (Vec<usize>, Vec<usize>, Vec<String>) = (Vec::new(), Vec::new(), Vec::new());
for e in &ids {
if let Some(c) = sk.curve_index(e) {
@@ -211,7 +212,7 @@ fn cut(s: &mut Session, p: &Value) -> Result<Value> {
if !(p.is_object() || p.is_null()) {
return Err(bad("sketch.cut", "params must be an object"));
}
let entities = chosen(s, p);
let entities = chosen(s, p, "sketch.cut")?;
let mut q = if p.is_null() { json!({}) } else { p.clone() };
q["entities"] = json!(entities);
let out = copy(s, &q)?;
@@ -244,7 +245,7 @@ fn paste(s: &mut Session, p: &Value) -> Result<Value> {
return Err(bad(cmd, "params must be an object"));
}
let Some(clip) = s.sketch_clipboard.clone() else { return Err(bad(cmd, "nothing copied (sketch.copy first)")) };
let target = target_sketch(s, p, cmd)?;
let target = sketch_store::target(s, p, cmd)?;
let positioned = p.get("at").is_some() || p.get("translate").is_some();
let t = if let Some(v) = p.get("at") {
vec2(v).ok_or_else(|| bad(cmd, "`at` must be [x, y]"))? - clip.center
+8 -12
View File
@@ -6,6 +6,7 @@ use solidcraft_geom::Vec2;
use solidcraft_sketch::{ConstraintKind, CurveKind, Sketch};
use super::sketch::{add_c, constraint_result, dof_now, edit, listed, redundancy, residual_of};
use super::sketch_store;
use super::{CommandSpec, in_sketch};
use crate::params::{bad, num, req_vec2, str_, string_list, vec2};
use crate::schema::sketch_constraints::{ToggleDiameterParams, ToggleDiameterResult};
@@ -86,7 +87,7 @@ fn curve(sk: &Sketch, v: Option<&Value>, cmd: &str) -> Result<usize> {
fn c_polygon(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.constraint.polygon";
let ids = string_list(p, "lines");
let ids = string_list(cmd, p, "lines")?;
if ids.len() < 3 || ids.len() > 200 {
return Err(bad(cmd, "`lines` must list 3 to 200 lines of a closed chain"));
}
@@ -361,7 +362,7 @@ fn guess(sk: &Sketch, ents: &[String], cmd: &str) -> Result<ConstraintKind> {
fn smart_constrain(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.constrainer";
let ents = string_list(p, "entities");
let ents = string_list(cmd, p, "entities")?;
let (mut before, mut residual) = (None, None);
let mut name = "";
let (id, info) = edit(s, p, cmd, true, |sk, _| {
@@ -403,8 +404,8 @@ fn toggle_driven(s: &mut Session, p: &Value) -> Result<Value> {
})?;
if !driven {
// Making it driving must not over-constrain: compare with the sketch without it.
let id = s.active_sketch.ok_or_else(|| bad(cmd, "no sketch"))?;
let mut sk = s.doc.sketch(id)?.clone();
let id = sketch_store::active(s).ok_or_else(|| bad(cmd, "no sketch"))?;
let mut sk = sketch_store::read(&s.doc, id)?.clone();
if let Some(c) = sk.constraints.iter_mut().find(|c| c.id == cid) {
c.driven = true;
}
@@ -422,7 +423,7 @@ fn toggle_diameter(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.toggle_diameter";
let cid = str_(p, "constraint").ok_or_else(|| bad(cmd, "`constraint` must be a dimension id"))?.to_string();
let want = crate::params::bool_(p, "diameter");
let sketch = super::sketch::target_sketch(s, p, cmd)?;
let sketch = sketch_store::target(s, p, cmd)?;
let (diameter, info) = edit(s, p, cmd, true, |sk, doc| {
let c = sk.constraints.iter().find(|c| c.id == cid).cloned().ok_or_else(|| bad(cmd, format!("no constraint `{cid}`")))?;
let round = |curve: usize| matches!(sk.curves.get(curve).map(|c| &c.kind), Some(CurveKind::Circle { .. } | CurveKind::Arc { .. }));
@@ -520,12 +521,7 @@ fn anchor(sk: &Sketch, k: &ConstraintKind) -> Option<Vec2> {
}
fn glyphs(s: &mut Session, p: &Value) -> Result<Value> {
let id = match p.get("sketch") {
Some(Value::Number(n)) => n.as_u64(),
Some(Value::String(x)) => s.doc.find_feature(x).map(|f| f.id),
_ => s.active_sketch,
}
.ok_or_else(|| bad("sketch.glyphs", "no such sketch"))?;
let id = sketch_store::target(s, p, "sketch.glyphs")?;
let st = s.model.state();
let ss = st.sketch(id).ok_or_else(|| EngineError::Other("that sketch is not evaluated".into()))?;
let failing = &ss.report.failing;
@@ -559,7 +555,7 @@ fn snap(s: &mut Session, p: &Value) -> Result<Value> {
let at = req_vec2(cmd, p, "at")?;
let from = p.get("from").and_then(vec2);
let r = num(p, "radius").filter(|r| *r > 0.0).unwrap_or(1.0);
let id = s.active_sketch.ok_or_else(|| bad(cmd, "no sketch is being edited"))?;
let id = sketch_store::active(s).ok_or_else(|| bad(cmd, "no sketch is being edited"))?;
let st = s.model.state();
let ss = st.sketch(id).ok_or_else(|| EngineError::Other("the sketch is not evaluated".into()))?;
let sk = &ss.sketch;
+48 -29
View File
@@ -13,9 +13,12 @@ macro_rules! text_style_doc {
}
use super::sketch::{add_c, circumcircle, edit, ids_of, mark_construction, req_parg, result, slot};
use super::sketch_store;
use super::{CommandSpec, in_sketch};
use crate::params::{bad, num, req_vec2, str_, string_list};
use crate::{Result, Session};
use crate::schema::sketch_create::{SplineReverseParams, SplineReverseResult};
use crate::schema::{TypedContract, output_schema_for};
use crate::{EngineError, Result, Session};
#[cfg(test)]
#[path = "sketch_text_move_tests.rs"]
@@ -25,6 +28,8 @@ mod text_move_tests;
#[path = "sketch_text_expr_tests.rs"]
mod text_expr_tests;
const SPLINE_REVERSE_CONTRACT: TypedContract<SplineReverseParams> = TypedContract::new();
pub static COMMANDS: &[CommandSpec] = &[
CommandSpec::new("sketch.line.midpoint", "Midpoint Line", midpoint_line)
.at("SKETCH", "CREATE")
@@ -83,6 +88,12 @@ pub static COMMANDS: &[CommandSpec] = &[
.icon("spline")
.enabled(in_sketch)
.params("curve: fit point spline id, at: [x,y]: a fit point is inserted where the curve passes nearest to it, between its neighbours (the curve still runs through it and all its old points; constraints on them stay); end?: start|end: `at` becomes the spline's new first or last point instead, and the curve runs on to it (refused when that end is joined to other geometry). Control point splines take no new points yet. Returns point: the new point's id"),
CommandSpec::new("sketch.spline.reverse", "Reverse Spline Direction", crate::checked!("sketch.spline.reverse", spline_reverse))
.icon("spline")
.enabled(in_sketch)
.contract(&SPLINE_REVERSE_CONTRACT)
.output(output_schema_for::<SplineReverseResult>)
.params("curve: spline id (fit point, control point or closed): its last point becomes its first and the curve runs the other way along the same path; its points and the constraints on them stay. Refused for a projected spline and for one an offset uses or makes. Returns curve"),
CommandSpec::new("sketch.conic", "Conic Curve", conic)
.at("SKETCH", "CREATE")
.icon("conic")
@@ -276,20 +287,19 @@ fn text(s: &mut Session, p: &Value) -> Result<Value> {
return Err(bad(cmd, AT_MUST));
}
let src = text_source(&s.doc, p, cmd, None)?;
let id = s.active_sketch.ok_or_else(|| bad(cmd, "no sketch is being edited"))?;
let mut doc = (*s.doc).clone();
let mut sk = doc.sketch(id)?.clone();
// A text needs nothing outside its sketch (a path is one of its curves).
let geom = sk.text_source_geometry(&src).map_err(|e| bad(cmd, e.to_string()))?;
if geom.is_empty() {
return Err(bad(cmd, "the text makes no outline"));
}
let guide = guide(&mut sk, &src);
let id = sketch_store::active(s).ok_or_else(|| bad(cmd, "no sketch is being edited"))?;
let exprs = text_exprs_json(&src);
let l = sk.add_link(LinkKind::Text, src, &geom)?;
let curves = ids_of(&sk, &sk.link_curves(&l));
*doc.sketch_mut(id)? = sk;
*s.doc_mut() = doc;
let (l, curves, guide) = sketch_store::update(s, id, |_, _, sk| {
// A text needs nothing outside its sketch (a path is one of its curves).
let geom = sk.text_source_geometry(&src).map_err(|e| bad(cmd, e.to_string()))?;
if geom.is_empty() {
return Err(bad(cmd, "the text makes no outline"));
}
let guide = guide(sk, &src);
let l = sk.add_link(LinkKind::Text, src, &geom)?;
let curves = ids_of(sk, &sk.link_curves(&l));
Ok((l, curves, guide))
})?;
let mut out = json!({"link": l, "curves": curves, "guide": guide});
if let Some(exprs) = exprs {
out["exprs"] = exprs;
@@ -364,23 +374,20 @@ const MAX_TEXT_EDITS: usize = 1000;
fn edit_text(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.edit_text";
let id = s.active_sketch.ok_or_else(|| bad(cmd, "no sketch is being edited"))?;
let mut doc = (*s.doc).clone();
let mut sk = doc.sketch(id)?.clone();
// The numbers its expressions give now (a parameter may have changed since the sketch was
// last stored); one that cannot be evaluated is left as it was, to be replaced by this edit.
let _ = doc.apply_text_values(&doc.param_values().0, &mut sk);
let out = apply_text_edits(&mut sk, &doc, p)?;
*doc.sketch_mut(id)? = sk;
*s.doc_mut() = doc;
Ok(out)
let id = sketch_store::active(s).ok_or_else(|| bad(cmd, "no sketch is being edited"))?;
sketch_store::update(s, id, |_, doc, sk| {
// The numbers its expressions give now (a parameter may have changed since the sketch was
// last stored); one that cannot be evaluated is left as it was, to be replaced by this edit.
let _ = doc.apply_text_values(&doc.param_values().0, sk);
apply_text_edits(sk, doc, p)
})
}
/// One frame of an interactive resize or rotation: the sketch is solved and shown, the
/// features after it keep their last result until `sketch.drag_end` evaluates the model.
fn edit_text_live(s: &mut Session, p: &Value) -> Result<(Value, Option<crate::LiveEdit>)> {
let cmd = "sketch.edit_text";
let id = s.active_sketch.ok_or_else(|| bad(cmd, "no sketch is being edited"))?;
let id = sketch_store::active(s).ok_or_else(|| bad(cmd, "no sketch is being edited"))?;
let (out, report) = super::sketch::edit_solved(s, id, false, solidcraft_sketch::Analysis::Full, |sk, doc| {
Ok((apply_text_edits(sk, doc, p)?, super::sketch::Left::Unsolved))
})?;
@@ -548,7 +555,7 @@ fn blend(s: &mut Session, p: &Value) -> Result<Value> {
fn centerline(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.centerline";
let ids = string_list(p, "curves");
let ids = string_list(cmd, p, "curves")?;
if ids.is_empty() {
return Err(bad(cmd, "`curves` must list lines"));
}
@@ -573,7 +580,7 @@ fn centerline(s: &mut Session, p: &Value) -> Result<Value> {
fn infinite_line(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.infinite";
let ids = string_list(p, "curves");
let ids = string_list(cmd, p, "curves")?;
if ids.is_empty() {
return Err(bad(cmd, "`curves` must list lines"));
}
@@ -704,6 +711,17 @@ fn spline_insert_point(s: &mut Session, p: &Value) -> Result<Value> {
Ok(json!({"point": point, "curve": p.get("curve"), "sketch": info}))
}
fn spline_reverse(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.spline.reverse";
let id = str_(p, "curve").ok_or_else(|| bad(cmd, "`curve` must be a spline id"))?.to_string();
let ((), info) = edit(s, p, cmd, false, |sk, _| {
let c = sk.curve_index(&id).ok_or_else(|| bad(cmd, format!("unknown curve `{id}`")))?;
sk.reverse_spline(c).map_err(|e| bad(cmd, e.to_string()))
})?;
let reply = SplineReverseResult { curve: id, sketch: info, param_warnings: Vec::new() };
serde_json::to_value(reply).map_err(|e| EngineError::Other(e.to_string()))
}
fn conic(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.conic";
let rho = num(p, "rho").unwrap_or(0.5);
@@ -893,7 +911,7 @@ fn tangent_centre(sk: &Sketch, a: usize, b: usize, r: f64, near: Vec2) -> Option
}
fn tangent_curves(sk: &Sketch, p: &Value, cmd: &str, n: usize) -> Result<Vec<usize>> {
let ids = string_list(p, "curves");
let ids = string_list(cmd, p, "curves")?;
if ids.len() != n {
return Err(bad(cmd, format!("`curves` must list {n} curves")));
}
@@ -990,11 +1008,12 @@ fn arc_slot(s: &mut Session, p: &Value, cmd: &str, o: Vec2, r: f64, t0: f64, swe
c.construction = true;
centre_arc = Some(c.id.clone());
}
// Each cap is tangent to both concentric sides, so its radius is half the gap between
// them: the two caps are equal without an Equal, which would only be redundant (#8355).
let mut cons = Vec::new();
for (a, b) in [(outer, cap_e), (inner, cap_e), (outer, cap_s), (inner, cap_s)] {
cons.push(add_c(sk, ConstraintKind::Tangent { a, b })?);
}
cons.push(add_c(sk, ConstraintKind::Equal { a: cap_s, b: cap_e })?);
Ok((ids_of(sk, &[outer, cap_e, inner, cap_s]), cons))
})?;
let mut out = result(out, info);
+2 -2
View File
@@ -306,8 +306,8 @@ fn place_from_origin(scratch: &mut Session, out: &Value, params: &Value, mut dof
/// Where a point of the target sketch is now, in the frame's coordinates: a plane sketch's
/// points have z = 0 (a 3D frame would read its points' own z).
fn coordinate(s: &Session, params: &Value, point: &str) -> Option<Vec3> {
let id = super::target_sketch(s, params, "sketch.draw.dimensioned").ok()?;
let sketch = s.doc.sketch(id).ok()?;
let id = super::sketch_store::target(s, params, "sketch.draw.dimensioned").ok()?;
let sketch = super::sketch_store::read(&s.doc, id).ok()?;
let at = sketch.point(sketch.resolve_point(point)?)?;
Some(Vec3::new(at.x, at.y, 0.0))
}
@@ -87,6 +87,41 @@ fn signed_arc_sweep_retains_radius_and_logical_endpoint_identity() {
}
}
/// #6645: a clicked end with `clockwise` sweeps from the start the other way round, keeps the
/// start's radius and its `.start`/`.end` names, with and without a typed radius.
#[test]
fn a_clockwise_centre_arc_runs_from_its_start_to_its_end_the_other_way_round() {
for dimensions in [None, Some(json!({"radius": 10}))] {
let mut s = session();
// The end is off the radius, as a cursor is: it is pulled onto the start's circle.
let params = json!({"center":[0,0],"start":[10,0],"end":[0,4],"clockwise":true,"id":"a1","connect":false});
match &dimensions {
Some(d) => draw(&mut s, "sketch.arc.center_point", params, d.clone()),
None => run(&mut s, "sketch.arc.center_point", params),
};
let sk = sketch(&s);
let arc = &sk.curves[sk.curve_index("a1").unwrap()];
assert!(arc.reversed, "{dimensions:?}");
let at = |r: &str| sk.points[sk.resolve_point(r).unwrap()].pos;
let (start, end) = (at("a1.start"), at("a1.end"));
close(start.x, 10.0);
close(start.y, 0.0);
close(end.x, 0.0);
close(end.y, 10.0);
// Clockwise from 0° to 90° is 270° of arc through the bottom (-90°), not the top.
let solidcraft_sketch::CurveKind::Arc { c, a, b } = arc.kind else { panic!("not an arc") };
let (c, a, b) = (sk.points[c].pos, sk.points[a].pos, sk.points[b].pos);
close(((b - c).angle() - (a - c).angle()).rem_euclid(std::f64::consts::TAU).to_degrees(), 270.0);
if dimensions.is_some() {
close(measure(&s, |k| matches!(k, ConstraintKind::Radius { .. })), 10.0);
}
}
// Without the flag the same clicks keep the counter-clockwise quarter.
let mut s = session();
run(&mut s, "sketch.arc.center_point", json!({"center":[0,0],"start":[10,0],"end":[0,4],"id":"a1"}));
assert!(!sketch(&s).curves[sketch(&s).curve_index("a1").unwrap()].reversed);
}
#[test]
fn slots_and_polygon_keep_permanent_typed_dimensions() {
for command in ["sketch.slot.center_to_center", "sketch.slot.overall", "sketch.slot.center_point"] {
+4 -3
View File
@@ -4,7 +4,8 @@
use serde_json::{Value, json};
use solidcraft_sketch::{ConstraintKind, EndReport, EndTouch, Sketch, loose_ends};
use super::sketch::{add_c, edit, target_sketch};
use super::sketch::{add_c, edit};
use super::sketch_store;
use super::{CommandSpec, in_sketch};
use crate::params::{bad, string_list};
use crate::{EngineError, Result, Session};
@@ -53,7 +54,7 @@ fn report_json(sk: &Sketch, r: &EndReport) -> Value {
}
fn loose_ends_cmd(s: &mut Session, p: &Value) -> Result<Value> {
let id = target_sketch(s, p, "sketch.loose_ends")?;
let id = sketch_store::target(s, p, "sketch.loose_ends")?;
let st = s.model.state();
let ss = st.sketch(id).ok_or_else(|| EngineError::Other("the sketch has not been evaluated".into()))?;
Ok(report_json(&ss.sketch, &ss.ends))
@@ -63,7 +64,7 @@ fn tie_cmd(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.loose_ends.tie";
let only = match p.get("points") {
None | Some(Value::Null) => None,
Some(Value::Array(a)) if a.iter().all(Value::is_string) => Some(string_list(p, "points")),
Some(Value::Array(a)) if a.iter().all(Value::is_string) => Some(string_list(cmd, p, "points")?),
Some(_) => return Err(bad(cmd, "`points` must be a list of point ids")),
};
let (added, info) = edit(s, p, cmd, true, |sk, _| {
+25
View File
@@ -328,3 +328,28 @@ fn commands_that_add_nothing_report_empty_lists() {
let r = run(&mut s, "sketch.move_point", json!({"point": strs(&c["points"])[1], "to": [12, 6]}));
assert_eq!(r["created"], json!({"points": [], "curves": [], "constraints": [], "dimensions": []}), "{r}");
}
/// Conic curves take the curve prefix `n`, not the constraints' `k` (#6173): however a conic
/// is created — drawn, a Move/Copy copy, a pattern copy — its id still says it is a curve.
#[test]
fn conic_curves_take_the_n_prefix_not_the_constraints_k() {
let mut s = new_sketch();
let drawn = run(&mut s, "sketch.conic", json!({"start": [80, 490], "end": [100, 490], "apex": [90, 505], "rho": 0.5}));
let conic = strs(&drawn["curves"])[0].clone();
assert_eq!(conic, "n1", "a drawn conic starts its own counter");
// A constraint keeps its prefix, and it no longer shares a counter with conics.
run(&mut s, "sketch.constraint.fix", json!({"entity": format!("{conic}.start")}));
let sk = sketch(&s);
assert!(sk.constraints.iter().any(|k| k.id == "k1"), "constraint ids: {:?}", sk.constraints.iter().map(|k| &k.id).collect::<Vec<_>>());
let copy = run(&mut s, "sketch.move", json!({"entities": [conic], "translate": [40, 0], "copy": true}));
assert_eq!(strs(&copy["curves"]), vec!["n2".to_string()], "a Move/Copy copy continues the conic counter");
let pattern = run(&mut s, "sketch.pattern.rectangular", json!({"entities": [conic], "count": 2, "spacing": 40}));
let made: Vec<String> = strs(&pattern["curves"]);
assert!(!made.is_empty(), "{pattern}");
for id in made {
assert!(id.starts_with('n'), "a pattern copy keeps the conic prefix: {id}");
}
}
+38 -9
View File
@@ -9,6 +9,7 @@ use solidcraft_sketch::{CurveKind, LinkKind, LinkSource, Sketch};
use super::CommandSpec;
use super::sketch::{edit, ids_of};
use super::sketch_store;
use crate::params::{bad, num, str_, vec2};
use crate::{EngineError, Result, Session};
@@ -152,6 +153,12 @@ fn bounds_of(geom: &[Geom2]) -> Option<(Vec2, Vec2)> {
pts.extend([*c + Vec2::new(r, r), *c - Vec2::new(r, r)]);
}
Geom2::Spline { pts: sp, .. } => pts.extend(sp.iter().copied()),
// Points along the curve, not its apex, which can lie well outside it (an elliptical
// arc of a DXF comes in as conics, #8155).
Geom2::Conic { a, apex, b, rho } if *rho > 0.0 && *rho < 1.0 => {
let seg = solidcraft_geom::Seg2::Conic { a: *a, apex: *apex, b: *b, w: rho / (1.0 - rho) };
pts.extend((0..=64).map(|i| seg.point_at(f64::from(i) / 64.0)));
}
Geom2::Conic { a, apex, b, .. } => pts.extend([*a, *apex, *b]),
Geom2::Text { at, .. } => pts.push(*at),
}
@@ -227,12 +234,13 @@ fn insert(s: &mut Session, p: &Value, cmd: &str, svg: bool) -> Result<Value> {
Ok((ids_of(sk, &cs), texts))
})?;
// Text becomes sketch text.
let id = s.active_sketch.ok_or_else(|| bad(cmd, "no sketch"))?;
let id = sketch_store::active(s).ok_or_else(|| bad(cmd, "no sketch"))?;
let mut n_text = 0;
for t in texts {
if let Geom2::Text { text, at, height, angle: own } = t {
// Not `sketch_store::update`: a text that can't be linked is skipped, not refused.
let mut doc = (*s.doc).clone();
let mut sk = doc.sketch(id)?.clone();
let mut sk = sketch_store::read(&doc, id)?.clone();
if super::sketch_project::add_link(
s,
&doc,
@@ -243,7 +251,7 @@ fn insert(s: &mut Session, p: &Value, cmd: &str, svg: bool) -> Result<Value> {
)
.is_ok()
{
*doc.sketch_mut(id)? = sk;
sketch_store::write(&mut doc, id, sk)?;
*s.doc_mut() = doc;
n_text += 1;
}
@@ -262,12 +270,7 @@ fn insert_svg(s: &mut Session, p: &Value) -> Result<Value> {
fn export_dxf(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.export_dxf";
let id = match p.get("sketch") {
Some(Value::Number(n)) => n.as_u64(),
Some(Value::String(x)) => s.doc.find_feature(x).map(|f| f.id),
_ => s.active_sketch,
}
.ok_or_else(|| bad(cmd, "no such sketch (and none is active)"))?;
let id = sketch_store::target(s, p, cmd)?;
let st = s.model.state();
let ss = st.sketch(id).ok_or_else(|| EngineError::Other("that sketch is not evaluated".into()))?;
let sk = &ss.sketch;
@@ -352,6 +355,32 @@ mod tests {
}
}
/// #8155: an elliptical arc of a DXF (as Fusion writes one) comes into the sketch as exact
/// conics, and the drawing's size is the arc's own, not the box round the conics' apexes.
#[test]
fn an_elliptical_arc_comes_in_as_conics_at_its_own_size() {
// Centre (0, 0), major radius 10 along X, minor 5, from -45° to 45°: one quarter turn,
// whose apex is at x = 10 / cos 45° = 14.1, past the arc's end at x = 10.
let dxf = format!(
"0\nSECTION\n2\nENTITIES\n0\nELLIPSE\n10\n0\n20\n0\n11\n10\n21\n0\n40\n0.5\n41\n{}\n42\n{}\n0\nENDSEC\n0\nEOF\n",
-std::f64::consts::FRAC_PI_4,
std::f64::consts::FRAC_PI_4
);
let mut s = Session::default();
let b = s.execute("sketch.drawing_bounds", &json!({"text": dxf, "format": "dxf"})).unwrap();
let (w, h) = (b["width"].as_f64().unwrap(), b["height"].as_f64().unwrap());
let half = std::f64::consts::FRAC_1_SQRT_2;
assert!((w - 10.0 * (1.0 - half)).abs() < 1e-3 && (h - 10.0 * half).abs() < 1e-3, "{b}");
let r = s.execute("sketch.insert_dxf", &json!({"text": dxf})).unwrap();
assert_eq!(r["curves"], 1, "{r}");
let st = s.model.state();
let sk = &st.sketch(r["sketch"].as_u64().unwrap()).unwrap().sketch;
assert!(matches!(sk.curves[0].kind, solidcraft_sketch::CurveKind::Conic { .. }), "{:?}", sk.curves[0].kind);
// Every point the sketch draws it with lies on the ellipse.
let worst = sk.polyline(0).iter().map(|p| ((p.x / 10.0).powi(2) + (p.y / 5.0).powi(2) - 1.0).abs()).fold(0.0, f64::max);
assert!(worst < 1e-9, "{worst}");
}
const PLATE: &str =
r#"<svg xmlns="http://www.w3.org/2000/svg" width="20mm" height="10mm" viewBox="0 0 20 10"><rect x="0" y="0" width="20" height="10"/></svg>"#;
+5 -15
View File
@@ -77,14 +77,9 @@ fn curvature_comb(s: &mut Session, p: &Value) -> Result<Value> {
let density = num(p, "density").map(|d| d.clamp(2.0, 2000.0) as usize).unwrap_or(40);
let st = s.model.state();
let mut combs: Vec<(String, Vec<(Vec3, f64, Vec3)>)> = Vec::new();
let curves = string_list(p, "curves");
let curves = string_list(cmd, p, "curves")?;
if !curves.is_empty() {
let id = match p.get("sketch") {
Some(Value::Number(n)) => n.as_u64(),
Some(Value::String(x)) => s.doc.find_feature(x).map(|f| f.id),
_ => s.active_sketch,
}
.ok_or_else(|| bad(cmd, "no such sketch (and none is active)"))?;
let id = super::sketch_store::target(s, p, cmd)?;
let ss = st.sketch(id).ok_or_else(|| EngineError::Other("that sketch is not evaluated".into()))?;
for c in curves.iter().take(1000) {
let ci = ss.sketch.curve_index(c).ok_or_else(|| bad(cmd, format!("unknown curve `{c}`")))?;
@@ -228,14 +223,9 @@ fn minimum_radius(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "inspect.minimum_radius";
let st = s.model.state();
let mut items: Vec<(String, Option<(f64, Vec3)>)> = Vec::new();
let curves = string_list(p, "curves");
let curves = string_list(cmd, p, "curves")?;
if !curves.is_empty() {
let id = match p.get("sketch") {
Some(Value::Number(n)) => n.as_u64(),
Some(Value::String(x)) => s.doc.find_feature(x).map(|f| f.id),
_ => s.active_sketch,
}
.ok_or_else(|| bad(cmd, "no such sketch (and none is active)"))?;
let id = super::sketch_store::target(s, p, cmd)?;
let ss = st.sketch(id).ok_or_else(|| EngineError::Other("that sketch is not evaluated".into()))?;
for c in curves.iter().take(1000) {
let ci = ss.sketch.curve_index(c).ok_or_else(|| bad(cmd, format!("unknown curve `{c}`")))?;
@@ -318,7 +308,7 @@ fn isocurves(s: &mut Session, p: &Value) -> Result<Value> {
fn center_of_mass(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "inspect.center_of_mass";
let names = string_list(p, "bodies");
let names = string_list(cmd, p, "bodies")?;
// The world state, as Measure reads: placed occurrences where they are, each placement once.
let st = s.world_state();
let mut out = Vec::new();
+7 -6
View File
@@ -9,6 +9,7 @@ use solidcraft_geom::Vec2;
use solidcraft_sketch::{ConstraintKind, Curve, CurveKind, Shape, Sketch, intersections, intersections_within};
use super::sketch::{add_c, edit, ids_of, solve_current};
use super::sketch_store;
use super::{CommandSpec, in_sketch};
use crate::params::{bad, bool_, expr, num, req_expr, req_vec2, str_, string_list, vec2, vec2_list};
use crate::{Result, Session};
@@ -1951,7 +1952,7 @@ fn offset_group(sk: &mut Sketch, ids: &[String], eligible: &std::collections::Ha
fn offset(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.offset";
let ids = string_list(p, "curves");
let ids = string_list(cmd, p, "curves")?;
if ids.is_empty() || ids.len() > 500 {
return Err(bad(cmd, "`curves` must list 1…500 curves"));
}
@@ -2070,7 +2071,7 @@ fn offset(s: &mut Session, p: &Value) -> Result<Value> {
fn break_offset(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.offset.break";
let sketch = super::sketch::target_sketch(s, p, cmd)?;
let sketch = sketch_store::target(s, p, cmd)?;
let (out, info) = edit(s, p, cmd, true, |sk, doc| {
let output = str_(p, "curve").ok_or_else(|| bad(cmd, "`curve` must name an offset output"))?;
let record = sk.freeform_offsets.iter().position(|record| record.output == output);
@@ -2090,7 +2091,7 @@ fn break_offset(s: &mut Session, p: &Value) -> Result<Value> {
if !rep.keeps_orientation() {
return Err(bad(cmd, rep.failure(|_| "cannot break an offset with conflicting dimensions".into())));
}
sk.validate_periodic_splines()?;
sketch_store::checked(sk)?;
if let Some(association) = sk
.offsets
.iter()
@@ -2229,7 +2230,7 @@ fn freeze_offset_profiles(doc: &mut solidcraft_doc::Document, sketch: u64, bound
/// Curves and lone points named in `entities`.
fn entities(sk: &Sketch, p: &Value, cmd: &str) -> Result<(Vec<usize>, Vec<usize>)> {
let ids = string_list(p, "entities");
let ids = string_list(cmd, p, "entities")?;
if ids.is_empty() {
return Err(bad(cmd, "`entities` must list sketch curves or points"));
}
@@ -2300,7 +2301,7 @@ fn copy_through(
(kind, "e")
}
CurveKind::Spline { .. } => (kind, "s"),
CurveKind::Conic { .. } => (kind, "k"),
CurveKind::Conic { .. } => (kind, "n"),
CurveKind::Line { a, b } => (CurveKind::Line { a: m(a), b: m(b) }, "l"),
CurveKind::Circle { c, r } => (CurveKind::Circle { c: m(c), r: r * scale_r }, "c"),
CurveKind::Arc { c, a, b } => {
@@ -2505,7 +2506,7 @@ fn move_copy(s: &mut Session, p: &Value) -> Result<Value> {
let rot = rotate(c, ang);
let f = move |q: Vec2| rot(q) + t;
let mut links = Vec::new();
for entity in string_list(p, "entities") {
for entity in string_list(cmd, p, "entities")? {
if let Some(link) = super::sketch_create::text_link(sk, &entity)
&& !links.iter().any(|old| old == link)
{
@@ -1142,6 +1142,67 @@ fn a_point_added_to_a_spline_keeps_its_constraints_and_undoes() {
assert_eq!(sketch(&s), unchanged, "refusals change nothing");
}
/// Reverse Spline Direction (#8224): a fit, a control point and a closed spline each run the
/// other way along the same path, their ends swapped. Their points, the constraints on them and
/// a line joined to an end stay, and undo restores the spline. A line and the source and output
/// of an offset are refused.
#[test]
fn a_reversed_spline_runs_the_other_way_along_the_same_path() {
use solidcraft_geom::Seg2;
let mut s = new_sketch();
let fit =
ids(&run(&mut s, "sketch.spline.fit_point", json!({"points": [[0, 0], [10, 8], [25, 2], [30, 6]], "connect": false}))["curves"])[0].clone();
run(&mut s, "sketch.line", json!({"points": [format!("{fit}.end"), [40, 6]], "connect": false}));
let cv = ids(&run(&mut s, "sketch.spline.control_point", json!({"points": [[0, 20], [10, 30], [25, 18], [30, 26]], "connect": false}))["curves"])
[0]
.clone();
let closed = ids(&run(
&mut s,
"sketch.spline.fit_point",
json!({"points": [[0, 40], [10, 48], [20, 40], [10, 34]], "periodic": true, "connect": false}),
)["curves"])[0]
.clone();
let off = |segs: &[Seg2], q: Vec2| segs.iter().map(|seg| seg.point_at(seg.closest_param(q)).dist(q)).fold(f64::INFINITY, f64::min);
let unit = |v: Vec2| v.normalized().unwrap_or_default();
for id in [&fit, &cv, &closed] {
let before = sketch(&s);
let ci = before.curve_index(id).unwrap();
let path = before.exact_segs(ci).unwrap();
let r = run(&mut s, "sketch.spline.reverse", json!({"curve": id}));
assert_eq!(r["curve"], json!(id), "{r}");
let after = sketch(&s);
let mut reversed = before.curves[ci].kind.point_ids();
reversed.reverse();
assert_eq!(after.curves[ci].kind.point_ids(), reversed, "{id}");
assert_eq!((&after.points, &after.constraints), (&before.points, &before.constraints), "{id}: points and constraints stay");
let segs = after.exact_segs(ci).unwrap();
for (a, b) in [(&path, &segs), (&segs, &path)] {
for seg in a {
for k in 0..=8 {
let q = seg.point_at(k as f64 / 8.0);
assert!(off(b, q) < 1e-7, "{id}: {q:?} is off the other path by {}", off(b, q));
}
}
}
// It starts where it ended, heading back the way it came.
let (first, last) = (segs.first().unwrap(), path.last().unwrap());
assert!(first.point_at(0.0).dist(last.point_at(1.0)) < 1e-9, "{id}");
assert!(unit(first.derivative(0.0)).dist(unit(last.derivative(1.0)) * -1.0) < 1e-7, "{id}");
}
let before = sketch(&s);
run(&mut s, "sketch.spline.reverse", json!({"curve": fit}));
run(&mut s, "edit.undo", json!({}));
assert_eq!(sketch(&s), before, "undo restores the spline");
let line = before.curves.iter().find(|c| matches!(c.kind, CurveKind::Line { .. })).unwrap().id.clone();
let made = ids(&run(&mut s, "sketch.offset", json!({"curves": [cv], "distance": 2, "side": [10, 40]}))["curves"]);
let unchanged = sketch(&s);
for id in [&line, &cv, &made[0]] {
assert!(s.execute("sketch.spline.reverse", &json!({"curve": id})).is_err(), "{id} is refused");
}
assert_eq!(sketch(&s), unchanged, "refusals change nothing");
}
#[test]
fn text_makes_profiles_that_extrude_and_edit() {
let mut s = new_sketch();

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