mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 02:25:55 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 18a734d029 |
@@ -597,12 +597,6 @@ Original SolidCraft drawings, Open for owner review; no runtime assignments.
|
||||
| assets/styles/ui/themes/1-bit-dark.json | SolidCraft contributors | original SolidCraft theme file | MIT OR Apache-2.0 |
|
||||
| `assets/locales/pending/navbar-customisation.json` | SolidCraft contributors | Original navigation bar editor translations | MIT OR Apache-2.0 |
|
||||
| `assets/locales/pending/navbar-slots.json` | SolidCraft contributors | Original navigation slot translations | MIT OR Apache-2.0 |
|
||||
| assets/locales/bs.json | Tarik Sijercic (tsijercic1) | Original Bosnian translation of SolidCraft's English catalog; no commercial CAD text used | MIT OR Apache-2.0 |
|
||||
| assets/locales/hr.json | Tarik Sijercic (tsijercic1) | Original Croatian translation of SolidCraft's English catalog; no commercial CAD text used | MIT OR Apache-2.0 |
|
||||
| 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
|
||||
|
||||
|
||||
+1
-3
@@ -268,9 +268,7 @@ a conflict. `crates/doc/src/format/pending/README.md` has an example.
|
||||
|
||||
**At release time** the maintainer runs `scripts/fold-format-pending.sh`, which moves the pending
|
||||
files into `crates/doc/src/format/v<N>/` and fixes that number for good (see
|
||||
[docs/releasing.md](docs/releasing.md)). Never add a file to a `v<N>/` folder afterwards. Drawings
|
||||
follow the same rules with their own numbers, from 1, in `crates/doc/src/drawing/format/` (see its
|
||||
`pending/README.md`), and the same script folds them.
|
||||
[docs/releasing.md](docs/releasing.md)). Never add a file to a `v<N>/` folder afterwards.
|
||||
|
||||
## Lists many changes add to
|
||||
|
||||
|
||||
Generated
+21
-28
@@ -1692,11 +1692,9 @@ 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]]
|
||||
@@ -4210,7 +4208,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft"
|
||||
version = "0.26.0"
|
||||
version = "0.25.1"
|
||||
dependencies = [
|
||||
"block2 0.6.2",
|
||||
"eframe",
|
||||
@@ -4240,16 +4238,15 @@ dependencies = [
|
||||
"ureq",
|
||||
"windows-sys 0.61.2",
|
||||
"winit",
|
||||
"x11rb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-3dx-client"
|
||||
version = "0.26.0"
|
||||
version = "0.25.1"
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-cli"
|
||||
version = "0.26.0"
|
||||
version = "0.25.1"
|
||||
dependencies = [
|
||||
"image",
|
||||
"log",
|
||||
@@ -4261,7 +4258,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-derive"
|
||||
version = "0.26.0"
|
||||
version = "0.25.1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4270,7 +4267,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-doc"
|
||||
version = "0.26.0"
|
||||
version = "0.25.1"
|
||||
dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
@@ -4287,7 +4284,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-engine"
|
||||
version = "0.26.0"
|
||||
version = "0.25.1"
|
||||
dependencies = [
|
||||
"image",
|
||||
"log",
|
||||
@@ -4308,7 +4305,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-geom"
|
||||
version = "0.26.0"
|
||||
version = "0.25.1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4317,7 +4314,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-gpu"
|
||||
version = "0.26.0"
|
||||
version = "0.25.1"
|
||||
dependencies = [
|
||||
"image",
|
||||
"log",
|
||||
@@ -4337,7 +4334,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-io"
|
||||
version = "0.26.0"
|
||||
version = "0.25.1"
|
||||
dependencies = [
|
||||
"cadmpeg-core",
|
||||
"cadmpeg-ir",
|
||||
@@ -4361,7 +4358,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-kernel"
|
||||
version = "0.26.0"
|
||||
version = "0.25.1"
|
||||
dependencies = [
|
||||
"log",
|
||||
"proptest",
|
||||
@@ -4381,7 +4378,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-mcp"
|
||||
version = "0.26.0"
|
||||
version = "0.25.1"
|
||||
dependencies = [
|
||||
"serde_json",
|
||||
"solidcraft-engine",
|
||||
@@ -4390,7 +4387,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-render"
|
||||
version = "0.26.0"
|
||||
version = "0.25.1"
|
||||
dependencies = [
|
||||
"image",
|
||||
"moxcms",
|
||||
@@ -4404,27 +4401,25 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-sketch"
|
||||
version = "0.26.0"
|
||||
version = "0.25.1"
|
||||
dependencies = [
|
||||
"harfrust",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"solidcraft-geom",
|
||||
"thiserror 2.0.21",
|
||||
"ttf-parser",
|
||||
"unicode-bidi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-space-mouse"
|
||||
version = "0.26.0"
|
||||
version = "0.25.1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-ui-egui"
|
||||
version = "0.26.0"
|
||||
version = "0.25.1"
|
||||
dependencies = [
|
||||
"arboard",
|
||||
"egui",
|
||||
@@ -4454,7 +4449,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-web"
|
||||
version = "0.26.0"
|
||||
version = "0.25.1"
|
||||
dependencies = [
|
||||
"eframe",
|
||||
"egui",
|
||||
@@ -4923,9 +4918,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "typed-path"
|
||||
version = "0.13.0"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce898823d8cd0f028662d2f349694b1af0a5cc9be83f1d213a00d3147445e1e"
|
||||
checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e"
|
||||
|
||||
[[package]]
|
||||
name = "typeid"
|
||||
@@ -5999,7 +5994,7 @@ checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7"
|
||||
|
||||
[[package]]
|
||||
name = "xtask"
|
||||
version = "0.26.0"
|
||||
version = "0.25.1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"serde",
|
||||
@@ -6297,18 +6292,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "9.1.3"
|
||||
version = "9.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1156b39d142a34b999c2310a50455e0cb3760bf7efad0122f796d813bee26ccb"
|
||||
checksum = "a954114b47a54df49f83d2722814e86b23d4bb1b54a1b90fb7d92b41619ddc22"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"flate2",
|
||||
"getrandom 0.4.3",
|
||||
"indexmap",
|
||||
"memchr",
|
||||
"nix 0.31.3",
|
||||
"typed-path",
|
||||
"windows-sys 0.61.2",
|
||||
"zopfli",
|
||||
]
|
||||
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@ members = ["crates/*", "apps/*", "xtask"]
|
||||
exclude = ["vendor"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.26.0"
|
||||
version = "0.25.1"
|
||||
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", "variable-fonts"] }
|
||||
ttf-parser = { version = "0.25", default-features = false, features = ["std"] }
|
||||
|
||||
# Ends a process group with the system call (MIT; already in the tree through zip).
|
||||
nix = { version = "0.31", default-features = false, features = ["signal"] }
|
||||
|
||||
@@ -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 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
|
||||
[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
|
||||
[updating](https://docs.solidcraft.io/guide/updates.html#updating-from-011-to-014).
|
||||
|
||||
## What works
|
||||
@@ -131,7 +131,8 @@ them: read [DISCLAIMER.md](DISCLAIMER.md).
|
||||
|
||||
MIT OR Apache-2.0, at your option. Developed under the
|
||||
[clean-room contribution rules](AGENTS.md); third-party code and assets retain their
|
||||
documented licences and attribution.
|
||||
documented licences and attribution. The licence covers the code, not the name and logo: see
|
||||
[TRADEMARKS.md](TRADEMARKS.md) for how to name a fork or a product built on SolidCraft.
|
||||
|
||||
SolidCraft is independent. Behaviour is designed from public documentation, general CAD
|
||||
conventions and user feedback. Reference tests use analytic values and open-source tools.
|
||||
|
||||
-694
@@ -80,697 +80,3 @@ 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.
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# SolidCraft name and logo
|
||||
|
||||
The code is yours to use under MIT OR Apache-2.0 (see [NOTICE](NOTICE)). The name is not part of
|
||||
that licence: "SolidCraft" and the SolidCraft logo identify this project and the builds its
|
||||
maintainers publish, and Tangency Labs, which maintains SolidCraft, uses them as its trademarks.
|
||||
Neither licence grants rights to them.
|
||||
|
||||
This page says how you can use the name. It is meant to be easy to follow: most uses are fine.
|
||||
|
||||
## Fine without asking
|
||||
|
||||
- Saying what your work is built on: "based on SolidCraft", "a fork of SolidCraft", "works with
|
||||
SolidCraft", "SolidCraft plugin for …".
|
||||
- Writing, teaching, reviewing or making videos about SolidCraft, including in titles.
|
||||
- Redistributing unmodified official builds under the SolidCraft name, for example in a Linux
|
||||
distribution or package manager.
|
||||
- Using the logo to link to the project or to show that something supports SolidCraft.
|
||||
- User groups, meetups and community spaces about SolidCraft, as long as they don't look official.
|
||||
|
||||
## Use your own name
|
||||
|
||||
- A fork, a modified build or a product built on SolidCraft needs its own name and logo. "SolidCraft"
|
||||
may appear in its description ("based on SolidCraft"), not as its name or the main part of it.
|
||||
- Don't use names or logos that could be mistaken for SolidCraft, such as "SolidCraft Pro",
|
||||
"SolidKraft" or a lightly changed version of the logo.
|
||||
- Don't imply that SolidCraft's maintainers made, endorse or support your product or service unless
|
||||
they do.
|
||||
- Don't use the name in domain names, app store listings or social media accounts in a way that
|
||||
looks official.
|
||||
|
||||
## Questions
|
||||
|
||||
If you're not sure whether a use is fine, open an issue or ask on [Discord](https://discord.gg/FtU4NQNVDH).
|
||||
For anything this page doesn't allow, ask first; we're generally happy to say yes.
|
||||
|
||||
Other companies' trademarks named in this repository are covered in [NOTICE](NOTICE).
|
||||
@@ -23,7 +23,6 @@
|
||||
//! 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)]
|
||||
@@ -100,7 +99,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 --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";
|
||||
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";
|
||||
|
||||
/// The commands the CLI takes, in the order the top-level usage lists them.
|
||||
const COMMANDS: &[Command] = &[
|
||||
@@ -132,10 +131,6 @@ 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).
|
||||
@@ -210,28 +205,6 @@ 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()
|
||||
}
|
||||
@@ -268,12 +241,11 @@ 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::limits::max_design_bytes())
|
||||
.map_err(|e| format!("{path}: {}", solidcraft_engine::io::limits::explain(solidcraft_engine::io::limits::Kind::Design, e)))?;
|
||||
let s = solidcraft_engine::io::vfs::read_to_string(path, solidcraft_engine::io::MAX_DESIGN_BYTES).map_err(|e| format!("{path}: {e}"))?;
|
||||
serde_json::from_str(&s).map_err(|e| format!("{path}: {e}"))
|
||||
}
|
||||
|
||||
/// A session from a design file, a STEP/3MF/STL file, a point cloud, a command script, or an oracle recipe.
|
||||
/// A session from a design file, a STEP/3MF/STL file, a command script, or an oracle recipe.
|
||||
pub fn load(path: &str) -> Result<Session, String> {
|
||||
load_checked(path, solidcraft_engine::schema::ParamCheck::Off)
|
||||
}
|
||||
@@ -283,11 +255,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) || solidcraft_engine::io::is_point_cloud_path(path) {
|
||||
if solidcraft_engine::io::is_step_path(path) || solidcraft_engine::io::is_mesh_path(path) {
|
||||
s.execute("doc.open", &json!({"path": path})).map_err(|e| e.to_string())?;
|
||||
return Ok(s);
|
||||
}
|
||||
let bytes = solidcraft_engine::io::limits::read(path, solidcraft_engine::io::limits::Kind::Design).map_err(|e| format!("{path}: {e}"))?;
|
||||
let bytes = solidcraft_engine::io::vfs::read(path, solidcraft_engine::io::MAX_DESIGN_BYTES).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));
|
||||
@@ -681,7 +653,7 @@ fn cmd_mcp(args: &[String]) -> Result<(), String> {
|
||||
if let Some(s) = &scope {
|
||||
let roots: Vec<String> = s.roots().iter().map(|r| r.display().to_string()).collect();
|
||||
eprintln!(
|
||||
"solidcraft mcp: files limited to {} and the folders of designs opened or saved here (--allow-path DIR adds one, --allow-any-path lifts the limit)",
|
||||
"solidcraft mcp: files limited to {} and the open design's folder (--allow-path DIR adds one, --allow-any-path lifts the limit)",
|
||||
roots.join(", ")
|
||||
);
|
||||
let design_dir = flag(args, "--in")
|
||||
@@ -848,8 +820,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 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 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 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();
|
||||
|
||||
@@ -157,34 +157,3 @@ 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);
|
||||
}
|
||||
|
||||
@@ -40,8 +40,6 @@ 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.
|
||||
@@ -617,15 +615,13 @@ fn choose_file(accept: &str) -> Result<js_sys::Promise, String> {
|
||||
}
|
||||
|
||||
/// Read files dropped on the page.
|
||||
/// `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 };
|
||||
pub fn take_drops(files: &Rc<Files>, raw: &mut egui::RawInput) {
|
||||
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),
|
||||
Ok(b) => files.arrive(name, b, Intent::Insert),
|
||||
Err(e) => files.say(format!("{name}: {e}"), true),
|
||||
}
|
||||
});
|
||||
|
||||
@@ -112,13 +112,6 @@ 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;
|
||||
@@ -170,23 +163,6 @@ 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
|
||||
@@ -295,10 +271,6 @@ 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.
|
||||
@@ -315,7 +287,7 @@ mod web {
|
||||
};
|
||||
match intent {
|
||||
Intent::Open => self.app.open_path(&path),
|
||||
Intent::Insert | Intent::Theme => self.app.drop_path(&path),
|
||||
Intent::Insert => self.app.drop_path(&path),
|
||||
}
|
||||
self.files.panel.set(false);
|
||||
}
|
||||
@@ -381,8 +353,7 @@ mod web {
|
||||
}
|
||||
let mut act: Option<(&'static str, String)> = None;
|
||||
let closed = solidcraft_ui_egui::ui_kit::Window::new(egui::Id::new("sc_browser_files"), "folder", "Files in this browser")
|
||||
// Room for long names; the kit still caps the window at the browser's width.
|
||||
.cards(4.0)
|
||||
.cards(3.0)
|
||||
.show_body(ctx, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
if ui_kit::button(ui, "Open from computer…", ui_kit::Button::Normal).clicked() {
|
||||
@@ -410,62 +381,61 @@ mod web {
|
||||
return;
|
||||
}
|
||||
let entries = self.files.entries.borrow().clone();
|
||||
if entries.is_empty() {
|
||||
ui.label("Designs you save appear here. They stay in this browser.");
|
||||
}
|
||||
egui::ScrollArea::vertical().max_height(360.0).show(ui, |ui| {
|
||||
// The name takes the width the size and the actions leave (#8097).
|
||||
let empty = "Designs you save appear here. They stay in this browser.";
|
||||
ui_kit::name_list(ui, "opfs_files", &["Name", "Size", "Actions"], empty, |list| {
|
||||
egui::Grid::new("opfs_files").num_columns(3).spacing([12.0, 6.0]).show(ui, |ui| {
|
||||
for e in &entries {
|
||||
list.row(&e.name, |row| {
|
||||
match &mut self.renaming {
|
||||
Some((from, to)) if *from == e.name => row.cell(|ui| {
|
||||
let r =
|
||||
ui_kit::cell_editor(ui, ui.make_persistent_id(("stored_file_name", from.as_str())), to, "", false);
|
||||
if std::mem::take(&mut self.rename_focus) {
|
||||
r.request_focus();
|
||||
}
|
||||
if r.lost_focus() {
|
||||
act = Some(if ui.input(|i| i.key_pressed(egui::Key::Enter)) {
|
||||
("rename", to.clone())
|
||||
} else {
|
||||
("rename_cancel", String::new())
|
||||
});
|
||||
}
|
||||
}),
|
||||
_ => {
|
||||
row.name(&e.name);
|
||||
match &mut self.renaming {
|
||||
Some((from, to)) if *from == e.name => {
|
||||
let r = ui_kit::cell_editor(ui, ui.make_persistent_id(("stored_file_name", from.as_str())), to, "", false);
|
||||
if std::mem::take(&mut self.rename_focus) {
|
||||
r.request_focus();
|
||||
}
|
||||
if r.lost_focus() {
|
||||
act = Some(if ui.input(|i| i.key_pressed(egui::Key::Enter)) {
|
||||
("rename", to.clone())
|
||||
} else {
|
||||
("rename_cancel", String::new())
|
||||
});
|
||||
}
|
||||
}
|
||||
row.cell(|ui| ui.label(format!("{:.1} KB", e.size as f64 / 1024.0)));
|
||||
row.cell(|ui| {
|
||||
if ui_kit::button(ui, "Open", ui_kit::Button::Small).clicked() {
|
||||
act = Some(("open", e.name.clone()));
|
||||
}
|
||||
let design = solidcraft_engine::io::is_design_path(&e.name);
|
||||
if !design
|
||||
&& ui_kit::button(ui, "Insert", ui_kit::Button::Small)
|
||||
.on_hover_text(solidcraft_ui_egui::i18n::tr("web.files.insert_stored_drawings_hint"))
|
||||
.clicked()
|
||||
{
|
||||
act = Some(("insert", e.name.clone()));
|
||||
}
|
||||
if ui_kit::button(ui, "Rename", ui_kit::Button::Small).clicked() {
|
||||
self.renaming = Some((e.name.clone(), e.name.clone()));
|
||||
self.rename_focus = true;
|
||||
}
|
||||
if ui_kit::button(ui, "Save to computer…", ui_kit::Button::Small)
|
||||
.on_hover_text("A copy in a file you choose; the stored design stays here")
|
||||
_ => {
|
||||
ui.label(&e.name);
|
||||
}
|
||||
}
|
||||
ui.label(format!("{:.1} KB", e.size as f64 / 1024.0));
|
||||
ui.horizontal(|ui| {
|
||||
if ui_kit::button(ui, "Open", ui_kit::Button::Small).clicked() {
|
||||
act = Some(("open", e.name.clone()));
|
||||
}
|
||||
let design = solidcraft_engine::io::is_design_path(&e.name);
|
||||
if !design
|
||||
&& ui_kit::button(ui, "Insert", ui_kit::Button::Small)
|
||||
.on_hover_text(solidcraft_ui_egui::i18n::tr("web.files.insert_stored_drawings_hint"))
|
||||
.clicked()
|
||||
{
|
||||
act = Some(("save_copy", e.name.clone()));
|
||||
}
|
||||
if ui_kit::button(ui, "Download", ui_kit::Button::Small).clicked() {
|
||||
act = Some(("download", e.name.clone()));
|
||||
}
|
||||
if ui_kit::button(ui, "Delete", ui_kit::Button::Small).clicked() {
|
||||
act = Some(("delete", e.name.clone()));
|
||||
}
|
||||
});
|
||||
{
|
||||
act = Some(("insert", e.name.clone()));
|
||||
}
|
||||
if ui_kit::button(ui, "Rename", ui_kit::Button::Small).clicked() {
|
||||
self.renaming = Some((e.name.clone(), e.name.clone()));
|
||||
self.rename_focus = true;
|
||||
}
|
||||
if ui_kit::button(ui, "Save to computer…", ui_kit::Button::Small)
|
||||
.on_hover_text("A copy in a file you choose; the stored design stays here")
|
||||
.clicked()
|
||||
{
|
||||
act = Some(("save_copy", e.name.clone()));
|
||||
}
|
||||
if ui_kit::button(ui, "Download", ui_kit::Button::Small).clicked() {
|
||||
act = Some(("download", e.name.clone()));
|
||||
}
|
||||
if ui_kit::button(ui, "Delete", ui_kit::Button::Small).clicked() {
|
||||
act = Some(("delete", e.name.clone()));
|
||||
}
|
||||
});
|
||||
ui.end_row();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -653,7 +623,6 @@ 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));
|
||||
@@ -661,8 +630,7 @@ mod web {
|
||||
ctx.request_repaint_after(delay);
|
||||
}
|
||||
if self.launch.persists() {
|
||||
let theme = solidcraft_ui_egui::prefs::theme_drop_target(&self.app, ctx);
|
||||
files::take_drops(&self.files, raw, theme);
|
||||
files::take_drops(&self.files, raw);
|
||||
} else {
|
||||
raw.dropped_files.clear();
|
||||
}
|
||||
@@ -754,7 +722,6 @@ 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}\">");
|
||||
@@ -987,18 +954,4 @@ 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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,11 +96,6 @@ libc = "0.2"
|
||||
|
||||
[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies]
|
||||
winit = { version = "0.30", default-features = false, features = ["x11"] }
|
||||
# Bringing the window forward on X11 when a close asks about unsaved designs (src/come_forward.rs):
|
||||
# the window's id, and the window manager request winit doesn't send (MIT OR Apache-2.0; both
|
||||
# already in the tree through eframe and winit).
|
||||
raw-window-handle = "0.6"
|
||||
x11rb = { version = "0.13", default-features = false }
|
||||
|
||||
[build-dependencies]
|
||||
embed-resource = "3.0.11"
|
||||
|
||||
@@ -1,234 +0,0 @@
|
||||
//! Bringing the window forward when a close stops at "save changes?".
|
||||
//!
|
||||
//! A close from the taskbar or a window switcher reaches the window while another app has the
|
||||
//! focus. The prompt it opens is only seen once the window comes forward, so the window asks for
|
||||
//! the focus then. A window the user is already in is left alone.
|
||||
//!
|
||||
//! winit's Focus asks as an application, which a window manager with focus-stealing prevention
|
||||
//! (KWin's default) refuses for a window used before the switch away: it only marks the taskbar
|
||||
//! entry. On X11 the request is sent again with the source EWMH gives "direct user actions"
|
||||
//! (`_NET_ACTIVE_WINDOW`, source 2), which is what this is: the prompt is the rest of the user's
|
||||
//! close. Native Wayland has no way for a window to raise itself, so there the attention request
|
||||
//! (xdg-activation) lets the compositor raise it or mark its taskbar entry.
|
||||
//!
|
||||
//! When: not at once. KWin raises the window, then the taskbar's closing context menu hands the
|
||||
//! focus back to the app that had it, a few milliseconds later, and the window would flicker
|
||||
//! forward and back. So the window asks once the menu is gone, `FIRST_AFTER` the close. It asks
|
||||
//! again only if the focus is taken back right after it came (a slow menu) or never came; a later
|
||||
//! switch is the user's own, and the window stays where the user put it.
|
||||
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
/// From the close to the first request: past the taskbar menu's focus hand-back (2 to 11 ms on
|
||||
/// KDE Plasma 6), too short to notice.
|
||||
const FIRST_AFTER: Duration = Duration::from_millis(120);
|
||||
/// After the first request, how long the window asks again when it loses the focus: short of
|
||||
/// anything the user does next.
|
||||
const KEEP_ASKING: Duration = Duration::from_millis(400);
|
||||
/// The least time between two requests.
|
||||
const AGAIN_AFTER: Duration = Duration::from_millis(100);
|
||||
/// Focus lost this soon after a request was taken back by the taskbar menu; later, by the user.
|
||||
const TAKEN_BACK_WITHIN: Duration = Duration::from_millis(50);
|
||||
|
||||
/// The window coming forward after a close that asks about unsaved designs.
|
||||
#[derive(Default)]
|
||||
pub struct ComeForward {
|
||||
/// The first request and the end of asking; `None`: not asking.
|
||||
window: Option<(Instant, Instant)>,
|
||||
/// The last request.
|
||||
last: Option<Instant>,
|
||||
/// The window has had the focus since the last request.
|
||||
held: bool,
|
||||
}
|
||||
|
||||
/// What a frame does while the window is coming forward.
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum Step {
|
||||
/// Ask for the focus now.
|
||||
Ask,
|
||||
/// Look again after this long.
|
||||
Wait(Duration),
|
||||
/// Done asking.
|
||||
Done,
|
||||
}
|
||||
|
||||
impl ComeForward {
|
||||
/// A close stopped at the prompt: ask for the focus shortly.
|
||||
pub fn start(&mut self, now: Instant) {
|
||||
let first = now + FIRST_AFTER;
|
||||
self.window = Some((first, first + KEEP_ASKING));
|
||||
self.last = None;
|
||||
self.held = false;
|
||||
}
|
||||
|
||||
/// Every frame: ask once the taskbar menu is gone, and again while the focus is taken back.
|
||||
pub fn tick(&mut self, ctx: &egui::Context, frame: &eframe::Frame) {
|
||||
if self.window.is_none() {
|
||||
return;
|
||||
}
|
||||
let (focused, minimized) = ctx.input(|i| (i.viewport().focused, i.viewport().minimized));
|
||||
// The app may be idle: a frame to look again.
|
||||
match self.step(Instant::now(), focused) {
|
||||
Step::Ask => {
|
||||
bring(ctx, frame, minimized);
|
||||
ctx.request_repaint_after(AGAIN_AFTER);
|
||||
}
|
||||
Step::Wait(after) => ctx.request_repaint_after(after),
|
||||
Step::Done => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn step(&mut self, now: Instant, focused: Option<bool>) -> Step {
|
||||
let Some((first, until)) = self.window else { return Step::Done };
|
||||
if now >= until {
|
||||
self.window = None;
|
||||
return Step::Done;
|
||||
}
|
||||
if now < first {
|
||||
return Step::Wait(first - now);
|
||||
}
|
||||
let Some(last) = self.last else {
|
||||
if focused == Some(true) {
|
||||
// The user went back to the window already.
|
||||
self.window = None;
|
||||
return Step::Done;
|
||||
}
|
||||
self.last = Some(now);
|
||||
return Step::Ask;
|
||||
};
|
||||
if focused == Some(true) {
|
||||
self.held = true;
|
||||
return Step::Wait(until - now);
|
||||
}
|
||||
if self.held {
|
||||
if now.duration_since(last) >= TAKEN_BACK_WITHIN {
|
||||
// The user switched away: leave the window there.
|
||||
self.window = None;
|
||||
return Step::Done;
|
||||
}
|
||||
self.held = false;
|
||||
}
|
||||
let next = last + AGAIN_AFTER;
|
||||
if now < next {
|
||||
return Step::Wait(next - now);
|
||||
}
|
||||
self.last = Some(now);
|
||||
Step::Ask
|
||||
}
|
||||
}
|
||||
|
||||
/// Ask for the focus once.
|
||||
fn bring(ctx: &egui::Context, frame: &eframe::Frame, minimized: Option<bool>) {
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
let raised = match x11_activate(frame) {
|
||||
Ok(sent) => sent,
|
||||
Err(e) => {
|
||||
log::warn!("bringing the window forward through X11: {e}");
|
||||
false
|
||||
}
|
||||
};
|
||||
#[cfg(not(all(unix, not(target_os = "macos"))))]
|
||||
let raised = {
|
||||
let _ = frame;
|
||||
false
|
||||
};
|
||||
for c in commands(minimized, raised) {
|
||||
ctx.send_viewport_cmd(c);
|
||||
}
|
||||
}
|
||||
|
||||
/// The viewport commands for a window in the background; `raised`: the X11 request went out,
|
||||
/// which also restores a minimized window.
|
||||
fn commands(minimized: Option<bool>, raised: bool) -> Vec<egui::ViewportCommand> {
|
||||
if raised {
|
||||
return vec![egui::ViewportCommand::Focus];
|
||||
}
|
||||
let mut commands = Vec::new();
|
||||
if minimized == Some(true) {
|
||||
commands.push(egui::ViewportCommand::Minimized(false));
|
||||
}
|
||||
commands.push(egui::ViewportCommand::Focus);
|
||||
commands.push(egui::ViewportCommand::RequestUserAttention(egui::UserAttentionType::Informational));
|
||||
commands
|
||||
}
|
||||
|
||||
/// Ask the X11 window manager to activate the window as a user action. `Ok(false)`: the window
|
||||
/// isn't an X11 window (native Wayland).
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
fn x11_activate(frame: &eframe::Frame) -> Result<bool, String> {
|
||||
use raw_window_handle::{HasWindowHandle, RawWindowHandle};
|
||||
use x11rb::connection::Connection;
|
||||
use x11rb::protocol::xproto::{ClientMessageEvent, ConnectionExt, EventMask};
|
||||
|
||||
let handle = frame.window_handle().map_err(|e| e.to_string())?;
|
||||
let window = match handle.as_raw() {
|
||||
RawWindowHandle::Xlib(h) => u32::try_from(h.window).map_err(|e| e.to_string())?,
|
||||
RawWindowHandle::Xcb(h) => h.window.get(),
|
||||
_ => return Ok(false),
|
||||
};
|
||||
// A connection of its own: winit's isn't reachable, and a close that asks is rare.
|
||||
let (conn, screen) = x11rb::connect(None).map_err(|e| e.to_string())?;
|
||||
let root = conn.setup().roots.get(screen).ok_or("no X11 screen")?.root;
|
||||
let atom = conn.intern_atom(false, b"_NET_ACTIVE_WINDOW").map_err(|e| e.to_string())?.reply().map_err(|e| e.to_string())?.atom;
|
||||
// Source 2: a direct user action; the time is the server's current time.
|
||||
let event = ClientMessageEvent::new(32, window, atom, [2, x11rb::CURRENT_TIME, 0, 0, 0]);
|
||||
conn.send_event(false, root, EventMask::SUBSTRUCTURE_REDIRECT | EventMask::SUBSTRUCTURE_NOTIFY, event).map_err(|e| e.to_string())?;
|
||||
conn.flush().map_err(|e| e.to_string())?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{AGAIN_AFTER, ComeForward, FIRST_AFTER, KEEP_ASKING, Step};
|
||||
use eframe::egui::{UserAttentionType, ViewportCommand as C};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
/// The first request waits until the taskbar menu has handed the focus back. The window asks
|
||||
/// again when the focus is taken back right after it came, or never came, no more often than
|
||||
/// every `AGAIN_AFTER` and only for `KEEP_ASKING`; a later switch away is the user's.
|
||||
#[test]
|
||||
fn the_window_asks_once_the_taskbar_menu_is_gone() {
|
||||
let t0 = Instant::now();
|
||||
let ms = |n: u64| Duration::from_millis(n);
|
||||
let at = |n: u64| t0 + ms(n);
|
||||
let (first, again, keep) = (FIRST_AFTER.as_millis() as u64, AGAIN_AFTER.as_millis() as u64, KEEP_ASKING.as_millis() as u64);
|
||||
let mut f = ComeForward::default();
|
||||
assert_eq!(f.step(t0, Some(false)), Step::Done, "not started");
|
||||
f.start(t0);
|
||||
assert_eq!(f.step(t0, Some(false)), Step::Wait(FIRST_AFTER), "the menu is still closing");
|
||||
assert_eq!(f.step(at(5), Some(false)), Step::Wait(ms(first - 5)));
|
||||
assert_eq!(f.step(at(first), Some(false)), Step::Ask);
|
||||
assert_eq!(f.step(at(first + 3), Some(true)), Step::Wait(ms(keep - 3)), "raised");
|
||||
// A slow menu takes the focus back 10 ms later: ask again, not before AGAIN_AFTER.
|
||||
assert_eq!(f.step(at(first + 10), Some(false)), Step::Wait(ms(again - 10)));
|
||||
assert_eq!(f.step(at(first + again), Some(false)), Step::Ask);
|
||||
assert_eq!(f.step(at(first + again + 3), Some(true)), Step::Wait(ms(keep - again - 3)));
|
||||
// The user clicks another app 200 ms later: the window stays behind.
|
||||
assert_eq!(f.step(at(first + again + 200), Some(false)), Step::Done);
|
||||
assert_eq!(f.step(at(first + again + 300), Some(false)), Step::Done);
|
||||
|
||||
// Never raised (the request was refused): ask again every AGAIN_AFTER, for KEEP_ASKING.
|
||||
f.start(t0);
|
||||
assert_eq!(f.step(at(first), None), Step::Ask, "unknown focus counts as out of focus");
|
||||
assert_eq!(f.step(at(first + again), Some(false)), Step::Ask);
|
||||
assert_eq!(f.step(at(first + keep), Some(false)), Step::Done);
|
||||
|
||||
// Back in the window before the first request: nothing to do.
|
||||
f.start(t0);
|
||||
assert_eq!(f.step(at(first), Some(true)), Step::Done);
|
||||
}
|
||||
|
||||
/// Without the X11 request: focus, and the attention mark where focus is refused; a minimized
|
||||
/// window is restored first.
|
||||
#[test]
|
||||
fn a_window_closed_from_the_background_comes_forward() {
|
||||
let forward = [C::Focus, C::RequestUserAttention(UserAttentionType::Informational)];
|
||||
assert_eq!(super::commands(Some(false), false), forward);
|
||||
assert_eq!(super::commands(None, false), forward, "unknown counts as not minimized");
|
||||
let minimized = super::commands(Some(true), false);
|
||||
assert_eq!(minimized.first(), Some(&C::Minimized(false)));
|
||||
assert_eq!(minimized.get(1..), Some(&forward[..]));
|
||||
// The window manager took the X11 request: nothing left to mark.
|
||||
assert_eq!(super::commands(Some(true), true), [C::Focus]);
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
#[path = "../../../assets/icons/app/shared/source/app_icon.rs"]
|
||||
mod app_icon;
|
||||
mod come_forward;
|
||||
mod control_server;
|
||||
mod device_lost;
|
||||
#[cfg(target_os = "windows")]
|
||||
@@ -85,8 +84,6 @@ struct App {
|
||||
frames: u32,
|
||||
/// Started over the control channel: the window doesn't ask for the foreground.
|
||||
take_focus_unwanted: bool,
|
||||
/// Coming forward after a close that asks about unsaved designs.
|
||||
come_forward: come_forward::ComeForward,
|
||||
/// The graphics device: lost, it can't draw again (`App::watch_device`, #4172).
|
||||
gpu: Option<LostWatch>,
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
@@ -142,13 +139,7 @@ impl eframe::App for App {
|
||||
if ctx.input(|i| i.viewport().close_requested()) && !self.solve.quit_requested {
|
||||
ctx.send_viewport_cmd(egui::ViewportCommand::CancelClose);
|
||||
solidcraft_ui_egui::documents::request_quit(&mut self.solve);
|
||||
// Closed from the taskbar or a window switcher while another app had the focus: the
|
||||
// "save changes?" prompt is only seen once the window comes forward.
|
||||
if !self.solve.quit_requested {
|
||||
self.come_forward.start(std::time::Instant::now());
|
||||
}
|
||||
}
|
||||
self.come_forward.tick(ctx, _frame);
|
||||
#[cfg(target_os = "macos")]
|
||||
for f in if self.solve.file_picker_busy() { Vec::new() } else { open_documents::take() } {
|
||||
self.solve.open_path_async(&f);
|
||||
@@ -761,15 +752,12 @@ 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"));
|
||||
@@ -790,21 +778,13 @@ fn main() -> eframe::Result {
|
||||
}
|
||||
"--help" | "-h" => {
|
||||
println!(
|
||||
"usage: solidcraft [--control PORT] [--sample] [--theme-file THEME.json] [--crash-log] [--safe-mode] [--max-file-size SIZE] [design.sc | part.step]"
|
||||
"usage: solidcraft [--control PORT] [--sample] [--theme-file THEME.json] [--crash-log] [--safe-mode] [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();
|
||||
@@ -1080,7 +1060,6 @@ fn main() -> eframe::Result {
|
||||
starting,
|
||||
frames: 0,
|
||||
take_focus_unwanted: control_port.is_some(),
|
||||
come_forward: come_forward::ComeForward::default(),
|
||||
gpu,
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
space_mouse,
|
||||
|
||||
@@ -302,14 +302,6 @@ 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());
|
||||
@@ -332,16 +324,6 @@ 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();
|
||||
|
||||
@@ -105,7 +105,6 @@ eye false
|
||||
fastener false
|
||||
fillet false
|
||||
finish false
|
||||
finish_edit false
|
||||
fit false
|
||||
fit_section false
|
||||
flange false
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<title>Finish Edit</title>
|
||||
<path d="M50 32L14 32M25 21L14 32L25 43" fill="none" stroke="#236ead" stroke-width="5.6" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 237 B |
@@ -38,7 +38,6 @@
|
||||
"All open designs": "كل التصاميم المفتوحة",
|
||||
"Allowed expressions, separated by semicolons": "التعابير المسموح بها، مفصولة بفواصل منقوطة",
|
||||
"Also deletes": "يحذف أيضًا",
|
||||
"Also place them from the origin (fully constrained)": "وضعها أيضًا انطلاقًا من نقطة الأصل (مقيّدة بالكامل)",
|
||||
"Anchor": "نقطة الإرساء",
|
||||
"Angle 2": "الزاوية 2",
|
||||
"Angle Display": "عرض الزاوية",
|
||||
@@ -181,9 +180,6 @@
|
||||
"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": "تجاهل",
|
||||
"Distance": "المسافة",
|
||||
@@ -200,8 +196,6 @@
|
||||
"Drag to turn the rest in 5° steps. Hold Alt or Ctrl to turn it freely.": "اسحب لتدوير المسند بخطوات 5°. اضغط Alt أو Ctrl لتدويره بحرية.",
|
||||
"Drive Joint": "تحريك المفصل",
|
||||
"Duration": "المدة",
|
||||
"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.": "يحصل كل شكل جديد أيضًا على أبعاد من نقطة أصل الرسم حتى لا يبقى فيه شيء حر. لا يحصل الشكل المتصل مسبقًا بنقطة الأصل أو بهندسة موضوعة على أي منها.",
|
||||
"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.": "يحصل كل شكل ترسمه على جميع أبعاده بالمقاسات التي رسمتها (القيم المكتوبة لها الأولوية). يُستبعد المقاس الذي يحدده الرسم مسبقًا.",
|
||||
"Edge": "حافة",
|
||||
"Edges": "الحواف",
|
||||
"Edit": "تعديل",
|
||||
@@ -260,8 +254,6 @@
|
||||
"Flanges": "الحواف المثنية",
|
||||
"Flat Pattern": "النمط المسطح",
|
||||
"Flat size": "الحجم المسطّح",
|
||||
"Flat size X": "الحجم المسطّح X",
|
||||
"Flat size Y": "الحجم المسطّح Y",
|
||||
"Follow camera (Look At sketches)": "تتبّع الكاميرا (النظر إلى الرسومات)",
|
||||
"Follow the camera (Look At sketches)": "تتبّع الكاميرا (النظر إلى الرسومات)",
|
||||
"Font": "الخط",
|
||||
@@ -595,7 +587,6 @@
|
||||
"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": "انتفاخ البداية",
|
||||
@@ -763,7 +754,6 @@
|
||||
"appearance.pbr_opaque": "PBR معتم",
|
||||
"appearance.roughness": "الخشونة",
|
||||
"appearance.surface_response": "استجابة السطح",
|
||||
"at the face": "عند الوجه",
|
||||
"bom.group_children": "الأجزاء الفرعية في صف الجزء الأصلي",
|
||||
"browser.body_name": "جسم {number}",
|
||||
"browser.calibrate_title": "معايرة: {name}",
|
||||
@@ -772,7 +762,6 @@
|
||||
"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}",
|
||||
@@ -823,9 +812,7 @@
|
||||
"cmd.component_create": "مكوّن جديد",
|
||||
"cmd.component_cycle_color": "تدوير لون المكوّن",
|
||||
"cmd.component_delete": "حذف المكوّن",
|
||||
"cmd.component_edit_in_place": "تحرير في المكان",
|
||||
"cmd.component_extract": "استخراج إلى ملف",
|
||||
"cmd.component_finish_edit": "إنهاء التحرير",
|
||||
"cmd.component_from_bodies": "إنشاء مكوّنات من الأجسام",
|
||||
"cmd.component_insert_design": "إدراج تصميم",
|
||||
"cmd.component_links": "قائمة الروابط",
|
||||
@@ -1000,10 +987,6 @@
|
||||
"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": "تصيير كل اللقطات",
|
||||
@@ -1026,20 +1009,15 @@
|
||||
"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",
|
||||
@@ -1142,7 +1120,6 @@
|
||||
"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": "تبديل البُعد المُشتق",
|
||||
@@ -1194,7 +1171,6 @@
|
||||
"cmd.view.render_dock": "نقل لوحة التصيير",
|
||||
"cmd.view.render_focus": "عرض تركيز التصيير",
|
||||
"cmd.view_ambient_occlusion": "الإظلال المحيطي",
|
||||
"cmd.view_angle_readout": "زوايا العرض",
|
||||
"cmd.view_apply": "استعادة عرض مسمّى",
|
||||
"cmd.view_back": "العرض الخلفي",
|
||||
"cmd.view_bottom": "العرض السفلي",
|
||||
@@ -1270,7 +1246,6 @@
|
||||
"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 مع النقر يبدّل تحديدها أيضًا. راجع المعاينة ثم اضغط موافق.",
|
||||
@@ -1330,7 +1305,6 @@
|
||||
"help.appearance.assign": "يمنح الأجسام أو الأوجه أو المكونات المحددة لونًا أو مظهر مادة، من دون تغيير وزنها. استخدمه للتمييز بين القطع أو لمعاينة التشطيب.",
|
||||
"help.attributions": "الإسنادات (ATTRIBUTION.md)",
|
||||
"help.build": "الإصدار {version} · الإيداع {commit} · بُني في {date}",
|
||||
"help.component.finish_edit": "يحفظ الجزء الذي تحرّره في مكانه، ويعود إلى تجميعه ويحدّث التجميع منه. استخدمه عند الانتهاء من تغييراتك على الجزء المرتبط.",
|
||||
"help.connect_note": "أعد تشغيل كل مساعد لتحميل خادم SolidCraft. نُسخ كل ملف احتياطيًا بامتداد .bak قبل تغييره.",
|
||||
"help.connect_title": "ربط مساعدي الذكاء الاصطناعي",
|
||||
"help.face.delete": "يحذف الأوجه المحددة ويترك الأوجه المجاورة تسد الفجوة، أو يترك الفتحة مفتوحة كسطح. استخدمه لإزالة حافة مقوّسة أو ثقب أو نتوء يعترض الطريق.",
|
||||
@@ -1341,7 +1315,6 @@
|
||||
"help.mit": "رخصة MIT",
|
||||
"help.notice": "NOTICE",
|
||||
"help.parameters.change": "يفتح جدول القيم المسماة والتعبيرات التي تتحكم في أبعاد التصميم. استخدمه لتغيير مقاس في مكان واحد فتتبعه كل الميزات التي تستخدمه.",
|
||||
"help.pointcloud.insert": "أدرج مسحًا بصيغة PLY أو XYZ أو PTS كسحابة نقاط بجانب النموذج. استخدمه كمرجع عند النمذجة انطلاقًا من بيانات القياس.",
|
||||
"help.solid.align": "يحرّك الأجسام المحددة بحيث تلتقي نقطة أو وجه أو مستوى منها بنقطة أو وجه أو مستوى في مكان آخر. استخدمه لوضع قطعة ملاصقة لأخرى دون قياس المسافة.",
|
||||
"help.solid.chamfer": "يقطع الحواف المحددة بشطفة مستوية بالمسافة التي تحددها. استخدمه لكسر الحواف الحادة أو لتسهيل إدخال مسمار أو برغي.",
|
||||
"help.solid.combine": "يضم الأجسام، أو يقتطعها من جسم هدف، أو يُبقي الحجم المشترك بينها فقط، ويمكنه الاحتفاظ بأجسام الأداة. استخدمه لدمج القطع، أو لقطع شكل بشكل آخر، أو لمعرفة موضع تداخل جسمين.",
|
||||
@@ -1367,8 +1340,6 @@
|
||||
"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": "هل تحذف هذا الإعداد المسبق الشخصي؟ الثقوب الموجودة لا تتغير.",
|
||||
@@ -1736,8 +1707,6 @@
|
||||
"prefs.anti_aliasing_note": "ينعّم الحواف المسننة.",
|
||||
"prefs.applies_on_restart": "يُطبَّق عند إعادة التشغيل",
|
||||
"prefs.assistant": "المساعد",
|
||||
"prefs.auto_operation": "اختيار العملية تلقائيًا",
|
||||
"prefs.auto_operation_note": "تختار نوافذ الميزات «ضم» أو «جسم جديد» أو «قص» بحسب ما تلامسه المادة الجديدة، إلى أن تختار عملية بنفسك. عند الإيقاف: تحتفظ بالعملية التي تُفتح بها.",
|
||||
"prefs.axes": "المحاور",
|
||||
"prefs.axis": "المحور",
|
||||
"prefs.bodies_folders": "مجلدات الأجسام",
|
||||
@@ -1813,19 +1782,8 @@
|
||||
"prefs.layout_note": "المضغوط يثبّت اللوحات على حواف النافذة. شفافية المستعرض متاحة في الوضع العائم. غيّر جهة المساعد من صفحة المساعد.",
|
||||
"prefs.left": "يسار",
|
||||
"prefs.left_click": "نقر أيسر",
|
||||
"prefs.linked_designs": "التصاميم المرتبطة",
|
||||
"prefs.live_input": "الإدخال المباشر",
|
||||
"prefs.live_preview": "المعاينة المباشرة",
|
||||
"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": "الفأرة ولوحة اللمس",
|
||||
@@ -1874,24 +1832,11 @@
|
||||
"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": "أدلة محاذاة المراكز ونقاط المنتصف",
|
||||
"prefs.sketch_tracking_guides_note": "مرّر المؤشر فوق مركز دائرة أو نقطة منتصف حافة مستقيمة، ثم حرّكه أفقيًا أو عموديًا لإظهار دليل منقّط مؤقت.",
|
||||
"prefs.sketching": "الرسم",
|
||||
"prefs.snap_drawing_note": "أثناء الرسم أو سحب نقطة في الرسم.",
|
||||
"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": "محدد",
|
||||
@@ -1899,16 +1844,12 @@
|
||||
"prefs.supersampling_brief": "يرسم العرض بحجم أكبر ثم يصغّره: حاد جدًا لكنه أثقل.",
|
||||
"prefs.tab_accent_note": "لون التمييز يتبع التبويب النشط",
|
||||
"prefs.theme_file": "ملف السمة",
|
||||
"prefs.theme_file_or_paste": "ملف أو لصق",
|
||||
"prefs.theme_follow": "اتباع ملف سمة",
|
||||
"prefs.theme_follow_error": "لم يُطبّق: {error}. تبقى آخر سمة سليمة مفعّلة.",
|
||||
"prefs.theme_follow_launch": "يُتّبع لهذه الجلسة فقط (--theme-file)؛ يُعاد تحميله كلما أعادت أداة أخرى كتابته.",
|
||||
"prefs.theme_follow_note": "يُعاد تحميله كلما أعادت أداة أخرى كتابته. اختيار سمة يوقف اتباعه.",
|
||||
"prefs.theme_follow_off": "إيقاف",
|
||||
"prefs.theme_follow_stop": "إيقاف الاتباع",
|
||||
"prefs.theme_import_hint": "انقر لاختيار ملف سمة، أو أفلت ملفًا هنا، أو اضغط Ctrl+V (Cmd+V على Mac) بعد نسخ سمة",
|
||||
"prefs.theme_paste": "لصق السمة",
|
||||
"prefs.theme_paste_none": "لا تحتوي الحافظة على سمة SolidCraft",
|
||||
"prefs.timeline_half_hour": "−30 دقيقة",
|
||||
"prefs.timeline_now": "الآن",
|
||||
"prefs.timeline_quarter_hour": "−15",
|
||||
@@ -1974,47 +1915,7 @@
|
||||
"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": "تركيب أصلي؛ راجعه قبل التطبيق",
|
||||
"render.availability_projection": "إسقاط مدعوم؛ راجع النطاق وما أُغفل",
|
||||
"render.availability_reference": "بيانات مرجعية؛ ليست تعيينًا تلقائيًا",
|
||||
@@ -2069,13 +1970,6 @@
|
||||
"render.camera_set": "تعيين كاميرا التصيير",
|
||||
"render.camera_target": "موضع الهدف · mm",
|
||||
"render.camera_yaw": "الانعطاف",
|
||||
"render.category_metal": "معدن",
|
||||
"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",
|
||||
@@ -2175,7 +2069,6 @@
|
||||
"render.effects_reset_lens": "إعادة ضبط تأثيرات العدسة",
|
||||
"render.effects_seed": "بذرة النمط",
|
||||
"render.emitted_axis": "المحور المنبعث",
|
||||
"render.environment_tiles_require_raytrace": "تتطلب البيئات تتبّع الأشعة على المعالج أو بطاقة الرسومات. اخترْه في محرر المخرجات.",
|
||||
"render.error_area_samples": "يجب أن تكون area_samples 1 أو 4 أو 16 أو 64",
|
||||
"render.error_binding_allocation": "تعذّر حجز ربط المادة",
|
||||
"render.error_body_bound": "معاينة المنتج تتجاوز حد الأجسام الملتقطة",
|
||||
@@ -2247,8 +2140,6 @@
|
||||
"render.flare_strength": "شدة وهج الإضاءات العالية",
|
||||
"render.flare_threshold": "عتبة الوهج الخطية للمشهد",
|
||||
"render.fold_browser": "طيّ المتصفح",
|
||||
"render.frame_presets": "إعدادات مسبقة للإطار",
|
||||
"render.frame_presets_pending": "تصل مع محرر اللقطة",
|
||||
"render.gpu_depth": "أقصى عمق للمسار",
|
||||
"render.gpu_options_help": "تُحفظ العيّنات والعمق والبذرة والتعريض مع هذا الإعداد. تُحفظ طريقة الصورة النشطة فقط.",
|
||||
"render.gpu_path_failed": "متتبع المسارات على GPU غير متاح",
|
||||
@@ -2309,36 +2200,6 @@
|
||||
"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": "الطبقة مفعّلة",
|
||||
@@ -2350,12 +2211,8 @@
|
||||
"render.lens_effects_title": "مستوحى من العدسات والتصوير",
|
||||
"render.library_all": "الفهرس",
|
||||
"render.library_all_states": "كل حالات التوفر",
|
||||
"render.library_appearance_available": "مظهر متاح",
|
||||
"render.library_applicable": "الموارد القابلة للتطبيق",
|
||||
"render.library_applied": "طُبّق إسقاط المورد. افحص طلب التصيير الكامل قبل البدء.",
|
||||
"render.library_apply_appearance": "تطبيق المظهر",
|
||||
"render.library_apply_hdr": "الحفظ مع بيئة HDR هذه",
|
||||
"render.library_apply_hint": "انقر للتطبيق · اسحب إلى النموذج",
|
||||
"render.library_apply_maps": "استخدام خرائط سطح GPU المُعدّة",
|
||||
"render.library_apply_studio": "حفظ إسقاط الاستوديو المُعدّ",
|
||||
"render.library_apply_uniform": "تطبيق مظهر معتم موحّد",
|
||||
@@ -2371,23 +2228,15 @@
|
||||
"render.library_extent": "امتداد الموضوع",
|
||||
"render.library_favorite": "إضافة إلى المفضلة",
|
||||
"render.library_favorites": "المفضلة",
|
||||
"render.library_filters": "الفهرس والتوفر",
|
||||
"render.library_grid": "شبكة",
|
||||
"render.library_hdr": "بيئات HDR المدمجة",
|
||||
"render.library_hdr_help": "يضمّن HDR الأصلي في الإعداد المحفوظ. يُحوَّل أصل خط الطول المُعدّ صراحةً؛ ولا حاجة إلى ملف خارجي بعد الحفظ.",
|
||||
"render.library_in_design": "في التصميم",
|
||||
"render.library_inspect_only": "اختر للفحص؛ لا يُطبّق شيء تلقائيًا. المفضلة والسجل الحديث يخصّان جلسة المستند المفتوحة هذه.",
|
||||
"render.library_ior": "معامل الانكسار",
|
||||
"render.library_light_concepts": "مفاهيم الإضاءة",
|
||||
"render.library_list": "قائمة",
|
||||
"render.library_maps_help": "يمكن حفظ الخرائط المدمجة بمصادر مثبّتة وتعيين محلي للتواجدات. تبقى التجاوزات البصرية المؤقتة وضعًا منفصلًا.",
|
||||
"render.library_material_concepts": "مفاهيم المواد",
|
||||
"render.library_metallic": "معدنية",
|
||||
"render.library_native_scenes": "الاستوديوهات الأصلية",
|
||||
"render.library_next": "الصفحة التالية",
|
||||
"render.library_omitted": "غير مضمّن",
|
||||
"render.library_opacity": "العتامة",
|
||||
"render.library_other": "مظاهر أخرى",
|
||||
"render.library_physical_profiles": "ملفات المراجع الفيزيائية",
|
||||
"render.library_previous": "الصفحة السابقة",
|
||||
"render.library_quality_concepts": "مواصفات الجودة",
|
||||
@@ -2397,7 +2246,6 @@
|
||||
"render.library_results": "الموارد المطابقة",
|
||||
"render.library_rig_help": "لاءم مرة واحدة عند مركز صريح وامتداد فيزيائي. الأطقم الكاملة تتضمن بطاقات سلبية؛ وتظل موانع المعايرة ترفض. يُفحص خلوص محافظ بين الباعثات والبطاقات قبل الحفظ.",
|
||||
"render.library_rigs": "أطقم الإضاءة المُعدّة",
|
||||
"render.library_roughness": "الخشونة",
|
||||
"render.library_save_maps": "حفظ الخرائط في هذا الإعداد",
|
||||
"render.library_scene_bounds": "استخدام حدود المشهد الملتقطة",
|
||||
"render.library_scenes": "مواصفات المشاهد",
|
||||
@@ -2421,7 +2269,6 @@
|
||||
"render.library_unfavorite": "إزالة من المفضلة",
|
||||
"render.library_uniform_ack": "استخدام التقريب الموحّد دون تأثيرات مكانية أو متقدمة",
|
||||
"render.library_usable": "إسقاطات قابلة للتنفيذ",
|
||||
"render.library_users": "المستخدمون",
|
||||
"render.light_anchor_pending": "تغيير نظام إحداثيات الضوء غير مدعوم بعد.",
|
||||
"render.lights_fixed_to": "الأضواء مثبّتة على",
|
||||
"render.linear_help": "يحتفظ بنسخة أصلية RGBA بفاصلة عائمة 32 بت قبل إزالة التشويش والتعريض وتخطيط الدرجات والمرشحات والخلفية. sRGB/D65 خطي؛ مضروب مسبقًا في التغطية. تُقبل الذاكرة الإضافية صراحةً. بدون ضغط أو تمريرات إضافية.",
|
||||
@@ -2505,34 +2352,12 @@
|
||||
"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": "المظهر",
|
||||
@@ -2593,18 +2418,10 @@
|
||||
"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": "إعادة تدرّج",
|
||||
@@ -2613,7 +2430,6 @@
|
||||
"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": "المراجعة الملتقطة",
|
||||
@@ -2671,7 +2487,6 @@
|
||||
"render.scene_review": "الحل والمراجعة",
|
||||
"render.scene_review_help": "راجع التركيب الأصلي الكامل قبل التطبيق. لا تُستبدل مواصفات SCENE المتقدمة غير المربوطة.",
|
||||
"render.scene_scope_details": "نطاق التركيب",
|
||||
"render.search_appearances": "ابحث في {count} مظهرًا",
|
||||
"render.setup_legacy_environment": "يستخدم هذا الإعداد الأقدم دورانًا أو شدة قديمة للبيئة. يُحتفظ بتلك القيم؛ حوّلها صراحةً قبل حفظ مهمة صورة كاملة.",
|
||||
"render.setup_name": "اسم الإعداد",
|
||||
"render.setup_numeric_help": "يسجّل الحفظ طريقة الصورة النشطة وكل إعداداتها الرقمية. إعادة التحميل أو اختيار إعداد آخر يستبدل هذه المسودة؛ واستعد الكاميرا بشكل منفصل.",
|
||||
@@ -2681,7 +2496,6 @@
|
||||
"render.sharpening": "زيادة حدة التفاصيل",
|
||||
"render.shell_tab_appearance": "مظهر التصيير",
|
||||
"render.shell_tab_camera": "كاميرا التصيير",
|
||||
"render.shell_tab_lighting": "الإضاءة",
|
||||
"render.shell_tab_scene": "مشهد التصيير",
|
||||
"render.shot_duplicate": "تكرار اللقطة المحفوظة",
|
||||
"render.shot_manager": "اللقطات المحفوظة",
|
||||
@@ -2691,23 +2505,10 @@
|
||||
"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": "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 خطي",
|
||||
@@ -2720,9 +2521,6 @@
|
||||
"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": "إشعاع البيئة الثابت",
|
||||
@@ -2761,10 +2559,8 @@
|
||||
"render.studio_sky_help": "تدرج سماء أصلي للإضاءة؛ وليس نموذجًا جويًا معايَرًا.",
|
||||
"render.studio_sun": "الشمس",
|
||||
"render.studio_sun_radius": "نصف القطر الزاوي",
|
||||
"render.studio_tiles_require_gpu": "تتطلب الاستوديوهات تتبّع المسار على بطاقة الرسومات. اخترْ GPU في محرر المخرجات.",
|
||||
"render.studio_title": "استوديو التصيير",
|
||||
"render.studio_tone_map": "تخطيط الدرجات",
|
||||
"render.studios_environments": "الاستوديوهات والبيئات",
|
||||
"render.subsurface_enable": "مسار عشوائي للتشتت الداخلي",
|
||||
"render.subsurface_help": "للعوازل المغلقة فقط. التشتت وامتصاص RGB معاملان منفصلان لكل mm. تتشتت المسارات داخل الهندسة الفعلية، ثم تنكسر أو تنعكس عند الحد. عطّل نفاذ الظل المستقيم وإزالة تشويش السطح والحجم الخارجي؛ واستخدم طبقة فرينل واحدة. ليس مظلل انتشار ولا شبكة كثافة ولا مادة مقيسة. الأوساط الكثيفة قد تحتاج مزيدًا من العيّنات وعمق المسار.",
|
||||
"render.subsurface_rate": "معدل اصطدامات التشتت",
|
||||
@@ -2818,7 +2614,6 @@
|
||||
"render.volume_max": "الحدود القصوى · mm العالم",
|
||||
"render.volume_min": "الحدود الدنيا · mm العالم",
|
||||
"render.volume_title": "حجم متجانس محدود",
|
||||
"render.wait_add_light": "غير مدعوم بعد: إنشاء الأضواء المسمّاة",
|
||||
"render.wait_batch": "غير مدعوم بعد: قائمة تصيير اللقطات المحفوظة",
|
||||
"render.wait_compare": "غير مدعوم بعد: مقارنة الصور المحتفظ بها",
|
||||
"render.wait_paint": "غير مدعوم بعد: اختيار المنتج وتلوين المظهر",
|
||||
@@ -2873,7 +2668,6 @@
|
||||
"select two components": "حدد مكوّنين اثنين",
|
||||
"set by the sketch's lines": "تحدده خطوط الرسم",
|
||||
"settings.display_quality_note": "المستويات الأدق ترسم الأوجه والحواف المنحنية بنعومة أكبر وتستغرق وقتًا أطول لإنشاء الشبكة. يتغير العرض فقط، لا التصميم ولا الملفات المصدّرة.",
|
||||
"settings.double_click_components": "انقر نقرًا مزدوجًا على مكوّن لتنشيطه، أو لتحرير مكوّن مرتبط في مكانه",
|
||||
"settings.frame_rate_details_note": "يضيف رسمًا لأزمنة الإطارات الأخيرة مع المثلثات المرسومة فوقه، ومدرّجًا تكراريًا لأزمنة الإطارات، وما رسمته وحدة GPU. انقر معدل الإطارات في العرض لإظهارها أو إخفائها.",
|
||||
"settings.frame_rate_note": "يعرض الإطارات في الثانية في الزاوية اليمنى السفلية من العرض. أثناء تفعيله يُعاد رسم العرض باستمرار.",
|
||||
"settings.measure_axis_distances": "إظهار المسافات على X/Y/Z",
|
||||
@@ -2886,7 +2680,6 @@
|
||||
"settings.reset_intro": "هذه الإعدادات لا تعمل على هذا الحاسوب وكانت ستمنع SolidCraft من البدء، لذا غُيّرت:",
|
||||
"settings.reset_title": "تم تغيير الإعدادات",
|
||||
"settings.supersampling_note": "يرسم العرض بدقة أعلى ثم يصفّيه: حواف وخطوط وشبكة أنعم على الشاشات الأقل من 4K. يكلّف وقتًا من معالج الرسوميات: نحو 2.25× من البكسلات عند 1.5× و4× عند 2×.",
|
||||
"settings.update_changed_links": "تحديث التصاميم المرتبطة التي تغيّرت عند العودة إلى علامة التبويب",
|
||||
"settings.window_system_details": "التفاصيل",
|
||||
"settings.window_system_note_now": "يُطبَّق بعد إعادة التشغيل. الحالي: {now}.",
|
||||
"settings.window_system_wayland_limited": "بعض الميزات محدودة على Wayland الأصلي.",
|
||||
@@ -2933,10 +2726,6 @@
|
||||
"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": "يجب أن يكون الاتجاه محورًا أو خط رسم أو حافة مستقيمة",
|
||||
@@ -2975,10 +2764,8 @@
|
||||
"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}. حدّد أمرًا لمنحه زرًا في شريط الأدوات. يمكنك أيضًا سحب صف من القائمة المنسدلة للوحة إلى شريط الأدوات، أو سحب زر خارج شريط الأدوات لإزالته، أو استخدام الدبوس في صف القائمة المنسدلة.",
|
||||
@@ -3389,8 +3176,6 @@
|
||||
"ui.cancel": "إلغاء",
|
||||
"ui.canvases": "اللوحات المرجعية",
|
||||
"ui.center": "المركز",
|
||||
"ui.center_arc_follows_cursor": "القوس بنقطة مركز يتبع اتجاه المؤشر",
|
||||
"ui.center_arc_follows_cursor_tip": "يُرسم القوس بنقطة مركز باتجاه عقارب الساعة أو عكسها حسب الاتجاه الذي يدور به المؤشر حول مركزه. عند الإيقاف: دائمًا عكس اتجاه عقارب الساعة من البداية.",
|
||||
"ui.centerline": "خط المركز",
|
||||
"ui.chain": "سلسلة",
|
||||
"ui.chamfer": "شطف",
|
||||
@@ -3498,24 +3283,6 @@
|
||||
"ui.easyprint_uploading": "جارٍ الرفع إلى Printables…",
|
||||
"ui.edit": "تعديل",
|
||||
"ui.editJointLimits": "تعديل حدود المفصل",
|
||||
"ui.edit_in_place": "تحرير في المكان",
|
||||
"ui.edit_in_place_after_turn": "ينتهي التحرير عند انتهاء دور الوكيل",
|
||||
"ui.edit_in_place_agent_busy": "لا يزال الوكيل يعمل في هذا التصميم؛ حاول مرة أخرى بعد انتهاء دوره",
|
||||
"ui.edit_in_place_already_queued": "هناك تحرير ينتظر بالفعل انتهاء دور الوكيل",
|
||||
"ui.edit_in_place_assembly_closed": "{name} لم يعد مفتوحًا",
|
||||
"ui.edit_in_place_banner": "تحرير داخل {name}",
|
||||
"ui.edit_in_place_busy": "أنهِ ما هو مفتوح أو ألغِه أولاً",
|
||||
"ui.edit_in_place_none": "لا يُحرَّر أي تصميم مرتبط في مكانه",
|
||||
"ui.edit_in_place_not_saved": "في الجزء تغييرات غير محفوظة؛ احفظه لإنهاء التحرير",
|
||||
"ui.edit_in_place_open_elsewhere": "{name} مفتوح في نسخة أخرى من SolidCraft",
|
||||
"ui.edit_in_place_opens_after_turn": "يُفتح الجزء عند انتهاء دور الوكيل",
|
||||
"ui.edit_in_place_refresh_dropped": "تغيّر التجميع أثناء تحديث الروابط؛ شغّله مرة أخرى",
|
||||
"ui.edit_in_place_refresh_stopped": "توقّف تحديث الروابط قبل أن ينتهي",
|
||||
"ui.edit_in_place_refreshing": "جارٍ تحديث الروابط في {name}…",
|
||||
"ui.edit_in_place_save_first": "احفظ التجميع أولاً: يُعثر على تصاميمه المرتبطة انطلاقًا من ملفه",
|
||||
"ui.edit_in_place_save_waits": "يجري تحديث الروابط في {name}؛ احفظه بعد انتهائه",
|
||||
"ui.edit_in_place_saved_elsewhere": "حُفظ الجزء في ملف آخر؛ ما زال التجميع مرتبطًا بـ {name}",
|
||||
"ui.edit_in_place_wait": "انتظر حتى ينتهي ما يجري في التصميم",
|
||||
"ui.edit_section_analysis": "تعديل تحليل المقطع",
|
||||
"ui.ellipse": "قطع ناقص",
|
||||
"ui.emboss_wrap_note": "تلتف ملامح الخطوط المتعامدة المماسة لأسطوانة مدعومة تلقائيًا. اختر جدارًا لتحديد الهدف.",
|
||||
@@ -3590,7 +3357,6 @@
|
||||
"ui.heading_edit": "تعديل",
|
||||
"ui.heading_extrude": "بثق",
|
||||
"ui.heading_fillet": "تقويس",
|
||||
"ui.heading_finish_editing": "إنهاء التحرير",
|
||||
"ui.heading_finish_sketch": "إنهاء الرسم",
|
||||
"ui.heading_form": "تشكيل",
|
||||
"ui.heading_front": "أمام",
|
||||
@@ -3803,15 +3569,6 @@
|
||||
"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": "مضلع",
|
||||
@@ -3859,8 +3616,6 @@
|
||||
"ui.restore_defaults": "استعادة الافتراضيات",
|
||||
"ui.reverse_horizontal_pan_direction": "عكس اتجاه الإزاحة الأفقية",
|
||||
"ui.reverse_horizontal_pan_tip": "التمرير الأفقي بإصبعين وعجلة الإبهام يزيحان في الاتجاه المعاكس",
|
||||
"ui.reverse_vertical_pan_direction": "عكس اتجاه التحريك الرأسي",
|
||||
"ui.reverse_vertical_pan_tip": "التمرير الرأسي بإصبعين يحرّك العرض في الاتجاه المعاكس",
|
||||
"ui.reverse_zoom_direction": "عكس اتجاه التكبير",
|
||||
"ui.revolutions": "عدد اللفات",
|
||||
"ui.revolve": "دوران",
|
||||
@@ -4006,15 +3761,6 @@
|
||||
"ui.updateToCurrentView": "التحديث إلى العرض الحالي",
|
||||
"ui.upload": "رفع",
|
||||
"ui.vertical_constraint": "قيد رأسي",
|
||||
"ui.view_angles": "زوايا العرض",
|
||||
"ui.view_angles_convention": "درجات حول المحور العلوي. الانعراج 0° هو العرض الأمامي، و90° الأيسر، و−90° الأيمن، و180° الخلفي؛ الانحدار 90° هو العرض العلوي؛ والدوران الموجب يدير النموذج على الشاشة باتجاه عقارب الساعة.",
|
||||
"ui.view_angles_help": "يعرض زوايا الانحدار والانعراج والدوران للعرض بالدرجات ونوع الإسقاط أسفل أداة الاتجاه. انقر على القراءة لكتابة زوايا دقيقة.",
|
||||
"ui.view_angles_ortho": "متعامد",
|
||||
"ui.view_angles_persp": "منظور",
|
||||
"ui.view_angles_pitch": "الانحدار",
|
||||
"ui.view_angles_readout": "الانحدار {pitch} · الانعراج {yaw} · الدوران {roll}",
|
||||
"ui.view_angles_roll": "الدوران",
|
||||
"ui.view_angles_yaw": "الانعراج",
|
||||
"ui.view_centre": "مركز العرض",
|
||||
"ui.viewport_theme_follow": "اتباع السمة",
|
||||
"ui.visual_shaded_hidden_edges": "مظلل مع الحواف المخفية",
|
||||
@@ -4065,13 +3811,11 @@
|
||||
"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": "فتح صفحة الإصدار",
|
||||
@@ -4088,7 +3832,6 @@
|
||||
"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
File diff suppressed because it is too large
Load Diff
@@ -33,7 +33,6 @@
|
||||
"All open designs": "Alle åbne designs",
|
||||
"Allowed expressions, separated by semicolons": "Tilladte udtryk adskilt af semikolon",
|
||||
"Also deletes": "Sletter også",
|
||||
"Also place them from the origin (fully constrained)": "Placer dem også fra origo (fuldt fastlåst)",
|
||||
"Anchor": "Forankring",
|
||||
"Angle 2": "Vinkel 2",
|
||||
"Animate": "Animér",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Designs you open or save appear here.": "Designs, du åbner eller gemmer, vises her.",
|
||||
"Determined by To Object. Choose Angle to edit the angular extent.": "Bestemmes af Til objekt. Vælg Vinkel for at ændre vinkeludstrækningen.",
|
||||
"Diameter": "Diameter",
|
||||
"Dimension new shapes": "Mål nye figurer",
|
||||
"Direction": "Retning",
|
||||
"Discard": "Kassér",
|
||||
"Distance": "Afstand",
|
||||
@@ -171,8 +169,6 @@
|
||||
"Drag to move the hole; click to remove it": "Træk for at flytte hullet; klik for at fjerne det",
|
||||
"Drive Joint": "Bevæg led",
|
||||
"Duration": "Varighed",
|
||||
"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 skitsens origo, indtil intet af den er frit. En figur, der allerede er knyttet til origo eller til placeret 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 sine mål i de størrelser, du tegnede (indtastede værdier vinder). En størrelse, som skitsen allerede fastlægger, udelades.",
|
||||
"Edges": "Kanter",
|
||||
"Edit": "Redigér",
|
||||
"Edit Profile Sketch": "Redigér profilskitse",
|
||||
@@ -618,7 +614,6 @@
|
||||
"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.",
|
||||
@@ -924,7 +919,6 @@
|
||||
"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",
|
||||
@@ -1292,13 +1286,6 @@
|
||||
"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",
|
||||
@@ -1408,13 +1395,6 @@
|
||||
"render.return_design": "Tilbage til Design",
|
||||
"render.save_setup": "Gem opsætning",
|
||||
"render.setup_name": "Opsætningsnavn",
|
||||
"render.shots_camera_changed": "Kameraet er ændret, siden denne optagelse blev gemt",
|
||||
"render.shots_name_help": "Indtast et unikt navn til den aktuelle optagelse.",
|
||||
"render.shots_no_result": "Ingen rendering endnu",
|
||||
"render.shots_result": "Rendering {number} · {width} × {height}",
|
||||
"render.shots_revert": "Gendan",
|
||||
"render.shots_save": "Gem optagelse",
|
||||
"render.shots_update": "Opdater optagelse",
|
||||
"render.unavailable_preview": "Denne opsætning bruger en type forhåndsvisning, der ikke er tilgængelig.",
|
||||
"render.unsaved": "Ændringer i forhåndsvisning er ikke gemt",
|
||||
"render.use_preview": "Brug integreret forhåndsvisning",
|
||||
|
||||
+2
-380
@@ -38,7 +38,6 @@
|
||||
"All open designs": "Alle geöffneten Designs",
|
||||
"Allowed expressions, separated by semicolons": "Zulässige Ausdrücke, durch Semikolons getrennt",
|
||||
"Also deletes": "Löscht auch",
|
||||
"Also place them from the origin (fully constrained)": "Auch vom Ursprung aus platzieren (vollständig bestimmt)",
|
||||
"Anchor": "Anker",
|
||||
"Angle 2": "Winkel 2",
|
||||
"Angle Display": "Winkelanzeige",
|
||||
@@ -180,9 +179,6 @@
|
||||
"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",
|
||||
"Distance": "Abstand",
|
||||
@@ -191,16 +187,9 @@
|
||||
"Don't ask again": "Nicht mehr fragen",
|
||||
"Double-click to rename": "Double-click to rename",
|
||||
"Double-clicking the middle button in the view: Home (the home view, framing what is shown), Fit (frame what is shown, keeping the orientation) or nothing": "Doppelklick mit der mittleren Taste in der Ansicht: Home (Startansicht, zeigt alles Sichtbare), Einpassen (Sichtbares einpassen, Ausrichtung beibehalten) oder nichts",
|
||||
"Drag to move the boss across the model. It snaps to sketch points on the face.": "Ziehen, um den Ansatz über das Modell zu verschieben. Er rastet an Skizzenpunkten auf der Fläche ein.",
|
||||
"Drag to move the hole; click to remove it": "Ziehen, um die Bohrung zu verschieben; anklicken, um sie zu entfernen",
|
||||
"Drag to move the rest across the model. It snaps to sketch points on the face.": "Ziehen, um die Auflage über das Modell zu verschieben. Sie rastet an Skizzenpunkten auf der Fläche ein.",
|
||||
"Drag to move the snap fit across the model. It snaps to sketch points on the face.": "Ziehen, um die Rastverbindung über das Modell zu verschieben. Sie rastet an Skizzenpunkten auf der Fläche ein.",
|
||||
"Drag to point the catch in another direction.": "Ziehen, um den Rasthaken anders auszurichten.",
|
||||
"Drag to turn the rest in 5° steps. Hold Alt or Ctrl to turn it freely.": "Ziehen, um die Auflage in 5°-Schritten zu drehen. Alt oder Strg gedrückt halten, um sie stufenlos zu drehen.",
|
||||
"Drive Joint": "Gelenk antreiben",
|
||||
"Duration": "Dauer",
|
||||
"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.": "Jede neue Form wird zusätzlich vom Skizzenursprung aus bemaßt, bis nichts mehr frei ist. Eine Form, die schon am Ursprung oder an platzierter Geometrie hängt, erhält keine.",
|
||||
"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.": "Jede gezeichnete Form erhält alle ihre Bemaßungen in den gezeichneten Größen (eingegebene Werte haben Vorrang). Eine Größe, die die Skizze bereits festlegt, wird ausgelassen.",
|
||||
"Edge": "Kante",
|
||||
"Edges": "Kanten",
|
||||
"Edit": "Bearbeiten",
|
||||
@@ -483,8 +472,6 @@
|
||||
"Rename Feature": "Merkmal umbenennen",
|
||||
"Rename Folder": "Ordner umbenennen",
|
||||
"Render Stage": "Renderphase",
|
||||
"Reopen designs": "Designs erneut öffnen",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "Die Designs erneut öffnen, die beim letzten Beenden von SolidCraft geöffnet waren",
|
||||
"Replace existing library file": "Vorhandene Bibliotheksdatei ersetzen",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "Fehler melden — Screenshot prüfen und einen GitHub-Entwurf öffnen",
|
||||
"Report text copied. Paste it into GitHub and submit your report there.": "Berichtstext kopiert. Fügen Sie ihn auf GitHub ein und reichen Sie den Bericht dort ein.",
|
||||
@@ -592,7 +579,6 @@
|
||||
"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",
|
||||
@@ -727,8 +713,6 @@
|
||||
"ai.prefs.background_commands": "Agentenbefehle im Hintergrund ausführen",
|
||||
"ai.prefs.connect_apps": "SolidCraft für KI-Apps auf diesem Computer verfügbar machen",
|
||||
"ai.prefs.connect_apps_note": "Trägt SolidCraft beim Start als MCP-Server in die auf diesem Computer gefundenen KI-Apps ein (OpenCode, Claude Desktop, Claude Code, Cursor, Codex, Gemini CLI, Copilot CLI, Windsurf, Zed, VS Code), damit ein Agent es ohne Einrichtung nutzen kann. Nur der Eintrag „solidcraft“ wird hinzugefügt oder sein Pfad aktualisiert; ein von Ihnen geänderter Eintrag bleibt unberührt, und die Datei wird vorher gesichert. Aus: Die hinzugefügten Einträge werden entfernt. Hilfe > KI-Assistenten verbinden… erledigt das von Hand.",
|
||||
"ai.prefs.connect_at_start": "Zuletzt verwendeten Agenten beim Start von SolidCraft verbinden",
|
||||
"ai.prefs.connect_at_start_note": "Verbindet beim Start von SolidCraft automatisch den zuletzt verwendeten Agenten (oder den Standardagenten) im Hintergrund, damit er für den Chat bereitsteht. Diese Funktion ist deaktiviert, wenn SolidCraft mit --control gestartet wird. Kann keine Verbindung hergestellt werden, zeigt der Assistent „Verbindung fehlgeschlagen“ und eine Schaltfläche zum erneuten Verbindungsversuch an.",
|
||||
"ai.prefs.follow_camera": "Kamera zu Skizzen drehen",
|
||||
"ai.prefs.follow_camera_off": "Die automatische Kameraführung ist aus.",
|
||||
"ai.prefs.follow_camera_on": "Die Kamera dreht sich zu Skizzen, die der Agent erstellt.",
|
||||
@@ -736,8 +720,6 @@
|
||||
"ai.prefs.make_default": "Standardmäßig verwenden",
|
||||
"ai.prefs.no_agents": "Noch keine Agenten. Fügen Sie einen Agenten hinzu oder aktualisieren Sie, um installierte Agenten zu finden.",
|
||||
"ai.prefs.no_saved_approvals": "Keine gespeicherten Freigaben.",
|
||||
"ai.prefs.open_at_start": "Assistentenbereich beim Start von SolidCraft öffnen",
|
||||
"ai.prefs.open_at_start_note": "Öffnet beim Start von SolidCraft automatisch den Assistentenbereich oder dessen separates Fenster. Diese Funktion ist deaktiviert, wenn SolidCraft mit --control gestartet wird.",
|
||||
"ai.prefs.override_all_warning": "Dieser Agent darf das Design ohne Rückfrage speichern, überschreiben, exportieren oder ersetzen.",
|
||||
"ai.prefs.permission_all": "Alles erlauben",
|
||||
"ai.prefs.permission_all_summary": "Verfügbare SolidCraft-Werkzeuge werden ohne Rückfrage ausgeführt.",
|
||||
@@ -760,7 +742,6 @@
|
||||
"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}",
|
||||
@@ -769,7 +750,6 @@
|
||||
"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}",
|
||||
@@ -816,13 +796,10 @@
|
||||
"cmd.component_break_link": "Verknüpfung trennen",
|
||||
"cmd.component_capture_position": "Position erfassen",
|
||||
"cmd.component_copies": "Identische Kopien finden",
|
||||
"cmd.component_copy_bodies": "Körper einfügen",
|
||||
"cmd.component_create": "Neue Komponente",
|
||||
"cmd.component_cycle_color": "Komponentenfarbe wechseln",
|
||||
"cmd.component_delete": "Komponente löschen",
|
||||
"cmd.component_edit_in_place": "An Ort und Stelle bearbeiten",
|
||||
"cmd.component_extract": "In Datei extrahieren",
|
||||
"cmd.component_finish_edit": "Bearbeitung beenden",
|
||||
"cmd.component_from_bodies": "Komponenten aus Körpern erstellen",
|
||||
"cmd.component_insert_design": "Konstruktion einfügen",
|
||||
"cmd.component_links": "Verknüpfungen auflisten",
|
||||
@@ -900,7 +877,6 @@
|
||||
"cmd.file_insert_step": "STEP einfügen",
|
||||
"cmd.file_open_in_easyprint": "Mit EasyPrint öffnen",
|
||||
"cmd.file_save_mesh": "Als Netz speichern",
|
||||
"cmd.file_send_to_slicer": "An Slicer senden",
|
||||
"cmd.file_slicers": "Installierte Slicer",
|
||||
"cmd.frame_member": "Rahmenelement",
|
||||
"cmd.frame_profile_insert": "Profil einfügen",
|
||||
@@ -972,7 +948,6 @@
|
||||
"cmd.occurrence_ground": "Festsetzen",
|
||||
"cmd.occurrence_move": "Instanz verschieben",
|
||||
"cmd.occurrence_properties": "Physikalische Eigenschaften der Instanz",
|
||||
"cmd.occurrence_reparent": "In andere Komponente verschieben",
|
||||
"cmd.parameters_add": "Benutzerparameter hinzufügen",
|
||||
"cmd.parameters_change": "Parameter ändern",
|
||||
"cmd.parameters_comment": "Parameter kommentieren",
|
||||
@@ -991,16 +966,12 @@
|
||||
"cmd.parts_library": "Standardbauteile",
|
||||
"cmd.parts_resize": "Bauteilgröße ändern",
|
||||
"cmd.plastic_assign_rule": "Kunststoffregel zuweisen",
|
||||
"cmd.plastic_boss": "Ansatz",
|
||||
"cmd.plastic_boss": "Erhöhung",
|
||||
"cmd.plastic_lip": "Lippe",
|
||||
"cmd.plastic_manage_rules": "Kunststoffregeln verwalten",
|
||||
"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",
|
||||
@@ -1023,20 +994,15 @@
|
||||
"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",
|
||||
@@ -1064,7 +1030,6 @@
|
||||
"cmd.sheet_refold": "Wieder falten",
|
||||
"cmd.sheet_rules": "Blechregeln auflisten",
|
||||
"cmd.sheet_unfold": "Entfalten",
|
||||
"cmd.sketch_arc3d": "3D-Bogen",
|
||||
"cmd.sketch_arc_center_point": "Mittelpunkt-Kreisbogen",
|
||||
"cmd.sketch_arc_tangent": "Tangentialbogen",
|
||||
"cmd.sketch_arc_three_point": "3-Punkt-Kreisbogen",
|
||||
@@ -1093,7 +1058,6 @@
|
||||
"cmd.sketch_drag": "Skizzengeometrie ziehen",
|
||||
"cmd.sketch_drag_end": "Skizzen-Ziehen beenden",
|
||||
"cmd.sketch_draw_dimensioned": "Skizzengeometrie mit Maßen zeichnen",
|
||||
"cmd.sketch_drawing_bounds": "Zeichnungsgröße",
|
||||
"cmd.sketch_edit": "Skizze bearbeiten",
|
||||
"cmd.sketch_edit_text": "Text bearbeiten",
|
||||
"cmd.sketch_export_dxf": "Als DXF speichern",
|
||||
@@ -1139,7 +1103,6 @@
|
||||
"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",
|
||||
@@ -1191,31 +1154,22 @@
|
||||
"cmd.view.render_dock": "Render-Panel verschieben",
|
||||
"cmd.view.render_focus": "Render-Fokusansicht",
|
||||
"cmd.view_ambient_occlusion": "Umgebungsverdeckung",
|
||||
"cmd.view_angle_readout": "Ansichtswinkel",
|
||||
"cmd.view_apply": "Benannte Ansicht wiederherstellen",
|
||||
"cmd.view_back": "Rückansicht",
|
||||
"cmd.view_bottom": "Untersicht",
|
||||
"cmd.view_delete": "Benannte Ansicht löschen",
|
||||
"cmd.view_fit": "Ansicht einpassen",
|
||||
"cmd.view_front": "Vorderansicht",
|
||||
"cmd.view_graphics_preset": "Grafikprofil",
|
||||
"cmd.view_grid": "Raster",
|
||||
"cmd.view_ground_shadow": "Bodenschatten",
|
||||
"cmd.view_home": "Startansicht",
|
||||
"cmd.view_isometric": "Isometrische Ansicht",
|
||||
"cmd.view_left": "Linke Seitenansicht",
|
||||
"cmd.view_list": "Benannte Ansichten auflisten",
|
||||
"cmd.view_navigate": "Ansicht navigieren",
|
||||
"cmd.view_navigation_bar": "Navigationsleiste",
|
||||
"cmd.view_normal_to": "Senkrecht zur Auswahl",
|
||||
"cmd.view_rename": "Benannte Ansicht umbenennen",
|
||||
"cmd.view_right": "Rechte Seitenansicht",
|
||||
"cmd.view_save": "Neue benannte Ansicht",
|
||||
"cmd.view_shots_strip": "Aufnahmenleiste",
|
||||
"cmd.view_spin": "Ansicht kontinuierlich drehen",
|
||||
"cmd.view_tool": "Navigationswerkzeug",
|
||||
"cmd.view_top": "Draufsicht",
|
||||
"cmd.view_visual_style": "Darstellungsstil",
|
||||
"color.copy": "Klicken, um Farbwerte zu kopieren",
|
||||
"color.edit": "Klicken, um Farbe zu bearbeiten",
|
||||
"color.gamma": "Farbwerte im Gammaraum 0-255 anzeigen",
|
||||
@@ -1285,7 +1239,6 @@
|
||||
"document.save_changes": "Änderungen in {name} speichern?",
|
||||
"document.save_copy": "Kopie speichern…",
|
||||
"document.unsaved_changes": "{name} enthält ungespeicherte Änderungen. Wenn Sie nicht speichern, gehen sie verloren.",
|
||||
"drive.rigid": "{joint} ist ein starres Gelenk. Es hält die verbundenen Teile fest zusammen und ermöglicht daher keine Bewegung. Um die Teile beweglich zu machen, ändern Sie den Gelenktyp unter „Gelenk bearbeiten“: Drehgelenk für Drehbewegungen, Schubgelenk für lineare Bewegungen oder Zylindergelenk für beides.",
|
||||
"drive.stopped_by_contact": "Angehalten bei {value}: {a} würde {b} durchdringen",
|
||||
"drive.stopped_by_loop": "Angehalten bei {value}: die Schleife über {joint} lässt sich nicht weiter schließen",
|
||||
"drive.unconnected": "Bewegt nichts: {part} ist mit nichts Festgesetztem verbunden",
|
||||
@@ -1325,7 +1278,6 @@
|
||||
"help.apache": "Apache-Lizenz 2.0",
|
||||
"help.attributions": "Quellenangaben (ATTRIBUTION.md)",
|
||||
"help.build": "Version {version} · Commit {commit} · erstellt am {date}",
|
||||
"help.component.finish_edit": "Speichert das an Ort und Stelle bearbeitete Teil, kehrt zu seiner Baugruppe zurück und aktualisiert die Baugruppe daraus. Verwenden Sie es, wenn Ihre Änderungen am verknüpften Teil fertig sind.",
|
||||
"help.connect_note": "Starten Sie jeden Assistenten neu, damit er den SolidCraft-Server lädt. Jede Datei wurde vor der Änderung als .bak gesichert.",
|
||||
"help.connect_title": "KI-Assistenten verbinden",
|
||||
"help.font_details": "Die Oberfläche verwendet die integrierten Schriftarten von egui (Ubuntu Light und Hack unter der Ubuntu Font Licence und der MIT/Bitstream-Vera-Lizenz; Noto Emoji und die Emoji-Symbolschrift unter OFL und MIT). Skizzentext verwendet Liberation Sans (SIL OFL 1.1). Das optionale Schriftpaket craft-fonts von The Crafting Apps (BIZ UDPGothic, Shippori Mincho, BIZ UDMincho, Noto Sans CJK SC, Noto Sans Arabic; SIL OFL 1.1) ist nicht in SolidCraft-Builds enthalten.",
|
||||
@@ -1333,7 +1285,6 @@
|
||||
"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.",
|
||||
@@ -1347,28 +1298,6 @@
|
||||
"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.",
|
||||
"hole_presets.full": "Die persönliche Vorlagenbibliothek enthält bereits 64 Vorlagen.",
|
||||
"hole_presets.invalid_name": "Geben Sie einen kurzen Namen ohne Zeilenumbrüche oder Steuerzeichen ein.",
|
||||
"hole_presets.invalid_parameters": "Vervollständigen Sie die Bohrungsparameter mit gültigen Ausdrücken angemessener Länge.",
|
||||
"hole_presets.missing": "Die ausgewählte Vorlage ist nicht mehr verfügbar.",
|
||||
"hole_presets.name": "Vorlagenname",
|
||||
"hole_presets.name_exists": "Eine Vorlage mit diesem Namen existiert bereits. Wählen Sie einen anderen Namen.",
|
||||
"hole_presets.name_hint": "Eindeutigen Namen eingeben",
|
||||
"hole_presets.overwrite": "Änderungen in der ausgewählten Vorlage speichern",
|
||||
"hole_presets.preset": "Vorlage",
|
||||
"hole_presets.recall": "Vorlage laden",
|
||||
"hole_presets.rename": "Umbenennen",
|
||||
"hole_presets.save": "Als neue Vorlage speichern",
|
||||
"hole_presets.scope": "In Ihrem Profil gespeichert und für alle Projekte verfügbar.",
|
||||
"hole_presets.select": "Vorlage auswählen",
|
||||
"hole_presets.title": "Persönliche Vorlagen",
|
||||
"hole_presets.unsupported_options": "Diese Vorlage enthält Gewindeoptionen, die in dieser Version nicht geladen werden können.",
|
||||
"hole_presets.unsupported_overwrite": "Diese Vorlage enthält Optionen, die nicht sicher überschrieben werden können.",
|
||||
"home.back": "Zurück zu {name}",
|
||||
"home.crashes": "Absturzberichte",
|
||||
"home.design_workspace": "Entwurf",
|
||||
@@ -1639,10 +1568,6 @@
|
||||
"navbar.visual_style": "Darstellung",
|
||||
"navbar.zoom": "Zoomen",
|
||||
"needs two joints": "benötigt zwei Gelenke",
|
||||
"network.allow_once": "Einmalig erlauben",
|
||||
"network.always_allow": "Diesen Server immer erlauben",
|
||||
"network.prompt_detail": "Dieses Design verweist auf eine Datei auf dem Netzwerkcomputer {host}. Beim Öffnen wird eine Verbindung zu diesem Computer hergestellt. Dabei werden möglicherweise Ihre Windows-Anmeldedaten an ihn übermittelt. Erlauben Sie den Zugriff nur auf Server, denen Sie vertrauen.",
|
||||
"network.prompt_title": "Netzwerkzugriff",
|
||||
"new configuration": "neue Konfiguration",
|
||||
"new parameter": "new parameter",
|
||||
"no hole": "keine Bohrung",
|
||||
@@ -1681,16 +1606,6 @@
|
||||
"pattern.direction_1": "Richtung 1",
|
||||
"pattern.direction_2": "Richtung 2",
|
||||
"pattern.flip_direction": "Richtung umkehren",
|
||||
"perf.click_for_details": "Für Details klicken: Frametime-Diagramm, Histogramm und Darstellung der GPU-Ausgabe",
|
||||
"perf.draw_calls": "Draw Calls",
|
||||
"perf.line_segments": "Liniensegmente",
|
||||
"perf.low_1": "1-%-Tiefstwert",
|
||||
"perf.renderer": "Renderer",
|
||||
"perf.scene_build": "Szenenaufbau",
|
||||
"perf.triangles": "Dreiecke",
|
||||
"perf.ui_time": "UI-Zeit",
|
||||
"perf.viewport": "Ansichtsfenster",
|
||||
"perf.worst": "schlechtester Wert",
|
||||
"pick a body or some of its faces": "einen Körper oder einige seiner Flächen wählen",
|
||||
"pick a hole to seat it, or OK places it at the origin": "Wählen Sie eine Bohrung zum Einsetzen, oder OK platziert es im Ursprung",
|
||||
"pick a planar face": "eine ebene Fläche wählen",
|
||||
@@ -1707,126 +1622,30 @@
|
||||
"pick sections in two or more sketches": "Querschnitte in zwei oder mehr Skizzen wählen",
|
||||
"pick sketch curves or edges": "Skizzenkurven oder Kanten wählen",
|
||||
"pick the face to untrim": "die Fläche wählen, deren Trimmung aufgehoben werden soll",
|
||||
"pick the path": "Pfad auswählen",
|
||||
"pick what to keep": "wählen, was behalten werden soll",
|
||||
"pick what to keep of the other surface": "wählen, was vom anderen Flächenkörper behalten werden soll",
|
||||
"prefs.add_item": "Element hinzufügen",
|
||||
"prefs.alt_frees_manipulators": "Alt / Option schaltet auch an Manipulatorpfeilen das Einrasten ab",
|
||||
"prefs.anti_aliasing_amd_dx12": "Die Kantenglättung verwendet 1×, da der Starttest mit Multisampling keine Ausgabe erzeugt hat, während der Test mit 1× erfolgreich war. Sie können oben eine andere Einstellung wählen. Starten Sie SolidCraft neu, damit die Änderung übernommen wird, sofern der Test erfolgreich ist.",
|
||||
"prefs.anti_aliasing_note": "Glättet gezackte Kanten.",
|
||||
"prefs.applies_on_restart": "Wird nach einem Neustart übernommen",
|
||||
"prefs.assistant": "Assistent",
|
||||
"prefs.axes": "Achsen",
|
||||
"prefs.axis": "Achse",
|
||||
"prefs.bodies_folders": "Körperordner",
|
||||
"prefs.bodies_folders_always": "Immer",
|
||||
"prefs.bodies_folders_never": "Nie",
|
||||
"prefs.bodies_folders_several": "Nur bei mehreren Körpern",
|
||||
"prefs.bottom": "Unten",
|
||||
"prefs.browser": "Browser",
|
||||
"prefs.built_in": "Integriert",
|
||||
"prefs.button_left": "Ziehen mit der linken Maustaste",
|
||||
"prefs.button_middle": "Ziehen mit der mittleren Maustaste",
|
||||
"prefs.button_right": "Ziehen mit der rechten Maustaste",
|
||||
"prefs.choose_font": "Schriftdatei auswählen…",
|
||||
"prefs.click": "Klicken",
|
||||
"prefs.contact_shading": "Weiche Kontaktschattierung",
|
||||
"prefs.contact_shading_note": "Erzeugt weiche Schatten an Stellen, an denen sich Bauteile nahekommen.",
|
||||
"prefs.context_menu": "Kontextmenü",
|
||||
"prefs.context_menu_ring": "Kontextmenü · Gedrückt halten, um das Radialmenü zu öffnen",
|
||||
"prefs.cpu": "CPU (Software)",
|
||||
"prefs.crash_reports": "Absturzberichte öffnen…",
|
||||
"prefs.crash_reports_note": "Die Berichte bleiben auf diesem Computer. Sie enthalten das Design und die Befehle, mit denen sich das Problem reproduzieren lässt.",
|
||||
"prefs.ctrl_cmd": "Strg / Cmd",
|
||||
"prefs.curved_surfaces": "Gekrümmte Flächen",
|
||||
"prefs.curves": "Gekrümmte Flächen",
|
||||
"prefs.dead_zone_note": "Der schraffierte Bereich kennzeichnet die Totzone. Bewegungen innerhalb dieses Bereichs werden ignoriert.",
|
||||
"prefs.default": "Standard",
|
||||
"prefs.design": "Design-Vorgaben",
|
||||
"prefs.design_note": "Legt fest, mit welchen Einstellungen neue Designs beginnen. Bereits geöffnete Designs behalten ihre eigenen Einstellungen.",
|
||||
"prefs.devices": "3D-Maus",
|
||||
"prefs.dialog_buttons": "Dialogschaltflächen",
|
||||
"prefs.dialogs": "Dialoge",
|
||||
"prefs.dialogs_edge": "Am Rand der Ansicht",
|
||||
"prefs.dialogs_left_column": "In der linken Spalte",
|
||||
"prefs.dialogs_note": "Wo Befehlsdialoge öffnen: am Rand der Ansicht angedockt oder in der linken Spalte anstelle von Browser und Verlauf, die beim Schließen des Dialogs wie zuvor zurückkehren.",
|
||||
"prefs.display": "Anzeige",
|
||||
"prefs.edges": "Kanten und Schattierung",
|
||||
"prefs.extend": "Erweitern",
|
||||
"prefs.extra_navigation": "Mit Strg und gedrückter linker Maustaste auf einer freien Fläche drehen. Leertaste gedrückt halten, um die Ansicht zu verschieben.",
|
||||
"prefs.follows_os": "Wie im Betriebssystem",
|
||||
"prefs.font": "Schrift",
|
||||
"prefs.font_fallback": "Zeichen, die in der ausgewählten Schrift fehlen, werden mit der integrierten Ersatzschrift dargestellt. So werden keine Beschriftungen als leere Kästchen angezeigt.",
|
||||
"prefs.font_sample": "Zwölf Boxkämpfer jagen Viktor quer über den großen Sylter Deich · Ø12,50 mm · 45,0° · 0123456789",
|
||||
"prefs.font_unreadable": "Die ausgewählte Schriftdatei konnte nicht gelesen werden.",
|
||||
"prefs.general": "Allgemein",
|
||||
"prefs.general_note": "Sprache, Speichern und Updates.",
|
||||
"prefs.gesture_orbit": "Ansicht drehen",
|
||||
"prefs.gesture_pan": "Ansicht verschieben",
|
||||
"prefs.gesture_zoom": "Ansicht zoomen",
|
||||
"prefs.graphics": "Grafik",
|
||||
"prefs.graphics_card": "Grafikkarte",
|
||||
"prefs.graphics_card_auto": "Automatisch",
|
||||
"prefs.graphics_card_missing": "Die gespeicherte Grafikkarte ist nicht verfügbar. Stattdessen wird „Automatisch“ verwendet.",
|
||||
"prefs.graphics_card_restart": "Diese Einstellung wird nach einem Neustart von SolidCraft wirksam. Die Umgebungsvariable WGPU_ADAPTER_NAME hat Vorrang vor dieser Auswahl.",
|
||||
"prefs.graphics_note": "Wägen Sie Leistung und Darstellungsqualität gegeneinander ab. Jede Option veranschaulicht ihre Wirkung anhand eines Beispiels.",
|
||||
"prefs.graphics_presets_note": "Mit den Profilen werden die unten aufgeführten Optionen gemeinsam eingestellt.",
|
||||
"prefs.history": "Verlauf",
|
||||
"prefs.history_origin_bodies": "Ursprung und Körper oben zeigen",
|
||||
"prefs.history_origin_bodies_note": "Der Verlauf links oder unter dem Browser beginnt mit dem Körper-Ordner der aktiven Komponente und dem Ursprung, mit Augen und Menüs wie im Browser. Der Verlauf unten bleibt unverändert.",
|
||||
"prefs.history_sketches": "Skizzen",
|
||||
"prefs.history_sketches_in_order": "In Erstellungsreihenfolge",
|
||||
"prefs.history_sketches_note": "Wenn „Skizzen“ auf „Unter dem zugehörigen Feature“ eingestellt ist, wird jede Skizze im linken Verlauf eine Ebene unter dem ersten daraus erstellten Feature angezeigt, sofern der Bereich breit genug für die Namen ist. Die Timeline unten zeigt weiterhin die Reihenfolge, in der die Elemente erstellt wurden.",
|
||||
"prefs.history_sketches_under_feature": "Unter dem zugehörigen Feature",
|
||||
"prefs.hold_stop_snapping": "Gedrückt halten, um das Einrasten zu deaktivieren",
|
||||
"prefs.icons_only": "Nur Symbole",
|
||||
"prefs.imported_file": "Importierte Datei",
|
||||
"prefs.input": "Eingabe",
|
||||
"prefs.interface": "Oberfläche",
|
||||
"prefs.interface_language": "Oberflächensprache",
|
||||
"prefs.interface_note": "Legt das Erscheinungsbild von SolidCraft und die Anordnung seiner Bereiche fest.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.labels": "Beschriftungen",
|
||||
"prefs.layout": "Layout",
|
||||
"prefs.layout_note": "Im Layout „Kompakt“ werden Bereiche an den Fensterrändern angedockt. Transparenz für den Browser ist nur im Layout „Schwebend“ verfügbar. Die Position des Assistenten ändern Sie auf der Seite „Assistent“.",
|
||||
"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.",
|
||||
"prefs.theme_follow_launch": "Nur für diesen Start (--theme-file); wird neu geladen, sobald ein anderes Werkzeug die Datei überschreibt.",
|
||||
"prefs.theme_follow_note": "Wird neu geladen, sobald ein anderes Werkzeug die Datei überschreibt. Die Auswahl eines Designs beendet das Folgen.",
|
||||
"prefs.theme_follow_off": "Aus",
|
||||
"prefs.theme_follow_stop": "Nicht mehr folgen",
|
||||
"prefs.theme_import_hint": "Klicken, um eine Designdatei zu wählen, eine hierher ziehen oder mit kopiertem Design Strg+V (Cmd+V auf dem Mac) drücken",
|
||||
"prefs.theme_paste": "Design einfügen",
|
||||
"prefs.theme_paste_none": "Die Zwischenablage enthält kein SolidCraft-Design",
|
||||
"properties.title": "Eigenschaften: {name}",
|
||||
"recovery.days_ago": "vor {count} Tagen",
|
||||
"recovery.detail": "{file} · {features} Merkmale · automatisch gespeichert {when}",
|
||||
@@ -1836,46 +1655,6 @@
|
||||
"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",
|
||||
"render.bloom_help": "Kreativer Ausgabeglanz aus linearen Glanzlichtern mit drei normierten Durchläufen. Radius in Ausgabepixeln; Addition vor sRGB. Keine Linsen-, Volumen- oder Konvergenzsimulation. Das rohe EXR bleibt unverändert.",
|
||||
@@ -1885,13 +1664,6 @@
|
||||
"render.bloom_threshold": "Linearer Glanzlichtschwellwert",
|
||||
"render.camera_pitch": "Kameraneigung",
|
||||
"render.camera_set": "Render-Kamera einstellen",
|
||||
"render.category_metal": "Metall",
|
||||
"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",
|
||||
@@ -1904,9 +1676,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",
|
||||
"render.error_binding_allocation": "Materialzuordnung konnte nicht reserviert werden",
|
||||
"render.error_body_bound": "Produktvorschau überschreitet die Grenze erfasster Körper",
|
||||
@@ -1970,8 +1740,6 @@
|
||||
"render.fixed_camera": "Kamera",
|
||||
"render.fixed_world": "Welt",
|
||||
"render.fold_browser": "Browser einklappen",
|
||||
"render.frame_presets": "Bildvorlagen",
|
||||
"render.frame_presets_pending": "Kommt mit dem Aufnahmeeditor",
|
||||
"render.heading_batch": "Stapel",
|
||||
"render.heading_frame": "Bildrahmen",
|
||||
"render.heading_lens": "Objektiv",
|
||||
@@ -2003,43 +1771,9 @@
|
||||
"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",
|
||||
"render.library_applied": "Ressourcenvariante angewendet. Vollständigen Renderauftrag vor dem Start prüfen.",
|
||||
"render.library_apply_appearance": "Erscheinungsbild anwenden",
|
||||
"render.library_apply_hdr": "Mit dieser HDR-Umgebung speichern",
|
||||
"render.library_apply_hint": "Klicken zum Anwenden · auf das Modell ziehen",
|
||||
"render.library_apply_maps": "Erstellte GPU-Oberflächenmaps verwenden",
|
||||
"render.library_apply_studio": "Erstellte Studiovariante speichern",
|
||||
"render.library_apply_uniform": "Einheitliches opakes Aussehen anwenden",
|
||||
@@ -2049,21 +1783,12 @@
|
||||
"render.library_emitters_only_ack": "Nur Strahler verwenden; dokumentierte passive Karten auslassen",
|
||||
"render.library_environment_concepts": "Umgebungskonzepte",
|
||||
"render.library_extent": "Objektausdehnung",
|
||||
"render.library_filters": "Katalog und Verfügbarkeit",
|
||||
"render.library_grid": "Raster",
|
||||
"render.library_hdr": "Integrierte HDR-Umgebungen",
|
||||
"render.library_hdr_help": "Bettet das originale HDR in die gespeicherte Einstellung ein. Der Längengradursprung wird ausdrücklich umgerechnet; nach dem Speichern ist keine externe Datei nötig.",
|
||||
"render.library_in_design": "Im Design",
|
||||
"render.library_inspect_only": "Zum Prüfen wählen · Im Inspektor anwenden",
|
||||
"render.library_ior": "Brechungsindex",
|
||||
"render.library_light_concepts": "Beleuchtungskonzepte",
|
||||
"render.library_list": "Liste",
|
||||
"render.library_maps_help": "Built-in maps can be saved with pinned sources and local occurrence mapping. Temporary optical overrides remain a separate mode.",
|
||||
"render.library_material_concepts": "Materialkonzepte",
|
||||
"render.library_metallic": "Metallisch",
|
||||
"render.library_next": "Nächste Seite",
|
||||
"render.library_opacity": "Deckkraft",
|
||||
"render.library_other": "Weitere Erscheinungsbilder",
|
||||
"render.library_physical_profiles": "Physical reference profiles",
|
||||
"render.library_previous": "Vorherige Seite",
|
||||
"render.library_quality_concepts": "Qualitätsspezifikationen",
|
||||
@@ -2072,7 +1797,6 @@
|
||||
"render.library_results": "Passende Ressourcen",
|
||||
"render.library_rig_help": "Fit once at an explicit center and physical extent. Complete rigs include passive cards; calibration blockers still refuse. Conservative emitter/card clearance is checked before saving.",
|
||||
"render.library_rigs": "Erstellte Lichtaufbauten",
|
||||
"render.library_roughness": "Rauheit",
|
||||
"render.library_save_maps": "Save maps to this setup",
|
||||
"render.library_scene_bounds": "Erfasste Szenengrenzen verwenden",
|
||||
"render.library_scenes": "Szenenspezifikationen",
|
||||
@@ -2083,7 +1807,6 @@
|
||||
"render.library_target": "Zielkörper",
|
||||
"render.library_title": "Render-Ressourcenbibliothek",
|
||||
"render.library_uniform_ack": "Einheitliche Näherung ohne räumliche oder erweiterte Effekte verwenden",
|
||||
"render.library_users": "Verwendungen",
|
||||
"render.light_anchor_pending": "Das Koordinatensystem der Lichter kann noch nicht geändert werden.",
|
||||
"render.lights_fixed_to": "Lichter gebunden an",
|
||||
"render.material_color_replace": "Basismap ersetzt Körperfarbe",
|
||||
@@ -2095,30 +1818,8 @@
|
||||
"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.",
|
||||
@@ -2148,20 +1849,11 @@
|
||||
"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",
|
||||
@@ -2192,28 +1884,14 @@
|
||||
"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.search_appearances": "{count} Erscheinungsbilder durchsuchen",
|
||||
"render.setup_name": "Name des Setups",
|
||||
"render.shell_tab_appearance": "Aussehen",
|
||||
"render.shell_tab_camera": "Kamera",
|
||||
"render.shell_tab_scene": "SZENE",
|
||||
"render.shots_camera_changed": "Die Kamera wurde seit dem Speichern dieser Aufnahme geändert",
|
||||
"render.shots_name_help": "Geben Sie einen eindeutigen Namen für die aktuelle Aufnahme ein.",
|
||||
"render.shots_no_result": "Noch kein Renderbild",
|
||||
"render.shots_result": "Rendering {number} · {width} × {height}",
|
||||
"render.shots_revert": "Zurücksetzen",
|
||||
"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",
|
||||
@@ -2222,12 +1900,7 @@
|
||||
"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",
|
||||
"render.tone_aces_fitted": "ACES angepasst",
|
||||
"render.tone_reinhard": "Reinhard",
|
||||
"render.tree_add_light": "Licht hinzufügen",
|
||||
@@ -2267,7 +1940,6 @@
|
||||
"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",
|
||||
@@ -2282,7 +1954,7 @@
|
||||
"sample.flange.name": "Schraubflansch",
|
||||
"sample.hinge.about": "Zwei Komponenten und ein Drehgelenk, auf 60° angetrieben",
|
||||
"sample.hinge.name": "Scharnierbaugruppe",
|
||||
"sample.plate.about": "Parametrische Platte mit Vollrundungen, Bohrungen und einem aufgebohrten Ansatz",
|
||||
"sample.plate.about": "Parametrische Platte mit Vollrundungen, Bohrungen und einer aufgebohrten Erhöhung",
|
||||
"sample.plate.name": "Beispielplatte",
|
||||
"sample.vents.about": "Langlöcher in einem Rechteckmuster: Zeilen, Spalten und Abstand sind Parameter",
|
||||
"sample.vents.name": "Lüftungsplatte",
|
||||
@@ -2320,7 +1992,6 @@
|
||||
"select the sheet edge first": "zuerst die Blechkante auswählen",
|
||||
"select two components": "zwei Komponenten auswählen",
|
||||
"settings.display_quality_note": "Feinere Stufen zeigen gekrümmte Flächen und Kanten glatter und brauchen länger zum Vernetzen. Nur die Ansicht ändert sich, nie das Design oder Exporte.",
|
||||
"settings.double_click_components": "Doppelklick auf eine Komponente aktiviert sie oder bearbeitet eine verknüpfte an Ort und Stelle",
|
||||
"settings.frame_rate_note": "Zeigt die Bilder pro Sekunde unten rechts in der Ansicht.",
|
||||
"settings.measure_axis_distances": "X/Y/Z-Maße anzeigen",
|
||||
"settings.measure_axis_distances_help": "Bei geöffneter Messung die Abstände entlang der Weltachsen zusätzlich zum Gesamtabstand anzeigen.",
|
||||
@@ -2332,7 +2003,6 @@
|
||||
"settings.reset_intro": "Diese Einstellungen funktionieren auf diesem Computer nicht und würden den Start von SolidCraft verhindern. Sie wurden daher geändert:",
|
||||
"settings.reset_title": "Einstellungen wurden geändert",
|
||||
"settings.supersampling_note": "Zeichnet die Ansicht in höherer Auflösung und rechnet sie herunter: glattere Kanten, Linien und Raster auf Bildschirmen unter 4K. Kostet GPU-Zeit: etwa 2,25-mal so viele Pixel bei 1,5× und 4-mal bei 2×.",
|
||||
"settings.update_changed_links": "Geänderte verknüpfte Designs aktualisieren, wenn ein Tab wieder angezeigt wird",
|
||||
"sheet.flange_type_base": "Basislasche",
|
||||
"sheet.flange_type_contour": "Konturlasche",
|
||||
"sheet.flange_type_edge": "Kantenlasche",
|
||||
@@ -2370,10 +2040,6 @@
|
||||
"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",
|
||||
@@ -2403,10 +2069,8 @@
|
||||
"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.",
|
||||
@@ -2767,8 +2431,6 @@
|
||||
"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",
|
||||
@@ -2874,24 +2536,6 @@
|
||||
"ui.easyprint_uploading": "Wird zu Printables hochgeladen …",
|
||||
"ui.edit": "Bearbeiten",
|
||||
"ui.editJointLimits": "Gelenkgrenzen bearbeiten",
|
||||
"ui.edit_in_place": "An Ort und Stelle bearbeiten",
|
||||
"ui.edit_in_place_after_turn": "Die Bearbeitung endet, sobald der Agent seine Antwort beendet hat",
|
||||
"ui.edit_in_place_agent_busy": "Der Agent arbeitet noch in diesem Design; erneut versuchen, sobald er seine Antwort beendet hat",
|
||||
"ui.edit_in_place_already_queued": "Eine Bearbeitung wartet bereits auf das Ende der Antwort des Agenten",
|
||||
"ui.edit_in_place_assembly_closed": "{name} ist nicht mehr geöffnet",
|
||||
"ui.edit_in_place_banner": "Bearbeitung in {name}",
|
||||
"ui.edit_in_place_busy": "Zuerst abschließen oder abbrechen, was offen ist",
|
||||
"ui.edit_in_place_none": "Es wird kein verknüpftes Design an Ort und Stelle bearbeitet",
|
||||
"ui.edit_in_place_not_saved": "Das Teil hat ungespeicherte Änderungen; zum Beenden der Bearbeitung speichern",
|
||||
"ui.edit_in_place_open_elsewhere": "{name} ist in einem anderen SolidCraft geöffnet",
|
||||
"ui.edit_in_place_opens_after_turn": "Das Teil öffnet sich, sobald der Agent seine Antwort beendet hat",
|
||||
"ui.edit_in_place_refresh_dropped": "Die Baugruppe wurde während der Aktualisierung der Verknüpfungen geändert; bitte erneut ausführen",
|
||||
"ui.edit_in_place_refresh_stopped": "Die Aktualisierung der Verknüpfungen wurde vorzeitig beendet",
|
||||
"ui.edit_in_place_refreshing": "Verknüpfungen in {name} werden aktualisiert…",
|
||||
"ui.edit_in_place_save_first": "Zuerst die Baugruppe speichern: ihre verknüpften Designs werden von ihrer Datei aus gefunden",
|
||||
"ui.edit_in_place_save_waits": "In {name} werden die Verknüpfungen aktualisiert; danach speichern",
|
||||
"ui.edit_in_place_saved_elsewhere": "Das Teil wurde in eine andere Datei gespeichert; die Baugruppe verknüpft weiterhin {name}",
|
||||
"ui.edit_in_place_wait": "Warten, bis abgeschlossen ist, was im Design läuft",
|
||||
"ui.edit_section_analysis": "Querschnittsanalyse bearbeiten",
|
||||
"ui.ellipse": "Ellipse",
|
||||
"ui.emboss_wrap_note": "Senkrechte Linienprofile, die einen unterstützten Zylinder berühren, werden automatisch aufgewickelt. Eine Wand auswählen, um ein Ziel festzulegen.",
|
||||
@@ -2963,7 +2607,6 @@
|
||||
"ui.heading_edit": "BEARBEITEN",
|
||||
"ui.heading_extrude": "EXTRUDIEREN",
|
||||
"ui.heading_fillet": "VOLLRUNDUNG",
|
||||
"ui.heading_finish_editing": "BEARBEITUNG BEENDEN",
|
||||
"ui.heading_finish_sketch": "SKIZZE BEENDEN",
|
||||
"ui.heading_form": "FORM",
|
||||
"ui.heading_front": "VORNE",
|
||||
@@ -3151,15 +2794,6 @@
|
||||
"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",
|
||||
@@ -3204,8 +2838,6 @@
|
||||
"ui.restore_defaults": "Standardeinstellungen wiederherstellen",
|
||||
"ui.reverse_horizontal_pan_direction": "Horizontale Schwenkrichtung umkehren",
|
||||
"ui.reverse_horizontal_pan_tip": "Horizontales Zwei-Finger-Scrollen und das Daumenrad schwenken in die andere Richtung",
|
||||
"ui.reverse_vertical_pan_direction": "Vertikale Schwenkrichtung umkehren",
|
||||
"ui.reverse_vertical_pan_tip": "Vertikales Zwei-Finger-Scrollen schwenkt in die andere Richtung",
|
||||
"ui.reverse_zoom_direction": "Zoomrichtung umkehren",
|
||||
"ui.revolutions": "Windungen",
|
||||
"ui.revolve": "Drehen",
|
||||
@@ -3333,15 +2965,6 @@
|
||||
"ui.up_axis_note": "Welche Achse in den Ansichten nach oben zeigt (Achsenansicht, Home, Ansehen). Designs bleiben unverändert.",
|
||||
"ui.updateToCurrentView": "Auf aktuelle Ansicht aktualisieren",
|
||||
"ui.upload": "Hochladen",
|
||||
"ui.view_angles": "Ansichtswinkel",
|
||||
"ui.view_angles_convention": "Grad um die Hochachse. Gieren 0° ist die Vorderansicht, 90° links, −90° rechts, 180° die Rückansicht; Nicken 90° ist die Draufsicht; ein positives Rollen dreht das Modell im Uhrzeigersinn.",
|
||||
"ui.view_angles_help": "Zeigt Nick-, Gier- und Rollwinkel der Ansicht in Grad sowie die Projektion unter dem Orientierungs-Gizmo. Klicken Sie auf die Anzeige, um genaue Winkel einzugeben.",
|
||||
"ui.view_angles_ortho": "Ortho",
|
||||
"ui.view_angles_persp": "Persp",
|
||||
"ui.view_angles_pitch": "Nicken",
|
||||
"ui.view_angles_readout": "Nicken {pitch} · Gieren {yaw} · Rollen {roll}",
|
||||
"ui.view_angles_roll": "Rollen",
|
||||
"ui.view_angles_yaw": "Gieren",
|
||||
"ui.view_centre": "Ansichtszentrum",
|
||||
"ui.viewport_theme_follow": "Dem Design folgen",
|
||||
"ui.visual_shaded_hidden_edges": "Schattiert mit verdeckten Kanten",
|
||||
@@ -3408,7 +3031,6 @@
|
||||
"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."
|
||||
}
|
||||
|
||||
@@ -17,18 +17,6 @@
|
||||
"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.",
|
||||
@@ -119,10 +107,6 @@
|
||||
"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)",
|
||||
|
||||
+4
-261
@@ -38,7 +38,6 @@
|
||||
"All open designs": "All open designs",
|
||||
"Allowed expressions, separated by semicolons": "Allowed expressions, separated by semicolons",
|
||||
"Also deletes": "Also deletes",
|
||||
"Also place them from the origin (fully constrained)": "Also place them from the origin (fully constrained)",
|
||||
"Anchor": "Anchor",
|
||||
"Angle 2": "Angle 2",
|
||||
"Angle Display": "Angle Display",
|
||||
@@ -181,9 +180,6 @@
|
||||
"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",
|
||||
"Distance": "Distance",
|
||||
@@ -200,8 +196,6 @@
|
||||
"Drag to turn the rest in 5° steps. Hold Alt or Ctrl to turn it freely.": "Drag to turn the rest in 5° steps. Hold Alt or Ctrl to turn it freely.",
|
||||
"Drive Joint": "Drive Joint",
|
||||
"Duration": "Duration",
|
||||
"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.": "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.",
|
||||
"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.": "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.",
|
||||
"Edge": "Edge",
|
||||
"Edges": "Edges",
|
||||
"Edit": "Edit",
|
||||
@@ -260,8 +254,6 @@
|
||||
"Flanges": "Flanges",
|
||||
"Flat Pattern": "Flat Pattern",
|
||||
"Flat size": "Flat size",
|
||||
"Flat size X": "Flat size X",
|
||||
"Flat size Y": "Flat size Y",
|
||||
"Follow camera (Look At sketches)": "Follow camera (Look At sketches)",
|
||||
"Follow the camera (Look At sketches)": "Follow the camera (Look At sketches)",
|
||||
"Font": "Font",
|
||||
@@ -595,7 +587,6 @@
|
||||
"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",
|
||||
@@ -723,7 +714,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 (Preferences > Assistant).",
|
||||
"ai.connected_toast": "SolidCraft is now available in {clients}. Restart them to use it (Settings > 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",
|
||||
@@ -763,7 +754,6 @@
|
||||
"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}",
|
||||
@@ -772,7 +762,6 @@
|
||||
"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}",
|
||||
@@ -823,9 +812,7 @@
|
||||
"cmd.component_create": "New Component",
|
||||
"cmd.component_cycle_color": "Cycle Component Color",
|
||||
"cmd.component_delete": "Delete Component",
|
||||
"cmd.component_edit_in_place": "Edit in Place",
|
||||
"cmd.component_extract": "Extract to File",
|
||||
"cmd.component_finish_edit": "Finish Edit",
|
||||
"cmd.component_from_bodies": "Create Components from Bodies",
|
||||
"cmd.component_insert_design": "Insert Design",
|
||||
"cmd.component_links": "List Links",
|
||||
@@ -1000,10 +987,6 @@
|
||||
"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",
|
||||
@@ -1026,20 +1009,15 @@
|
||||
"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",
|
||||
@@ -1142,7 +1120,6 @@
|
||||
"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",
|
||||
@@ -1194,7 +1171,6 @@
|
||||
"cmd.view.render_dock": "Move Render Dock",
|
||||
"cmd.view.render_focus": "Render Focus View",
|
||||
"cmd.view_ambient_occlusion": "Ambient Occlusion",
|
||||
"cmd.view_angle_readout": "View Angles",
|
||||
"cmd.view_apply": "Restore Named View",
|
||||
"cmd.view_back": "Back View",
|
||||
"cmd.view_bottom": "Bottom View",
|
||||
@@ -1270,7 +1246,6 @@
|
||||
"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.",
|
||||
@@ -1330,7 +1305,6 @@
|
||||
"help.appearance.assign": "Gives the selected bodies, faces or components a colour or a material look, without changing their weight. Use it to tell parts apart or to preview a finish.",
|
||||
"help.attributions": "Attributions (ATTRIBUTION.md)",
|
||||
"help.build": "Version {version} · commit {commit} · built {date}",
|
||||
"help.component.finish_edit": "Saves the part you are editing in place, returns to its assembly and updates the assembly from it. Use it when your changes to the linked part are done.",
|
||||
"help.connect_note": "Restart each assistant to load the SolidCraft server. Each file was backed up as .bak before it changed.",
|
||||
"help.connect_title": "Connect AI assistants",
|
||||
"help.face.delete": "Removes the selected faces and lets the neighbouring faces close the gap, or leaves the gap open as a surface. Use it to take off a rounded edge, a hole or a boss that is in the way.",
|
||||
@@ -1341,7 +1315,6 @@
|
||||
"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.",
|
||||
@@ -1367,8 +1340,6 @@
|
||||
"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.",
|
||||
@@ -1434,7 +1405,7 @@
|
||||
"home.recovery_hint": "SolidCraft closed before these designs were saved.",
|
||||
"home.report_issue": "Report an Issue",
|
||||
"home.samples": "Samples",
|
||||
"home.settings": "Preferences",
|
||||
"home.settings": "Settings",
|
||||
"home.short_date": "{day} {month}",
|
||||
"home.shortcuts": "Keyboard shortcuts",
|
||||
"home.start_hint": "Create a design, open a recent file, or explore a sample.",
|
||||
@@ -1736,8 +1707,6 @@
|
||||
"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",
|
||||
@@ -1813,19 +1782,8 @@
|
||||
"prefs.layout_note": "Compact docks panels to the window edges. Browser translucency is available in Floating. Change the Assistant side on the Assistant page.",
|
||||
"prefs.left": "Left",
|
||||
"prefs.left_click": "Left click",
|
||||
"prefs.linked_designs": "Linked designs",
|
||||
"prefs.live_input": "Live input",
|
||||
"prefs.live_preview": "Live preview",
|
||||
"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",
|
||||
@@ -1857,8 +1815,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 preferences",
|
||||
"prefs.search_empty": "No matching preferences",
|
||||
"prefs.search": "Search settings",
|
||||
"prefs.search_empty": "No matching settings",
|
||||
"prefs.select": "Select",
|
||||
"prefs.select_one_item": "Select one item",
|
||||
"prefs.selecting": "Selecting",
|
||||
@@ -1874,24 +1832,11 @@
|
||||
"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",
|
||||
"prefs.sketch_tracking_guides_note": "Hover over a circle center or straight-edge midpoint, then move horizontally or vertically to show a temporary dotted guide.",
|
||||
"prefs.sketching": "Sketching",
|
||||
"prefs.snap_drawing_note": "While drawing or dragging a sketch point.",
|
||||
"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",
|
||||
@@ -1899,16 +1844,12 @@
|
||||
"prefs.supersampling_brief": "Draws the view larger and filters it down: very crisp, and heavier.",
|
||||
"prefs.tab_accent_note": "The accent follows the active tab",
|
||||
"prefs.theme_file": "theme file",
|
||||
"prefs.theme_file_or_paste": "file or paste",
|
||||
"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.",
|
||||
"prefs.theme_follow_note": "Reloaded whenever another tool rewrites it. Choosing a theme stops following it.",
|
||||
"prefs.theme_follow_off": "Off",
|
||||
"prefs.theme_follow_stop": "Stop following",
|
||||
"prefs.theme_import_hint": "Click to choose a theme file, drop one here, or press Ctrl+V (Cmd+V on a Mac) with a theme copied",
|
||||
"prefs.theme_paste": "Paste theme",
|
||||
"prefs.theme_paste_none": "The clipboard holds no SolidCraft theme",
|
||||
"prefs.timeline_half_hour": "−30 min",
|
||||
"prefs.timeline_now": "now",
|
||||
"prefs.timeline_quarter_hour": "−15",
|
||||
@@ -1974,47 +1915,7 @@
|
||||
"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",
|
||||
"render.availability_projection": "Supported projection; see scope and omissions",
|
||||
"render.availability_reference": "Reference data; not an automatic assignment",
|
||||
@@ -2069,13 +1970,6 @@
|
||||
"render.camera_set": "Set Render Camera",
|
||||
"render.camera_target": "Target position · mm",
|
||||
"render.camera_yaw": "Yaw",
|
||||
"render.category_metal": "Metal",
|
||||
"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",
|
||||
@@ -2175,7 +2069,6 @@
|
||||
"render.effects_reset_lens": "Reset lens effects",
|
||||
"render.effects_seed": "Pattern seed",
|
||||
"render.emitted_axis": "Emitted axis",
|
||||
"render.environment_tiles_require_raytrace": "Environments require CPU or GPU ray tracing. Select it in the Output editor.",
|
||||
"render.error_area_samples": "area_samples must be 1, 4, 16 or 64",
|
||||
"render.error_binding_allocation": "could not reserve material binding",
|
||||
"render.error_body_bound": "product preview exceeds its captured-body bound",
|
||||
@@ -2247,8 +2140,6 @@
|
||||
"render.flare_strength": "Highlight flare strength",
|
||||
"render.flare_threshold": "Scene-linear flare threshold",
|
||||
"render.fold_browser": "Fold Browser",
|
||||
"render.frame_presets": "Frame presets",
|
||||
"render.frame_presets_pending": "Arrives with the Shot editor",
|
||||
"render.gpu_depth": "Maximum path depth",
|
||||
"render.gpu_options_help": "Samples, depth, seed and exposure are saved with this setup. Only the active image method is saved.",
|
||||
"render.gpu_path_failed": "GPU path tracer unavailable",
|
||||
@@ -2309,36 +2200,6 @@
|
||||
"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",
|
||||
@@ -2350,12 +2211,8 @@
|
||||
"render.lens_effects_title": "Lens-inspired & photographic",
|
||||
"render.library_all": "Catalog",
|
||||
"render.library_all_states": "All availability states",
|
||||
"render.library_appearance_available": "Available appearance",
|
||||
"render.library_applicable": "Applicable resources",
|
||||
"render.library_applied": "Resource projection applied. Check the complete render request before starting.",
|
||||
"render.library_apply_appearance": "Apply appearance",
|
||||
"render.library_apply_hdr": "Save with this HDR environment",
|
||||
"render.library_apply_hint": "Click to apply · drag onto the model",
|
||||
"render.library_apply_maps": "Use authored GPU surface maps",
|
||||
"render.library_apply_studio": "Save authored studio projection",
|
||||
"render.library_apply_uniform": "Apply uniform opaque appearance",
|
||||
@@ -2371,23 +2228,15 @@
|
||||
"render.library_extent": "Subject extent",
|
||||
"render.library_favorite": "Add favorite",
|
||||
"render.library_favorites": "Favorites",
|
||||
"render.library_filters": "Catalog and availability",
|
||||
"render.library_grid": "Grid",
|
||||
"render.library_hdr": "Built-in HDR environments",
|
||||
"render.library_hdr_help": "Embeds the original HDR in the saved setup. The authored longitude origin is converted explicitly; no external file is needed after saving.",
|
||||
"render.library_in_design": "In Design",
|
||||
"render.library_inspect_only": "Select to inspect; nothing is applied automatically. Favorites and recent history belong to this open document session.",
|
||||
"render.library_ior": "IOR",
|
||||
"render.library_light_concepts": "Lighting concepts",
|
||||
"render.library_list": "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_material_concepts": "Material concepts",
|
||||
"render.library_metallic": "Metallic",
|
||||
"render.library_native_scenes": "Native studios",
|
||||
"render.library_next": "Next page",
|
||||
"render.library_omitted": "Not included",
|
||||
"render.library_opacity": "Opacity",
|
||||
"render.library_other": "Other appearances",
|
||||
"render.library_physical_profiles": "Physical reference profiles",
|
||||
"render.library_previous": "Previous page",
|
||||
"render.library_quality_concepts": "Quality specifications",
|
||||
@@ -2397,7 +2246,6 @@
|
||||
"render.library_results": "Matching resources",
|
||||
"render.library_rig_help": "Fit once at an explicit center and physical extent. Complete rigs include passive cards; calibration blockers still refuse. Conservative emitter/card clearance is checked before saving.",
|
||||
"render.library_rigs": "Authored lighting rigs",
|
||||
"render.library_roughness": "Roughness",
|
||||
"render.library_save_maps": "Save maps to this setup",
|
||||
"render.library_scene_bounds": "Use captured scene bounds",
|
||||
"render.library_scenes": "Scene specifications",
|
||||
@@ -2421,7 +2269,6 @@
|
||||
"render.library_unfavorite": "Remove favorite",
|
||||
"render.library_uniform_ack": "Use the uniform approximation without spatial or advanced effects",
|
||||
"render.library_usable": "Executable projections",
|
||||
"render.library_users": "Users",
|
||||
"render.light_anchor_pending": "Changing the light coordinate frame is not supported yet.",
|
||||
"render.lights_fixed_to": "Lights fixed to",
|
||||
"render.linear_help": "Keeps a 32-bit float RGBA master before denoising, exposure, tone mapping, filters and backdrop. Linear sRGB/D65; coverage-premultiplied. Additional memory is admitted explicitly. No compression or extra passes.",
|
||||
@@ -2505,34 +2352,12 @@
|
||||
"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",
|
||||
@@ -2593,18 +2418,10 @@
|
||||
"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",
|
||||
@@ -2613,7 +2430,6 @@
|
||||
"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",
|
||||
@@ -2671,7 +2487,6 @@
|
||||
"render.scene_review": "Resolve and review",
|
||||
"render.scene_review_help": "Review the complete native composition before applying. Unbound advanced SCENE specifications are not substituted.",
|
||||
"render.scene_scope_details": "Composition scope",
|
||||
"render.search_appearances": "Search {count} appearances",
|
||||
"render.setup_legacy_environment": "This older setup uses legacy environment rotation or intensity. Those values are retained; convert them explicitly before saving a full image job.",
|
||||
"render.setup_name": "Setup name",
|
||||
"render.setup_numeric_help": "Save records the active image method and all its numeric settings. Reload or choosing another setup replaces this draft; restore the camera separately.",
|
||||
@@ -2681,7 +2496,6 @@
|
||||
"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",
|
||||
@@ -2691,23 +2505,10 @@
|
||||
"render.shot_remove_title": "Remove saved shot?",
|
||||
"render.shot_rename": "Rename saved shot",
|
||||
"render.shot_saved_only": "Save the current draft first. Duplicate and rename preserve exact saved options and resource references.",
|
||||
"render.shots_camera_changed": "Camera changed since this shot was saved",
|
||||
"render.shots_name_help": "Enter a unique name for the current shot.",
|
||||
"render.shots_no_result": "No render yet",
|
||||
"render.shots_result": "Render {number} · {width} × {height}",
|
||||
"render.shots_revert": "Revert",
|
||||
"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",
|
||||
@@ -2720,9 +2521,6 @@
|
||||
"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",
|
||||
@@ -2761,10 +2559,8 @@
|
||||
"render.studio_sky_help": "Original sky gradient for lighting; not a calibrated atmospheric model.",
|
||||
"render.studio_sun": "Sun",
|
||||
"render.studio_sun_radius": "Angular radius",
|
||||
"render.studio_tiles_require_gpu": "Studios require GPU path tracing. Select GPU in the Output editor.",
|
||||
"render.studio_title": "Render studio",
|
||||
"render.studio_tone_map": "Tone mapping",
|
||||
"render.studios_environments": "Studios and Environments",
|
||||
"render.subsurface_enable": "Interior scattering random walk",
|
||||
"render.subsurface_help": "Closed dielectric only. Scattering and RGB absorption are separate per-mm coefficients. Paths scatter inside actual geometry, then refract or reflect at the boundary. Disable straight shadow transmission, surface denoising and exterior volume; use one Fresnel stratum. Not a diffusion shader, density grid or measured material. Dense media may need more samples and path depth.",
|
||||
"render.subsurface_rate": "Scattering collision rate",
|
||||
@@ -2818,7 +2614,6 @@
|
||||
"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",
|
||||
@@ -2873,7 +2668,6 @@
|
||||
"select two components": "select two components",
|
||||
"set by the sketch's lines": "set by the sketch's lines",
|
||||
"settings.display_quality_note": "Only the display. Designs and exports are not affected.",
|
||||
"settings.double_click_components": "Double-click a component to activate it, or to edit a linked one in place",
|
||||
"settings.frame_rate_details_note": "Adds a graph of the last frames' times with the triangles drawn over it, a histogram of frame times and what the GPU drew. Click the frame rate in the view to show or hide them.",
|
||||
"settings.frame_rate_note": "Shows frames per second in the view's bottom-right corner.",
|
||||
"settings.measure_axis_distances": "Show X/Y/Z distances",
|
||||
@@ -2886,7 +2680,6 @@
|
||||
"settings.reset_intro": "These settings don't work on this computer and would keep SolidCraft from starting, so they were changed:",
|
||||
"settings.reset_title": "Settings were changed",
|
||||
"settings.supersampling_note": "Draws the view at a higher resolution and filters it down: smoother edges, lines and grid on screens below 4K. It costs GPU time: about 2.25× the pixels at 1.5× and 4× at 2×.",
|
||||
"settings.update_changed_links": "Update changed linked designs when a tab comes back",
|
||||
"settings.window_system_details": "Details",
|
||||
"settings.window_system_note_now": "Applies after a restart. Now: {now}.",
|
||||
"settings.window_system_wayland_limited": "Some features are limited on native Wayland.",
|
||||
@@ -2933,10 +2726,6 @@
|
||||
"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",
|
||||
@@ -2975,10 +2764,8 @@
|
||||
"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.",
|
||||
@@ -3389,8 +3176,6 @@
|
||||
"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",
|
||||
@@ -3498,24 +3283,6 @@
|
||||
"ui.easyprint_uploading": "Uploading to Printables…",
|
||||
"ui.edit": "Edit",
|
||||
"ui.editJointLimits": "Edit Joint Limits",
|
||||
"ui.edit_in_place": "Edit in Place",
|
||||
"ui.edit_in_place_after_turn": "The edit finishes when the agent's turn ends",
|
||||
"ui.edit_in_place_agent_busy": "The agent is still working in this design; try again once its turn is done",
|
||||
"ui.edit_in_place_already_queued": "An edit already waits for the end of the agent's turn",
|
||||
"ui.edit_in_place_assembly_closed": "{name} is no longer open",
|
||||
"ui.edit_in_place_banner": "Editing in {name}",
|
||||
"ui.edit_in_place_busy": "Finish or cancel what is open first",
|
||||
"ui.edit_in_place_none": "No linked design is edited in place",
|
||||
"ui.edit_in_place_not_saved": "The part has changes that are not saved; save it to finish editing",
|
||||
"ui.edit_in_place_open_elsewhere": "{name} is open in another SolidCraft",
|
||||
"ui.edit_in_place_opens_after_turn": "The part opens when the agent's turn ends",
|
||||
"ui.edit_in_place_refresh_dropped": "The assembly changed while Update Links ran; run it again",
|
||||
"ui.edit_in_place_refresh_stopped": "Update Links stopped before it was done",
|
||||
"ui.edit_in_place_refreshing": "Updating links in {name}…",
|
||||
"ui.edit_in_place_save_first": "Save the assembly first: its linked designs are found from its file",
|
||||
"ui.edit_in_place_save_waits": "Update Links is running in {name}; save it once it is done",
|
||||
"ui.edit_in_place_saved_elsewhere": "The part was saved to another file; the assembly still links {name}",
|
||||
"ui.edit_in_place_wait": "Wait until what is running in the design is done",
|
||||
"ui.edit_section_analysis": "Edit Section Analysis",
|
||||
"ui.ellipse": "Ellipse",
|
||||
"ui.emboss_wrap_note": "Orthogonal line profiles tangent to one supported cylinder wrap automatically. Pick a wall to choose a target.",
|
||||
@@ -3590,7 +3357,6 @@
|
||||
"ui.heading_edit": "EDIT",
|
||||
"ui.heading_extrude": "EXTRUDE",
|
||||
"ui.heading_fillet": "FILLET",
|
||||
"ui.heading_finish_editing": "FINISH EDITING",
|
||||
"ui.heading_finish_sketch": "FINISH SKETCH",
|
||||
"ui.heading_form": "FORM",
|
||||
"ui.heading_front": "FRONT",
|
||||
@@ -3803,15 +3569,6 @@
|
||||
"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",
|
||||
@@ -3859,8 +3616,6 @@
|
||||
"ui.restore_defaults": "Restore Defaults",
|
||||
"ui.reverse_horizontal_pan_direction": "Reverse horizontal pan direction",
|
||||
"ui.reverse_horizontal_pan_tip": "Horizontal two-finger scrolling and the thumb wheel pan the other way",
|
||||
"ui.reverse_vertical_pan_direction": "Reverse vertical pan direction",
|
||||
"ui.reverse_vertical_pan_tip": "Vertical two-finger scrolling pans the other way",
|
||||
"ui.reverse_zoom_direction": "Reverse zoom direction",
|
||||
"ui.revolutions": "Revolutions",
|
||||
"ui.revolve": "Revolve",
|
||||
@@ -4006,15 +3761,6 @@
|
||||
"ui.updateToCurrentView": "Update to Current View",
|
||||
"ui.upload": "Upload",
|
||||
"ui.vertical_constraint": "Vertical Constraint",
|
||||
"ui.view_angles": "View angles",
|
||||
"ui.view_angles_convention": "Degrees about the up axis. Yaw 0° is the Front view, 90° Left, −90° Right, 180° Back; pitch 90° is the Top view; a positive roll turns the model clockwise.",
|
||||
"ui.view_angles_help": "Shows the view's pitch, yaw and roll in degrees and its projection under the orientation gizmo. Click the readout to type exact angles.",
|
||||
"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": "View centre",
|
||||
"ui.viewport_theme_follow": "Follow theme",
|
||||
"ui.visual_shaded_hidden_edges": "Shaded with hidden edges",
|
||||
@@ -4065,13 +3811,11 @@
|
||||
"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",
|
||||
@@ -4088,7 +3832,6 @@
|
||||
"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",
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
"All open designs": "Todos los diseños abiertos",
|
||||
"Allowed expressions, separated by semicolons": "Expresiones permitidas, separadas por punto y coma",
|
||||
"Also deletes": "También elimina",
|
||||
"Also place them from the origin (fully constrained)": "Situarlas también desde el origen (totalmente restringidas)",
|
||||
"Angle 2": "Ángulo 2",
|
||||
"Animate": "Animar",
|
||||
"Appearance name": "Nombre de apariencia",
|
||||
@@ -125,7 +124,6 @@
|
||||
"Dependents": "Dependientes",
|
||||
"Designs you open or save appear here.": "Aquí aparecen los diseños que abras o guardes.",
|
||||
"Determined by To Object. Choose Angle to edit the angular extent.": "Determinado por «Hasta objeto». Elige «Ángulo» para modificar la extensión angular.",
|
||||
"Dimension new shapes": "Acotar las formas nuevas",
|
||||
"Direction": "Dirección",
|
||||
"Discard": "Descartar",
|
||||
"Do not export hidden edges": "No exportar aristas ocultas",
|
||||
@@ -134,8 +132,6 @@
|
||||
"Double-clicking the middle button in the view: Home (the home view, framing what is shown), Fit (frame what is shown, keeping the orientation) or nothing": "Doble clic con el botón central en la vista: Inicio (vista inicial que encuadra lo visible), Ajustar (encuadrar sin cambiar la orientación) o nada",
|
||||
"Drive Joint": "Accionar unión",
|
||||
"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 nueva también se acota desde el origen del croquis hasta que no quede nada libre. Una forma ya unida al origen o a geometría situada no recibe ninguna.",
|
||||
"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 dibujas recibe todas sus cotas con los tamaños dibujados (los valores escritos tienen prioridad). Se omite un tamaño que el croquis ya fija.",
|
||||
"Edge": "Arista",
|
||||
"Edges": "Aristas",
|
||||
"Edit": "Editar",
|
||||
@@ -516,7 +512,6 @@
|
||||
"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}",
|
||||
@@ -855,7 +850,6 @@
|
||||
"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",
|
||||
@@ -1228,13 +1222,6 @@
|
||||
"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.",
|
||||
|
||||
+1
-465
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -38,7 +38,6 @@
|
||||
"All open designs": "כל התכנים הפתוחים",
|
||||
"Allowed expressions, separated by semicolons": "ביטויים מותרים, מופרדים בנקודה ופסיק",
|
||||
"Also deletes": "נמחקים גם",
|
||||
"Also place them from the origin (fully constrained)": "מיקום גם ביחס לראשית (מוגבל לחלוטין)",
|
||||
"Anchor": "נקודת עיגון",
|
||||
"Angle 2": "זווית 2",
|
||||
"Angle Display": "תצוגת זווית",
|
||||
@@ -181,9 +180,6 @@
|
||||
"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": "השלכה",
|
||||
"Distance": "מרחק",
|
||||
@@ -200,8 +196,6 @@
|
||||
"Drag to turn the rest in 5° steps. Hold Alt or Ctrl to turn it freely.": "יש לגרור כדי לסובב את המשען בצעדים של 5°. יש להחזיק Alt או Ctrl לסיבוב חופשי.",
|
||||
"Drive Joint": "הנעת מפרק",
|
||||
"Duration": "משך",
|
||||
"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.": "כל צורה חדשה מקבלת גם מידות מראשית הסקיצה עד שלא נשאר בה דבר חופשי. צורה שכבר מחוברת לראשית או לגאומטריה ממוקמת לא מקבלת אף אחת.",
|
||||
"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.": "כל צורה שתשרטט תקבל את כל המידות שלה בגדלים ששרטטת (ערכים שהוקלדו קודמים). גודל שהסקיצה כבר קובעת מושמט.",
|
||||
"Edge": "מקצוע",
|
||||
"Edges": "מקצועות",
|
||||
"Edit": "עריכה",
|
||||
@@ -260,8 +254,6 @@
|
||||
"Flanges": "אוגנים",
|
||||
"Flat Pattern": "פריסה",
|
||||
"Flat size": "מידות הפריסה",
|
||||
"Flat size X": "מידות הפריסה X",
|
||||
"Flat size Y": "מידות הפריסה Y",
|
||||
"Follow camera (Look At sketches)": "מעקב אחר המצלמה (הבטה אל סקיצות)",
|
||||
"Follow the camera (Look At sketches)": "מעקב אחר המצלמה (הבטה אל סקיצות)",
|
||||
"Font": "גופן",
|
||||
@@ -595,7 +587,6 @@
|
||||
"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": "תפיחה בהתחלה",
|
||||
@@ -763,7 +754,6 @@
|
||||
"appearance.pbr_opaque": "PBR אטום",
|
||||
"appearance.roughness": "חספוס",
|
||||
"appearance.surface_response": "תגובת המשטח",
|
||||
"at the face": "על הפאה",
|
||||
"bom.group_children": "תת-חלקים בשורת החלק שלהם",
|
||||
"browser.body_name": "גוף{number}",
|
||||
"browser.calibrate_title": "כיול: {name}",
|
||||
@@ -772,7 +762,6 @@
|
||||
"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}",
|
||||
@@ -823,9 +812,7 @@
|
||||
"cmd.component_create": "רכיב חדש",
|
||||
"cmd.component_cycle_color": "החלפת צבע הרכיב במחזוריות",
|
||||
"cmd.component_delete": "מחיקת רכיב",
|
||||
"cmd.component_edit_in_place": "עריכה במקום",
|
||||
"cmd.component_extract": "חילוץ לקובץ",
|
||||
"cmd.component_finish_edit": "סיום עריכה",
|
||||
"cmd.component_from_bodies": "יצירת רכיבים מגופים",
|
||||
"cmd.component_insert_design": "הוספת תכן",
|
||||
"cmd.component_links": "רשימת קישורים",
|
||||
@@ -1000,10 +987,6 @@
|
||||
"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": "רינדור כל הצילומים",
|
||||
@@ -1026,20 +1009,15 @@
|
||||
"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",
|
||||
@@ -1142,7 +1120,6 @@
|
||||
"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": "מעבר בין מידה מונעת למניעה",
|
||||
@@ -1194,7 +1171,6 @@
|
||||
"cmd.view.render_dock": "הזז את עגינת הרינדור",
|
||||
"cmd.view.render_focus": "תצוגת מיקוד רינדור",
|
||||
"cmd.view_ambient_occlusion": "הצללה סביבתית",
|
||||
"cmd.view_angle_readout": "זוויות תצוגה",
|
||||
"cmd.view_apply": "שחזור תצוגה שמורה",
|
||||
"cmd.view_back": "תצוגה אחורית",
|
||||
"cmd.view_bottom": "תצוגה תחתונה",
|
||||
@@ -1270,7 +1246,6 @@
|
||||
"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+לחיצה מחליפה את מצבו. לאחר בדיקת התצוגה המקדימה יש ללחוץ על אישור.",
|
||||
@@ -1330,7 +1305,6 @@
|
||||
"help.appearance.assign": "מתן צבע או מראה של חומר לגופים, לפאות או לרכיבים שנבחרו, בלי לשנות את משקלם. מתאים להבחנה בין חלקים או לתצוגה מקדימה של גימור.",
|
||||
"help.attributions": "ייחוסים (ATTRIBUTION.md)",
|
||||
"help.build": "גרסה {version} · קומיט {commit} · נבנתה ב־{date}",
|
||||
"help.component.finish_edit": "שומר את החלק שנערך במקומו, חוזר להרכבה שלו ומעדכן ממנו את ההרכבה. יש להשתמש בו כשהשינויים בחלק המקושר הושלמו.",
|
||||
"help.connect_note": "יש להפעיל מחדש כל עוזר כדי לטעון את שרת SolidCraft. לכל קובץ נשמר גיבוי בסיומת .bak לפני השינוי.",
|
||||
"help.connect_title": "חיבור עוזרי בינה מלאכותית",
|
||||
"help.face.delete": "מחיקת הפאות שנבחרו, כשהפאות השכנות סוגרות את הפער, או השארת הפתח פתוח כמשטח. מתאים להסרת עיגול, חור או בליטה שמפריעים.",
|
||||
@@ -1341,7 +1315,6 @@
|
||||
"help.mit": "רישיון MIT",
|
||||
"help.notice": "NOTICE",
|
||||
"help.parameters.change": "פתיחת הטבלה של ערכים בעלי שם וביטויים שקובעים את מידות התכנון. כך משנים מידה במקום אחד וכל הפיצ'רים שמשתמשים בה מתעדכנים.",
|
||||
"help.pointcloud.insert": "הוספת סריקה בפורמט PLY, XYZ או PTS כענן נקודות לצד המודל. משמש כעזר למידול על סמך נתוני מדידה.",
|
||||
"help.solid.align": "הזזת הגופים שנבחרו כך שנקודה, פאה או מישור שלהם יפגשו נקודה, פאה או מישור במקום אחר. מתאים להצמדת חלק לחלק אחר בלי למדוד את המרחק.",
|
||||
"help.solid.chamfer": "חיתוך המקצועות שנבחרו בשיפוע ישר במרחק שנקבע. כך שוברים מקצועות חדים או מקלים על הכנסה של פין או בורג.",
|
||||
"help.solid.combine": "איחוד גופים, חיסורם מגוף יעד או השארת הנפח המשותף להם בלבד, עם אפשרות לשמור את גופי הכלי. מתאים למיזוג חלקים, לחיתוך צורה אחת בעזרת אחרת או למציאת החפיפה בין שני גופים.",
|
||||
@@ -1367,8 +1340,6 @@
|
||||
"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": "למחוק את ההגדרה המוכנה האישית הזו? חורים קיימים לא ישתנו.",
|
||||
@@ -1736,8 +1707,6 @@
|
||||
"prefs.anti_aliasing_note": "מחליק קצוות משוננים.",
|
||||
"prefs.applies_on_restart": "ייכנס לתוקף בהפעלה מחדש",
|
||||
"prefs.assistant": "עוזר",
|
||||
"prefs.auto_operation": "בחירת הפעולה באופן אוטומטי",
|
||||
"prefs.auto_operation_note": "חלונות הפיצ'רים בוחרים „איחוד”, „גוף חדש” או „הסרה” לפי מה שהחומר החדש נוגע בו, עד שתבחרו פעולה בעצמכם. כבוי: הם שומרים על הפעולה שאיתה נפתחו.",
|
||||
"prefs.axes": "צירים",
|
||||
"prefs.axis": "ציר",
|
||||
"prefs.bodies_folders": "תיקיות גופים",
|
||||
@@ -1813,19 +1782,8 @@
|
||||
"prefs.layout_note": "הפריסה הדחוסה מצמידה חלוניות לשולי החלון. שקיפות עץ התכן זמינה בפריסה הצפה. את צד העוזר אפשר לשנות בעמוד העוזר.",
|
||||
"prefs.left": "שמאל",
|
||||
"prefs.left_click": "לחיצה שמאלית",
|
||||
"prefs.linked_designs": "תכנים מקושרים",
|
||||
"prefs.live_input": "קלט חי",
|
||||
"prefs.live_preview": "תצוגה מקדימה חיה",
|
||||
"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": "עכבר ומשטח מגע",
|
||||
@@ -1874,24 +1832,11 @@
|
||||
"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": "קווי עזר ליישור מרכזים ונקודות אמצע",
|
||||
"prefs.sketch_tracking_guides_note": "יש לרחף עם הסמן מעל מרכז מעגל או נקודת אמצע של צלע ישרה, ואז להזיז אותו אופקית או אנכית כדי להציג קו עזר מקווקו זמני.",
|
||||
"prefs.sketching": "שרטוט סקיצות",
|
||||
"prefs.snap_drawing_note": "בזמן ציור או גרירה של נקודת סקיצה.",
|
||||
"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": "נבחר",
|
||||
@@ -1899,16 +1844,12 @@
|
||||
"prefs.supersampling_brief": "מצייר את המבט גדול יותר ומקטין אותו: חד מאוד, וכבד יותר.",
|
||||
"prefs.tab_accent_note": "צבע ההדגשה עוקב אחר הלשונית הפעילה",
|
||||
"prefs.theme_file": "קובץ ערכת נושא",
|
||||
"prefs.theme_file_or_paste": "קובץ או הדבקה",
|
||||
"prefs.theme_follow": "מעקב אחר קובץ ערכת נושא",
|
||||
"prefs.theme_follow_error": "לא הוחל: {error}. ערכת הנושא התקינה האחרונה נשארת פעילה.",
|
||||
"prefs.theme_follow_launch": "מעקב להפעלה זו בלבד (--theme-file); נטען מחדש בכל פעם שכלי אחר משכתב את הקובץ.",
|
||||
"prefs.theme_follow_note": "נטען מחדש בכל פעם שכלי אחר משכתב את הקובץ. בחירת ערכת נושא מפסיקה את המעקב.",
|
||||
"prefs.theme_follow_off": "כבוי",
|
||||
"prefs.theme_follow_stop": "הפסקת המעקב",
|
||||
"prefs.theme_import_hint": "לחצו כדי לבחור קובץ ערכת נושא, שחררו קובץ כאן, או הקישו Ctrl+V (Cmd+V ב-Mac) אחרי שהעתקתם ערכת נושא",
|
||||
"prefs.theme_paste": "הדבקת ערכת נושא",
|
||||
"prefs.theme_paste_none": "בלוח אין ערכת נושא של SolidCraft",
|
||||
"prefs.timeline_half_hour": "−30 דק׳",
|
||||
"prefs.timeline_now": "עכשיו",
|
||||
"prefs.timeline_quarter_hour": "−15",
|
||||
@@ -1974,47 +1915,7 @@
|
||||
"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": "קומפוזיציה מקורית; יש לסקור לפני ההחלה",
|
||||
"render.availability_projection": "השלכה נתמכת; ראו היקף והשמטות",
|
||||
"render.availability_reference": "נתוני ייחוס; לא הקצאה אוטומטית",
|
||||
@@ -2069,13 +1970,6 @@
|
||||
"render.camera_set": "הגדרת מצלמת ההדמיה",
|
||||
"render.camera_target": "מיקום היעד · mm",
|
||||
"render.camera_yaw": "סבסוב",
|
||||
"render.category_metal": "מתכת",
|
||||
"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",
|
||||
@@ -2175,7 +2069,6 @@
|
||||
"render.effects_reset_lens": "איפוס אפקטי העדשה",
|
||||
"render.effects_seed": "זרע הדוגמה",
|
||||
"render.emitted_axis": "ציר פליטה",
|
||||
"render.environment_tiles_require_raytrace": "סביבות דורשות מעקב קרניים במעבד או בכרטיס המסך. בחר זאת בעורך הפלט.",
|
||||
"render.error_area_samples": "area_samples חייב להיות 1, 4, 16 או 64",
|
||||
"render.error_binding_allocation": "לא ניתן היה להקצות קישור חומר",
|
||||
"render.error_body_bound": "תצוגת המוצר חורגת ממגבלת הגופים שנקלטו",
|
||||
@@ -2247,8 +2140,6 @@
|
||||
"render.flare_strength": "עוצמת בוהק ההבהרות",
|
||||
"render.flare_threshold": "סף בוהק ליניארי של הסצנה",
|
||||
"render.fold_browser": "קפל דפדפן",
|
||||
"render.frame_presets": "הגדרות מסגרת מוכנות",
|
||||
"render.frame_presets_pending": "יגיע עם עורך הצילום",
|
||||
"render.gpu_depth": "עומק מסלול מרבי",
|
||||
"render.gpu_options_help": "הדגימות, העומק, הזרע והחשיפה נשמרים עם ההגדרה הזאת. נשמרת רק שיטת התמונה הפעילה.",
|
||||
"render.gpu_path_failed": "מעקב המסלולים ב־GPU אינו זמין",
|
||||
@@ -2309,36 +2200,6 @@
|
||||
"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": "השכבה מופעלת",
|
||||
@@ -2350,12 +2211,8 @@
|
||||
"render.lens_effects_title": "בהשראת עדשות וצילום",
|
||||
"render.library_all": "קטלוג",
|
||||
"render.library_all_states": "כל מצבי הזמינות",
|
||||
"render.library_appearance_available": "מראה זמין",
|
||||
"render.library_applicable": "משאבים ישימים",
|
||||
"render.library_applied": "השלכת המשאב הוחלה. יש לבדוק את בקשת ההדמיה המלאה לפני ההתחלה.",
|
||||
"render.library_apply_appearance": "החל מראה",
|
||||
"render.library_apply_hdr": "שמירה עם סביבת HDR זו",
|
||||
"render.library_apply_hint": "לחץ להחלה · גרור אל המודל",
|
||||
"render.library_apply_maps": "שימוש במפות משטח GPU מעוצבות",
|
||||
"render.library_apply_studio": "שמירת השלכת הסטודיו המעוצב",
|
||||
"render.library_apply_uniform": "החלת מראה אטום אחיד",
|
||||
@@ -2371,23 +2228,15 @@
|
||||
"render.library_extent": "היקף הנושא",
|
||||
"render.library_favorite": "הוספה למועדפים",
|
||||
"render.library_favorites": "מועדפים",
|
||||
"render.library_filters": "קטלוג וזמינות",
|
||||
"render.library_grid": "רשת",
|
||||
"render.library_hdr": "סביבות HDR מובנות",
|
||||
"render.library_hdr_help": "מטמיע את ה־HDR המקורי בהגדרה השמורה. ראשית קו האורך המעוצבת מומרת במפורש; אין צורך בקובץ חיצוני אחרי השמירה.",
|
||||
"render.library_in_design": "בתכנון",
|
||||
"render.library_inspect_only": "בחירה לבדיקה; דבר אינו מוחל אוטומטית. המועדפים וההיסטוריה האחרונה שייכים להפעלת המסמך הפתוחה הזאת.",
|
||||
"render.library_ior": "מקדם שבירה",
|
||||
"render.library_light_concepts": "מושגי תאורה",
|
||||
"render.library_list": "רשימה",
|
||||
"render.library_maps_help": "ניתן לשמור מפות מובנות עם מקורות מוצמדים ומיפוי מקומי של מופעים. דריסות אופטיות זמניות נשארות מצב נפרד.",
|
||||
"render.library_material_concepts": "מושגי חומר",
|
||||
"render.library_metallic": "מתכתיות",
|
||||
"render.library_native_scenes": "סטודיו מקוריים",
|
||||
"render.library_next": "הדף הבא",
|
||||
"render.library_omitted": "לא נכלל",
|
||||
"render.library_opacity": "אטימות",
|
||||
"render.library_other": "מראות אחרים",
|
||||
"render.library_physical_profiles": "פרופילי ייחוס פיזיקליים",
|
||||
"render.library_previous": "הדף הקודם",
|
||||
"render.library_quality_concepts": "מפרטי איכות",
|
||||
@@ -2397,7 +2246,6 @@
|
||||
"render.library_results": "משאבים תואמים",
|
||||
"render.library_rig_help": "התאמה פעם אחת במרכז מפורש ובהיקף פיזיקלי. מערכים מלאים כוללים כרטיסים סבילים; חסמי כיול עדיין דוחים. מרווח שמרני בין פולטים לכרטיסים נבדק לפני השמירה.",
|
||||
"render.library_rigs": "מערכי תאורה מעוצבים",
|
||||
"render.library_roughness": "חספוס",
|
||||
"render.library_save_maps": "שמירת המפות בהגדרה הזאת",
|
||||
"render.library_scene_bounds": "שימוש בגבולות הסצנה שנלכדו",
|
||||
"render.library_scenes": "מפרטי סצנה",
|
||||
@@ -2421,7 +2269,6 @@
|
||||
"render.library_unfavorite": "הסרה מהמועדפים",
|
||||
"render.library_uniform_ack": "שימוש בקירוב האחיד ללא אפקטים מרחביים או מתקדמים",
|
||||
"render.library_usable": "השלכות ניתנות להרצה",
|
||||
"render.library_users": "משתמשים",
|
||||
"render.light_anchor_pending": "שינוי מערכת הצירים של האור אינו נתמך עדיין.",
|
||||
"render.lights_fixed_to": "האורות מקובעים אל",
|
||||
"render.linear_help": "שומר מאסטר RGBA בנקודה צפה של 32 סיביות לפני הסרת רעש, חשיפה, מיפוי גוונים, מסננים ורקע. sRGB/D65 ליניארי; מוכפל מראש בכיסוי. זיכרון נוסף מאושר במפורש. ללא דחיסה או מעברים נוספים.",
|
||||
@@ -2505,34 +2352,12 @@
|
||||
"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": "מראה",
|
||||
@@ -2593,18 +2418,10 @@
|
||||
"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": "כיול מחדש",
|
||||
@@ -2613,7 +2430,6 @@
|
||||
"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": "הגרסה שנלכדה",
|
||||
@@ -2671,7 +2487,6 @@
|
||||
"render.scene_review": "פתרון וסקירה",
|
||||
"render.scene_review_help": "יש לסקור את הקומפוזיציה המקורית המלאה לפני ההחלה. מפרטי SCENE מתקדמים שאינם מקושרים אינם מוחלפים.",
|
||||
"render.scene_scope_details": "היקף הקומפוזיציה",
|
||||
"render.search_appearances": "חפש בין {count} מראות",
|
||||
"render.setup_legacy_environment": "הגדרה ישנה זו משתמשת בסיבוב או בעוצמה ישנים של הסביבה. הערכים האלה נשמרים; יש להמיר אותם במפורש לפני שמירת משימת תמונה מלאה.",
|
||||
"render.setup_name": "שם ההגדרה",
|
||||
"render.setup_numeric_help": "השמירה מתעדת את שיטת התמונה הפעילה ואת כל ההגדרות המספריות שלה. טעינה מחדש או בחירת הגדרה אחרת מחליפה את הטיוטה הזאת; את המצלמה יש לשחזר בנפרד.",
|
||||
@@ -2681,7 +2496,6 @@
|
||||
"render.sharpening": "חידוד פרטים",
|
||||
"render.shell_tab_appearance": "מראה רינדור",
|
||||
"render.shell_tab_camera": "מצלמת רינדור",
|
||||
"render.shell_tab_lighting": "תאורה",
|
||||
"render.shell_tab_scene": "סצנת רינדור",
|
||||
"render.shot_duplicate": "שכפול הצילום השמור",
|
||||
"render.shot_manager": "צילומים שמורים",
|
||||
@@ -2691,23 +2505,10 @@
|
||||
"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": "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 ליניארי",
|
||||
@@ -2720,9 +2521,6 @@
|
||||
"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": "קרינת סביבה קבועה",
|
||||
@@ -2761,10 +2559,8 @@
|
||||
"render.studio_sky_help": "מדרג שמיים מקורי לתאורה; לא מודל אטמוספרי מכויל.",
|
||||
"render.studio_sun": "שמש",
|
||||
"render.studio_sun_radius": "רדיוס זוויתי",
|
||||
"render.studio_tiles_require_gpu": "סטודיו דורש מעקב נתיב במעבד הגרפי. בחר GPU בעורך הפלט.",
|
||||
"render.studio_title": "סטודיו הדמיה",
|
||||
"render.studio_tone_map": "מיפוי גוונים",
|
||||
"render.studios_environments": "סטודיו וסביבות",
|
||||
"render.subsurface_enable": "הילוך אקראי לפיזור פנימי",
|
||||
"render.subsurface_help": "לדיאלקטרי סגור בלבד. פיזור ובליעת RGB הם מקדמים נפרדים לכל mm. המסלולים מתפזרים בתוך הגאומטריה בפועל, ואז נשברים או מוחזרים בגבול. יש לבטל העברת צל ישרה, הסרת רעש משטחית ונפח חיצוני; ולהשתמש בשכבת פרנל אחת. אינו שיידר דיפוזיה, אינו רשת צפיפות ואינו חומר מדוד. תווך צפוף עשוי לדרוש יותר דגימות ועומק מסלול.",
|
||||
"render.subsurface_rate": "קצב התנגשויות הפיזור",
|
||||
@@ -2818,7 +2614,6 @@
|
||||
"render.volume_max": "גבולות מרביים · mm בעולם",
|
||||
"render.volume_min": "גבולות מזעריים · mm בעולם",
|
||||
"render.volume_title": "נפח הומוגני תחום",
|
||||
"render.wait_add_light": "לא נתמך עדיין: יצירת אורות בעלי שם",
|
||||
"render.wait_batch": "לא נתמך עדיין: תור רינדור של צילומים שמורים",
|
||||
"render.wait_compare": "לא נתמך עדיין: השוואת תמונות שנשמרו",
|
||||
"render.wait_paint": "לא נתמך עדיין: בחירת מוצר וצביעת מראה",
|
||||
@@ -2873,7 +2668,6 @@
|
||||
"select two components": "יש לבחור שני רכיבים",
|
||||
"set by the sketch's lines": "נקבע לפי קווי הסקיצה",
|
||||
"settings.display_quality_note": "ברמות עדינות יותר, פאות ומקצועות מעוקלים מוצגים חלקים יותר ויצירת הרשת אורכת זמן רב יותר. רק אזור התצוגה משתנה, לא התכן ולא קובצי הייצוא.",
|
||||
"settings.double_click_components": "לחיצה כפולה על רכיב מפעילה אותו, או עורכת רכיב מקושר במקומו",
|
||||
"settings.frame_rate_details_note": "מוסיף גרף של זמני התמונות האחרונות עם המשולשים שצוירו מעליו, היסטוגרמה של זמני התמונות ואת מה שה־GPU צייר. לחיצה על קצב התמונות בתצוגה מציגה או מסתירה אותם.",
|
||||
"settings.frame_rate_note": "הצגת מספר הפריימים לשנייה בפינה הימנית התחתונה של התצוגה. כל עוד האפשרות פעילה, התצוגה מצוירת מחדש ברציפות.",
|
||||
"settings.measure_axis_distances": "הצגת מרחקי X/Y/Z",
|
||||
@@ -2886,7 +2680,6 @@
|
||||
"settings.reset_intro": "ההגדרות האלה לא פועלות במחשב הזה והיו מונעות מ־SolidCraft לעלות, ולכן הן שונו:",
|
||||
"settings.reset_title": "ההגדרות שונו",
|
||||
"settings.supersampling_note": "התצוגה מצוירת ברזולוציה גבוהה יותר ומסוננת כלפי מטה: מקצוות, קווים ורשת חלקים יותר במסכים שמתחת ל־4K. הדבר צורך זמן GPU: בערך 2.25× פיקסלים ב־1.5×, ו־4× ב־2×.",
|
||||
"settings.update_changed_links": "עדכון תכנים מקושרים שהשתנו כשלשונית מוצגת שוב",
|
||||
"settings.window_system_details": "פרטים",
|
||||
"settings.window_system_note_now": "ייכנס לתוקף אחרי הפעלה מחדש. כעת: {now}.",
|
||||
"settings.window_system_wayland_limited": "חלק מהתכונות מוגבלות ב־Wayland מקורי.",
|
||||
@@ -2933,10 +2726,6 @@
|
||||
"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": "הכיוון חייב להיות ציר, קו סקיצה או מקצוע ישר",
|
||||
@@ -2975,10 +2764,8 @@
|
||||
"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}. סימון פקודה מוסיף לה לחצן בסרגל הכלים. אפשר גם לגרור שורה מהרשימה הנפתחת של חלונית אל סרגל הכלים, לגרור לחצן אל מחוץ לסרגל הכלים כדי להסיר אותו, או להשתמש בנעץ שבשורה של רשימה נפתחת.",
|
||||
@@ -3389,8 +3176,6 @@
|
||||
"ui.cancel": "ביטול",
|
||||
"ui.canvases": "קנבסים",
|
||||
"ui.center": "מרכז",
|
||||
"ui.center_arc_follows_cursor": "קשת מרכז ונקודה עוקבת אחר כיוון הסמן",
|
||||
"ui.center_arc_follows_cursor_tip": "קשת מרכז ונקודה נמשכת בכיוון השעון או נגדו, לפי הכיוון שבו הסמן מסתובב סביב המרכז. כבוי: תמיד נגד כיוון השעון מנקודת ההתחלה.",
|
||||
"ui.centerline": "קו מרכז",
|
||||
"ui.chain": "שרשרת",
|
||||
"ui.chamfer": "פאזה",
|
||||
@@ -3498,24 +3283,6 @@
|
||||
"ui.easyprint_uploading": "מעלה ל־Printables…",
|
||||
"ui.edit": "עריכה",
|
||||
"ui.editJointLimits": "עריכת גבולות מפרק",
|
||||
"ui.edit_in_place": "עריכה במקום",
|
||||
"ui.edit_in_place_after_turn": "העריכה תסתיים כשתור הסוכן יסתיים",
|
||||
"ui.edit_in_place_agent_busy": "הסוכן עדיין עובד בתכן הזה; יש לנסות שוב כשתורו יסתיים",
|
||||
"ui.edit_in_place_already_queued": "עריכה כבר ממתינה לסיום תור הסוכן",
|
||||
"ui.edit_in_place_assembly_closed": "{name} כבר אינו פתוח",
|
||||
"ui.edit_in_place_banner": "עריכה בתוך {name}",
|
||||
"ui.edit_in_place_busy": "יש לסיים או לבטל קודם את מה שפתוח",
|
||||
"ui.edit_in_place_none": "אין תכן מקושר בעריכה במקום",
|
||||
"ui.edit_in_place_not_saved": "בחלק יש שינויים שלא נשמרו; יש לשמור כדי לסיים את העריכה",
|
||||
"ui.edit_in_place_open_elsewhere": "{name} פתוח במופע אחר של SolidCraft",
|
||||
"ui.edit_in_place_opens_after_turn": "החלק ייפתח כשתור הסוכן יסתיים",
|
||||
"ui.edit_in_place_refresh_dropped": "ההרכבה השתנתה בזמן עדכון הקישורים; יש להריץ שוב",
|
||||
"ui.edit_in_place_refresh_stopped": "עדכון הקישורים נעצר לפני שהסתיים",
|
||||
"ui.edit_in_place_refreshing": "מעדכן קישורים ב-{name}…",
|
||||
"ui.edit_in_place_save_first": "יש לשמור קודם את ההרכבה: התכנים המקושרים שלה נמצאים לפי הקובץ שלה",
|
||||
"ui.edit_in_place_save_waits": "עדכון הקישורים פועל ב-{name}; יש לשמור לאחר שיסתיים",
|
||||
"ui.edit_in_place_saved_elsewhere": "החלק נשמר לקובץ אחר; ההרכבה עדיין מקושרת אל {name}",
|
||||
"ui.edit_in_place_wait": "יש להמתין עד שמה שפועל בתכן יסתיים",
|
||||
"ui.edit_section_analysis": "עריכת ניתוח חתך",
|
||||
"ui.ellipse": "אליפסה",
|
||||
"ui.emboss_wrap_note": "פרופילי קווים ניצבים המשיקים לגליל נתמך נכרכים אוטומטית. יש לבחור דופן כדי לקבוע יעד.",
|
||||
@@ -3590,7 +3357,6 @@
|
||||
"ui.heading_edit": "עריכה",
|
||||
"ui.heading_extrude": "שליפה",
|
||||
"ui.heading_fillet": "עיגול פינה",
|
||||
"ui.heading_finish_editing": "סיום עריכה",
|
||||
"ui.heading_finish_sketch": "סיום סקיצה",
|
||||
"ui.heading_form": "צורה",
|
||||
"ui.heading_front": "חזית",
|
||||
@@ -3803,15 +3569,6 @@
|
||||
"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": "מצולע",
|
||||
@@ -3859,8 +3616,6 @@
|
||||
"ui.restore_defaults": "שחזור ברירות המחדל",
|
||||
"ui.reverse_horizontal_pan_direction": "היפוך כיוון ההזזה האופקית",
|
||||
"ui.reverse_horizontal_pan_tip": "גלילה אופקית בשתי אצבעות וגלגל האגודל מזיזים את המבט לכיוון ההפוך",
|
||||
"ui.reverse_vertical_pan_direction": "היפוך כיוון ההזזה האנכית",
|
||||
"ui.reverse_vertical_pan_tip": "גלילה אנכית בשתי אצבעות מזיזה בכיוון ההפוך",
|
||||
"ui.reverse_zoom_direction": "היפוך כיוון הזום",
|
||||
"ui.revolutions": "סיבובים",
|
||||
"ui.revolve": "סיבוב",
|
||||
@@ -4006,15 +3761,6 @@
|
||||
"ui.updateToCurrentView": "עדכון למבט הנוכחי",
|
||||
"ui.upload": "העלאה",
|
||||
"ui.vertical_constraint": "אילוץ אנכי",
|
||||
"ui.view_angles": "זוויות תצוגה",
|
||||
"ui.view_angles_convention": "מעלות סביב הציר העליון. סבסוב 0° הוא התצוגה הקדמית, 90° שמאלית, −90° ימנית, 180° אחורית; עלרוד 90° הוא התצוגה העליונה; גלגול חיובי מסובב את המודל על המסך עם כיוון השעון.",
|
||||
"ui.view_angles_help": "מציג מתחת לגיזמו הכיוון את זוויות העלרוד, הסבסוב והגלגול של התצוגה במעלות ואת סוג ההטלה. לחצו על התצוגה כדי להקליד זוויות מדויקות.",
|
||||
"ui.view_angles_ortho": "אורתוגרפי",
|
||||
"ui.view_angles_persp": "פרספקטיבה",
|
||||
"ui.view_angles_pitch": "עלרוד",
|
||||
"ui.view_angles_readout": "עלרוד {pitch} · סבסוב {yaw} · גלגול {roll}",
|
||||
"ui.view_angles_roll": "גלגול",
|
||||
"ui.view_angles_yaw": "סבסוב",
|
||||
"ui.view_centre": "מרכז המבט",
|
||||
"ui.viewport_theme_follow": "לפי ערכת הנושא",
|
||||
"ui.visual_shaded_hidden_edges": "מוצלל עם מקצועות נסתרים",
|
||||
@@ -4065,13 +3811,11 @@
|
||||
"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": "פתיחת דף הגרסה",
|
||||
@@ -4088,7 +3832,6 @@
|
||||
"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
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
+29
-286
@@ -38,7 +38,6 @@
|
||||
"All open designs": "すべての開いているデザイン",
|
||||
"Allowed expressions, separated by semicolons": "使用可能な式(セミコロンで区切る)",
|
||||
"Also deletes": "同時に削除される要素:",
|
||||
"Also place them from the origin (fully constrained)": "原点からの位置も付ける(完全拘束)",
|
||||
"Anchor": "基準点",
|
||||
"Angle 2": "角度 2",
|
||||
"Angle Display": "角度の表示",
|
||||
@@ -181,9 +180,6 @@
|
||||
"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": "破棄",
|
||||
"Distance": "距離",
|
||||
@@ -200,8 +196,6 @@
|
||||
"Drag to turn the rest in 5° steps. Hold Alt or Ctrl to turn it freely.": "ドラッグしてレストを 5° 刻みで回転します。Alt または Ctrl を押したままにすると自由に回転します。",
|
||||
"Drive Joint": "ジョイントを駆動",
|
||||
"Duration": "時間",
|
||||
"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.": "新しい図形には、自由な部分がなくなるまでスケッチの原点からの寸法も付きます。原点や配置済みの形状にすでにつながっている図形には付きません。",
|
||||
"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.": "描いた図形に、描いたサイズですべての寸法が付きます(入力した値が優先されます)。スケッチですでに決まっているサイズは省かれます。",
|
||||
"Edge": "エッジ",
|
||||
"Edges": "エッジ",
|
||||
"Edit": "編集",
|
||||
@@ -260,8 +254,6 @@
|
||||
"Flanges": "フランジ",
|
||||
"Flat Pattern": "フラットパターン",
|
||||
"Flat size": "展開サイズ",
|
||||
"Flat size X": "展開サイズ X",
|
||||
"Flat size Y": "展開サイズ Y",
|
||||
"Follow camera (Look At sketches)": "カメラ追従(スケッチを正面視)",
|
||||
"Follow the camera (Look At sketches)": "カメラ追従(スケッチを正面視)",
|
||||
"Font": "フォント",
|
||||
@@ -595,7 +587,6 @@
|
||||
"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": "開始側の膨らみ",
|
||||
@@ -763,7 +754,6 @@
|
||||
"appearance.pbr_opaque": "不透明PBR",
|
||||
"appearance.roughness": "粗さ",
|
||||
"appearance.surface_response": "表面の応答",
|
||||
"at the face": "面の位置",
|
||||
"bom.group_children": "サブパーツを親パーツの行に含める",
|
||||
"browser.body_name": "ボディ{number}",
|
||||
"browser.calibrate_title": "尺度を設定: {name}",
|
||||
@@ -772,7 +762,6 @@
|
||||
"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}",
|
||||
@@ -823,9 +812,7 @@
|
||||
"cmd.component_create": "新規コンポーネント",
|
||||
"cmd.component_cycle_color": "コンポーネントの色を順に変更",
|
||||
"cmd.component_delete": "コンポーネントを削除",
|
||||
"cmd.component_edit_in_place": "その場で編集",
|
||||
"cmd.component_extract": "ファイルに抽出",
|
||||
"cmd.component_finish_edit": "編集を終了",
|
||||
"cmd.component_from_bodies": "ボディからコンポーネントを作成",
|
||||
"cmd.component_insert_design": "デザインを挿入",
|
||||
"cmd.component_links": "リンクを一覧表示",
|
||||
@@ -1000,10 +987,6 @@
|
||||
"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": "すべてのショットをレンダリング",
|
||||
@@ -1018,28 +1001,23 @@
|
||||
"cmd.render_library_describe": "レンダリングリソースを調査",
|
||||
"cmd.render_library_environment_apply": "組み込みHDR環境を適用",
|
||||
"cmd.render_library_list": "レンダリングライブラリを参照",
|
||||
"cmd.render_library_physical_prepare": "物性参照を準備",
|
||||
"cmd.render_library_physical_prepare": "Prepare Physical Reference",
|
||||
"cmd.render_library_preview": "元の表面マップを確認",
|
||||
"cmd.render_library_studio_apply": "作成済みスタジオ照明を適用",
|
||||
"cmd.render_library_studio_prepare": "作成済みスタジオ照明を準備",
|
||||
"cmd.render_library_surface_bind": "サーフェス割り当てを保存",
|
||||
"cmd.render_library_surface_bind": "Save Surface Binding",
|
||||
"cmd.render_library_surface_prepare": "作成済みサーフェスマップを準備",
|
||||
"cmd.render_library_surface_unbind": "サーフェス割り当てを削除",
|
||||
"cmd.render_library_surface_unbind": "Remove Surface Binding",
|
||||
"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とレンダリング設定を保存",
|
||||
@@ -1049,8 +1027,8 @@
|
||||
"cmd.render_shot_remove": "レンダーショットを削除",
|
||||
"cmd.render_shot_rename": "レンダーショットの名前を変更",
|
||||
"cmd.render_stage_backdrop": "物理レンダー背景を保存",
|
||||
"cmd.render_stage_clear": "レンダーステージを削除",
|
||||
"cmd.render_stage_ground": "レンダーの床を保存",
|
||||
"cmd.render_stage_clear": "Remove Render Stage",
|
||||
"cmd.render_stage_ground": "Save Render Ground",
|
||||
"cmd.render_start": "画像をレンダリング",
|
||||
"cmd.render_status": "レンダリングの進行状況",
|
||||
"cmd.select_clear": "選択をクリア",
|
||||
@@ -1142,7 +1120,6 @@
|
||||
"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": "従動寸法の切り替え",
|
||||
@@ -1194,7 +1171,6 @@
|
||||
"cmd.view.render_dock": "レンダードックを移動",
|
||||
"cmd.view.render_focus": "Renderフォーカス表示",
|
||||
"cmd.view_ambient_occlusion": "アンビエントオクルージョン",
|
||||
"cmd.view_angle_readout": "ビュー角度",
|
||||
"cmd.view_apply": "名前付きビューを復元",
|
||||
"cmd.view_back": "背面ビュー",
|
||||
"cmd.view_bottom": "下面ビュー",
|
||||
@@ -1270,7 +1246,6 @@
|
||||
"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 を押してください。",
|
||||
@@ -1330,7 +1305,6 @@
|
||||
"help.appearance.assign": "選択した立体、面、コンポーネントに、重さを変えずに色や材質の見た目を付けます。部品を見分けるときや仕上げを確認するときに使います。",
|
||||
"help.attributions": "帰属表示 (ATTRIBUTION.md)",
|
||||
"help.build": "バージョン {version} · コミット {commit} · ビルド日 {date}",
|
||||
"help.component.finish_edit": "その場で編集中の部品を保存し、アセンブリに戻って、その部品でアセンブリを更新します。リンクされた部品への変更が終わったときに使います。",
|
||||
"help.connect_note": "各アシスタントを再起動すると SolidCraft サーバーが読み込まれます。変更したファイルは .bak としてバックアップされています。",
|
||||
"help.connect_title": "AI アシスタントを接続",
|
||||
"help.face.delete": "選択した面を削除し、隣の面でその穴をふさぎます。穴を開いたまま面として残すこともできます。じゃまな丸み、穴、突起を取り除くときに使います。",
|
||||
@@ -1341,7 +1315,6 @@
|
||||
"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つの立体が重なる範囲の確認に使います。",
|
||||
@@ -1367,8 +1340,6 @@
|
||||
"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": "この個人用プリセットを削除しますか?既存の穴は変更されません。",
|
||||
@@ -1736,8 +1707,6 @@
|
||||
"prefs.anti_aliasing_note": "輪郭のぎざぎざを滑らかにします。",
|
||||
"prefs.applies_on_restart": "再起動後に適用",
|
||||
"prefs.assistant": "アシスタント",
|
||||
"prefs.auto_operation": "操作を自動で選択",
|
||||
"prefs.auto_operation_note": "フィーチャーのウィンドウは、新しい形状が何に接するかから結合・新規ボディ・切り取りを選びます(手動で選ぶまで)。オフ:開いたときの操作のままです。",
|
||||
"prefs.axes": "軸",
|
||||
"prefs.axis": "軸",
|
||||
"prefs.bodies_folders": "ボディフォルダー",
|
||||
@@ -1813,19 +1782,8 @@
|
||||
"prefs.layout_note": "コンパクトではパネルをウィンドウの端に固定します。ブラウザーの半透明表示はフローティングで使用できます。アシスタントの配置はアシスタントページで変更します。",
|
||||
"prefs.left": "左",
|
||||
"prefs.left_click": "左クリック",
|
||||
"prefs.linked_designs": "リンクされたデザイン",
|
||||
"prefs.live_input": "現在の入力",
|
||||
"prefs.live_preview": "ライブプレビュー",
|
||||
"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": "マウスとトラックパッド",
|
||||
@@ -1874,24 +1832,11 @@
|
||||
"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": "中心・中点の整列ガイド",
|
||||
"prefs.sketch_tracking_guides_note": "円の中心や直線辺の中点にカーソルを合わせ、水平・垂直に移動すると一時的な点線ガイドを表示します。",
|
||||
"prefs.sketching": "スケッチ",
|
||||
"prefs.snap_drawing_note": "スケッチの描画中または点のドラッグ中に使用します。",
|
||||
"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": "選択中",
|
||||
@@ -1899,16 +1844,12 @@
|
||||
"prefs.supersampling_brief": "大きく描画して縮小するため、非常に鮮明になりますが負荷も増えます。",
|
||||
"prefs.tab_accent_note": "アクセント色はアクティブなタブに従います",
|
||||
"prefs.theme_file": "テーマファイル",
|
||||
"prefs.theme_file_or_paste": "ファイルまたは貼り付け",
|
||||
"prefs.theme_follow": "テーマファイルに従う",
|
||||
"prefs.theme_follow_error": "適用できません: {error}。最後に正常に適用されたテーマを維持します。",
|
||||
"prefs.theme_follow_launch": "今回の起動時のみ有効です (--theme-file)。別のツールがファイルを書き換えるたびに再読み込みします。",
|
||||
"prefs.theme_follow_note": "別のツールがファイルを書き換えるたびに再読み込みします。テーマを選ぶと追従を停止します。",
|
||||
"prefs.theme_follow_off": "オフ",
|
||||
"prefs.theme_follow_stop": "追従を停止",
|
||||
"prefs.theme_import_hint": "クリックしてテーマファイルを選ぶか、ここにドロップするか、テーマをコピーした状態で Ctrl+V(Mac では Cmd+V)を押します",
|
||||
"prefs.theme_paste": "テーマを貼り付け",
|
||||
"prefs.theme_paste_none": "クリップボードに SolidCraft のテーマがありません",
|
||||
"prefs.timeline_half_hour": "−30 分",
|
||||
"prefs.timeline_now": "現在",
|
||||
"prefs.timeline_quarter_hour": "−15",
|
||||
@@ -1943,7 +1884,7 @@
|
||||
"render.adaptive_pending": "GPU の独立予備サンプルによる適応配分と条件付きフレネル層化が利用できます。収束の認証、まれな事象の保証、CPU 適応輸送は未対応です。",
|
||||
"render.adaptive_pilots": "ピクセルごとの予備サンプル",
|
||||
"render.adaptive_relative": "RGB 相対誤差の目標",
|
||||
"render.add_ground": "フィットした床をセットアップに追加",
|
||||
"render.add_ground": "Add fitted ground to setup",
|
||||
"render.advanced_material_scope": "明示的な制限付きで、有限の均一 GPU 媒質を利用できます。密度グリッド、ガラスとの入れ子、表面の内部散乱、ディスプレイスメント、分光輸送、未接続の高度な素材は未対応です。",
|
||||
"render.aging_add": "エイジング層を追加",
|
||||
"render.aging_airborne": "空中のほこり・散乱媒質",
|
||||
@@ -1974,47 +1915,7 @@
|
||||
"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": "ネイティブ構成。適用前に確認してください",
|
||||
"render.availability_projection": "対応する部分適用。範囲と省略項目を確認してください",
|
||||
"render.availability_reference": "参照データ。自動割り当てではありません",
|
||||
@@ -2069,15 +1970,8 @@
|
||||
"render.camera_set": "レンダーカメラを設定",
|
||||
"render.camera_target": "ターゲット位置・mm",
|
||||
"render.camera_yaw": "ヨー",
|
||||
"render.category_metal": "金属",
|
||||
"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.clear_stage": "Remove saved stage",
|
||||
"render.copy_request": "JSONをコピー",
|
||||
"render.cpu_aa_help": "空間サンプル数を増やすと輪郭が滑らかになりますが、描画時間も増えます。",
|
||||
"render.cpu_area_add": "CPU矩形ライトを追加",
|
||||
@@ -2175,7 +2069,6 @@
|
||||
"render.effects_reset_lens": "レンズ効果をリセット",
|
||||
"render.effects_seed": "パターンシード",
|
||||
"render.emitted_axis": "照射軸",
|
||||
"render.environment_tiles_require_raytrace": "環境には CPU または GPU レイトレーシングが必要です。出力エディターで選択してください。",
|
||||
"render.error_area_samples": "area_samplesは1、4、16、64のいずれかです",
|
||||
"render.error_binding_allocation": "マテリアルの割り当て領域を確保できません",
|
||||
"render.error_body_bound": "製品プレビューが取得ボディ数の上限を超えています",
|
||||
@@ -2247,8 +2140,6 @@
|
||||
"render.flare_strength": "ハイライトフレア強度",
|
||||
"render.flare_threshold": "シーンリニアしきい値",
|
||||
"render.fold_browser": "ブラウザーを折りたたむ",
|
||||
"render.frame_presets": "構図プリセット",
|
||||
"render.frame_presets_pending": "ショットエディターとともに追加予定",
|
||||
"render.gpu_depth": "最大パス深度",
|
||||
"render.gpu_options_help": "サンプル数、深度、シード、露出をこの設定に保存します。保存するのは選択中の画像方式だけです。",
|
||||
"render.gpu_path_failed": "GPUパストレーサーを利用できません",
|
||||
@@ -2305,40 +2196,10 @@
|
||||
"render.image_unknown": "レンダリング状態を取得できません",
|
||||
"render.image_use_largest": "この縦横比での最大サイズを使用:",
|
||||
"render.image_width": "幅",
|
||||
"render.include_rig_cards": "受動的なカードとフラッグを含める",
|
||||
"render.include_rig_cards": "Include passive cards and flags",
|
||||
"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": "層を有効にする",
|
||||
@@ -2350,12 +2211,8 @@
|
||||
"render.lens_effects_title": "レンズ風・写真効果",
|
||||
"render.library_all": "カタログ",
|
||||
"render.library_all_states": "すべての利用可能状態",
|
||||
"render.library_appearance_available": "利用可能な外観",
|
||||
"render.library_applicable": "適用に適したリソース",
|
||||
"render.library_applied": "リソース変換を適用しました。開始前にレンダリング要求全体を確認してください。",
|
||||
"render.library_apply_appearance": "外観を適用",
|
||||
"render.library_apply_hdr": "このHDR環境と一緒に保存",
|
||||
"render.library_apply_hint": "クリックで適用 · モデルにドラッグ",
|
||||
"render.library_apply_maps": "作成済みGPUサーフェスマップを使用",
|
||||
"render.library_apply_studio": "作成済みスタジオ変換を保存",
|
||||
"render.library_apply_uniform": "均一な不透明外観を適用",
|
||||
@@ -2371,34 +2228,25 @@
|
||||
"render.library_extent": "対象の大きさ",
|
||||
"render.library_favorite": "お気に入りに追加",
|
||||
"render.library_favorites": "お気に入り",
|
||||
"render.library_filters": "カタログと利用状況",
|
||||
"render.library_grid": "グリッド",
|
||||
"render.library_hdr": "組み込みHDR環境",
|
||||
"render.library_hdr_help": "元のHDRを保存設定に埋め込みます。作成時の経度原点を明示的に変換します。保存後は外部ファイルが不要です。",
|
||||
"render.library_in_design": "デザイン内",
|
||||
"render.library_inspect_only": "選択すると詳細を確認できます。自動適用はされません。お気に入りと履歴は、この開いている文書のセッションに属します。",
|
||||
"render.library_ior": "屈折率",
|
||||
"render.library_light_concepts": "照明構想",
|
||||
"render.library_list": "リスト",
|
||||
"render.library_maps_help": "組み込みマップは、固定したソースとローカルの配置マッピングとともに保存できます。一時的な光学上書きは別のモードのままです。",
|
||||
"render.library_maps_help": "Built-in maps can be saved with pinned sources and local occurrence mapping. Temporary optical overrides remain a separate mode.",
|
||||
"render.library_material_concepts": "マテリアル構想",
|
||||
"render.library_metallic": "金属度",
|
||||
"render.library_native_scenes": "ネイティブスタジオ",
|
||||
"render.library_next": "次のページ",
|
||||
"render.library_omitted": "含まれない項目",
|
||||
"render.library_opacity": "不透明度",
|
||||
"render.library_other": "その他の外観",
|
||||
"render.library_physical_profiles": "物性参照プロファイル",
|
||||
"render.library_physical_profiles": "Physical reference profiles",
|
||||
"render.library_previous": "前のページ",
|
||||
"render.library_quality_concepts": "品質仕様",
|
||||
"render.library_recent": "最近使用した項目",
|
||||
"render.library_replace_environment": "既存の環境を明示的に置換",
|
||||
"render.library_replace_studio": "保存済みの照明・環境・ステージを置換",
|
||||
"render.library_replace_studio": "Replace saved lighting, environment and stage",
|
||||
"render.library_results": "一致するリソース",
|
||||
"render.library_rig_help": "明示した中心と物理的な大きさで一度だけフィットします。完全なリグには受動的なカードが含まれ、校正が必要な場合は引き続き拒否されます。保存前に、光源とカードの保守的なクリアランスを確認します。",
|
||||
"render.library_rig_help": "Fit once at an explicit center and physical extent. Complete rigs include passive cards; calibration blockers still refuse. Conservative emitter/card clearance is checked before saving.",
|
||||
"render.library_rigs": "作成済み照明リグ",
|
||||
"render.library_roughness": "粗さ",
|
||||
"render.library_save_maps": "マップをこのセットアップに保存",
|
||||
"render.library_save_maps": "Save maps to this setup",
|
||||
"render.library_scene_bounds": "取得したシーン境界を使用",
|
||||
"render.library_scenes": "シーン仕様",
|
||||
"render.library_search": "ライブラリを検索",
|
||||
@@ -2421,7 +2269,6 @@
|
||||
"render.library_unfavorite": "お気に入りから削除",
|
||||
"render.library_uniform_ack": "空間変化や高度な効果のない均一近似を使用",
|
||||
"render.library_usable": "実行可能な部分適用",
|
||||
"render.library_users": "使用箇所",
|
||||
"render.light_anchor_pending": "ライトの座標系の変更はまだサポートされていません。",
|
||||
"render.lights_fixed_to": "ライトの固定先",
|
||||
"render.linear_help": "ノイズ除去、露出、トーンマッピング、フィルター、背景処理より前の32ビット浮動小数点RGBAを保持します。線形sRGB/D65で、被覆率を乗算した値です。追加メモリを明示的に見積もります。圧縮や追加パスはありません。",
|
||||
@@ -2505,34 +2352,12 @@
|
||||
"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": "外観",
|
||||
@@ -2546,20 +2371,20 @@
|
||||
"render.page_scene": "シーン",
|
||||
"render.paint_label": "ペイント",
|
||||
"render.passive_stage": "Render-only stage",
|
||||
"render.passive_stage_help": "有限の受動パネルはレンダリング画像にのみ属します。CADボディや質量は追加しません。配置は適用後に固定され、製品の境界と交差する場合は拒否されます。CPUでは拡散バウンス照明は得られません。",
|
||||
"render.passive_stage_help": "Finite passive panels belong only to rendered images. They do not add CAD bodies or mass. Placement is fixed after applying; intersecting product bounds refuse. CPU does not provide diffuse bounce lighting.",
|
||||
"render.perspective": "透視投影カメラ",
|
||||
"render.physical_acknowledge": "この部分的な参照にはグレード・状態・出典の確認が必要であることを理解しました",
|
||||
"render.physical_associations": "物性の候補と役割",
|
||||
"render.physical_acknowledge": "I understand this partial reference needs grade, condition and source review",
|
||||
"render.physical_associations": "Physical candidates and roles",
|
||||
"render.physical_background_help": "カメラ背景とは別の実際の受動的な床と壁です。サイクロラマは24枚の多角形帯で近似し、無限面や解析的な滑面ではありません。保存済みステージの置換には確認が必要です。",
|
||||
"render.physical_copy_plan": "出典と省略項目を含む計画をコピー",
|
||||
"render.physical_copy_plan": "Copy plan with sources and omissions",
|
||||
"render.physical_cyclorama": "多角形サイクロラマをフィット",
|
||||
"render.physical_floor_wall": "物理的な床と壁をフィット",
|
||||
"render.physical_material_name": "提案する参照マテリアル名",
|
||||
"render.physical_omissions": "ネイティブのマテリアルカードで表現できない特性",
|
||||
"render.physical_plan_only": "読み取り専用の計画を準備しました。マテリアルの作成や割り当ては行っていません。ネイティブのマテリアルカードを確認する際は、拡張プロファイルと省略項目も保持してください。",
|
||||
"render.physical_prepare_plan": "作成計画を準備(適用はしません)",
|
||||
"render.physical_separate": "外観から材料を特定することはできません。これらは出典付きの参照であり、認証された実効特性ではありません。既存の質量、工学材料、光学設定は変更されません。",
|
||||
"render.physical_unknown": "数値の候補は確定していません。グレード、配合、または構成を指定する必要があります。不明はゼロではありません。",
|
||||
"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.pick_body": "ボディ",
|
||||
"render.pick_component": "コンポーネント",
|
||||
"render.pick_face": "面",
|
||||
@@ -2593,27 +2418,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": "この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": "この対象の既存の保存済み割り当てを置換",
|
||||
"render.replace_saved_surface": "Replace an existing saved binding for this target",
|
||||
"render.restore_camera": "保存したカメラを復元",
|
||||
"render.result_actual": "100%",
|
||||
"render.result_actual_help": "画像の1ピクセルを画面の1ピクセルで表示します。",
|
||||
"render.result_actual_size": "100%",
|
||||
"render.result_capture": "キャプチャ時のリビジョン",
|
||||
@@ -2659,10 +2475,10 @@
|
||||
"render.save_continue": "保存して続行",
|
||||
"render.save_setup": "セットアップを保存",
|
||||
"render.save_shot": "ショットを保存",
|
||||
"render.saved_maps_runtime_conflict": "保存済みサーフェス割り当ては、一時的な光学上書きや未保存のHDRと併用できません。HDRを保存するか、一時的な入力を明示的に削除してください。",
|
||||
"render.saved_surface_remove": "保存済み割り当てを削除",
|
||||
"render.saved_surfaces": "保存済みサーフェス割り当て",
|
||||
"render.saved_surfaces_help": "固定した組み込みマップは、選択した配置にローカルのミリメートル単位で追従します。置換または欠落した対象はエラーとなり、自動的に再割り当てされることはありません。",
|
||||
"render.saved_maps_runtime_conflict": "Saved surface bindings cannot be mixed with temporary optical overrides or an unsaved HDR. Save the HDR or remove the temporary inputs explicitly.",
|
||||
"render.saved_surface_remove": "Remove saved binding",
|
||||
"render.saved_surfaces": "Saved surface bindings",
|
||||
"render.saved_surfaces_help": "Pinned built-in maps follow the selected occurrence in local millimeters. A replaced or missing target is an error, never an automatic reassignment.",
|
||||
"render.scene_apply": "確認したスタジオを適用",
|
||||
"render.scene_consent": "照明・環境・露出・ステージの置換を確認",
|
||||
"render.scene_preserved_fields": "カメラ、サンプル設定、画像寸法、光学割り当て、CAD形状、工学材料は保持されます。",
|
||||
@@ -2671,7 +2487,6 @@
|
||||
"render.scene_review": "解決して確認",
|
||||
"render.scene_review_help": "適用前にネイティブ構成の全体を確認してください。未接続の高度なSCENE仕様への暗黙の置換はありません。",
|
||||
"render.scene_scope_details": "構成の範囲",
|
||||
"render.search_appearances": "{count} 件の外観を検索",
|
||||
"render.setup_legacy_environment": "この旧設定には従来の環境回転または強度が含まれています。値は保持されます。画像ジョブ全体を保存する前に明示的に変換してください。",
|
||||
"render.setup_name": "セットアップ名",
|
||||
"render.setup_numeric_help": "保存すると、選択中の画像方式とその数値設定をすべて保存します。再読み込みや別の設定の選択で下書きを置き換えます。カメラは別途復元します。",
|
||||
@@ -2681,7 +2496,6 @@
|
||||
"render.sharpening": "ディテールのシャープ化",
|
||||
"render.shell_tab_appearance": "外観",
|
||||
"render.shell_tab_camera": "カメラ",
|
||||
"render.shell_tab_lighting": "ライティング",
|
||||
"render.shell_tab_scene": "シーン",
|
||||
"render.shot_duplicate": "保存済みショットを複製",
|
||||
"render.shot_manager": "保存済みショット",
|
||||
@@ -2691,23 +2505,10 @@
|
||||
"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": "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",
|
||||
@@ -2718,11 +2519,8 @@
|
||||
"render.stage_look": "外観",
|
||||
"render.stage_orthographic": "平行投影",
|
||||
"render.stage_output": "出力",
|
||||
"render.stage_panel_count": "保存済みパネル",
|
||||
"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_add_area": "エリアライトを追加",
|
||||
"render.studio_ambient": "一定の環境放射輝度",
|
||||
@@ -2761,10 +2559,8 @@
|
||||
"render.studio_sky_help": "照明用のオリジナル空グラデーションです。校正された大気モデルではありません。",
|
||||
"render.studio_sun": "太陽",
|
||||
"render.studio_sun_radius": "角半径",
|
||||
"render.studio_tiles_require_gpu": "スタジオには GPU パストレーシングが必要です。出力エディターで GPU を選択してください。",
|
||||
"render.studio_title": "レンダースタジオ",
|
||||
"render.studio_tone_map": "トーンマッピング",
|
||||
"render.studios_environments": "スタジオと環境",
|
||||
"render.subsurface_enable": "内部散乱ランダムウォーク",
|
||||
"render.subsurface_help": "閉じた誘電体専用。散乱とRGB吸収は別々の毎mm係数です。実際の形状内で散乱し、境界で屈折・反射します。直線透過影、表面ノイズ除去、外部ボリュームを無効にし、フレネル層数を1にします。拡散シェーダー、密度グリッド、実測物性ではありません。高密度ではサンプル数と深度が必要です。",
|
||||
"render.subsurface_rate": "散乱衝突率",
|
||||
@@ -2818,7 +2614,6 @@
|
||||
"render.volume_max": "最大境界 · ワールド mm",
|
||||
"render.volume_min": "最小境界 · ワールド mm",
|
||||
"render.volume_title": "有限の均一ボリューム",
|
||||
"render.wait_add_light": "未対応:名前付きライトの作成",
|
||||
"render.wait_batch": "未対応: 保存ショットのレンダーキュー",
|
||||
"render.wait_compare": "未対応: 保持された画像の比較",
|
||||
"render.wait_paint": "未対応: 製品の選択と外観のペイント",
|
||||
@@ -2873,7 +2668,6 @@
|
||||
"select two components": "2つのコンポーネントを選択してください",
|
||||
"set by the sketch's lines": "スケッチの線で決まります",
|
||||
"settings.display_quality_note": "表示だけに影響します。設計や書き出しは変わりません。",
|
||||
"settings.double_click_components": "コンポーネントをダブルクリックしてアクティブにする、またはリンクされたものをその場で編集する",
|
||||
"settings.frame_rate_details_note": "直近のフレーム時間のグラフ(描画した三角形数を重ねて表示)、フレーム時間のヒストグラム、GPU が描画した内容を追加します。ビューのフレームレートをクリックすると表示/非表示を切り替えます。",
|
||||
"settings.frame_rate_note": "ビューの右下に 1 秒あたりのフレーム数を表示します。",
|
||||
"settings.measure_axis_distances": "計測のX・Y・Z距離を表示",
|
||||
@@ -2886,7 +2680,6 @@
|
||||
"settings.reset_intro": "次の設定はこのコンピューターでは使用できず、SolidCraft が起動できなくなるため変更されました:",
|
||||
"settings.reset_title": "設定が変更されました",
|
||||
"settings.supersampling_note": "ビューを高い解像度で描画してから縮小します。4K 未満の画面でエッジ、線、グリッドが滑らかになります。GPU の負荷が増えます(1.5× で約 2.25 倍、2× で 4 倍のピクセル)。",
|
||||
"settings.update_changed_links": "タブに戻ったときに変更されたリンク先デザインを更新",
|
||||
"settings.window_system_details": "詳細",
|
||||
"settings.window_system_note_now": "再起動後に適用されます。現在: {now}。",
|
||||
"settings.window_system_wayland_limited": "ネイティブ Wayland では一部の機能が制限されます。",
|
||||
@@ -2933,10 +2726,6 @@
|
||||
"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": "方向は、軸、スケッチ線、または直線のエッジにしてください",
|
||||
@@ -2975,10 +2764,8 @@
|
||||
"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}タブ。コマンドにチェックを入れると、ツールバーにボタンが追加されます。パネルのドロップダウンの行をツールバーにドラッグしたり、ボタンをツールバーの外にドラッグして削除したり、ドロップダウンの行のピンを使うこともできます。",
|
||||
@@ -3389,8 +3176,6 @@
|
||||
"ui.cancel": "キャンセル",
|
||||
"ui.canvases": "キャンバス",
|
||||
"ui.center": "中心",
|
||||
"ui.center_arc_follows_cursor": "中心点円弧はカーソルの向きに従う",
|
||||
"ui.center_arc_follows_cursor_tip": "中心点円弧は、カーソルが中心の周りを回る向きに合わせて時計回りまたは反時計回りに描かれます。オフ: 常に始点から反時計回りです。",
|
||||
"ui.centerline": "中心線",
|
||||
"ui.chain": "チェーン",
|
||||
"ui.chamfer": "面取り",
|
||||
@@ -3498,24 +3283,6 @@
|
||||
"ui.easyprint_uploading": "Printables にアップロード中…",
|
||||
"ui.edit": "編集",
|
||||
"ui.editJointLimits": "ジョイント制限を編集",
|
||||
"ui.edit_in_place": "その場で編集",
|
||||
"ui.edit_in_place_after_turn": "エージェントのターンが終わると編集を終了します",
|
||||
"ui.edit_in_place_agent_busy": "エージェントがまだこのデザインで作業中です。そのターンが終わってから再度お試しください",
|
||||
"ui.edit_in_place_already_queued": "エージェントのターンの終了を待っている編集がすでにあります",
|
||||
"ui.edit_in_place_assembly_closed": "{name} はもう開いていません",
|
||||
"ui.edit_in_place_banner": "{name} 内で編集中",
|
||||
"ui.edit_in_place_busy": "先に開いているものを終了またはキャンセルしてください",
|
||||
"ui.edit_in_place_none": "その場で編集中のリンクされたデザインはありません",
|
||||
"ui.edit_in_place_not_saved": "部品に保存されていない変更があります。編集を終了するには保存してください",
|
||||
"ui.edit_in_place_open_elsewhere": "{name} は別の SolidCraft で開かれています",
|
||||
"ui.edit_in_place_opens_after_turn": "エージェントのターンが終わると部品が開きます",
|
||||
"ui.edit_in_place_refresh_dropped": "リンクの更新中にアセンブリが変更されました。もう一度実行してください",
|
||||
"ui.edit_in_place_refresh_stopped": "リンクの更新が完了前に停止しました",
|
||||
"ui.edit_in_place_refreshing": "{name} のリンクを更新中…",
|
||||
"ui.edit_in_place_save_first": "先にアセンブリを保存してください。リンクされたデザインはそのファイルから見つけます",
|
||||
"ui.edit_in_place_save_waits": "{name} のリンクを更新中です。完了してから保存してください",
|
||||
"ui.edit_in_place_saved_elsewhere": "部品は別のファイルに保存されました。アセンブリは引き続き {name} にリンクしています",
|
||||
"ui.edit_in_place_wait": "デザインで実行中の処理が終わるまでお待ちください",
|
||||
"ui.edit_section_analysis": "断面解析を編集",
|
||||
"ui.ellipse": "楕円",
|
||||
"ui.emboss_wrap_note": "サポートされる円柱に接する直交線のプロファイルは自動的に巻き付けられます。対象を選ぶには壁を選択してください。",
|
||||
@@ -3590,7 +3357,6 @@
|
||||
"ui.heading_edit": "編集",
|
||||
"ui.heading_extrude": "押し出し",
|
||||
"ui.heading_fillet": "フィレット",
|
||||
"ui.heading_finish_editing": "編集を終了",
|
||||
"ui.heading_finish_sketch": "スケッチを終了",
|
||||
"ui.heading_form": "フォーム",
|
||||
"ui.heading_front": "正面",
|
||||
@@ -3803,15 +3569,6 @@
|
||||
"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": "ポリゴン",
|
||||
@@ -3859,8 +3616,6 @@
|
||||
"ui.restore_defaults": "初期値に戻す",
|
||||
"ui.reverse_horizontal_pan_direction": "水平パンの方向を反転",
|
||||
"ui.reverse_horizontal_pan_tip": "水平方向の2本指スクロールとサムホイールで、反対方向にパンします",
|
||||
"ui.reverse_vertical_pan_direction": "垂直パンの方向を反転",
|
||||
"ui.reverse_vertical_pan_tip": "垂直方向の2本指スクロールで、反対方向にパンします",
|
||||
"ui.reverse_zoom_direction": "ズーム方向を反転",
|
||||
"ui.revolutions": "巻き数",
|
||||
"ui.revolve": "回転",
|
||||
@@ -4006,15 +3761,6 @@
|
||||
"ui.updateToCurrentView": "現在のビューに更新",
|
||||
"ui.upload": "アップロード",
|
||||
"ui.vertical_constraint": "垂直拘束",
|
||||
"ui.view_angles": "ビュー角度",
|
||||
"ui.view_angles_convention": "上方向の軸まわりの角度(度)。ヨー 0° が正面ビュー、90° が左、−90° が右、180° が背面です。ピッチ 90° が上面ビューです。ロールが正のとき、モデルは画面上で時計回りに回ります。",
|
||||
"ui.view_angles_help": "方向ギズモの下に、ビューのピッチ、ヨー、ロール(度)と投影法を表示します。表示をクリックすると正確な角度を入力できます。",
|
||||
"ui.view_angles_ortho": "平行投影",
|
||||
"ui.view_angles_persp": "透視投影",
|
||||
"ui.view_angles_pitch": "ピッチ",
|
||||
"ui.view_angles_readout": "ピッチ {pitch} · ヨー {yaw} · ロール {roll}",
|
||||
"ui.view_angles_roll": "ロール",
|
||||
"ui.view_angles_yaw": "ヨー",
|
||||
"ui.view_centre": "ビュー中心",
|
||||
"ui.viewport_theme_follow": "テーマに従う",
|
||||
"ui.visual_shaded_hidden_edges": "隠線付きシェーディング",
|
||||
@@ -4065,13 +3811,11 @@
|
||||
"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": "リリースページを開く",
|
||||
@@ -4088,7 +3832,6 @@
|
||||
"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 で開く",
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
"All open designs": "열린 모든 디자인",
|
||||
"Allowed expressions, separated by semicolons": "허용되는 식, 세미콜론으로 구분",
|
||||
"Also deletes": "함께 삭제됨",
|
||||
"Also place them from the origin (fully constrained)": "원점 기준 위치도 지정(완전 구속)",
|
||||
"Angle 2": "각도 2",
|
||||
"Animate": "애니메이션",
|
||||
"App/system information and command settings are included. Review private details before sharing.": "앱/시스템 정보와 명령 설정이 포함됩니다. 공유하기 전에 개인 정보를 확인하세요.",
|
||||
@@ -158,7 +157,6 @@
|
||||
"Designs you open or save appear here.": "열거나 저장한 디자인이 여기에 표시됩니다.",
|
||||
"Determined by To Object. Choose Angle to edit the angular extent.": "대상 객체까지로 결정됩니다. 각도 범위를 편집하려면 각도를 선택하세요.",
|
||||
"Diameter": "지름",
|
||||
"Dimension new shapes": "새 도형에 치수 지정",
|
||||
"Direction": "방향",
|
||||
"Discard": "버리기",
|
||||
"Distance": "거리",
|
||||
@@ -169,8 +167,6 @@
|
||||
"Double-clicking the middle button in the view: Home (the home view, framing what is shown), Fit (frame what is shown, keeping the orientation) or nothing": "뷰에서 가운데 버튼을 더블 클릭: 홈(홈 뷰, 표시된 항목에 맞춤), 맞춤(방향을 유지하며 표시된 항목에 맞춤) 또는 없음",
|
||||
"Drive Joint": "조인트 구동",
|
||||
"Duration": "지속 시간",
|
||||
"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.": "새 도형마다 자유로운 부분이 없어질 때까지 스케치 원점 기준 치수도 지정됩니다. 원점이나 배치된 형상에 이미 연결된 도형은 지정되지 않습니다.",
|
||||
"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.": "그린 도형마다 그린 크기로 모든 치수가 지정됩니다(입력한 값이 우선). 스케치에서 이미 정해진 크기는 생략됩니다.",
|
||||
"Edge": "모서리",
|
||||
"Edges": "모서리",
|
||||
"Edit": "편집",
|
||||
@@ -668,7 +664,6 @@
|
||||
"appearance.pbr_opaque": "불투명 PBR",
|
||||
"appearance.roughness": "거칠기",
|
||||
"appearance.surface_response": "표면 반응",
|
||||
"at the face": "면에서",
|
||||
"bom.group_children": "하위 부품을 상위 부품 행에 표시",
|
||||
"browser.body_name": "바디{number}",
|
||||
"browser.calibrate_title": "보정: {name}",
|
||||
@@ -677,7 +672,6 @@
|
||||
"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}",
|
||||
@@ -723,8 +717,6 @@
|
||||
"cmd.component_capture_position": "위치 캡처",
|
||||
"cmd.component_create": "새 구성 요소",
|
||||
"cmd.component_delete": "구성 요소 삭제",
|
||||
"cmd.component_edit_in_place": "제자리에서 편집",
|
||||
"cmd.component_finish_edit": "편집 완료",
|
||||
"cmd.component_from_bodies": "바디로 구성 요소 만들기",
|
||||
"cmd.component_insert_design": "설계 삽입",
|
||||
"cmd.component_links": "링크 목록",
|
||||
@@ -890,10 +882,6 @@
|
||||
"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": "렌더 대상 화면 맞춤",
|
||||
@@ -913,14 +901,9 @@
|
||||
"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과 렌더 설정 저장",
|
||||
@@ -1015,7 +998,6 @@
|
||||
"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": "변환",
|
||||
@@ -1066,7 +1048,6 @@
|
||||
"cmd.view.render_dock": "렌더 도크 이동",
|
||||
"cmd.view.render_focus": "렌더 집중 보기",
|
||||
"cmd.view_ambient_occlusion": "앰비언트 오클루전",
|
||||
"cmd.view_angle_readout": "뷰 각도",
|
||||
"cmd.view_apply": "이름 있는 보기 복원",
|
||||
"cmd.view_delete": "이름 있는 보기 삭제",
|
||||
"cmd.view_graphics_preset": "그래픽 프리셋",
|
||||
@@ -1168,13 +1149,11 @@
|
||||
"help.apache": "Apache 라이선스 2.0",
|
||||
"help.attributions": "저작자 표시 (ATTRIBUTION.md)",
|
||||
"help.build": "버전 {version} · 커밋 {commit} · 빌드 날짜 {date}",
|
||||
"help.component.finish_edit": "제자리에서 편집 중인 부품을 저장하고 어셈블리로 돌아가 그 부품으로 어셈블리를 업데이트합니다. 연결된 부품의 변경을 마쳤을 때 사용하세요.",
|
||||
"help.font_details": "인터페이스는 egui의 내장 글꼴을 사용합니다(Ubuntu Font Licence 및 MIT/Bitstream Vera 라이선스의 Ubuntu Light와 Hack, OFL 및 MIT의 Noto Emoji와 이모지 아이콘 글꼴). 스케치 텍스트는 Liberation Sans(SIL OFL 1.1)를 사용합니다. The Crafting Apps의 선택적 글꼴 팩 craft-fonts(BIZ UDPGothic, Shippori Mincho, BIZ UDMincho, Noto Sans CJK SC, Noto Sans Arabic; SIL OFL 1.1)는 SolidCraft 빌드에 포함되지 않습니다.",
|
||||
"help.fonts": "글꼴",
|
||||
"help.licences": "라이선스",
|
||||
"help.mit": "MIT 라이선스",
|
||||
"help.notice": "NOTICE",
|
||||
"help.pointcloud.insert": "PLY, XYZ 또는 PTS 스캔을 모델 옆에 포인트 클라우드로 삽입합니다. 측정 데이터를 참고하여 모델링할 때 사용합니다.",
|
||||
"help.solid.chamfer": "선택한 모서리를 지정한 거리만큼 평평하게 깎습니다. 날카로운 모서리를 다듬거나 핀 또는 나사가 쉽게 들어가도록 할 때 사용합니다.",
|
||||
"help.solid.extrude": "스케치 프로파일을 지정한 거리만큼 3D로 밀어내 재료를 더하거나 제거합니다. 평면 윤곽을 블록, 보스 또는 포켓으로 만들 때 사용합니다.",
|
||||
"help.solid.fillet": "선택한 모서리를 지정한 반지름의 매끄러운 원호로 둥글게 만듭니다. 강도, 편의성 또는 외관을 위해 날카로운 모서리를 부드럽게 할 때 사용합니다.",
|
||||
@@ -1182,8 +1161,6 @@
|
||||
"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": "설계",
|
||||
@@ -1536,13 +1513,6 @@
|
||||
"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": "마우스 및 트랙패드",
|
||||
@@ -1580,19 +1550,11 @@
|
||||
"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": "중심 및 중점 정렬 안내선",
|
||||
"prefs.sketch_tracking_guides_note": "원 중심이나 직선 모서리의 중점에 마우스를 올린 다음 수평 또는 수직으로 이동하면 임시 점선 안내선이 표시됩니다.",
|
||||
"prefs.sketching": "스케치",
|
||||
"prefs.snap_drawing_note": "스케치 점을 그리거나 드래그하는 동안.",
|
||||
"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": "선택됨",
|
||||
@@ -1634,46 +1596,6 @@
|
||||
"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": "표시 블룸",
|
||||
"render.bloom_help": "양의 선형 하이라이트를 세 번의 정규화 필터로 확산하는 출력 전용 효과입니다. 반경은 출력 픽셀 단위이며 sRGB 전에 더합니다. 렌즈·볼륨·수렴 개선이 아니며 원본 EXR은 변하지 않습니다.",
|
||||
@@ -1681,13 +1603,6 @@
|
||||
"render.bloom_radius": "블룸 반경",
|
||||
"render.bloom_strength": "블룸 강도",
|
||||
"render.bloom_threshold": "선형 하이라이트 임계값",
|
||||
"render.category_metal": "금속",
|
||||
"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": "바디 수 상한",
|
||||
@@ -1700,9 +1615,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여야 함",
|
||||
"render.error_binding_allocation": "재질 연결을 할당할 수 없음",
|
||||
"render.error_body_bound": "제품 미리 보기가 캡처된 바디 한도를 초과함",
|
||||
@@ -1766,8 +1679,6 @@
|
||||
"render.fixed_camera": "카메라",
|
||||
"render.fixed_world": "월드",
|
||||
"render.fold_browser": "브라우저 접기",
|
||||
"render.frame_presets": "구도 프리셋",
|
||||
"render.frame_presets_pending": "샷 편집기와 함께 제공 예정",
|
||||
"render.heading_batch": "일괄 처리",
|
||||
"render.heading_frame": "프레임",
|
||||
"render.heading_lens": "렌즈",
|
||||
@@ -1799,43 +1710,9 @@
|
||||
"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": "적용 가능한 리소스",
|
||||
"render.library_applied": "리소스 변환을 적용했습니다. 시작 전에 전체 렌더 요청을 확인하세요.",
|
||||
"render.library_apply_appearance": "외관 적용",
|
||||
"render.library_apply_hdr": "이 HDR 환경과 함께 저장",
|
||||
"render.library_apply_hint": "클릭하여 적용 · 모델로 드래그",
|
||||
"render.library_apply_maps": "제작된 GPU 표면 맵 사용",
|
||||
"render.library_apply_studio": "제작된 스튜디오 변환 저장",
|
||||
"render.library_apply_uniform": "균일한 불투명 외관 적용",
|
||||
@@ -1845,21 +1722,12 @@
|
||||
"render.library_emitters_only_ack": "광원만 사용하고 명시된 수동 반사판은 생략",
|
||||
"render.library_environment_concepts": "환경 구상",
|
||||
"render.library_extent": "대상 크기",
|
||||
"render.library_filters": "카탈로그 및 사용 가능 여부",
|
||||
"render.library_grid": "격자",
|
||||
"render.library_hdr": "내장 HDR 환경",
|
||||
"render.library_hdr_help": "원본 HDR을 저장 설정에 포함합니다. 제작된 경도 원점을 명시적으로 변환하며 저장 후 외부 파일이 필요하지 않습니다.",
|
||||
"render.library_in_design": "디자인 내",
|
||||
"render.library_inspect_only": "선택하여 확인 · 검사기에서 적용",
|
||||
"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_material_concepts": "재질 구상",
|
||||
"render.library_metallic": "금속성",
|
||||
"render.library_next": "다음 페이지",
|
||||
"render.library_opacity": "불투명도",
|
||||
"render.library_other": "기타 외관",
|
||||
"render.library_physical_profiles": "Physical reference profiles",
|
||||
"render.library_previous": "이전 페이지",
|
||||
"render.library_quality_concepts": "품질 사양",
|
||||
@@ -1868,7 +1736,6 @@
|
||||
"render.library_results": "일치하는 리소스",
|
||||
"render.library_rig_help": "Fit once at an explicit center and physical extent. Complete rigs include passive cards; calibration blockers still refuse. Conservative emitter/card clearance is checked before saving.",
|
||||
"render.library_rigs": "제작된 조명 리그",
|
||||
"render.library_roughness": "거칠기",
|
||||
"render.library_save_maps": "Save maps to this setup",
|
||||
"render.library_scene_bounds": "캡처된 장면 범위 사용",
|
||||
"render.library_scenes": "장면 사양",
|
||||
@@ -1879,7 +1746,6 @@
|
||||
"render.library_target": "대상 바디",
|
||||
"render.library_title": "렌더 리소스 라이브러리",
|
||||
"render.library_uniform_ack": "공간 변화나 고급 효과 없는 균일 근사 사용",
|
||||
"render.library_users": "사용 위치",
|
||||
"render.light_anchor_pending": "조명 좌표계 변경은 아직 지원되지 않습니다.",
|
||||
"render.lights_fixed_to": "조명 고정 기준",
|
||||
"render.material_color_replace": "기본 맵으로 바디 색상 교체",
|
||||
@@ -1891,30 +1757,8 @@
|
||||
"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.",
|
||||
@@ -1944,20 +1788,11 @@
|
||||
"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": "설계로 돌아가기",
|
||||
@@ -1988,28 +1823,14 @@
|
||||
"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.search_appearances": "외관 {count}개 검색",
|
||||
"render.setup_name": "설정 이름",
|
||||
"render.shell_tab_appearance": "모양",
|
||||
"render.shell_tab_camera": "카메라",
|
||||
"render.shell_tab_scene": "장면",
|
||||
"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": "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": "조명",
|
||||
@@ -2018,12 +1839,7 @@
|
||||
"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": "스튜디오 및 환경",
|
||||
"render.tone_aces_fitted": "ACES 근사",
|
||||
"render.tone_reinhard": "Reinhard",
|
||||
"render.tree_add_light": "조명 추가",
|
||||
@@ -2063,7 +1879,6 @@
|
||||
"render.unavailable_preview": "이 설정은 사용할 수 없는 미리보기 프로필을 요청합니다.",
|
||||
"render.unsaved": "저장하지 않은 미리보기 변경 사항",
|
||||
"render.use_preview": "통합 미리보기 사용",
|
||||
"render.wait_add_light": "아직 지원되지 않음: 이름이 있는 조명 만들기",
|
||||
"render.wait_batch": "아직 지원되지 않음: 저장된 샷 렌더 대기열",
|
||||
"render.wait_compare": "아직 지원되지 않음: 보관된 이미지 비교",
|
||||
"render.wait_paint": "아직 지원되지 않음: 제품 선택 및 모양 페인팅",
|
||||
@@ -2116,7 +1931,6 @@
|
||||
"select the sheet edge first": "판금 모서리를 먼저 선택하세요",
|
||||
"select two components": "구성요소 두 개를 선택하세요",
|
||||
"settings.display_quality_note": "세밀할수록 곡면과 모서리가 더 매끄럽게 표시되지만 메시 생성 시간이 늘어납니다. 뷰포트만 바뀌며 설계나 내보내기는 바뀌지 않습니다.",
|
||||
"settings.double_click_components": "구성 요소를 두 번 클릭하여 활성화하거나 연결된 구성 요소를 제자리에서 편집",
|
||||
"settings.frame_rate_note": "뷰의 오른쪽 아래에 초당 프레임 수를 표시합니다.",
|
||||
"settings.measure_axis_distances": "X/Y/Z 거리 표시",
|
||||
"settings.measure_axis_distances_help": "측정 대화상자가 열려 있는 동안 전체 거리와 함께 월드 축 방향 치수와 거리를 표시합니다.",
|
||||
@@ -2165,10 +1979,6 @@
|
||||
"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": "방향은 축, 스케치 선 또는 직선 모서리여야 합니다",
|
||||
@@ -2197,9 +2007,7 @@
|
||||
"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} 탭입니다. 명령을 선택하면 도구 모음에 해당 버튼이 추가됩니다. 패널의 드롭다운에서 행을 도구 모음으로 끌거나, 버튼을 도구 모음 밖으로 끌어 제거하거나, 드롭다운 행의 고정 핀을 사용할 수도 있습니다.",
|
||||
@@ -2667,24 +2475,6 @@
|
||||
"ui.easyprint_uploading": "Printables에 업로드하는 중…",
|
||||
"ui.edit": "편집",
|
||||
"ui.editJointLimits": "조인트 제한 편집",
|
||||
"ui.edit_in_place": "제자리에서 편집",
|
||||
"ui.edit_in_place_after_turn": "에이전트의 차례가 끝나면 편집이 완료됩니다",
|
||||
"ui.edit_in_place_agent_busy": "에이전트가 아직 이 디자인에서 작업 중입니다. 차례가 끝난 후 다시 시도하세요",
|
||||
"ui.edit_in_place_already_queued": "에이전트의 차례가 끝나기를 기다리는 편집이 이미 있습니다",
|
||||
"ui.edit_in_place_assembly_closed": "{name}이(가) 더 이상 열려 있지 않습니다",
|
||||
"ui.edit_in_place_banner": "{name}에서 편집 중",
|
||||
"ui.edit_in_place_busy": "먼저 열려 있는 작업을 완료하거나 취소하세요",
|
||||
"ui.edit_in_place_none": "제자리에서 편집 중인 연결된 디자인이 없습니다",
|
||||
"ui.edit_in_place_not_saved": "부품에 저장되지 않은 변경 사항이 있습니다. 편집을 완료하려면 저장하세요",
|
||||
"ui.edit_in_place_open_elsewhere": "{name}이(가) 다른 SolidCraft에서 열려 있습니다",
|
||||
"ui.edit_in_place_opens_after_turn": "에이전트의 차례가 끝나면 부품이 열립니다",
|
||||
"ui.edit_in_place_refresh_dropped": "링크 업데이트 중에 어셈블리가 변경되었습니다. 다시 실행하세요",
|
||||
"ui.edit_in_place_refresh_stopped": "링크 업데이트가 완료되기 전에 중지되었습니다",
|
||||
"ui.edit_in_place_refreshing": "{name}의 링크 업데이트 중…",
|
||||
"ui.edit_in_place_save_first": "먼저 어셈블리를 저장하세요. 연결된 디자인은 해당 파일을 기준으로 찾습니다",
|
||||
"ui.edit_in_place_save_waits": "{name}에서 링크 업데이트가 실행 중입니다. 완료된 후 저장하세요",
|
||||
"ui.edit_in_place_saved_elsewhere": "부품이 다른 파일에 저장되었습니다. 어셈블리는 여전히 {name}에 연결되어 있습니다",
|
||||
"ui.edit_in_place_wait": "디자인에서 실행 중인 작업이 끝날 때까지 기다리세요",
|
||||
"ui.edit_section_analysis": "단면 분석 편집",
|
||||
"ui.ellipse": "타원",
|
||||
"ui.emboss_wrap_note": "지원되는 원통에 접하는 직교 선 프로파일은 자동으로 감깁니다. 대상을 고르려면 벽을 선택하세요.",
|
||||
@@ -2752,7 +2542,6 @@
|
||||
"ui.heading_edit": "편집",
|
||||
"ui.heading_extrude": "돌출",
|
||||
"ui.heading_fillet": "필렛",
|
||||
"ui.heading_finish_editing": "편집 완료",
|
||||
"ui.heading_finish_sketch": "스케치 종료",
|
||||
"ui.heading_form": "폼",
|
||||
"ui.heading_front": "정면",
|
||||
@@ -2943,15 +2732,6 @@
|
||||
"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": "다각형",
|
||||
@@ -2995,8 +2775,6 @@
|
||||
"ui.restore_defaults": "기본값 복원",
|
||||
"ui.reverse_horizontal_pan_direction": "수평 이동 방향 반전",
|
||||
"ui.reverse_horizontal_pan_tip": "두 손가락 수평 스크롤과 엄지 휠이 반대 방향으로 이동합니다",
|
||||
"ui.reverse_vertical_pan_direction": "수직 이동 방향 반전",
|
||||
"ui.reverse_vertical_pan_tip": "두 손가락 수직 스크롤이 반대 방향으로 이동합니다",
|
||||
"ui.reverse_zoom_direction": "확대/축소 방향 반전",
|
||||
"ui.revolutions": "권수",
|
||||
"ui.revolve": "회전",
|
||||
@@ -3124,15 +2902,6 @@
|
||||
"ui.up_axis_note": "뷰(축 뷰, 홈, 보기)에서 위쪽을 가리키는 축입니다. 디자인은 변경되지 않습니다.",
|
||||
"ui.updateToCurrentView": "현재 보기로 업데이트",
|
||||
"ui.upload": "업로드",
|
||||
"ui.view_angles": "뷰 각도",
|
||||
"ui.view_angles_convention": "위쪽 축을 기준으로 한 각도(도)입니다. 요 0°는 정면 뷰, 90°는 왼쪽, −90°는 오른쪽, 180°는 뒷면이고, 피치 90°는 평면 뷰입니다. 롤이 양수이면 모델이 화면에서 시계 방향으로 회전합니다.",
|
||||
"ui.view_angles_help": "방향 기즈모 아래에 뷰의 피치, 요, 롤(도)과 투영 방식을 표시합니다. 표시를 클릭하면 정확한 각도를 입력할 수 있습니다.",
|
||||
"ui.view_angles_ortho": "직교",
|
||||
"ui.view_angles_persp": "원근",
|
||||
"ui.view_angles_pitch": "피치",
|
||||
"ui.view_angles_readout": "피치 {pitch} · 요 {yaw} · 롤 {roll}",
|
||||
"ui.view_angles_roll": "롤",
|
||||
"ui.view_angles_yaw": "요",
|
||||
"ui.view_centre": "뷰 중심",
|
||||
"ui.viewport_theme_follow": "테마 따르기",
|
||||
"ui.visual_shaded_hidden_edges": "음영 처리 및 숨겨진 모서리",
|
||||
@@ -3199,6 +2968,5 @@
|
||||
"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 닫기"
|
||||
}
|
||||
|
||||
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
@@ -26,7 +26,6 @@
|
||||
"All open designs": "Wszystkie otwarte projekty",
|
||||
"Allowed expressions, separated by semicolons": "Dozwolone wyrażenia, rozdzielone średnikami",
|
||||
"Also deletes": "Usuwa również",
|
||||
"Also place them from the origin (fully constrained)": "Umieść je też względem początku (w pełni związane)",
|
||||
"Angle 2": "Kąt 2",
|
||||
"Animate": "Animuj",
|
||||
"App/system information and command settings are included. Review private details before sharing.": "Dołączone są informacje o aplikacji i systemie oraz ustawienia poleceń. Przed udostępnieniem sprawdź, czy nie ma tam prywatnych danych.",
|
||||
@@ -119,7 +118,6 @@
|
||||
"Describe what happened before continuing.": "Zanim przejdziesz dalej, opisz, co się stało.",
|
||||
"Designs you open or save appear here.": "Tutaj pojawiają się otwierane lub zapisywane projekty.",
|
||||
"Determined by To Object. Choose Angle to edit the angular extent.": "Wynika z opcji Do obiektu. Wybierz Kąt, aby edytować zakres kątowy.",
|
||||
"Dimension new shapes": "Wymiaruj nowe kształty",
|
||||
"Direction": "Kierunek",
|
||||
"Discard": "Odrzuć",
|
||||
"Do not export hidden edges": "Nie eksportuj krawędzi niewidocznych",
|
||||
@@ -133,8 +131,6 @@
|
||||
"Drag to turn the rest in 5° steps. Hold Alt or Ctrl to turn it freely.": "Przeciągnij, aby obrócić podparcie co 5°. Przytrzymaj Alt lub Ctrl, aby obracać swobodnie.",
|
||||
"Drive Joint": "Steruj złączem",
|
||||
"Duration": "Czas trwania",
|
||||
"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.": "Każdy nowy kształt otrzymuje też wymiary od początku szkicu, aż nic nie pozostanie swobodne. Kształt już połączony z początkiem lub z umieszczoną geometrią nie otrzymuje żadnych.",
|
||||
"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.": "Każdy narysowany kształt otrzymuje wszystkie swoje wymiary w narysowanych rozmiarach (wpisane wartości mają pierwszeństwo). Rozmiar, który szkic już określa, jest pomijany.",
|
||||
"Edges": "Krawędzie",
|
||||
"Edit": "Edytuj",
|
||||
"Edit Profile Sketch": "Edytuj szkic profilu",
|
||||
@@ -479,7 +475,6 @@
|
||||
"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.",
|
||||
@@ -780,7 +775,6 @@
|
||||
"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",
|
||||
@@ -1138,13 +1132,6 @@
|
||||
"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",
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
"All open designs": "Todos os projetos abertos",
|
||||
"Allowed expressions, separated by semicolons": "Expressões permitidas, separadas por ponto e vírgula",
|
||||
"Also deletes": "Também exclui",
|
||||
"Also place them from the origin (fully constrained)": "Posicioná-las também a partir da origem (totalmente restritas)",
|
||||
"Angle 2": "Ângulo 2",
|
||||
"Animate": "Animar",
|
||||
"App/system information and command settings are included. Review private details before sharing.": "Informações do aplicativo/sistema e configurações estão incluídas. Revise detalhes privados antes de compartilhar.",
|
||||
@@ -146,7 +145,6 @@
|
||||
"Designs you open or save appear here.": "Os projetos que você abrir ou salvar aparecem aqui.",
|
||||
"Determined by To Object. Choose Angle to edit the angular extent.": "Determinado por Até o objeto. Escolha Ângulo para editar a extensão angular.",
|
||||
"Diameter": "Diâmetro",
|
||||
"Dimension new shapes": "Cotar novas formas",
|
||||
"Direction": "Direção",
|
||||
"Discard": "Descartar",
|
||||
"Distance": "Distância",
|
||||
@@ -157,8 +155,6 @@
|
||||
"Double-clicking the middle button in the view: Home (the home view, framing what is shown), Fit (frame what is shown, keeping the orientation) or nothing": "Clique duplo com o botão do meio na vista: Início (a vista inicial, enquadrando o que é mostrado), Ajustar (enquadra o que é mostrado, mantendo a orientação) ou nada",
|
||||
"Drive Joint": "Acionar junta",
|
||||
"Duration": "Duração",
|
||||
"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 nova forma também recebe cotas a partir da origem do esboço até nada ficar livre. Uma forma já ligada à origem ou a uma geometria posicionada não recebe nenhuma.",
|
||||
"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 você desenha recebe todas as suas cotas nos tamanhos desenhados (valores digitados têm prioridade). Um tamanho que o esboço já define é omitido.",
|
||||
"Edges": "Arestas",
|
||||
"Edit": "Editar",
|
||||
"Edit Profile Sketch": "Editar esboço do perfil",
|
||||
@@ -582,7 +578,6 @@
|
||||
"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.",
|
||||
@@ -886,7 +881,6 @@
|
||||
"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",
|
||||
@@ -1261,13 +1255,6 @@
|
||||
"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",
|
||||
|
||||
+1
-2024
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
+921
-1604
File diff suppressed because it is too large
Load Diff
@@ -38,7 +38,6 @@
|
||||
"All open designs": "Tất cả thiết kế đang mở",
|
||||
"Allowed expressions, separated by semicolons": "Các biểu thức hợp lệ, phân tách bằng dấu chấm phẩy",
|
||||
"Also deletes": "Đồng thời xóa",
|
||||
"Also place them from the origin (fully constrained)": "Đồng thời định vị từ gốc tọa độ (ràng buộc hoàn toàn)",
|
||||
"Anchor": "Điểm neo",
|
||||
"Angle 2": "Góc 2",
|
||||
"Angle Display": "Hiển thị góc",
|
||||
@@ -181,9 +180,6 @@
|
||||
"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",
|
||||
"Distance": "Khoảng cách",
|
||||
@@ -200,8 +196,6 @@
|
||||
"Drag to turn the rest in 5° steps. Hold Alt or Ctrl to turn it freely.": "Kéo để xoay gờ đỡ theo từng bước 5°. Giữ phím Alt hoặc Ctrl để xoay tự do.",
|
||||
"Drive Joint": "Dẫn động khớp nối",
|
||||
"Duration": "Thời lượng",
|
||||
"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.": "Mỗi hình mới cũng được ghi kích thước từ gốc phác thảo cho đến khi không còn phần nào tự do. Hình đã gắn với gốc hoặc với hình học đã đặt sẽ không nhận thêm.",
|
||||
"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.": "Mỗi hình bạn vẽ nhận đủ các kích thước theo cỡ đã vẽ (giá trị nhập tay được ưu tiên). Kích thước mà bản phác thảo đã xác định sẽ được bỏ qua.",
|
||||
"Edge": "Cạnh",
|
||||
"Edges": "Các cạnh",
|
||||
"Edit": "Sửa",
|
||||
@@ -260,8 +254,6 @@
|
||||
"Flanges": "Gờ viền (Flanges)",
|
||||
"Flat Pattern": "Bản khai triển phẳng",
|
||||
"Flat size": "Kích thước phôi phẳng",
|
||||
"Flat size X": "Kích thước phôi phẳng X",
|
||||
"Flat size Y": "Kích thước phôi phẳng Y",
|
||||
"Follow camera (Look At sketches)": "Đi theo camera (Nhìn vuông góc phác thảo)",
|
||||
"Follow the camera (Look At sketches)": "Đi theo camera (Nhìn vuông góc phác thảo)",
|
||||
"Font": "Phông chữ",
|
||||
@@ -595,7 +587,6 @@
|
||||
"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",
|
||||
@@ -763,7 +754,6 @@
|
||||
"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}",
|
||||
@@ -772,7 +762,6 @@
|
||||
"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}",
|
||||
@@ -823,9 +812,7 @@
|
||||
"cmd.component_create": "Tạo mới Cụm chi tiết",
|
||||
"cmd.component_cycle_color": "Đổi màu cụm chi tiết xoay vòng",
|
||||
"cmd.component_delete": "Xóa Cụm chi tiết",
|
||||
"cmd.component_edit_in_place": "Chỉnh sửa tại chỗ",
|
||||
"cmd.component_extract": "Trích xuất ra tệp riêng",
|
||||
"cmd.component_finish_edit": "Hoàn tất chỉnh sửa",
|
||||
"cmd.component_from_bodies": "Tạo cụm chi tiết từ các thân khối",
|
||||
"cmd.component_insert_design": "Chèn thiết kế vào cụm",
|
||||
"cmd.component_links": "Danh sách liên kết",
|
||||
@@ -1000,10 +987,6 @@
|
||||
"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",
|
||||
@@ -1026,20 +1009,15 @@
|
||||
"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",
|
||||
@@ -1142,7 +1120,6 @@
|
||||
"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)",
|
||||
@@ -1194,7 +1171,6 @@
|
||||
"cmd.view.render_dock": "Di chuyển neo kết xuất",
|
||||
"cmd.view.render_focus": "Chế độ xem tập trung kết xuất",
|
||||
"cmd.view_ambient_occlusion": "Đổ bóng che khuất môi trường (AO)",
|
||||
"cmd.view_angle_readout": "Góc hướng nhìn",
|
||||
"cmd.view_apply": "Áp dụng góc nhìn đã lưu",
|
||||
"cmd.view_back": "Góc nhìn phía sau",
|
||||
"cmd.view_bottom": "Góc nhìn từ dưới",
|
||||
@@ -1270,7 +1246,6 @@
|
||||
"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.",
|
||||
@@ -1330,7 +1305,6 @@
|
||||
"help.appearance.assign": "Gán màu hoặc vẻ ngoài vật liệu cho các khối, mặt hoặc thành phần đã chọn mà không thay đổi khối lượng. Dùng để phân biệt các chi tiết hoặc xem trước lớp hoàn thiện.",
|
||||
"help.attributions": "Ghi công (ATTRIBUTION.md)",
|
||||
"help.build": "Phiên bản {version} · commit {commit} · dựng ngày {date}",
|
||||
"help.component.finish_edit": "Lưu chi tiết đang được chỉnh sửa tại chỗ, quay lại cụm lắp ráp của nó và cập nhật cụm lắp ráp từ chi tiết đó. Dùng khi bạn đã hoàn tất các thay đổi cho chi tiết liên kết.",
|
||||
"help.connect_note": "Khởi động lại từng trợ lý ảo để nạp SolidCraft server. Mỗi tập tin cấu hình đều đã được sao lưu thành .bak trước khi thay đổi.",
|
||||
"help.connect_title": "Kết nối trợ lý ảo AI",
|
||||
"help.face.delete": "Xóa các mặt đã chọn và để các mặt lân cận khép kín chỗ trống, hoặc giữ lỗ hở dưới dạng bề mặt. Dùng để bỏ một cạnh bo tròn, một lỗ hoặc một phần lồi đang vướng.",
|
||||
@@ -1341,7 +1315,6 @@
|
||||
"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.",
|
||||
@@ -1367,8 +1340,6 @@
|
||||
"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.",
|
||||
@@ -1736,8 +1707,6 @@
|
||||
"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",
|
||||
@@ -1813,19 +1782,8 @@
|
||||
"prefs.layout_note": "Chế độ Gọn (Compact) gắn các bảng vào mép cửa sổ. Độ trong suốt của Cây thư mục chỉ khả dụng ở chế độ Nổi (Floating). Thay đổi vị trí của Trợ lý trong trang Trợ lý.",
|
||||
"prefs.left": "Bên trái",
|
||||
"prefs.left_click": "Nhấp chuột trái",
|
||||
"prefs.linked_designs": "Thiết kế liên kết",
|
||||
"prefs.live_input": "Nhập liệu trực tiếp",
|
||||
"prefs.live_preview": "Xem trước trực tiếp",
|
||||
"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",
|
||||
@@ -1874,24 +1832,11 @@
|
||||
"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",
|
||||
"prefs.sketch_tracking_guides_note": "Di con trỏ lên tâm đường tròn hoặc trung điểm của cạnh thẳng, rồi di chuyển theo phương ngang hoặc dọc để hiển thị đường căn chỉnh nét đứt tạm thời.",
|
||||
"prefs.sketching": "Vẽ phác thảo",
|
||||
"prefs.snap_drawing_note": "Khi đang vẽ hoặc kéo một điểm phác thảo.",
|
||||
"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",
|
||||
@@ -1899,16 +1844,12 @@
|
||||
"prefs.supersampling_brief": "Vẽ khung nhìn ở độ phân giải lớn hơn rồi lọc giảm tỷ lệ: rất sắc nét, nhưng tốn tài nguyên hơn.",
|
||||
"prefs.tab_accent_note": "Màu nhấn theo tab đang kích hoạt",
|
||||
"prefs.theme_file": "tệp chủ đề",
|
||||
"prefs.theme_file_or_paste": "tệp hoặc dán",
|
||||
"prefs.theme_follow": "Theo dõi tệp chủ đề",
|
||||
"prefs.theme_follow_error": "Chưa áp dụng: {error}. Chủ đề hợp lệ gần nhất vẫn được giữ.",
|
||||
"prefs.theme_follow_launch": "Chỉ theo dõi trong lần chạy này (--theme-file); tải lại mỗi khi công cụ khác ghi lại tệp.",
|
||||
"prefs.theme_follow_note": "Tải lại mỗi khi công cụ khác ghi lại tệp. Chọn chủ đề sẽ dừng theo dõi.",
|
||||
"prefs.theme_follow_off": "Tắt",
|
||||
"prefs.theme_follow_stop": "Dừng theo dõi",
|
||||
"prefs.theme_import_hint": "Nhấp để chọn tệp giao diện, thả tệp vào đây, hoặc nhấn Ctrl+V (Cmd+V trên Mac) khi đã sao chép một giao diện",
|
||||
"prefs.theme_paste": "Dán giao diện",
|
||||
"prefs.theme_paste_none": "Bộ nhớ tạm không chứa giao diện SolidCraft nào",
|
||||
"prefs.timeline_half_hour": "−30 phút",
|
||||
"prefs.timeline_now": "bây giờ",
|
||||
"prefs.timeline_quarter_hour": "−15 phút",
|
||||
@@ -1974,47 +1915,7 @@
|
||||
"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",
|
||||
"render.availability_projection": "Chiếu một phần được hỗ trợ; xem phạm vi và các mục lược bỏ",
|
||||
"render.availability_reference": "Dữ liệu tham chiếu; không tự động gán",
|
||||
@@ -2069,13 +1970,6 @@
|
||||
"render.camera_set": "Đặt camera kết xuất",
|
||||
"render.camera_target": "Vị trí mục tiêu · mm",
|
||||
"render.camera_yaw": "Góc quay ngang camera (Yaw)",
|
||||
"render.category_metal": "Kim loại",
|
||||
"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",
|
||||
@@ -2175,7 +2069,6 @@
|
||||
"render.effects_reset_lens": "Đặt lại hiệu ứng ống kính",
|
||||
"render.effects_seed": "Hạt giống hoa văn",
|
||||
"render.emitted_axis": "Trục phát",
|
||||
"render.environment_tiles_require_raytrace": "Môi trường cần dò tia trên CPU hoặc GPU. Chọn trong trình chỉnh Đầu ra.",
|
||||
"render.error_area_samples": "area_samples phải là 1, 4, 16 hoặc 64",
|
||||
"render.error_binding_allocation": "không thể dành chỗ cho liên kết vật liệu",
|
||||
"render.error_body_bound": "xem trước sản phẩm vượt quá giới hạn thân đã ghi",
|
||||
@@ -2247,8 +2140,6 @@
|
||||
"render.flare_strength": "Cường độ vệt lóe điểm sáng",
|
||||
"render.flare_threshold": "Ngưỡng lóe sáng tuyến tính cảnh",
|
||||
"render.fold_browser": "Gập Trình duyệt",
|
||||
"render.frame_presets": "Khung đặt sẵn",
|
||||
"render.frame_presets_pending": "Sẽ có cùng trình chỉnh cảnh chụp",
|
||||
"render.gpu_depth": "Độ sâu đường truyền tia tối đa",
|
||||
"render.gpu_options_help": "Số mẫu, độ sâu, hạt giống và độ phơi sáng được lưu cùng thiết lập này. Chỉ lưu phương thức tạo ảnh đang hoạt động.",
|
||||
"render.gpu_path_failed": "Bộ dò đường truyền tia GPU không khả dụng",
|
||||
@@ -2309,36 +2200,6 @@
|
||||
"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",
|
||||
@@ -2350,12 +2211,8 @@
|
||||
"render.lens_effects_title": "Phong cách ống kính và nhiếp ảnh",
|
||||
"render.library_all": "Danh mục",
|
||||
"render.library_all_states": "Tất cả trạng thái khả dụng",
|
||||
"render.library_appearance_available": "Diện mạo khả dụng",
|
||||
"render.library_applicable": "Tài nguyên áp dụng được",
|
||||
"render.library_applied": "Đã áp dụng phép chiếu tài nguyên. Hãy kiểm tra toàn bộ yêu cầu kết xuất trước khi bắt đầu.",
|
||||
"render.library_apply_appearance": "Áp dụng diện mạo",
|
||||
"render.library_apply_hdr": "Lưu kèm môi trường HDR này",
|
||||
"render.library_apply_hint": "Bấm để áp dụng · kéo vào mô hình",
|
||||
"render.library_apply_maps": "Sử dụng bản đồ bề mặt GPU đã tạo",
|
||||
"render.library_apply_studio": "Lưu phép chiếu studio đã tạo",
|
||||
"render.library_apply_uniform": "Áp dụng diện mạo đục đồng nhất",
|
||||
@@ -2371,23 +2228,15 @@
|
||||
"render.library_extent": "Phạm vi chủ thể",
|
||||
"render.library_favorite": "Thêm vào yêu thích",
|
||||
"render.library_favorites": "Yêu thích",
|
||||
"render.library_filters": "Danh mục và tình trạng sẵn có",
|
||||
"render.library_grid": "Lưới",
|
||||
"render.library_hdr": "Môi trường HDR tích hợp",
|
||||
"render.library_hdr_help": "Nhúng HDR gốc vào thiết lập đã lưu. Gốc kinh độ đã thiết kế được chuyển đổi rõ ràng; không cần tệp bên ngoài sau khi lưu.",
|
||||
"render.library_in_design": "Trong thiết kế",
|
||||
"render.library_inspect_only": "Chọn để kiểm tra; không có gì được tự động áp dụng. Mục yêu thích và lịch sử gần đây thuộc về phiên tài liệu đang mở này.",
|
||||
"render.library_ior": "Chiết suất",
|
||||
"render.library_light_concepts": "Khái niệm chiếu sáng",
|
||||
"render.library_list": "Danh sách",
|
||||
"render.library_maps_help": "Các bản đồ tích hợp có thể lưu cùng nguồn đã ghim và ánh xạ thực thể cục bộ. Ghi đè quang học tạm thời vẫn là chế độ riêng biệt.",
|
||||
"render.library_material_concepts": "Khái niệm vật liệu",
|
||||
"render.library_metallic": "Độ kim loại",
|
||||
"render.library_native_scenes": "Studio gốc",
|
||||
"render.library_next": "Trang sau",
|
||||
"render.library_omitted": "Không bao gồm",
|
||||
"render.library_opacity": "Độ đục",
|
||||
"render.library_other": "Diện mạo khác",
|
||||
"render.library_physical_profiles": "Hồ sơ tham chiếu vật lý",
|
||||
"render.library_previous": "Trang trước",
|
||||
"render.library_quality_concepts": "Thông số kỹ thuật chất lượng",
|
||||
@@ -2397,7 +2246,6 @@
|
||||
"render.library_results": "Tài nguyên phù hợp",
|
||||
"render.library_rig_help": "Khớp một lần tại tâm và phạm vi vật lý rõ ràng. Bộ đèn đầy đủ bao gồm các tấm phản quang thụ động; các bộ phận chưa hiệu chuẩn vẫn bị từ chối. Khoảng cách an toàn của bộ phát sáng/tấm hắt được kiểm tra trước khi lưu.",
|
||||
"render.library_rigs": "Bộ đèn chiếu sáng đã thiết kế",
|
||||
"render.library_roughness": "Độ nhám",
|
||||
"render.library_save_maps": "Lưu bản đồ vào thiết lập này",
|
||||
"render.library_scene_bounds": "Sử dụng ranh giới khung cảnh đã ghi nhận",
|
||||
"render.library_scenes": "Thông số kỹ thuật khung cảnh",
|
||||
@@ -2421,7 +2269,6 @@
|
||||
"render.library_unfavorite": "Bỏ yêu thích",
|
||||
"render.library_uniform_ack": "Sử dụng xấp xỉ đồng nhất không có hiệu ứng không gian hoặc nâng cao",
|
||||
"render.library_usable": "Phép chiếu có thể thực thi",
|
||||
"render.library_users": "Người dùng",
|
||||
"render.light_anchor_pending": "Chưa hỗ trợ đổi hệ tọa độ của đèn.",
|
||||
"render.lights_fixed_to": "Đèn gắn cố định với",
|
||||
"render.linear_help": "Giữ lại bản gốc RGBA số thực 32-bit trước khi khử nhiễu, phơi sáng, ánh xạ sắc điệu, lọc và phông nền. Tuyến tính sRGB/D65; nhân trước với độ phủ. Bộ nhớ bổ sung được tiếp nhận rõ ràng. Không nén hay thêm kênh phụ.",
|
||||
@@ -2505,34 +2352,12 @@
|
||||
"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",
|
||||
@@ -2593,18 +2418,10 @@
|
||||
"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",
|
||||
@@ -2613,7 +2430,6 @@
|
||||
"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",
|
||||
@@ -2671,7 +2487,6 @@
|
||||
"render.scene_review": "Xử lý và xem xét",
|
||||
"render.scene_review_help": "Xem xét toàn bộ tổ hợp gốc trước khi áp dụng. Không tự động thay thế bằng các thông số kỹ thuật SCENE nâng cao chưa liên kết.",
|
||||
"render.scene_scope_details": "Phạm vi tổ hợp",
|
||||
"render.search_appearances": "Tìm trong {count} diện mạo",
|
||||
"render.setup_legacy_environment": "Thiết lập cũ này sử dụng góc xoay hoặc cường độ môi trường kiểu cũ. Các giá trị đó được giữ lại; hãy chủ động chuyển đổi trước khi lưu toàn bộ tác vụ hình ảnh.",
|
||||
"render.setup_name": "Tên thiết lập",
|
||||
"render.setup_numeric_help": "Lưu sẽ ghi lại phương thức tạo ảnh đang hoạt động và tất cả cài đặt số của nó. Tải lại hoặc chọn thiết lập khác sẽ thay thế bản nháp này; khôi phục camera riêng biệt.",
|
||||
@@ -2681,7 +2496,6 @@
|
||||
"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",
|
||||
@@ -2691,23 +2505,10 @@
|
||||
"render.shot_remove_title": "Xóa khung hình đã lưu?",
|
||||
"render.shot_rename": "Đổi tên khung hình đã lưu",
|
||||
"render.shot_saved_only": "Hãy lưu bản nháp hiện tại trước. Nhân bản và đổi tên sẽ giữ nguyên các tùy chọn đã lưu và tham chiếu tài nguyên chính xác.",
|
||||
"render.shots_camera_changed": "Máy ảnh đã đổi kể từ khi lưu cảnh chụp này",
|
||||
"render.shots_name_help": "Nhập tên duy nhất cho cảnh chụp hiện tại.",
|
||||
"render.shots_no_result": "Chưa kết xuất",
|
||||
"render.shots_result": "Kết xuất {number} · {width} × {height}",
|
||||
"render.shots_revert": "Hoàn nguyên",
|
||||
"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",
|
||||
@@ -2720,9 +2521,6 @@
|
||||
"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",
|
||||
@@ -2761,10 +2559,8 @@
|
||||
"render.studio_sky_help": "Dải chuyển màu bầu trời gốc dùng cho chiếu sáng; không phải mô hình khí quyển hiệu chuẩn.",
|
||||
"render.studio_sun": "Mặt trời",
|
||||
"render.studio_sun_radius": "Bán kính góc",
|
||||
"render.studio_tiles_require_gpu": "Studio cần dò đường đi của tia bằng GPU. Chọn GPU trong trình chỉnh Đầu ra.",
|
||||
"render.studio_title": "Studio kết xuất",
|
||||
"render.studio_tone_map": "Ánh xạ sắc điệu (Tone mapping)",
|
||||
"render.studios_environments": "Studio và môi trường",
|
||||
"render.subsurface_enable": "Bước đi ngẫu nhiên tán xạ bên trong",
|
||||
"render.subsurface_help": "Chỉ dành cho chất điện môi kín. Hệ số tán xạ và hấp thụ RGB là các hệ số riêng biệt trên mỗi mm. Tia sáng tán xạ bên trong hình học thực tế, sau đó khúc xạ hoặc phản xạ tại ranh giới. Hãy tắt bóng đổ thẳng, khử nhiễu bề mặt và thể tích bên ngoài; dùng một phân tầng Fresnel. Đây không phải bộ đổ bóng khuếch tán, lưới mật độ hay vật liệu đo đạc. Môi trường đậm đặc có thể cần nhiều mẫu và độ sâu tia lớn hơn.",
|
||||
"render.subsurface_rate": "Tần suất va chạm tán xạ",
|
||||
@@ -2818,7 +2614,6 @@
|
||||
"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",
|
||||
@@ -2873,7 +2668,6 @@
|
||||
"select two components": "chọn hai thành phần",
|
||||
"set by the sketch's lines": "xác định bởi các đường của phác thảo",
|
||||
"settings.display_quality_note": "Mức độ chi tiết cao hơn giúp hiển thị các mặt cong và cạnh mượt mà hơn nhưng tốn nhiều thời gian tạo lưới hơn. Thiết lập này chỉ thay đổi vùng nhìn, không ảnh hưởng đến thiết kế hay dữ liệu xuất tệp.",
|
||||
"settings.double_click_components": "Nhấp đúp vào một thành phần để kích hoạt nó, hoặc chỉnh sửa tại chỗ một thành phần liên kết",
|
||||
"settings.frame_rate_details_note": "Bổ sung biểu đồ thời gian các khung hình gần nhất xếp chồng số tam giác đã vẽ, biểu đồ phân bố thời gian khung hình và nội dung GPU đã vẽ. Bấm vào tốc độ khung hình trên khung nhìn để ẩn/hiện.",
|
||||
"settings.frame_rate_note": "Hiển thị số khung hình trên giây (FPS) ở góc dưới bên phải khung nhìn.",
|
||||
"settings.measure_axis_distances": "Hiển thị khoảng cách theo trục X/Y/Z",
|
||||
@@ -2886,7 +2680,6 @@
|
||||
"settings.reset_intro": "Các cài đặt sau không hoạt động trên máy tính này và có thể khiến SolidCraft không thể khởi động, vì vậy chúng đã được tự động điều chỉnh:",
|
||||
"settings.reset_title": "Cài đặt đã được thay đổi",
|
||||
"settings.supersampling_note": "Vẽ khung nhìn ở độ phân giải cao hơn rồi nén lại: giúp các cạnh, đường nét và lưới mượt mà hơn trên màn hình dưới 4K. Tính năng này tiêu tốn tài nguyên GPU: gấp khoảng 2,25 lần số điểm ảnh ở mức 1,5× và 4 lần ở mức 2×.",
|
||||
"settings.update_changed_links": "Cập nhật các thiết kế liên kết đã thay đổi khi quay lại một thẻ",
|
||||
"settings.window_system_details": "Chi tiết",
|
||||
"settings.window_system_note_now": "Áp dụng sau khi khởi động lại. Hiện tại: {now}.",
|
||||
"settings.window_system_wayland_limited": "Một số tính năng bị giới hạn trên Wayland gốc.",
|
||||
@@ -2933,10 +2726,6 @@
|
||||
"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",
|
||||
@@ -2975,10 +2764,8 @@
|
||||
"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.",
|
||||
@@ -3389,8 +3176,6 @@
|
||||
"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",
|
||||
@@ -3498,24 +3283,6 @@
|
||||
"ui.easyprint_uploading": "Đang tải lên Printables…",
|
||||
"ui.edit": "Chỉnh sửa",
|
||||
"ui.editJointLimits": "Chỉnh sửa giới hạn khớp nối",
|
||||
"ui.edit_in_place": "Chỉnh sửa tại chỗ",
|
||||
"ui.edit_in_place_after_turn": "Việc chỉnh sửa sẽ hoàn tất khi lượt của tác tử kết thúc",
|
||||
"ui.edit_in_place_agent_busy": "Tác tử vẫn đang làm việc trong thiết kế này; hãy thử lại khi lượt của nó kết thúc",
|
||||
"ui.edit_in_place_already_queued": "Đã có một thao tác chỉnh sửa đang chờ lượt của tác tử kết thúc",
|
||||
"ui.edit_in_place_assembly_closed": "{name} không còn mở",
|
||||
"ui.edit_in_place_banner": "Đang chỉnh sửa trong {name}",
|
||||
"ui.edit_in_place_busy": "Hãy hoàn tất hoặc hủy những gì đang mở trước",
|
||||
"ui.edit_in_place_none": "Không có thiết kế liên kết nào đang được chỉnh sửa tại chỗ",
|
||||
"ui.edit_in_place_not_saved": "Chi tiết có thay đổi chưa lưu; hãy lưu để hoàn tất chỉnh sửa",
|
||||
"ui.edit_in_place_open_elsewhere": "{name} đang mở trong một SolidCraft khác",
|
||||
"ui.edit_in_place_opens_after_turn": "Chi tiết sẽ mở khi lượt của tác tử kết thúc",
|
||||
"ui.edit_in_place_refresh_dropped": "Cụm lắp ráp đã thay đổi trong khi cập nhật liên kết; hãy chạy lại",
|
||||
"ui.edit_in_place_refresh_stopped": "Cập nhật liên kết đã dừng trước khi hoàn tất",
|
||||
"ui.edit_in_place_refreshing": "Đang cập nhật liên kết trong {name}…",
|
||||
"ui.edit_in_place_save_first": "Hãy lưu cụm lắp ráp trước: các thiết kế liên kết của nó được tìm từ tệp của nó",
|
||||
"ui.edit_in_place_save_waits": "Đang cập nhật liên kết trong {name}; hãy lưu sau khi hoàn tất",
|
||||
"ui.edit_in_place_saved_elsewhere": "Chi tiết đã được lưu vào tệp khác; cụm lắp ráp vẫn liên kết với {name}",
|
||||
"ui.edit_in_place_wait": "Hãy chờ những gì đang chạy trong thiết kế hoàn tất",
|
||||
"ui.edit_section_analysis": "Chỉnh sửa phân tích mặt cắt",
|
||||
"ui.ellipse": "Hình elip",
|
||||
"ui.emboss_wrap_note": "Các biên dạng đường trực giao tiếp xúc với một hình trụ được hỗ trợ sẽ tự động quấn. Chọn một thành để chọn mục tiêu.",
|
||||
@@ -3590,7 +3357,6 @@
|
||||
"ui.heading_edit": "CHỈNH SỬA",
|
||||
"ui.heading_extrude": "ĐÙN KHỐI",
|
||||
"ui.heading_fillet": "BO TRÒN",
|
||||
"ui.heading_finish_editing": "HOÀN TẤT CHỈNH SỬA",
|
||||
"ui.heading_finish_sketch": "HOÀN TẤT PHÁC THẢO",
|
||||
"ui.heading_form": "TẠO HÌNH",
|
||||
"ui.heading_front": "HÌNH CHIẾU ĐỨNG",
|
||||
@@ -3803,15 +3569,6 @@
|
||||
"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",
|
||||
@@ -3859,8 +3616,6 @@
|
||||
"ui.restore_defaults": "Khôi phục mặc định",
|
||||
"ui.reverse_horizontal_pan_direction": "Đảo chiều di chuyển góc nhìn ngang",
|
||||
"ui.reverse_horizontal_pan_tip": "Cuộn hai ngón tay theo phương ngang và con lăn phụ sẽ di chuyển góc nhìn theo chiều ngược lại",
|
||||
"ui.reverse_vertical_pan_direction": "Đảo hướng kéo dọc",
|
||||
"ui.reverse_vertical_pan_tip": "Cuộn dọc bằng hai ngón tay sẽ kéo theo hướng ngược lại",
|
||||
"ui.reverse_zoom_direction": "Đảo chiều thu phóng",
|
||||
"ui.revolutions": "Số vòng",
|
||||
"ui.revolve": "Xoay tròn tạo khối",
|
||||
@@ -4006,15 +3761,6 @@
|
||||
"ui.updateToCurrentView": "Cập nhật theo góc nhìn hiện tại",
|
||||
"ui.upload": "Tải lên",
|
||||
"ui.vertical_constraint": "Ràng buộc thẳng đứng",
|
||||
"ui.view_angles": "Góc hướng nhìn",
|
||||
"ui.view_angles_convention": "Độ quanh trục hướng lên. Lệch 0° là góc nhìn phía trước, 90° bên trái, −90° bên phải, 180° phía sau; chúc 90° là góc nhìn từ trên; nghiêng dương xoay mô hình trên màn hình theo chiều kim đồng hồ.",
|
||||
"ui.view_angles_help": "Hiển thị góc chúc, góc lệch và góc nghiêng của góc nhìn theo độ cùng kiểu chiếu, bên dưới gizmo định hướng. Bấm vào phần hiển thị để nhập góc chính xác.",
|
||||
"ui.view_angles_ortho": "Trực giao",
|
||||
"ui.view_angles_persp": "Phối cảnh",
|
||||
"ui.view_angles_pitch": "Chúc",
|
||||
"ui.view_angles_readout": "Chúc {pitch} · Lệch {yaw} · Nghiêng {roll}",
|
||||
"ui.view_angles_roll": "Nghiêng",
|
||||
"ui.view_angles_yaw": "Lệch",
|
||||
"ui.view_centre": "Tâm góc nhìn",
|
||||
"ui.viewport_theme_follow": "Theo chủ đề giao diện",
|
||||
"ui.visual_shaded_hidden_edges": "Tô bóng kèm nét khuất",
|
||||
@@ -4065,13 +3811,11 @@
|
||||
"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",
|
||||
@@ -4088,7 +3832,6 @@
|
||||
"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",
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
"All open designs": "所有開啟的設計",
|
||||
"Allowed expressions, separated by semicolons": "允許的運算式,以分號分隔",
|
||||
"Also deletes": "同時刪除",
|
||||
"Also place them from the origin (fully constrained)": "同時從原點定位(完全約束)",
|
||||
"Anchor": "錨點",
|
||||
"Angle 2": "角度 2",
|
||||
"Angle Display": "角度顯示",
|
||||
@@ -181,7 +180,6 @@
|
||||
"Designs you open or save appear here.": "您開啟或儲存的設計會顯示在此處。",
|
||||
"Determined by To Object. Choose Angle to edit the angular extent.": "由「到物件」確定。選擇「角度」可編輯角度範圍。",
|
||||
"Diameter": "直徑",
|
||||
"Dimension new shapes": "為新圖形標註尺寸",
|
||||
"Direction": "方向",
|
||||
"Discard": "捨棄",
|
||||
"Distance": "距離",
|
||||
@@ -198,8 +196,6 @@
|
||||
"Drag to turn the rest in 5° steps. Hold Alt or Ctrl to turn it freely.": "拖曳以 5° 為間隔旋轉墊塊。按住 Alt 或 Ctrl 可自由旋轉。",
|
||||
"Drive Joint": "驅動接頭",
|
||||
"Duration": "時長",
|
||||
"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.": "每個新圖形也會從草圖原點標註尺寸,直到沒有任何自由度。已連接到原點或已放置幾何的圖形不會再加。",
|
||||
"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.": "您繪製的每個圖形都會依繪製的大小取得所有尺寸(輸入的值優先)。草圖已決定的尺寸會略過。",
|
||||
"Edge": "邊",
|
||||
"Edges": "邊",
|
||||
"Edit": "編輯",
|
||||
@@ -757,7 +753,6 @@
|
||||
"appearance.pbr_opaque": "不透明 PBR",
|
||||
"appearance.roughness": "粗糙度",
|
||||
"appearance.surface_response": "表面反應",
|
||||
"at the face": "在面上",
|
||||
"bom.group_children": "子零件併入所屬零件的列",
|
||||
"browser.body_name": "實體{number}",
|
||||
"browser.calibrate_title": "校準:{name}",
|
||||
@@ -1089,7 +1084,6 @@
|
||||
"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": "切換從動尺寸",
|
||||
@@ -1745,13 +1739,6 @@
|
||||
"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": "滑鼠與觸控板",
|
||||
@@ -1844,317 +1831,21 @@
|
||||
"recovery.just_now": "剛剛",
|
||||
"recovery.min_ago": "{count} 分鐘前",
|
||||
"recovery.never_saved": "從未儲存",
|
||||
"render.adaptive_absolute": "絕對 RGB 誤差下限",
|
||||
"render.adaptive_coverage": "覆蓋率誤差目標",
|
||||
"render.adaptive_enable": "以獨立先導取樣分配取樣數",
|
||||
"render.adaptive_help": "僅限 GPU。獨立先導取樣會被捨棄,接著為每個像素分配 16 個以上的正式取樣,上限為「每像素取樣數」。RGB 與覆蓋率的變異數會引導分配;罕見光徑可能被遺漏,達到上限的像素仍可能有雜訊。最終報告會計入實際工作量。不保證經認證的雜訊量或時間。",
|
||||
"render.adaptive_minimum": "最小正式取樣數",
|
||||
"render.adaptive_pending": "已可使用 GPU 獨立先導式自適應分配與條件式菲涅耳分層。經認證的收斂、罕見事件保證與 CPU 自適應傳輸仍無法使用。",
|
||||
"render.adaptive_pilots": "每像素先導取樣數",
|
||||
"render.adaptive_relative": "相對 RGB 誤差目標",
|
||||
"render.add_ground": "將貼合的地面新增至設定",
|
||||
"render.advanced_material_scope": "在明確限制下,可使用單一有限的均質 GPU 介質。密度網格、巢狀玻璃/體積組合、次表面散射曲面、置換、光譜傳輸,以及未綁定的進階材質概念仍無法使用。",
|
||||
"render.aging_add": "新增老化層",
|
||||
"render.aging_airborne": "空氣中的灰塵 / 參與介質",
|
||||
"render.aging_airborne_help": "空氣中的灰塵是獨立的有限均質散射體積。它會改變光傳輸與原始 EXR,但不屬於表面老化堆疊。現有的體積/玻璃/降噪限制依然明確適用;不會自動加入大氣。",
|
||||
"render.aging_all_bodies": "所有符合條件的不透明實體",
|
||||
"render.aging_chips": "剝落的漆面",
|
||||
"render.aging_class_any": "所有不透明材質類別",
|
||||
"render.aging_class_dielectric": "僅非金屬表面",
|
||||
"render.aging_class_metal": "僅金屬表面",
|
||||
"render.aging_color": "層顏色 · sRGB",
|
||||
"render.aging_coverage": "覆蓋率",
|
||||
"render.aging_dust": "沉積灰塵",
|
||||
"render.aging_empty": "沒有老化層:原始表面保持不變。",
|
||||
"render.aging_fading": "褪色的塗層",
|
||||
"render.aging_grime": "污垢與髒汙",
|
||||
"render.aging_help": "依序組合最多八個光學層。鏽蝕與銅綠是人工製作的外觀,並非老化/腐蝕模擬。比例以世界座標毫米為單位。不會變更幾何、質量或任何工程屬性;玻璃與被動佈景不受影響。",
|
||||
"render.aging_metallic": "層金屬比例",
|
||||
"render.aging_origin": "場域原點 · 世界座標 mm",
|
||||
"render.aging_patina": "銅綠",
|
||||
"render.aging_refresh_targets": "檢查目前實體",
|
||||
"render.aging_roughness": "層粗糙度",
|
||||
"render.aging_rust": "氧化鐵 / 鏽蝕",
|
||||
"render.aging_scale": "圖樣縮放",
|
||||
"render.aging_scratches": "細微刮痕",
|
||||
"render.aging_target": "目標與座標",
|
||||
"render.aging_target_help": "具名目標必須仍與其擷取的索引及名稱相符。全域遮罩錨定於世界座標,不會跟隨移動的零件。這既不是凹陷偵測,也不是真正的邊緣磨損。請篩選清單以找到其他實體。",
|
||||
"render.aging_target_search": "篩選實體名稱",
|
||||
"render.aging_upward": "朝上的沉積",
|
||||
"render.all_controls_help": "密集檢視目前所有控制項。各分區頁面提供專注的編輯方式,不會變更底層請求。",
|
||||
"render.all_shots_label": "所有鏡頭",
|
||||
"render.appearance_physics_separate": "視覺外觀不會指定密度、熱學資料或其他工程屬性。",
|
||||
"render.availability_native": "原生組合;套用前請先檢閱",
|
||||
"render.availability_projection": "支援的投影;請參閱範圍與省略項目",
|
||||
"render.availability_reference": "參考資料;不會自動指派",
|
||||
"render.backdrop_alpha_conflict": "請擇一使用此背景或透明 PNG 輸出;彩現器不會默默忽略任一項設定。",
|
||||
"render.backdrop_enable": "使用僅限相機的漸層背景",
|
||||
"render.backdrop_first": "起點 / 中心顏色",
|
||||
"render.backdrop_help": "此螢幕空間背景不會照明、投射陰影、產生反射,也不代表地形。它不包含在原始 EXR 中。幾何、燈光與 HDR 環境保持獨立。",
|
||||
"render.backdrop_horizontal": "水平漸層",
|
||||
"render.backdrop_radial": "放射狀漸層",
|
||||
"render.backdrop_radius": "半徑",
|
||||
"render.backdrop_second": "終點 / 邊緣顏色",
|
||||
"render.backdrop_vertical": "垂直漸層",
|
||||
"render.backdrop_x": "中心 X",
|
||||
"render.backdrop_y": "中心 Y",
|
||||
"render.background": "場景背景",
|
||||
"render.background_camera_section": "相機背景 · PNG 呈現",
|
||||
"render.background_completed_frozen": "目前顯示的結果是已完成的凍結影像。編輯背景不會自動重繪該結果。僅相機層級的變更請使用「重新調色」,若要拍攝新的實體佈景,請使用「彩現」。",
|
||||
"render.background_hdr_separate": "目前的 HDR 資源會照亮表面並在表面上產生反射;它們不會自動顯示為主體後方的全景。變更漸層不會旋轉燈光,也不會改變燈光的顏色。",
|
||||
"render.background_lighting_section": "HDR 照明 · 與相機背景分開",
|
||||
"render.background_no_alpha_override": "相機背景不會默默加入透明輸出。原始母檔的重新調色會使用已擷取的覆蓋率,且不會重新套用降噪;來源 EXR 保持不變。",
|
||||
"render.background_not_light": "背景顏色僅用於呈現,並非額外的光源。",
|
||||
"render.background_regrade": "套用至保留的原始母檔",
|
||||
"render.background_stage_occlusion": "此鏡頭包含實體佈景幾何。大面積的地板或牆面可能佔滿畫面,並遮住僅限相機的背景。請刻意清除或調整佈景大小,或變更其實體反射率。重新調色無法移除已擷取的幾何。",
|
||||
"render.background_workflow": "請選擇您想要的背景:僅限相機的顏色/漸層、接收光線的實體佈景,或 HDR 照明。三者各自獨立。即時預覽是建模檢視;請彩現新影像,或對保留的母檔重新調色,才能看到相機後製效果。",
|
||||
"render.base_appearance": "可編輯的實體外觀",
|
||||
"render.base_apply": "套用實體外觀",
|
||||
"render.base_apply_help": "對已檢閱的確切實體進行一次可復原的視覺指派。既有的各面專屬外觀與工程材質保持獨立。",
|
||||
"render.base_color": "基底顏色 · sRGB",
|
||||
"render.base_metallic": "金屬比例",
|
||||
"render.base_opaque_only": "此編輯器保留不透明實體的語意。光學穿透請使用專屬的玻璃控制項。",
|
||||
"render.bloom_enable": "顯示泛光",
|
||||
"render.bloom_help": "僅用於輸出的創意輝光,由線性正值高光經三次正規化方框模糊擴散而成。半徑以輸出像素為單位。在轉換為 sRGB 之前,於線性顯示光中疊加;並非鏡頭效果、體積效果,也無法改善收斂。原始 EXR 保持不變。",
|
||||
"render.bloom_opaque": "泛光需要不透明輸出。透明 PNG 會被拒絕,而不會默默壓平。",
|
||||
"render.bloom_radius": "泛光半徑",
|
||||
"render.bloom_strength": "泛光強度",
|
||||
"render.bloom_threshold": "線性高光臨界值",
|
||||
"render.browse_capabilities": "檢查完整功能目錄",
|
||||
"render.browse_environments": "瀏覽原創 HDR 環境",
|
||||
"render.browse_native_scenes": "瀏覽原生 Studio",
|
||||
"render.browse_physical": "檢查物理參考資料",
|
||||
"render.browse_quality": "檢查品質規格",
|
||||
"render.browse_rigs": "瀏覽預先設計的燈光組",
|
||||
"render.browse_scene_specs": "瀏覽進階場景規格",
|
||||
"render.browse_surfaces": "瀏覽預先設計的表面",
|
||||
"render.browser_library": "資源庫",
|
||||
"render.browser_model": "模型",
|
||||
"render.camera_distance": "視點至目標距離",
|
||||
"render.camera_exact_help": "世界座標毫米;角度以度顯示,並以弧度儲存。導覽使用與視埠相同的相機。",
|
||||
"render.camera_fov": "垂直視角",
|
||||
"render.camera_pitch": "相機俯仰",
|
||||
"render.camera_roll": "滾轉",
|
||||
"render.camera_set": "設定彩現相機",
|
||||
"render.camera_target": "目標位置 · mm",
|
||||
"render.camera_yaw": "偏航",
|
||||
"render.chromatic_offset": "邊緣色差偏移",
|
||||
"render.clear_stage": "移除已儲存的佈景",
|
||||
"render.copy_request": "複製 JSON",
|
||||
"render.cpu_aa_help": "空間取樣:取樣越多,邊緣越平滑,但彩現時間也越長。",
|
||||
"render.cpu_area_add": "新增矩形 CPU 燈光",
|
||||
"render.cpu_area_editor": "CPU 矩形燈光",
|
||||
"render.cpu_area_editor_help": "完整邊向量以毫米為單位;U 叉乘 V 指向發光側。這與 GPU 的半邊向量不同。",
|
||||
"render.cpu_area_full_u": "完整 U 邊 · mm",
|
||||
"render.cpu_area_full_v": "完整 V 邊 · mm",
|
||||
"render.cpu_area_samples": "面光源取樣數",
|
||||
"render.cpu_binding_confirm": "確認此實體",
|
||||
"render.cpu_binding_stale": "場景已變更。彩現前請重新確認此反射目標。",
|
||||
"render.cpu_body_choose": "選擇實體",
|
||||
"render.cpu_body_filter": "依名稱篩選實體",
|
||||
"render.cpu_body_filter_more": "請調整篩選條件以查看更多實體。",
|
||||
"render.cpu_body_missing": "反射目標遺失、已隱藏、有歧義,或名稱超過 128 位元組。請重新選擇。",
|
||||
"render.cpu_budget": "CPU 資源設定檔",
|
||||
"render.cpu_budget_bodies": "實體上限",
|
||||
"render.cpu_budget_compact": "精簡(舊版限制)",
|
||||
"render.cpu_budget_details": "資源限制",
|
||||
"render.cpu_budget_help": "請明確選擇較大的限制。解析度與取樣數絕不會被自動降低。",
|
||||
"render.cpu_budget_memory": "工作記憶體上限",
|
||||
"render.cpu_budget_not_guarantee": "這些是受理上限,並非 RAM 保留或速度保證。幾何、影像緩衝區與保留的預覽共用記憶體預算;彩現仍可隨時取消。",
|
||||
"render.cpu_budget_resize": "草稿超出此 CPU 設定檔或其相機取樣預算。請明確接受較小的尺寸,或選擇較大的設定檔。",
|
||||
"render.cpu_budget_studio": "Studio(大型影像)",
|
||||
"render.cpu_budget_triangles": "三角形上限",
|
||||
"render.cpu_budget_workstation": "工作站(重型場景)",
|
||||
"render.cpu_captured_hdr": "此影像擷取的 HDR",
|
||||
"render.cpu_check_capture_help": "「檢查」和「開始」會驗證相同的檔案摘要,或使用內嵌的 HDR。每張影像都保留其擷取的來源。",
|
||||
"render.cpu_check_changed": "此次檢查之後,控制項或模型已變更。請在彩現前重新檢查。",
|
||||
"render.cpu_constant_help": "恆定輻射亮度對反射或穿透的光線可見,但不提供直接的漫射照明。",
|
||||
"render.cpu_custom_directions": "以自訂方向光取代預設",
|
||||
"render.cpu_direction": "朝向光源的方向(X, Y, Z)",
|
||||
"render.cpu_directional": "方向光",
|
||||
"render.cpu_directional_add": "新增方向光",
|
||||
"render.cpu_environment": "CPU 環境",
|
||||
"render.cpu_environment_background": "影像背景與環境照明保持獨立。",
|
||||
"render.cpu_environment_bounds": "CPU 強度必須介於 0–64,HDR 旋轉必須為有限值且在 ±1,000,000 弧度內,HDR 取樣數必須為 1、4、16 或 64。",
|
||||
"render.cpu_environment_constant": "恆定的次級光線環境",
|
||||
"render.cpu_environment_none": "無 CPU 環境",
|
||||
"render.cpu_environment_samples": "HDR 照明取樣數",
|
||||
"render.cpu_exposure_help": "CPU 曝光必須介於 −16 和 16 EV 之間。超出此範圍的 GPU 曝光值會保留,直到您變更為止。",
|
||||
"render.cpu_fixed": "單張影像",
|
||||
"render.cpu_hdr_help": "Radiance .hdr,2:1 全景圖,最大 2048 × 1024 且不超過 16 MiB。CPU 要求檔案中明確指定 sRGB/D65 原色;未指定原色的檔案和 EXR 會被拒絕。",
|
||||
"render.cpu_hdr_reflection_help": "HDR 粗糙反射需要完全金屬的實體和面表面。「檢查」會回報不支援的組合。",
|
||||
"render.cpu_key_rim": "主光與輪廓光",
|
||||
"render.cpu_last_check": "上次 CPU 檢查",
|
||||
"render.cpu_light_help": "最多八個方向光。自訂清單為空時會關閉所有方向光;選擇預設則會還原其光源。顏色為線性 RGB,強度為相對值,而非瓦特。",
|
||||
"render.cpu_light_intensity": "相對強度(0–64)",
|
||||
"render.cpu_light_minimum": "衰減最小距離",
|
||||
"render.cpu_light_position": "世界座標位置(X, Y, Z)",
|
||||
"render.cpu_light_reference": "強度參考距離",
|
||||
"render.cpu_light_remove": "移除光源",
|
||||
"render.cpu_lighting": "照明預設",
|
||||
"render.cpu_lights": "CPU 光源",
|
||||
"render.cpu_linear_color": "線性顏色(R, G, B)",
|
||||
"render.cpu_neutral": "中性",
|
||||
"render.cpu_opaque_required": "CPU 光線追蹤需要不透明背景。請取消勾選「透明背景」以繼續。",
|
||||
"render.cpu_options_pending": "此 CPU 面板尚無法編輯發光、表面效果、玻璃或各向異性。使用前請先清除這些 API 選項。",
|
||||
"render.cpu_point": "點光源",
|
||||
"render.cpu_point_add": "新增點光源",
|
||||
"render.cpu_point_help": "最多八個點光源,疊加於所選的方向光照明之上。距離單位為 mm;最小距離不得超過參考距離。",
|
||||
"render.cpu_primary_samples": "每像素邊緣取樣數",
|
||||
"render.cpu_progressive": "CPU 漸進式",
|
||||
"render.cpu_progressive_help": "以每輪一個主取樣為單位,累積現有的 CPU 積分器。所選反射需使用一個取樣;面光源、介電質和各向異性需使用「單張影像」。",
|
||||
"render.cpu_progressive_limits": "最多 1,048,576 像素和 16,777,216 次像素 × 取樣評估。變更取樣數不會自動調整影像大小。",
|
||||
"render.cpu_progressive_seed": "CPU 取樣種子",
|
||||
"render.cpu_progressive_spp": "取樣/像素",
|
||||
"render.cpu_reflection_add": "新增粗糙反射",
|
||||
"render.cpu_reflection_depth": "反射深度(1–8)",
|
||||
"render.cpu_reflection_empty": "未選取任何實體:場景反射已關閉。",
|
||||
"render.cpu_reflection_help": "選擇實體並設為「關閉」、「鏡面」或「粗糙」。「粗糙」使用所指派的表面粗糙度。取樣數和彈射次數越多,耗時越長;這是有限度的鏡面光傳輸,並非全域照明。",
|
||||
"render.cpu_reflection_limit": "最多支援 64 個反射實體。",
|
||||
"render.cpu_reflection_mirror": "鏡面",
|
||||
"render.cpu_reflection_off": "關閉",
|
||||
"render.cpu_reflection_rough": "粗糙",
|
||||
"render.cpu_reflection_samples": "反射取樣數(1–64)",
|
||||
"render.cpu_reflections": "CPU 反射",
|
||||
"render.cpu_runtime_help": "光源和環境的數值設定會與此設定一併儲存。實體反射和 HDR 檔案仍為暫存。既有的面光源和聚光錐角數值會保留。",
|
||||
"render.cpu_sampling_mode": "CPU 取樣模式",
|
||||
"render.cpu_shadows": "投射陰影",
|
||||
"render.cpu_summary": "CPU 光線追蹤 · 反射 · HDR · 方向光和點光源",
|
||||
"render.cpu_warm_cool": "暖光與冷光",
|
||||
"render.diag_bodies": "可見實體",
|
||||
"render.diag_job_details": "擷取的工作詳細資料",
|
||||
"render.diag_last_check": "上次預檢",
|
||||
"render.diag_memory": "邏輯准入估算",
|
||||
"render.diag_not_implemented": "明確的功能邊界",
|
||||
"render.diag_preflight_details": "預檢詳細資料",
|
||||
"render.diag_request": "目前的完整請求",
|
||||
"render.diag_triangles": "三角形",
|
||||
"render.discard_continue": "捨棄草稿並繼續",
|
||||
"render.draft_confirm_help": "目前的彩現草稿含有尚未套用的編輯。請在切換前儲存、明確捨棄,或繼續編輯。",
|
||||
"render.draft_confirm_title": "保留已編輯的彩現設定",
|
||||
"render.effects_gpu_only": "這些效果需要 GPU 路徑追蹤。切換頁面不會自動變更彩現器。",
|
||||
"render.effects_help": "組合依序排列的色彩風格與影像空間鏡頭效果。彩現和原始母版重新調色使用相同的已儲存設定。原始 EXR 保持不變。這些是原創的藝術控制項,並非經校準的鏡頭或品牌底片模擬。",
|
||||
"render.effects_reset_lens": "重設鏡頭效果",
|
||||
"render.effects_seed": "圖樣種子",
|
||||
"render.emitted_axis": "發射軸",
|
||||
"render.error_area_samples": "area_samples 必須為 1、4、16 或 64",
|
||||
"render.error_binding_allocation": "無法保留材質繫結",
|
||||
"render.error_body_bound": "產品預覽超出其擷取實體的上限",
|
||||
"render.error_checkpoint_dimensions": "檢查點尺寸與擷取的請求不符",
|
||||
"render.error_decode_budget": "影像解碼預算溢位",
|
||||
"render.error_denoise_setting": "原生降噪設定無效",
|
||||
"render.error_display_dimensions": "影像顯示尺寸無效",
|
||||
"render.error_display_fit": "影像顯示無法適配此裝置",
|
||||
"render.error_display_height": "顯示高度溢位",
|
||||
"render.error_display_width": "顯示寬度溢位",
|
||||
"render.error_duplicate_face": "產品面的外觀重複",
|
||||
"render.error_export_document": "選擇目的地時,彩現匯出的文件已變更",
|
||||
"render.error_face_cap": "產品預覽超出面外觀的准入上限",
|
||||
"render.error_finish_gesture": "請先完成建模操作再預覽",
|
||||
"render.error_finish_operation": "請先完成目前的建模操作再進入彩現",
|
||||
"render.error_fixed_preview": "固定攝影棚預覽需要曝光 0、環境旋轉 0 和強度 1",
|
||||
"render.error_geometry_size": "產品幾何大小溢位",
|
||||
"render.error_glass_recipe": "未知的玻璃配方",
|
||||
"render.error_gpu_preview": "此主機無法使用 GPU 產品預覽。已儲存的設定仍可使用。",
|
||||
"render.error_gpu_samples": "不支援的 GPU 取樣數",
|
||||
"render.error_image_dimensions": "彩現影像尺寸與其工作不符",
|
||||
"render.error_image_profile": "彩現影像超出其擷取的資源設定檔",
|
||||
"render.error_interactive_command": "未繫結的互動式彩現命令",
|
||||
"render.error_job_action": "不是互動式影像工作的操作",
|
||||
"render.error_job_identity": "彩現工作識別碼無效:{error}",
|
||||
"render.error_library_reply": "彩現庫的回應無效",
|
||||
"render.error_material_binding": "缺少材質繫結",
|
||||
"render.error_no_completed_image": "彩現沒有已完成的影像",
|
||||
"render.error_no_geometry": "沒有產品幾何",
|
||||
"render.error_no_scene": "沒有產品場景",
|
||||
"render.error_opacity": "產品外觀的不透明度無效",
|
||||
"render.error_pbr_opacity": "不透明 PBR 需要不透明度為 1",
|
||||
"render.error_physical_glass": "所選配方不是物理玻璃",
|
||||
"render.error_preview_device": "產品預覽需要已安裝且具備深度緩衝區的裝置",
|
||||
"render.error_product_triangle": "產品三角形無效",
|
||||
"render.error_provisional_identity": "暫定彩現識別碼或取樣數無效",
|
||||
"render.error_quality_bounds": "品質預估超出原生範圍",
|
||||
"render.error_quality_method": "品質預估需要相同的明確 GPU 方法",
|
||||
"render.error_quality_projection": "原生品質預估無效",
|
||||
"render.error_regrade_identity": "重新調色識別碼無效:{error}",
|
||||
"render.error_scene_bodies": "缺少彩現場景實體",
|
||||
"render.error_scene_body_limit": "彩現場景超出其實體數量上限",
|
||||
"render.error_scene_identity": "彩現場景識別碼無效",
|
||||
"render.error_setup_panicked": "GPU 路徑追蹤器設定發生嚴重錯誤",
|
||||
"render.error_setup_stopped": "設定工作程序在未傳回主機的情況下停止",
|
||||
"render.error_setup_worker": "無法啟動設定工作程序:{error}",
|
||||
"render.error_source_preview": "來源預覽超出其位元組範圍",
|
||||
"render.error_surface_normals": "PBR 需要非零的表面法線",
|
||||
"render.error_surface_reply": "原創表面回覆無效",
|
||||
"render.error_triangle_bound": "產品預覽超出八百萬個三角形的來源上限",
|
||||
"render.error_triangle_count": "產品三角形數量溢位",
|
||||
"render.error_upload_slot": "產品預覽上傳槽位無法使用",
|
||||
"render.error_vertex_allocation": "產品預覽頂點配置失敗",
|
||||
"render.error_vertex_envelope": "產品預覽超出 {limit} MiB 的頂點資料上限;幾何未經簡化",
|
||||
"render.error_vertex_packing": "產品預覽的封裝與已接受的頂點資料不一致",
|
||||
"render.error_visible_solid": "請在設計中建立可見的實體,即可在此預覽。",
|
||||
"render.exit_focus": "結束聚焦",
|
||||
"render.export_display": "匯出:顯示用 PNG",
|
||||
"render.export_image_label": "匯出影像",
|
||||
"render.export_linear": "匯出:原始線性 EXR",
|
||||
"render.exr_pending": "GPU 可保留原始線性 EXR。壓縮、多圖層影像和輔助彩現通道仍無法使用。",
|
||||
"render.film_grain": "攝影顆粒",
|
||||
"render.fit": "適合產品檢視",
|
||||
"render.fixed_camera": "相機",
|
||||
"render.fixed_world": "世界",
|
||||
"render.flare_ghosts": "每個光源的鬼影環數",
|
||||
"render.flare_halo": "眩光光暈",
|
||||
"render.flare_streak": "變形鏡頭風格光條",
|
||||
"render.flare_strength": "亮部眩光強度",
|
||||
"render.flare_threshold": "場景線性眩光閾值",
|
||||
"render.fold_browser": "摺疊瀏覽器",
|
||||
"render.gpu_depth": "最大路徑深度",
|
||||
"render.gpu_options_help": "取樣數、深度、種子和曝光會隨此設定一併儲存。僅儲存目前啟用的影像方法。",
|
||||
"render.gpu_path_failed": "GPU 路徑追蹤器無法使用",
|
||||
"render.gpu_path_first_setup": "此 GPU 上的首次設定可能需要數分鐘;期間 SolidCraft 仍可正常使用。若驅動程式保留已編譯的著色器,之後啟動時會重複使用。",
|
||||
"render.gpu_path_preparing": "GPU 路徑追蹤器:正在編譯著色器…",
|
||||
"render.gpu_seed": "隨機種子",
|
||||
"render.gpu_unavailable": "此裝置尚未準備好 GPU 路徑追蹤。",
|
||||
"render.ground_apply": "擬合並儲存地面",
|
||||
"render.ground_gap": "明確指定的間隙",
|
||||
"render.ground_linear_rgb": "漫反射率 · 線性 RGB",
|
||||
"render.ground_margin": "地面範圍餘量",
|
||||
"render.hdr_details": "HDR 來源詳細資料",
|
||||
"render.hdr_embedded": "HDR 已儲存於此設計內",
|
||||
"render.hdr_embedded_help": "彩現使用設計內含的位元組;不再需要原始檔案。",
|
||||
"render.hdr_file_captured": "HDR 檔案已檢查。儲存時將內嵌這些位元組;若檔案有變更,請再次明確檢查。",
|
||||
"render.hdr_inspect": "檢查 HDR 檔案",
|
||||
"render.hdr_inspect_required": "儲存或彩現前,請先選擇並檢查 HDR 檔案。",
|
||||
"render.hdr_primaries_declared": "來源已宣告其色彩原色。",
|
||||
"render.hdr_primaries_srgb": "色彩原色:sRGB/D65",
|
||||
"render.hdr_primaries_undeclared": "來源未宣告原色;請查看彩現器的色彩需求。",
|
||||
"render.hdr_resource_missing": "此設計缺少已儲存的 HDR 資源。",
|
||||
"render.heading_batch": "批次",
|
||||
"render.heading_frame": "畫面",
|
||||
"render.heading_lens": "透鏡",
|
||||
"render.heading_results": "結果",
|
||||
"render.heading_shots": "鏡頭",
|
||||
"render.hide_shots_label": "隱藏鏡頭列",
|
||||
"render.image_advanced": "進階 GPU 控制項",
|
||||
"render.image_cancelled": "彩現已取消",
|
||||
"render.image_cancelling": "正在取消",
|
||||
"render.image_completed": "影像可匯出",
|
||||
"render.image_encoding": "正在編碼 PNG",
|
||||
"render.image_export": "匯出 PNG",
|
||||
"render.image_exposure": "曝光",
|
||||
"render.image_failed": "彩現失敗",
|
||||
"render.image_height": "高度",
|
||||
"render.image_help": "CPU 預覽影像 · PNG · 近似透明度",
|
||||
"render.image_host_missing": "未安裝影像主機。",
|
||||
"render.image_method": "影像方法",
|
||||
"render.image_overwrite": "取代現有檔案",
|
||||
"render.image_preparing": "正在準備影像",
|
||||
"render.image_progressive": "漸進式彩現",
|
||||
"render.image_queued": "等待彩現",
|
||||
"render.image_reduce": "請嘗試更小的影像或更簡單的場景。",
|
||||
"render.image_running": "正在彩現影像",
|
||||
"render.image_samples": "每像素取樣數",
|
||||
"render.image_scene_stale": "請先完成建模操作或重新整理場景,再檢查或開始影像。",
|
||||
"render.image_stale": "此影像使用後續變更之前擷取的場景。",
|
||||
"render.image_start": "開始彩現",
|
||||
"render.image_supported": "支援此彩現請求;開始時將再次檢查佇列和記憶體池。",
|
||||
@@ -2164,437 +1855,20 @@
|
||||
"render.image_unknown": "彩現狀態不可用",
|
||||
"render.image_use_largest": "使用此寬高比下的最大尺寸:",
|
||||
"render.image_width": "寬度",
|
||||
"render.include_rig_cards": "包含被動反光卡與遮光旗",
|
||||
"render.inner_half_angle": "內半角",
|
||||
"render.inspect_scene": "重新整理已擷取的場景摘要",
|
||||
"render.inspector": "檢查器",
|
||||
"render.keep_editing": "繼續編輯",
|
||||
"render.layer_down": "下移",
|
||||
"render.layer_enabled": "圖層已啟用",
|
||||
"render.layer_off_suffix": " (關閉)",
|
||||
"render.layer_remove": "移除圖層",
|
||||
"render.layer_strength": "圖層強度",
|
||||
"render.layer_up": "上移",
|
||||
"render.lens_effects_help": "眩光最多使用實際線性影像中四個彼此分離的亮光源。鬼影、光暈、光條、色差偏移和徑向畸變是影像空間中的藝術化近似。空間效果需要不透明輸出;超出範圍的取樣會延伸影像邊緣。顆粒具確定性。不會更改任何物理運算、EXR 或幾何。",
|
||||
"render.lens_effects_title": "仿透鏡與攝影效果",
|
||||
"render.library_all": "目錄",
|
||||
"render.library_all_states": "所有可用性狀態",
|
||||
"render.library_applied": "已套用資源投影。開始前請檢查完整的彩現請求。",
|
||||
"render.library_apply_hdr": "以此 HDR 環境儲存",
|
||||
"render.library_apply_maps": "使用原創 GPU 表面貼圖",
|
||||
"render.library_apply_studio": "儲存原創攝影棚投影",
|
||||
"render.library_apply_uniform": "套用均勻不透明外觀",
|
||||
"render.library_authored_help": "原創資源僅供示意,並非實測的工程資料。每個操作只會套用其所述的投影。",
|
||||
"render.library_blocked": "已封鎖 / 需要校準",
|
||||
"render.library_capabilities": "功能規格",
|
||||
"render.library_center": "燈組中心",
|
||||
"render.library_clear_search": "清除",
|
||||
"render.library_details": "規格與來源出處",
|
||||
"render.library_emitters_only_ack": "僅使用發光體;省略已記載的被動反光卡",
|
||||
"render.library_empty": "沒有符合的資源。請嘗試其他查詢或可用性篩選。",
|
||||
"render.library_environment_concepts": "環境概念",
|
||||
"render.library_extent": "主體範圍",
|
||||
"render.library_favorite": "加入收藏",
|
||||
"render.library_favorites": "收藏",
|
||||
"render.library_hdr": "內建 HDR 環境",
|
||||
"render.library_hdr_help": "將原始 HDR 內嵌於已儲存的設定。原創經度原點會明確轉換;儲存後不再需要外部檔案。",
|
||||
"render.library_inspect_only": "選擇即可檢視;不會自動套用任何內容。收藏和最近記錄屬於此開啟的文件工作階段。",
|
||||
"render.library_light_concepts": "燈光概念",
|
||||
"render.library_maps_help": "內建貼圖可連同已固定的來源和局部實例對應一併儲存。暫時的光學覆寫仍為獨立模式。",
|
||||
"render.library_material_concepts": "材質概念",
|
||||
"render.library_native_scenes": "原生攝影棚",
|
||||
"render.library_next": "下一頁",
|
||||
"render.library_omitted": "未包含",
|
||||
"render.library_physical_profiles": "物理參考設定檔",
|
||||
"render.library_previous": "上一頁",
|
||||
"render.library_quality_concepts": "品質規格",
|
||||
"render.library_recent": "最近使用",
|
||||
"render.library_replace_environment": "明確取代現有環境",
|
||||
"render.library_replace_studio": "取代已儲存的燈光、環境和佈景",
|
||||
"render.library_results": "符合的資源",
|
||||
"render.library_rig_help": "在明確指定的中心和實際範圍一次完成擬合。完整燈組包含被動反光卡;校準阻礙仍會被拒絕。儲存前會以保守標準檢查發光體與反光卡的間隙。",
|
||||
"render.library_rigs": "原創燈組",
|
||||
"render.library_save_maps": "將貼圖儲存到此設定",
|
||||
"render.library_scene_bounds": "使用已擷取的場景邊界",
|
||||
"render.library_scenes": "場景規格",
|
||||
"render.library_search": "搜尋資源庫",
|
||||
"render.library_select_hint": "在資源庫中選擇資源,即可檢視其支援的操作與限制。",
|
||||
"render.library_selene": "SELENE 不透明外觀",
|
||||
"render.library_selene_shots": "SELENE 鏡頭規格",
|
||||
"render.library_short_blocked": "無法使用",
|
||||
"render.library_short_native": "原生構成",
|
||||
"render.library_short_projection": "支援投影",
|
||||
"render.library_short_reference": "僅供參考",
|
||||
"render.library_short_specification": "僅有規格",
|
||||
"render.library_source_preview": "載入來源貼圖預覽",
|
||||
"render.library_source_preview_help": "原創基礎色來源貼圖。這不是彩現或實測的材質預覽。",
|
||||
"render.library_specification_only": "僅有規格:並非完整可執行的預設。所需的效果、資源與主體角色仍須明確實作並綁定。",
|
||||
"render.library_specs": "僅有規格",
|
||||
"render.library_status": "可用性",
|
||||
"render.library_surfaces": "原創 PBR 表面",
|
||||
"render.library_target": "目標實體",
|
||||
"render.library_title": "彩現資源庫",
|
||||
"render.library_unfavorite": "移除收藏",
|
||||
"render.library_uniform_ack": "使用均勻近似,不含空間或進階效果",
|
||||
"render.library_usable": "可執行的投影",
|
||||
"render.light_anchor_pending": "尚無法變更燈光座標系。",
|
||||
"render.lights_fixed_to": "燈光固定於",
|
||||
"render.linear_help": "在降噪、曝光、色調對應、濾鏡與背景之前,保留 32 位元浮點 RGBA 母檔。線性 sRGB/D65;覆蓋率預乘。額外記憶體須明確核可。無壓縮,也無額外通道。",
|
||||
"render.linear_retain": "保留原始線性 EXR",
|
||||
"render.linear_unavailable": "所選的已完成畫面沒有保留 EXR。請在彩現前啟用保留;較舊的 PNG 無法還原原始輻射亮度。",
|
||||
"render.look_add": "新增色彩風格",
|
||||
"render.look_bleach": "仿留銀沖印風格",
|
||||
"render.look_cool": "冷色",
|
||||
"render.look_cyanotype": "仿藍曬風格",
|
||||
"render.look_faded": "褪色相片",
|
||||
"render.look_mono": "單色",
|
||||
"render.look_sepia": "深褐色",
|
||||
"render.look_strength": "風格強度",
|
||||
"render.look_teal_orange": "青綠 / 橘橙",
|
||||
"render.look_title": "創意風格堆疊",
|
||||
"render.look_warm": "暖色",
|
||||
"render.mapping_scale": "實際重複倍率",
|
||||
"render.material_absorption": "吸收 R · G · B(每 mm)",
|
||||
"render.material_add": "新增或編輯此實體",
|
||||
"render.material_assignments": "實體指派",
|
||||
"render.material_binding_enable": "納入此實體的覆寫",
|
||||
"render.material_box": "軸向對齊長方體",
|
||||
"render.material_browse": "選擇影像…",
|
||||
"render.material_certification": "封閉玻璃採用幾何距離。拓撲、方向、重疊與光學邊界會在 GPU 準備期間檢查;「檢查」僅受理該請求。",
|
||||
"render.material_choose_body": "選擇場景實體",
|
||||
"render.material_closed": "封閉實體網格",
|
||||
"render.material_coat": "基底材質上的透明塗層",
|
||||
"render.material_coat_enable": "啟用透明塗層",
|
||||
"render.material_coat_help": "疊加在既有不透明外觀上的示意性介電塗層。權重為零時,基底維持不變。",
|
||||
"render.material_color_map": "基底顏色影像(sRGB)",
|
||||
"render.material_color_replace": "基底貼圖取代實體顏色",
|
||||
"render.material_custom": "自訂示意光學",
|
||||
"render.material_enable": "使用這些 GPU 材質覆寫",
|
||||
"render.material_filter": "篩選場景實體…",
|
||||
"render.material_filter_help": "最多顯示 128 個符合的實體。場景較大時,請縮小篩選範圍。",
|
||||
"render.material_glass": "玻璃與光學吸收",
|
||||
"render.material_glass_enable": "在此實體上使用物理玻璃",
|
||||
"render.material_glass_exclusive": "在此彩現器中,玻璃不能與透明塗層或執行階段貼圖共用同一實體。",
|
||||
"render.material_glass_help": "原創示意配方。色票顯示的是穿透率,並非彩現預覽。吸收 = −ln(線性穿透率) / 參考距離;Alpha 不變。",
|
||||
"render.material_gpu_required": "這些啟用中的材質覆寫需要 GPU 路徑追蹤。請在「檢查」或「開始」前選擇 GPU,或停用覆寫。",
|
||||
"render.material_ior": "折射率(IOR)",
|
||||
"render.material_limits": "最多 128 個實體覆寫、32 個互不相交的玻璃實體、64 個塗層與 128 個紋理綁定。相同貼圖共用儲存空間;裝置與總記憶體的實際限制仍然適用。",
|
||||
"render.material_map_invalid": "貼圖路徑必須是有界的本機路徑;法線強度必須為有限值,且介於 0 到 1 之間。",
|
||||
"render.material_map_required": "使用貼圖前,請先選擇顏色影像或法線影像。",
|
||||
"render.material_maps": "平面顏色與法線貼圖",
|
||||
"render.material_maps_enable": "為此不透明實體啟用貼圖",
|
||||
"render.material_maps_help": "PNG/JPEG 最大 1024×1024;編碼後總計 16 MiB。U 與 V 為互相正交的世界座標 mm 重複向量(最多重複 128 次)。顏色為 sRGB;法線資料採用 OpenGL +Y。檔案會在「檢查」/「開始」時擷取。",
|
||||
"render.material_metallic_map": "金屬度貼圖(線性)",
|
||||
"render.material_normal_map": "法線影像(OpenGL, +Y)",
|
||||
"render.material_normal_strength": "法線強度(0–1)",
|
||||
"render.material_origin": "貼圖原點 X · Y · Z(mm)",
|
||||
"render.material_refresh": "重新整理場景實體",
|
||||
"render.material_refresh_needed": "場景已變更。新增目標前,請先重新整理實體清單。",
|
||||
"render.material_remove": "移除此指派",
|
||||
"render.material_repeat_u": "單次 U 重複 X · Y · Z(mm)",
|
||||
"render.material_repeat_v": "單次 V 重複 X · Y · Z(mm)",
|
||||
"render.material_revalidate": "重新驗證此實體",
|
||||
"render.material_revalidate_needed": "此指派屬於較早的場景修訂版本。彩現前,請重新驗證其確切的實體身分。",
|
||||
"render.material_roughness": "塗層粗糙度(0.045–1)",
|
||||
"render.material_roughness_map": "粗糙度貼圖(線性)",
|
||||
"render.material_shadow": "穿透直線陰影光線",
|
||||
"render.material_target_missing": "實體序號與名稱已不相符。請移除此指派,並明確重新選擇目標實體。",
|
||||
"render.material_thickness": "薄板厚度(mm)",
|
||||
"render.material_thin": "薄板",
|
||||
"render.material_title": "GPU 影像材質",
|
||||
"render.material_transient": "此文件分頁的暫時光學覆寫。不會儲存到設計、彩現設定或材質資源庫中。",
|
||||
"render.material_v_up": "紋理 V 軸在影像中朝上",
|
||||
"render.material_weight": "塗層權重(0–1)",
|
||||
"render.method_cpu": "CPU 光線追蹤",
|
||||
"render.method_gpu": "GPU 路徑追蹤",
|
||||
"render.method_limit_raster": "光柵化使用固定的不透明攝影棚預覽。若要使用支援的光學控制項,請明確選擇光線追蹤方法。",
|
||||
"render.method_raster": "CPU 光柵化",
|
||||
"render.navigation_help": "相機的操作與設計中相同(偏好設定 › 導覽)。儲存設定會記錄目前相機。",
|
||||
"render.nested_enable": "經認證的巢狀介電質",
|
||||
"render.nested_help": "最多 16 個包含層;每個父層都必須是凸形,且每個子層都必須嚴格位於其內部。相交、表面接觸、薄板與直線陰影穿透都會被拒絕。相機位於內部的狀態遵循經認證的階層。不會建立液體表面,也不會合併相接的 CAD 邊界。",
|
||||
"render.opaque_image_summary": "預覽 PNG · 不透明 PBR · 混合透明",
|
||||
"render.opaque_preview_help": "不透明 PBR 和純色;透明效果使用混合。不支援折射、HDRI 和最終彩現。",
|
||||
"render.optics_remove": "移除已儲存的光學設定",
|
||||
"render.optics_replace": "明確取代此目標已儲存的光學設定",
|
||||
"render.optics_save": "將這些光學設定儲存到設定中",
|
||||
"render.optics_saved": "已儲存的光學目標",
|
||||
"render.optics_saved_help": "確切的定義、特徵與實例身分在離線重新開啟後仍會保留。遺失、被取代或隱藏的目標會被拒絕;工程材質不變。移除已儲存的目標前,請先儲存或捨棄數值編輯。",
|
||||
"render.outer_half_angle": "外半角",
|
||||
"render.output_format_status": "PNG 是確切的已完成顯示影像。選用的 GPU EXR 會在顯示濾鏡之前,保留原始場景線性 FLOAT32 輻射亮度。匯出絕不會以預覽快取或其他工作取代。",
|
||||
"render.output_lock_ratio": "鎖定長寬比",
|
||||
"render.output_over_budget": "所要求的影像超出所選的資源範圍。請明確選擇相容的尺寸或設定檔;不會自動降級。",
|
||||
"render.output_ratio_refused": "連動的尺寸超出此方法的大小上限。先前的尺寸會保留;請解除鎖定比例,或選擇較小的值。",
|
||||
"render.output_swap_orientation": "交換寬度與高度",
|
||||
"render.output_tools_inspector": "請在「輸出」檢查器中編輯尺寸與格式。",
|
||||
"render.page_aging": "老化與污染",
|
||||
"render.page_all": "所有控制項",
|
||||
"render.page_appearance": "外觀",
|
||||
"render.page_background": "背景",
|
||||
"render.page_camera": "相機",
|
||||
"render.page_diagnostics": "診斷",
|
||||
"render.page_effects": "透鏡與後製效果",
|
||||
"render.page_environment": "環境",
|
||||
"render.page_lighting": "照明",
|
||||
"render.page_output": "輸出",
|
||||
"render.page_scene": "場景",
|
||||
"render.paint_label": "塗料",
|
||||
"render.passive_stage": "地面與被動佈景",
|
||||
"render.passive_stage_help": "有限的被動面板僅屬於彩現影像,不會新增 CAD 實體或質量。放置位置在套用後即固定;與產品邊界相交時會被拒絕。CPU 不提供漫射反彈照明。",
|
||||
"render.perspective": "透視相機",
|
||||
"render.physical_acknowledge": "我了解這份部分參考資料需要檢閱等級、狀況與來源",
|
||||
"render.physical_associations": "物理候選項與角色",
|
||||
"render.physical_background_help": "真實的被動地板與牆面,與相機背景分開。環形幕(cyclorama)使用 24 條多邊形帶;它並非無限或解析平滑的表面。僅在您同意後,才會取代已儲存的佈景。",
|
||||
"render.physical_copy_plan": "複製計畫(含來源與省略項目)",
|
||||
"render.physical_cyclorama": "擬合多邊形環形幕",
|
||||
"render.physical_floor_wall": "擬合物理地板與牆面",
|
||||
"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": "面",
|
||||
"render.pick_help": "選擇實體、面或元件。點擊空白處可清除;按住 Shift 或 Ctrl 可加選或切換。醒目提示僅會顯示於即時產品檢視中。",
|
||||
"render.pick_label": "選取",
|
||||
"render.pick_up_label": "拾取",
|
||||
"render.pick_waiting": "尚未支援產品選取。",
|
||||
"render.post_contrast": "對比",
|
||||
"render.post_gain_b": "藍色增益",
|
||||
"render.post_gain_g": "綠色增益",
|
||||
"render.post_gain_r": "紅色增益",
|
||||
"render.post_help": "僅供創意呈現。RGB 增益在色調對應之前套用;對比、飽和度與暈影則在顯示線性光中接續套用。「開始彩現」會產生新的傳輸取樣。「重新調色」使用所選保留的原始母檔,不進行降噪,也不發射新光線。",
|
||||
"render.post_reset": "重設呈現濾鏡",
|
||||
"render.post_saturation": "飽和度",
|
||||
"render.post_title": "呈現濾鏡",
|
||||
"render.post_vignette": "暈影",
|
||||
"render.preview": "整合預覽",
|
||||
"render.preview_help": "光柵著色和混合透明度。暫不支援折射、HDRI 和最終彩現。",
|
||||
"render.preview_image_settings_help": "即時預覽維持固定的攝影棚照明。曝光與影像照明會在您按下「檢查」或「開始」時套用於影像工作。",
|
||||
"render.preview_lighting_help": "即時預覽僅顯示直接光,不含陰影。",
|
||||
"render.preview_retry": "重試即時預覽",
|
||||
"render.preview_unavailable_help": "無法使用即時預覽。已儲存的設定與影像工作控制項仍可使用。請變更場景或明確重試;遭拒絕的場景不會在每個影格重新建構。",
|
||||
"render.preview_upload": "即時預覽頂點資料",
|
||||
"render.preview_upload_help": "預覽頂點的確切位元組數,並非 GPU 或處理程序的總記憶體。幾何不會經過簡化。影像工作有各自的受理條件與照明。",
|
||||
"render.progressive_image_summary": "預覽 PNG · PBR 與玻璃 · 漸進式取樣",
|
||||
"render.property_conditions": "條件:{value}",
|
||||
"render.property_fallback": "屬性",
|
||||
"render.property_qualifier": "限定條件:{value}",
|
||||
"render.property_source": "來源:{id} - {locator}",
|
||||
"render.property_uncertainty": "不確定度:{value}",
|
||||
"render.property_unknown": "未知",
|
||||
"render.quality_ack": "確認固定預算的投影與省略項目",
|
||||
"render.quality_apply": "使用此固定取樣預算",
|
||||
"render.quality_projection_help": "Q 預設包含未來的需求。此操作僅投影受支援的固定取樣 PNG 預算,並明確省略自適應停止、原始線性輸出與獨立的穿透上限。",
|
||||
"render.quality_review": "檢閱取樣預算",
|
||||
"render.queue_pending": "此介面沒有自動化的正式生產佇列或動畫彩現器。已完成的靜態影像仍可單獨匯出。",
|
||||
"render.radial_distortion": "徑向畸變",
|
||||
"render.regrade_action": "重新調色原始母檔(不降噪)",
|
||||
"render.regrade_help": "以這些呈現控制項,從所選的已完成原始母檔建立獨立的 PNG。不會追蹤光線、變更已擷取的尺寸/相機/光源,也不會覆寫 EXR。不會重新套用降噪:原始母檔不保留其引導資料。衍生檔仍可透過相同的工作控制項匯出。",
|
||||
"render.regrade_label": "重新調色",
|
||||
"render.reload_settings": "重新載入已儲存設定",
|
||||
"render.replace_ground": "確認取代現有佈景",
|
||||
"render.replace_label": "取代",
|
||||
"render.replace_saved_surface": "取代此目標現有的已儲存繫結",
|
||||
"render.restore_camera": "還原已儲存相機",
|
||||
"render.result_actual_help": "每個顯示像素對應一個影像像素。",
|
||||
"render.result_actual_size": "100%",
|
||||
"render.result_capture": "擷取的修訂版本",
|
||||
"render.result_current": "擷取的模型修訂版本為最新。",
|
||||
"render.result_empty": "尚無已完成的影像。請檢查設定,然後開始彩現。",
|
||||
"render.result_fit": "適合影像",
|
||||
"render.result_job": "工作",
|
||||
"render.result_navigation": "平移與縮放遵循「導覽」偏好設定。Space + 拖曳可平移。",
|
||||
"render.result_open": "在主畫布中開啟已完成的影像。",
|
||||
"render.result_preview": "即時預覽",
|
||||
"render.result_provisional": "暫定 CPU 影像",
|
||||
"render.result_provisional_help": "此檢查點無法匯出。匯出會保留上一個已完成的結果;取消則會回到該結果。",
|
||||
"render.result_reduced_display": "顯示預覽已縮減至視埠裝置/快取的上限。保留的 PNG 與匯出檔維持完整解析度;此縮減檢視無法進行原生 100% 檢視。",
|
||||
"render.result_retained": "顯示上一張已完成的影像;較新的彩現尚未取代它。",
|
||||
"render.result_sequence": "檢查點",
|
||||
"render.result_title": "彩現結果",
|
||||
"render.return_design": "返回設計",
|
||||
"render.review_stale": "此檢閱已過期。請先解決或重新檢查,再套用。",
|
||||
"render.ribbon_aging": "老化",
|
||||
"render.ribbon_backgrounds": "背景",
|
||||
"render.ribbon_check": "檢查",
|
||||
"render.ribbon_check_help": "驗證「彩現」將提交的目前草稿原貌。不會自動改用其他彩現器、默默調整大小或取代資源。",
|
||||
"render.ribbon_edit_lights": "編輯燈光",
|
||||
"render.ribbon_effects": "效果",
|
||||
"render.ribbon_environment_help": "HDR 照明、僅限相機的背景與實體佈景表面為各自獨立的控制項,取代時需明確同意。",
|
||||
"render.ribbon_finish_dialog": "請先完成目前的檔案或確認對話方塊。",
|
||||
"render.ribbon_frame": "取景",
|
||||
"render.ribbon_hdr_library": "HDR 資源庫",
|
||||
"render.ribbon_job_active": "彩現正在執行;請先明確取消,再開始另一個。",
|
||||
"render.ribbon_light_rigs": "燈組",
|
||||
"render.ribbon_lights_help": "檢視原創燈組或編輯受支援的光源,在您明確套用變更之前,不會取代目前的設定。",
|
||||
"render.ribbon_materials": "材質",
|
||||
"render.ribbon_materials_help": "開啟共用的外觀檢視器與原創表面資源庫。光學編輯絕不會默默變更工程屬性。",
|
||||
"render.ribbon_no_active_job": "沒有可取消的作用中彩現。",
|
||||
"render.ribbon_no_result": "請先完成彩現,再匯出其確切影像。",
|
||||
"render.ribbon_no_saved_camera": "請先儲存鏡頭,再還原其相機。",
|
||||
"render.ribbon_saved_camera": "已儲存的相機",
|
||||
"render.ribbon_shots": "鏡頭",
|
||||
"render.ribbon_studios": "攝影棚",
|
||||
"render.ribbon_studios_help": "瀏覽已實作的攝影棚組合,並在套用前檢閱將取代的內容。僅為概念的場景仍會加上標示。",
|
||||
"render.ribbon_surface_maps": "表面貼圖",
|
||||
"render.sampling_title": "自適應與穿透取樣",
|
||||
"render.save_continue": "儲存並繼續",
|
||||
"render.save_setup": "儲存設定",
|
||||
"render.save_shot": "儲存鏡頭",
|
||||
"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 幾何與工程材質皆會保留。",
|
||||
"render.scene_preserves_design": "為可見的主體組合燈光與被動佈景。幾何、工程材質與相機維持獨立。",
|
||||
"render.scene_replaces": "取代內容",
|
||||
"render.scene_review": "解析並檢閱",
|
||||
"render.scene_review_help": "套用前請檢閱完整的原生組合。未繫結的進階 SCENE 規格不會被替代。",
|
||||
"render.scene_scope_details": "組合範圍",
|
||||
"render.setup_legacy_environment": "此舊版設定使用傳統的環境旋轉或強度。這些值會保留;儲存完整影像工作前,請明確轉換它們。",
|
||||
"render.setup_name": "設定名稱",
|
||||
"render.setup_numeric_help": "「儲存」會記錄作用中的影像方法及其所有數值設定。重新載入或選擇其他設定會取代此草稿;相機請另行還原。",
|
||||
"render.setup_offline_help": "沒有彩現器也可編輯並儲存此方法。「檢查」與「開始」需要可用的主機。",
|
||||
"render.setup_runtime_unsupported": "尚無法儲存此完整工作:其中包含貼圖或場景實體繫結。請先移除這些暫時性輸入,再儲存可攜式設定。",
|
||||
"render.setup_save_missing": "指令完成後,找不到已儲存的彩現組態。",
|
||||
"render.sharpening": "細節銳利化",
|
||||
"render.shell_tab_appearance": "彩現外觀",
|
||||
"render.shell_tab_camera": "彩現相機",
|
||||
"render.shell_tab_scene": "彩現場景",
|
||||
"render.shot_duplicate": "複製已儲存的鏡頭",
|
||||
"render.shot_manager": "已儲存的鏡頭",
|
||||
"render.shot_new_name": "新鏡頭名稱",
|
||||
"render.shot_remove": "移除已儲存的鏡頭",
|
||||
"render.shot_remove_help": "僅移除此已儲存的設定。來源幾何與已完成的影像都會保留;此操作可復原。",
|
||||
"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",
|
||||
"render.size_hd": "HD",
|
||||
"render.sky_ground": "地面 · 線性 RGB",
|
||||
"render.sky_horizon": "地平線 · 線性 RGB",
|
||||
"render.sky_zenith": "天頂 · 線性 RGB",
|
||||
"render.soft_focus": "柔焦",
|
||||
"render.spot_cone": "聚光錐角",
|
||||
"render.stage_frame": "畫面",
|
||||
"render.stage_light": "燈光",
|
||||
"render.stage_look": "風格",
|
||||
"render.stage_orthographic": "正交",
|
||||
"render.stage_output": "輸出",
|
||||
"render.stage_panel_count": "已儲存的面板",
|
||||
"render.stage_preview_note": "整合式預覽 · 直接光",
|
||||
"render.strip_label": "鏡頭列",
|
||||
"render.studio_add_area": "新增面光源",
|
||||
"render.studio_ambient": "恆定環境輻射亮度",
|
||||
"render.studio_ambient_rgb": "恆定環境輻射亮度(線性 RGB)",
|
||||
"render.studio_aperture": "光圈半徑",
|
||||
"render.studio_area": "面光源",
|
||||
"render.studio_area_help": "最多四個單面矩形。互相正交的半邊長 U 與 V 決定大小;U × V 朝向受光面。座標為世界座標,單位為毫米。",
|
||||
"render.studio_center": "中心 X, Y, Z(mm)",
|
||||
"render.studio_denoise": "降低完成影像的雜訊",
|
||||
"render.studio_denoise_help": "最終的邊緣感知濾鏡;細節可能會變柔和。玻璃與銳利的鏡面反射表面會略過此濾鏡。",
|
||||
"render.studio_direction": "朝向太陽的方向(X, Y, Z)",
|
||||
"render.studio_environment": "環境",
|
||||
"render.studio_environment_bounds": "環境旋轉必須為有限值,且在 ±1,000,000 弧度以內;強度必須介於 0 到 100 之間。",
|
||||
"render.studio_environment_none": "無環境貼圖",
|
||||
"render.studio_focus": "軸向對焦距離",
|
||||
"render.studio_half_u": "半邊長 U(mm)",
|
||||
"render.studio_half_v": "半邊長 V(mm)",
|
||||
"render.studio_hdr": "Radiance HDR 檔案",
|
||||
"render.studio_hdr_browse": "選擇 HDR…",
|
||||
"render.studio_hdr_help": "Radiance .hdr,2:1 全景圖,最大 2048 × 1024。不支援 EXR。色彩假設僅在未指定原色時適用。",
|
||||
"render.studio_hdr_required": "請選擇本機的 .hdr 檔案。",
|
||||
"render.studio_hdr_srgb": "將未指定的原色視為線性 sRGB",
|
||||
"render.studio_intensity": "環境強度",
|
||||
"render.studio_lens": "景深",
|
||||
"render.studio_lens_perspective": "景深需要透視相機。",
|
||||
"render.studio_lighting": "攝影棚燈光",
|
||||
"render.studio_lighting_enable": "使用自訂攝影棚燈光",
|
||||
"render.studio_needs_review": "檢閱方法與設定",
|
||||
"render.studio_radiance": "線性 RGB 輻射亮度",
|
||||
"render.studio_ready": "可供檢查",
|
||||
"render.studio_remove_area": "移除面光源",
|
||||
"render.studio_rotation": "環境旋轉",
|
||||
"render.studio_runtime_help": "攝影棚的數值設定和已檢查的 HDR 來源可隨此設定一併儲存。實體的光學/材質覆寫仍為暫時性,不會變更已儲存的外觀。",
|
||||
"render.studio_saved": "已儲存的設定 / 目前相機",
|
||||
"render.studio_sky": "藝術天空",
|
||||
"render.studio_sky_help": "用於照明的原創天空漸層;並非經校準的大氣模型。",
|
||||
"render.studio_sun": "太陽",
|
||||
"render.studio_sun_radius": "角半徑",
|
||||
"render.studio_title": "彩現攝影棚",
|
||||
"render.studio_tone_map": "色調對應",
|
||||
"render.subsurface_enable": "內部散射隨機漫步",
|
||||
"render.subsurface_help": "僅限封閉的介電質。散射係數與 RGB 吸收係數是各自獨立的每 mm 係數。光徑會在實際幾何內部散射,然後在邊界處折射或反射。請停用直接陰影穿透、表面降噪和外部體積,並使用單一菲涅爾分層。這不是擴散著色器、密度網格或實測材質。高密度介質可能需要更多取樣數和路徑深度。",
|
||||
"render.subsurface_rate": "散射碰撞率",
|
||||
"render.tone_aces_fitted": "ACES 擬合",
|
||||
"render.tone_reinhard": "Reinhard",
|
||||
"render.transmission_scope": "GPU 玻璃支援經認證的互不相交介質。不提供不受限制的巢狀介質,也不提供無雜訊的穿透估計器。",
|
||||
"render.transmission_strata": "條件式菲涅爾分層",
|
||||
"render.transmission_strata_help": "1 會保留原始亂數串流。2 或 4 條子光徑會對第一次玻璃反射/穿透的選擇進行分層,司乃耳定律與吸收定律保持不變。光徑越多,運算量越大;這不是降噪、巢狀介質支援,也不保證彩現更快。",
|
||||
"render.tree_add_light": "新增燈光",
|
||||
"render.tree_add_shot": "新增鏡頭",
|
||||
"render.tree_browser": "彩現瀏覽器",
|
||||
"render.tree_by_look": "依風格",
|
||||
"render.tree_by_object": "依物件",
|
||||
"render.tree_constant": "恆定環境",
|
||||
"render.tree_default": "預設",
|
||||
"render.tree_directional": "方向光",
|
||||
"render.tree_environment": "環境",
|
||||
"render.tree_environment_values": "{rotation}° · {intensity}×",
|
||||
"render.tree_face_count": "{count} 個面",
|
||||
"render.tree_face_one": "{count} 個面",
|
||||
"render.tree_fixed_studio": "固定攝影棚",
|
||||
"render.tree_hdr": "HDR",
|
||||
"render.tree_lights": "燈光",
|
||||
"render.tree_lights_pending": "具名燈光命令尚無法使用。",
|
||||
"render.tree_mixed": "混合",
|
||||
"render.tree_more": "另外 {count} 個",
|
||||
"render.tree_mute": "靜音燈光",
|
||||
"render.tree_no_environment": "無環境",
|
||||
"render.tree_objects": "物件",
|
||||
"render.tree_output": "輸出",
|
||||
"render.tree_point": "點",
|
||||
"render.tree_rectangle": "矩形",
|
||||
"render.tree_search": "搜尋物件",
|
||||
"render.tree_selection": "彩現所選內容",
|
||||
"render.tree_shots": "鏡頭",
|
||||
"render.tree_sky": "天空",
|
||||
"render.tree_solo": "獨奏燈光",
|
||||
"render.tree_spot": "聚光燈",
|
||||
"render.tree_sun": "太陽",
|
||||
"render.tree_switch": "切換",
|
||||
"render.tree_user_one": "{count} 個物件",
|
||||
"render.tree_users": "{count} 個物件",
|
||||
"render.unavailable_preview": "此設定要求的預覽設定檔無法使用。",
|
||||
"render.unsaved": "未儲存的預覽變更",
|
||||
"render.use_preview": "使用整合預覽",
|
||||
"render.volume_albedo": "散射反照率 · 線性 RGB",
|
||||
"render.volume_anisotropy": "相位平均餘弦",
|
||||
"render.volume_enable": "啟用參與介質",
|
||||
"render.volume_extinction": "消光係數",
|
||||
"render.volume_help": "GPU 光傳輸在單一有限長方體內進行,於相機光徑和間接光徑上套用比爾衰減與 HG 散射。使用明確指定的攝影棚/HDR/天空照明,包含陰影衰減。正的相位餘弦會向前散射。必須明確停用玻璃邊界和表面降噪。消光係數為零時,光傳輸保持不變。這不是真實的月球大氣、密度網格、體積發光、光譜傳輸,也不是經認證的材質屬性。",
|
||||
"render.volume_max": "最大邊界 · 世界座標 mm",
|
||||
"render.volume_min": "最小邊界 · 世界座標 mm",
|
||||
"render.volume_title": "有界均質體積",
|
||||
"render.wait_batch": "尚未支援:已儲存鏡頭的彩現佇列",
|
||||
"render.wait_compare": "尚未支援:保留影像的比較",
|
||||
"render.wait_paint": "尚未支援:產品拾取與外觀塗刷",
|
||||
"render.wait_pick_up": "尚未支援:產品拾取與外觀吸取",
|
||||
"render.wait_replace": "尚未支援:取代外觀的所有使用處",
|
||||
"render.wait_solo": "尚未支援:燈光可見性與獨奏控制項",
|
||||
"sample.bracket.about": "以 2 mm 規則建立的基礎凸緣、邊線凸緣和摺邊",
|
||||
"sample.bracket.name": "鈑金支架",
|
||||
"sample.enclosure.about": "薄殼盒體,含唇緣、螺絲凸座、墊塊和卡扣",
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
"All open designs": "所有打开的设计",
|
||||
"Allowed expressions, separated by semicolons": "允许的表达式,以分号分隔",
|
||||
"Also deletes": "同时删除",
|
||||
"Also place them from the origin (fully constrained)": "同时从原点定位(完全约束)",
|
||||
"Anchor": "基准点",
|
||||
"Angle 2": "角度 2",
|
||||
"Angle Display": "角度显示",
|
||||
@@ -181,9 +180,6 @@
|
||||
"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": "放弃",
|
||||
"Distance": "距离",
|
||||
@@ -200,8 +196,6 @@
|
||||
"Drag to turn the rest in 5° steps. Hold Alt or Ctrl to turn it freely.": "拖动以 5° 为步长旋转垫块。按住 Alt 或 Ctrl 可自由旋转。",
|
||||
"Drive Joint": "驱动接头",
|
||||
"Duration": "时长",
|
||||
"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.": "每个新图形也会从草图原点标注尺寸,直到没有任何自由度。已连接到原点或已放置几何的图形不会再加。",
|
||||
"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.": "您绘制的每个图形都会按绘制的大小获得所有尺寸(输入的值优先)。草图已确定的尺寸会被跳过。",
|
||||
"Edge": "边",
|
||||
"Edges": "边",
|
||||
"Edit": "编辑",
|
||||
@@ -260,8 +254,6 @@
|
||||
"Flanges": "法兰",
|
||||
"Flat Pattern": "展开图",
|
||||
"Flat size": "平板尺寸",
|
||||
"Flat size X": "平板尺寸 X",
|
||||
"Flat size Y": "平板尺寸 Y",
|
||||
"Follow camera (Look At sketches)": "跟随相机(注视草图)",
|
||||
"Follow the camera (Look At sketches)": "跟随相机(注视草图)",
|
||||
"Font": "字体",
|
||||
@@ -595,7 +587,6 @@
|
||||
"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": "起始鼓度",
|
||||
@@ -763,7 +754,6 @@
|
||||
"appearance.pbr_opaque": "不透明 PBR",
|
||||
"appearance.roughness": "粗糙度",
|
||||
"appearance.surface_response": "表面响应",
|
||||
"at the face": "在面上",
|
||||
"bom.group_children": "子零件并入所属零件的行",
|
||||
"browser.body_name": "实体{number}",
|
||||
"browser.calibrate_title": "校准:{name}",
|
||||
@@ -772,7 +762,6 @@
|
||||
"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}",
|
||||
@@ -823,9 +812,7 @@
|
||||
"cmd.component_create": "新建组件",
|
||||
"cmd.component_cycle_color": "循环切换组件颜色",
|
||||
"cmd.component_delete": "删除组件",
|
||||
"cmd.component_edit_in_place": "原位编辑",
|
||||
"cmd.component_extract": "提取到文件",
|
||||
"cmd.component_finish_edit": "完成编辑",
|
||||
"cmd.component_from_bodies": "由实体创建组件",
|
||||
"cmd.component_insert_design": "插入设计",
|
||||
"cmd.component_links": "列出链接",
|
||||
@@ -1000,10 +987,6 @@
|
||||
"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": "渲染所有镜头",
|
||||
@@ -1026,20 +1009,15 @@
|
||||
"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",
|
||||
@@ -1142,7 +1120,6 @@
|
||||
"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": "切换被驱动尺寸",
|
||||
@@ -1194,7 +1171,6 @@
|
||||
"cmd.view.render_dock": "移动渲染停靠面板",
|
||||
"cmd.view.render_focus": "渲染专注视图",
|
||||
"cmd.view_ambient_occlusion": "环境光遮蔽",
|
||||
"cmd.view_angle_readout": "视图角度",
|
||||
"cmd.view_apply": "还原命名视图",
|
||||
"cmd.view_back": "后视图",
|
||||
"cmd.view_bottom": "仰视图",
|
||||
@@ -1270,7 +1246,6 @@
|
||||
"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 点击也可切换选择。检查预览后,再按“确定”。",
|
||||
@@ -1330,7 +1305,6 @@
|
||||
"help.appearance.assign": "为所选的实体、面或组件设置颜色或材质外观,不改变其重量。用于区分零件或预览表面效果。",
|
||||
"help.attributions": "署名信息 (ATTRIBUTION.md)",
|
||||
"help.build": "版本 {version} · 提交 {commit} · 构建日期 {date}",
|
||||
"help.component.finish_edit": "保存正在原位编辑的零件,返回其装配体,并用该零件更新装配体。对链接零件的更改完成后使用。",
|
||||
"help.connect_note": "请重启各个助手以加载 SolidCraft 服务器。每个被修改的文件都已备份为 .bak。",
|
||||
"help.connect_title": "连接 AI 助手",
|
||||
"help.face.delete": "删除所选的面,并由相邻的面封闭留下的缺口,也可以让缺口保持开放、成为曲面。用于去掉碍事的圆角、孔或凸台。",
|
||||
@@ -1341,7 +1315,6 @@
|
||||
"help.mit": "MIT 许可证",
|
||||
"help.notice": "NOTICE",
|
||||
"help.parameters.change": "打开驱动设计尺寸的命名数值和表达式表。用于在一处修改尺寸,让所有引用它的特征随之更新。",
|
||||
"help.pointcloud.insert": "将 PLY、XYZ 或 PTS 扫描作为点云插入到模型旁。根据测量数据建模时,可将其用作参考。",
|
||||
"help.solid.align": "移动所选实体,使其上的点、面或平面与别处的点、面或平面对齐。用于把一个零件贴靠到另一个零件上,而无需测量距离。",
|
||||
"help.solid.chamfer": "用指定的距离在所选边上切出一个平直的斜面。用于去除锐边,或使销钉、螺钉更容易插入。",
|
||||
"help.solid.combine": "将多个实体合并、从目标实体中切除,或只保留它们共有的体积,还可以保留工具实体。用于合并零件、用一个形体切割另一个形体,或找出两个实体重叠的位置。",
|
||||
@@ -1367,8 +1340,6 @@
|
||||
"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": "删除此个人预设?现有孔不会改变。",
|
||||
@@ -1736,8 +1707,6 @@
|
||||
"prefs.anti_aliasing_note": "平滑锯齿边缘。",
|
||||
"prefs.applies_on_restart": "重启后生效",
|
||||
"prefs.assistant": "助手",
|
||||
"prefs.auto_operation": "自动选择操作",
|
||||
"prefs.auto_operation_note": "特征窗口根据新材料接触的对象选择连接、新建实体或剪切,直到您手动选择。关闭:保持窗口打开时的操作。",
|
||||
"prefs.axes": "坐标轴",
|
||||
"prefs.axis": "轴",
|
||||
"prefs.bodies_folders": "实体文件夹",
|
||||
@@ -1813,19 +1782,8 @@
|
||||
"prefs.layout_note": "紧凑模式将面板停靠在窗口边缘。浏览器半透明仅适用于浮动模式。助手面板的位置在助手页面中设置。",
|
||||
"prefs.left": "左侧",
|
||||
"prefs.left_click": "左键单击",
|
||||
"prefs.linked_designs": "链接设计",
|
||||
"prefs.live_input": "实时输入",
|
||||
"prefs.live_preview": "实时预览",
|
||||
"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": "鼠标与触控板",
|
||||
@@ -1874,24 +1832,11 @@
|
||||
"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": "圆心和中点对齐辅助线",
|
||||
"prefs.sketch_tracking_guides_note": "将光标悬停在圆心或直线边的中点,然后水平或垂直移动以显示临时虚线辅助线。",
|
||||
"prefs.sketching": "草图",
|
||||
"prefs.snap_drawing_note": "在绘制或拖动草图点时使用。",
|
||||
"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": "已选择",
|
||||
@@ -1899,16 +1844,12 @@
|
||||
"prefs.supersampling_brief": "以更高分辨率绘制后滤波缩小:更清晰,但负载更高。",
|
||||
"prefs.tab_accent_note": "强调色跟随当前选项卡",
|
||||
"prefs.theme_file": "主题文件",
|
||||
"prefs.theme_file_or_paste": "文件或粘贴",
|
||||
"prefs.theme_follow": "跟随主题文件",
|
||||
"prefs.theme_follow_error": "未应用:{error}。保留上次有效的主题。",
|
||||
"prefs.theme_follow_launch": "仅本次运行跟随 (--theme-file);其他工具改写文件时会重新加载。",
|
||||
"prefs.theme_follow_note": "其他工具改写文件时会重新加载。选择主题将停止跟随。",
|
||||
"prefs.theme_follow_off": "关闭",
|
||||
"prefs.theme_follow_stop": "停止跟随",
|
||||
"prefs.theme_import_hint": "单击选择主题文件,拖放到此处,或复制主题后按 Ctrl+V(Mac 上为 Cmd+V)",
|
||||
"prefs.theme_paste": "粘贴主题",
|
||||
"prefs.theme_paste_none": "剪贴板中没有 SolidCraft 主题",
|
||||
"prefs.timeline_half_hour": "−30 分钟",
|
||||
"prefs.timeline_now": "现在",
|
||||
"prefs.timeline_quarter_hour": "−15",
|
||||
@@ -1974,47 +1915,7 @@
|
||||
"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": "原生组合;应用前请审阅",
|
||||
"render.availability_projection": "支持的部分映射;请查看范围和省略项",
|
||||
"render.availability_reference": "参考数据;不会自动分配",
|
||||
@@ -2069,13 +1970,6 @@
|
||||
"render.camera_set": "设置渲染相机",
|
||||
"render.camera_target": "目标位置 · mm",
|
||||
"render.camera_yaw": "偏航",
|
||||
"render.category_metal": "金属",
|
||||
"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",
|
||||
@@ -2175,7 +2069,6 @@
|
||||
"render.effects_reset_lens": "重置镜头效果",
|
||||
"render.effects_seed": "图案种子",
|
||||
"render.emitted_axis": "发射轴",
|
||||
"render.environment_tiles_require_raytrace": "环境需要 CPU 或 GPU 光线追踪。请在输出编辑器中选择。",
|
||||
"render.error_area_samples": "area_samples 必须为 1、4、16 或 64",
|
||||
"render.error_binding_allocation": "无法分配材质绑定",
|
||||
"render.error_body_bound": "产品预览超出捕获实体数上限",
|
||||
@@ -2247,8 +2140,6 @@
|
||||
"render.flare_strength": "高光耀斑强度",
|
||||
"render.flare_threshold": "场景线性耀斑阈值",
|
||||
"render.fold_browser": "折叠浏览器",
|
||||
"render.frame_presets": "构图预设",
|
||||
"render.frame_presets_pending": "随镜头编辑器推出",
|
||||
"render.gpu_depth": "最大路径深度",
|
||||
"render.gpu_options_help": "采样数、深度、种子和曝光会随此配置保存。仅保存当前图像方法。",
|
||||
"render.gpu_path_failed": "GPU 路径追踪器不可用",
|
||||
@@ -2309,36 +2200,6 @@
|
||||
"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": "启用此层",
|
||||
@@ -2350,12 +2211,8 @@
|
||||
"render.lens_effects_title": "镜头风格与摄影",
|
||||
"render.library_all": "目录",
|
||||
"render.library_all_states": "全部可用状态",
|
||||
"render.library_appearance_available": "可用外观",
|
||||
"render.library_applicable": "适用资源",
|
||||
"render.library_applied": "资源映射已应用;开始前请检查完整渲染请求。",
|
||||
"render.library_apply_appearance": "应用外观",
|
||||
"render.library_apply_hdr": "使用此 HDR 环境保存",
|
||||
"render.library_apply_hint": "点击应用 · 拖到模型上",
|
||||
"render.library_apply_maps": "使用已制作的 GPU 表面贴图",
|
||||
"render.library_apply_studio": "保存已制作的工作室映射",
|
||||
"render.library_apply_uniform": "应用均匀不透明外观",
|
||||
@@ -2371,23 +2228,15 @@
|
||||
"render.library_extent": "主体范围",
|
||||
"render.library_favorite": "添加收藏",
|
||||
"render.library_favorites": "收藏",
|
||||
"render.library_filters": "目录与可用性",
|
||||
"render.library_grid": "网格",
|
||||
"render.library_hdr": "内置 HDR 环境",
|
||||
"render.library_hdr_help": "将原始 HDR 嵌入已保存设置。明确转换创作时的经度原点;保存后不再需要外部文件。",
|
||||
"render.library_in_design": "设计中",
|
||||
"render.library_inspect_only": "选择仅用于检查,不会自动应用;收藏和最近记录属于当前打开的文档会话。",
|
||||
"render.library_ior": "折射率",
|
||||
"render.library_light_concepts": "照明概念",
|
||||
"render.library_list": "列表",
|
||||
"render.library_maps_help": "内置贴图可连同固定源和局部实例映射一起保存;临时光学覆盖仍属于独立模式。",
|
||||
"render.library_material_concepts": "材质概念",
|
||||
"render.library_metallic": "金属度",
|
||||
"render.library_native_scenes": "原生工作室",
|
||||
"render.library_next": "下一页",
|
||||
"render.library_omitted": "未包含的内容",
|
||||
"render.library_opacity": "不透明度",
|
||||
"render.library_other": "其他外观",
|
||||
"render.library_physical_profiles": "物理参考配置",
|
||||
"render.library_previous": "上一页",
|
||||
"render.library_quality_concepts": "质量规格",
|
||||
@@ -2397,7 +2246,6 @@
|
||||
"render.library_results": "匹配资源",
|
||||
"render.library_rig_help": "在明确的中心和物理范围下适配一次。完整组合包含被动反光板;需要校准时仍会拒绝。保存前检查发光体及反光板的保守间隙。",
|
||||
"render.library_rigs": "已制作的灯光组合",
|
||||
"render.library_roughness": "粗糙度",
|
||||
"render.library_save_maps": "将贴图保存到此设置",
|
||||
"render.library_scene_bounds": "使用捕获的场景边界",
|
||||
"render.library_scenes": "场景规格",
|
||||
@@ -2421,7 +2269,6 @@
|
||||
"render.library_unfavorite": "移除收藏",
|
||||
"render.library_uniform_ack": "使用不含空间或高级效果的均匀近似",
|
||||
"render.library_usable": "可执行映射",
|
||||
"render.library_users": "使用位置",
|
||||
"render.light_anchor_pending": "暂不支持更改灯光坐标系。",
|
||||
"render.lights_fixed_to": "灯光固定到",
|
||||
"render.linear_help": "保留降噪、曝光、色调映射、滤镜和背景处理前的 32 位浮点 RGBA。线性 sRGB/D65,预乘覆盖率。额外内存经明确准入检查;无压缩或额外通道。",
|
||||
@@ -2505,34 +2352,12 @@
|
||||
"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": "外观",
|
||||
@@ -2593,18 +2418,10 @@
|
||||
"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": "重新调色",
|
||||
@@ -2613,7 +2430,6 @@
|
||||
"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": "捕获版本",
|
||||
@@ -2671,7 +2487,6 @@
|
||||
"render.scene_review": "解析并审阅",
|
||||
"render.scene_review_help": "应用前请审阅完整原生组合;不会替换为未绑定的高级 SCENE 规格。",
|
||||
"render.scene_scope_details": "组合范围",
|
||||
"render.search_appearances": "搜索 {count} 种外观",
|
||||
"render.setup_legacy_environment": "此旧设置使用旧版环境旋转或强度。这些值会保留;保存完整图像任务前,请明确转换它们。",
|
||||
"render.setup_name": "设置名称",
|
||||
"render.setup_numeric_help": "保存会记录当前图像方法及其全部数值设置。重新加载或选择其他设置会替换此草稿;相机需单独恢复。",
|
||||
@@ -2681,7 +2496,6 @@
|
||||
"render.sharpening": "细节锐化",
|
||||
"render.shell_tab_appearance": "外观",
|
||||
"render.shell_tab_camera": "相机",
|
||||
"render.shell_tab_lighting": "灯光",
|
||||
"render.shell_tab_scene": "场景",
|
||||
"render.shot_duplicate": "复制已保存镜头",
|
||||
"render.shot_manager": "已保存镜头",
|
||||
@@ -2691,23 +2505,10 @@
|
||||
"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": "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",
|
||||
@@ -2720,9 +2521,6 @@
|
||||
"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": "恒定环境辐亮度",
|
||||
@@ -2761,10 +2559,8 @@
|
||||
"render.studio_sky_help": "原创天空渐变用于照明,并非校准的大气模型。",
|
||||
"render.studio_sun": "太阳",
|
||||
"render.studio_sun_radius": "角半径",
|
||||
"render.studio_tiles_require_gpu": "摄影棚需要 GPU 路径追踪。请在输出编辑器中选择 GPU。",
|
||||
"render.studio_title": "渲染工作室",
|
||||
"render.studio_tone_map": "色调映射",
|
||||
"render.studios_environments": "摄影棚和环境",
|
||||
"render.subsurface_enable": "内部散射随机游走",
|
||||
"render.subsurface_help": "仅适用于封闭介质。散射和RGB吸收使用独立的每毫米系数。路径在实际几何内部散射,并在边界折射或反射。关闭直线透射阴影、表面降噪和外部体积;使用一个菲涅耳分层。这不是扩散着色器、密度网格或实测材料。高密度可能需要更多样本和路径深度。",
|
||||
"render.subsurface_rate": "散射碰撞率",
|
||||
@@ -2818,7 +2614,6 @@
|
||||
"render.volume_max": "最大边界 · 世界坐标 mm",
|
||||
"render.volume_min": "最小边界 · 世界坐标 mm",
|
||||
"render.volume_title": "有界均匀体积",
|
||||
"render.wait_add_light": "尚不支持:创建命名灯光",
|
||||
"render.wait_batch": "暂不支持:已保存镜头渲染队列",
|
||||
"render.wait_compare": "暂不支持:保留图像比较",
|
||||
"render.wait_paint": "暂不支持:产品拾取和外观涂绘",
|
||||
@@ -2873,7 +2668,6 @@
|
||||
"select two components": "请选择两个组件",
|
||||
"set by the sketch's lines": "由草图的线条决定",
|
||||
"settings.display_quality_note": "仅影响显示,不影响设计和导出。",
|
||||
"settings.double_click_components": "双击组件以激活它,或在原位编辑链接组件",
|
||||
"settings.frame_rate_details_note": "添加最近各帧耗时的图表(叠加绘制的三角形数)、帧耗时直方图以及 GPU 绘制的内容。点击视图中的帧率可显示或隐藏。",
|
||||
"settings.frame_rate_note": "在视图右下角显示每秒帧数。",
|
||||
"settings.measure_axis_distances": "显示 X/Y/Z 距离",
|
||||
@@ -2886,7 +2680,6 @@
|
||||
"settings.reset_intro": "这些设置在此计算机上无法正常工作,会导致 SolidCraft 无法启动,因此已更改:",
|
||||
"settings.reset_title": "设置已更改",
|
||||
"settings.supersampling_note": "以更高分辨率绘制视图再缩小:在低于 4K 的屏幕上边、线和网格更平滑。会消耗 GPU 时间:1.5× 时约 2.25 倍像素,2× 时 4 倍。",
|
||||
"settings.update_changed_links": "返回标签页时更新已更改的链接设计",
|
||||
"settings.window_system_details": "详情",
|
||||
"settings.window_system_note_now": "重启后生效。当前:{now}。",
|
||||
"settings.window_system_wayland_limited": "在原生 Wayland 下部分功能受限。",
|
||||
@@ -2933,10 +2726,6 @@
|
||||
"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": "方向必须是轴、草图直线或直边",
|
||||
@@ -2975,10 +2764,8 @@
|
||||
"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} 选项卡。勾选命令可将其按钮添加到工具栏。您也可以将面板下拉菜单中的某一行拖到工具栏,将按钮拖出工具栏以移除,或使用下拉菜单行中的固定图标。",
|
||||
@@ -3389,8 +3176,6 @@
|
||||
"ui.cancel": "取消",
|
||||
"ui.canvases": "画布",
|
||||
"ui.center": "中心",
|
||||
"ui.center_arc_follows_cursor": "中心点圆弧跟随光标方向",
|
||||
"ui.center_arc_follows_cursor_tip": "中心点圆弧按光标绕中心移动的方向沿顺时针或逆时针绘制。关闭:始终从起点逆时针绘制。",
|
||||
"ui.centerline": "中心线",
|
||||
"ui.chain": "链",
|
||||
"ui.chamfer": "倒角",
|
||||
@@ -3498,24 +3283,6 @@
|
||||
"ui.easyprint_uploading": "正在上传到 Printables…",
|
||||
"ui.edit": "编辑",
|
||||
"ui.editJointLimits": "编辑接头限制",
|
||||
"ui.edit_in_place": "原位编辑",
|
||||
"ui.edit_in_place_after_turn": "代理本轮结束后将完成编辑",
|
||||
"ui.edit_in_place_agent_busy": "代理仍在此设计中工作;请在其本轮结束后重试",
|
||||
"ui.edit_in_place_already_queued": "已有编辑在等待代理本轮结束",
|
||||
"ui.edit_in_place_assembly_closed": "{name} 已不再打开",
|
||||
"ui.edit_in_place_banner": "正在 {name} 中编辑",
|
||||
"ui.edit_in_place_busy": "请先完成或取消打开的操作",
|
||||
"ui.edit_in_place_none": "没有正在原位编辑的链接设计",
|
||||
"ui.edit_in_place_not_saved": "零件有未保存的更改;请保存以完成编辑",
|
||||
"ui.edit_in_place_open_elsewhere": "{name} 已在另一个 SolidCraft 中打开",
|
||||
"ui.edit_in_place_opens_after_turn": "代理本轮结束后将打开零件",
|
||||
"ui.edit_in_place_refresh_dropped": "更新链接期间装配体已更改;请再次运行",
|
||||
"ui.edit_in_place_refresh_stopped": "更新链接在完成前已停止",
|
||||
"ui.edit_in_place_refreshing": "正在更新 {name} 中的链接…",
|
||||
"ui.edit_in_place_save_first": "请先保存装配体:其链接设计根据其文件查找",
|
||||
"ui.edit_in_place_save_waits": "正在更新 {name} 中的链接;请在完成后保存",
|
||||
"ui.edit_in_place_saved_elsewhere": "零件已保存到其他文件;装配体仍链接 {name}",
|
||||
"ui.edit_in_place_wait": "请等待设计中正在运行的操作完成",
|
||||
"ui.edit_section_analysis": "编辑截面分析",
|
||||
"ui.ellipse": "椭圆",
|
||||
"ui.emboss_wrap_note": "与受支持圆柱相切的正交线轮廓会自动缠绕。选择一个壁以指定目标。",
|
||||
@@ -3590,7 +3357,6 @@
|
||||
"ui.heading_edit": "编辑",
|
||||
"ui.heading_extrude": "拉伸",
|
||||
"ui.heading_fillet": "圆角",
|
||||
"ui.heading_finish_editing": "完成编辑",
|
||||
"ui.heading_finish_sketch": "完成草图",
|
||||
"ui.heading_form": "造型",
|
||||
"ui.heading_front": "前视",
|
||||
@@ -3803,15 +3569,6 @@
|
||||
"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": "多边形",
|
||||
@@ -3859,8 +3616,6 @@
|
||||
"ui.restore_defaults": "恢复默认值",
|
||||
"ui.reverse_horizontal_pan_direction": "反转水平平移方向",
|
||||
"ui.reverse_horizontal_pan_tip": "水平双指滚动和拇指滚轮向相反方向平移",
|
||||
"ui.reverse_vertical_pan_direction": "反转垂直平移方向",
|
||||
"ui.reverse_vertical_pan_tip": "双指垂直滚动向相反方向平移",
|
||||
"ui.reverse_zoom_direction": "反转缩放方向",
|
||||
"ui.revolutions": "圈数",
|
||||
"ui.revolve": "旋转",
|
||||
@@ -4006,15 +3761,6 @@
|
||||
"ui.updateToCurrentView": "更新为当前视图",
|
||||
"ui.upload": "上传",
|
||||
"ui.vertical_constraint": "垂直约束",
|
||||
"ui.view_angles": "视图角度",
|
||||
"ui.view_angles_convention": "绕向上轴的角度(度)。偏航 0° 为前视图,90° 为左视图,−90° 为右视图,180° 为后视图;俯仰 90° 为俯视图;滚转为正时,模型在屏幕上顺时针旋转。",
|
||||
"ui.view_angles_help": "在方向小部件下方显示视图的俯仰、偏航和滚转角(度)以及投影方式。单击读数可输入精确角度。",
|
||||
"ui.view_angles_ortho": "正交",
|
||||
"ui.view_angles_persp": "透视",
|
||||
"ui.view_angles_pitch": "俯仰",
|
||||
"ui.view_angles_readout": "俯仰 {pitch} · 偏航 {yaw} · 滚转 {roll}",
|
||||
"ui.view_angles_roll": "滚转",
|
||||
"ui.view_angles_yaw": "偏航",
|
||||
"ui.view_centre": "视图中心",
|
||||
"ui.viewport_theme_follow": "跟随主题",
|
||||
"ui.visual_shaded_hidden_edges": "带隐藏边的着色",
|
||||
@@ -4065,13 +3811,11 @@
|
||||
"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": "打开发布页面",
|
||||
@@ -4088,7 +3832,6 @@
|
||||
"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 中打开",
|
||||
|
||||
@@ -10,7 +10,7 @@ struct U {
|
||||
acc_u: vec4<f32>, // accessibility map axes (xyz, offset)
|
||||
acc_v: vec4<f32>,
|
||||
eye: vec4<f32>, // eye position, w = 1 for perspective (0: orthographic, looking along -back)
|
||||
ao: vec4<f32>, // x: radius in world units (zero: disabled), y: samples per pixel
|
||||
ao: vec4<f32>, // radius in world units (zero: disabled)
|
||||
// analysis colours (`theme::AnalysisColors`): draft positive, between, negative; reachable,
|
||||
// blocked; curvature flat, middle, tight
|
||||
ana_c: array<vec4<f32>, 8>,
|
||||
@@ -333,12 +333,9 @@ fn occlusion(i: TOut) -> f32 {
|
||||
// the buffer runs reversed). The map is cleared to its far plane either way, so a texel
|
||||
// with nothing in it reads as 1 and the `d < 1.0` test below skips it.
|
||||
let flip = reversed();
|
||||
// Fewer samples while the view moves (#7165), spread over the same hemisphere. 32 is the
|
||||
// still picture's count, `AO_SAMPLES` in view.rs.
|
||||
let count = u32(clamp(u.ao.y, 1.0, 32.0));
|
||||
var blocked = 0.0;
|
||||
for (var j = 0u; j < count; j = j + 1u) {
|
||||
let f = (f32(j) + 0.5) / f32(count);
|
||||
for (var j = 0u; j < 32u; j = j + 1u) {
|
||||
let f = (f32(j) + 0.5) / 32.0;
|
||||
let angle = f32(j) * 2.39996323;
|
||||
let direction = t * (cos(angle) * sqrt(f)) + b * (sin(angle) * sqrt(f)) + n * sqrt(1.0 - f);
|
||||
let reach = radius * (0.2 + 0.8 * fract(f32(j) * 0.61803399 + 0.5));
|
||||
@@ -359,7 +356,7 @@ fn occlusion(i: TOut) -> f32 {
|
||||
blocked = blocked + smoothstep(0.005, 0.15, delta) * (1.0 - smoothstep(0.5, 1.0, delta));
|
||||
}
|
||||
}
|
||||
return 1.0 - 0.85 * blocked / f32(count);
|
||||
return 1.0 - 0.85 * blocked / 32.0;
|
||||
}
|
||||
|
||||
/// How fast the normal turns across the pixel (per mm).
|
||||
@@ -653,39 +650,3 @@ 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);
|
||||
}
|
||||
|
||||
@@ -193,10 +193,8 @@ impl Tokens {
|
||||
sketch_halo: Color32::from_rgb(240, 243, 247),
|
||||
sketch_point: Color32::BLACK,
|
||||
sketch_locked: Color32::from_rgb(0, 140, 60),
|
||||
// Sketch dimensions in a plum of their own, apart from the blue and black curves for
|
||||
// every colour vision mode (#7861).
|
||||
dimension: Color32::from_rgb(130, 42, 110),
|
||||
dimension_driven: Color32::from_rgb(160, 90, 120),
|
||||
dimension: Color32::from_rgb(36, 40, 48),
|
||||
dimension_driven: Color32::from_rgb(120, 126, 138),
|
||||
sketch_point_free: Color32::from_rgb(30, 90, 200),
|
||||
section_cap: Color32::from_rgb(214, 128, 72),
|
||||
dialog_bg: Color32::from_rgb(250, 251, 252),
|
||||
@@ -360,9 +358,8 @@ impl Tokens {
|
||||
t.sketch_halo = Color32::from_rgb(28, 32, 40);
|
||||
t.sketch_point = Color32::from_rgb(232, 234, 238);
|
||||
t.sketch_locked = Color32::from_rgb(96, 204, 116);
|
||||
// Sketch dimensions: Light's plum, lightened for the dark view (#7861).
|
||||
t.dimension = Color32::from_rgb(224, 176, 224);
|
||||
t.dimension_driven = Color32::from_rgb(176, 104, 144);
|
||||
t.dimension = Color32::from_rgb(224, 228, 234);
|
||||
t.dimension_driven = Color32::from_rgb(142, 150, 164);
|
||||
t.sketch_point_free = Color32::from_rgb(92, 150, 255);
|
||||
t.profile_fill = Color32::from_rgba_unmultiplied_const(120, 150, 200, 40);
|
||||
t.dialog_bg = Color32::from_rgb(39, 43, 52);
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
"dialog_footer": "#efece8",
|
||||
"dialog_label": "#403d38",
|
||||
"dialog_rule": "#d9d5d0",
|
||||
"dimension": "#006a70",
|
||||
"dimension_driven": "#4d8a8c",
|
||||
"dimension": "#403d38",
|
||||
"dimension_driven": "#6c6863",
|
||||
"error": "#c71529",
|
||||
"field": "#fffdfa",
|
||||
"file_rail": "#e1ddd8",
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
"dialog_footer": "#100b04",
|
||||
"dialog_label": "#f3ca87",
|
||||
"dialog_rule": "#3b280d",
|
||||
"dimension": "#f5e050",
|
||||
"dimension_driven": "#a8962e",
|
||||
"dimension": "#ffe2a5",
|
||||
"dimension_driven": "#9b6922",
|
||||
"error": "#fff0ca",
|
||||
"field": "#100b04",
|
||||
"form_accent": "#ca892b",
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
"dialog_footer": "#0c223c",
|
||||
"dialog_label": "#e3f0fa",
|
||||
"dialog_rule": "#254764",
|
||||
"dimension": "#ffd970",
|
||||
"dimension_driven": "#c9a95a",
|
||||
"dimension": "#e3f0fa",
|
||||
"dimension_driven": "#94b2cb",
|
||||
"error": "#ff968d",
|
||||
"field": "#071d35",
|
||||
"form_accent": "#baade8",
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
"dialog_footer": "#1e1e2e",
|
||||
"dialog_label": "#cdd6f4",
|
||||
"dialog_rule": "#45475a",
|
||||
"dimension": "#94e2d5",
|
||||
"dimension_driven": "#6c9d95",
|
||||
"dimension": "#cdd6f4",
|
||||
"dimension_driven": "#a6adc8",
|
||||
"error": "#f38ba8",
|
||||
"field": "#181825",
|
||||
"form_accent": "#a9b2f2",
|
||||
|
||||
@@ -45,8 +45,8 @@
|
||||
"dialog_footer": "#000000",
|
||||
"dialog_label": "#55ffff",
|
||||
"dialog_rule": "#00aaaa",
|
||||
"dimension": "#ffff55",
|
||||
"dimension_driven": "#aa5500",
|
||||
"dimension": "#ffffff",
|
||||
"dimension_driven": "#aaaaaa",
|
||||
"error": "#ff5555",
|
||||
"field": "#000000",
|
||||
"file_background": "#000000",
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
"dialog_footer": "#29241f",
|
||||
"dialog_label": "#c3bdb5",
|
||||
"dialog_rule": "#403930",
|
||||
"dimension": "#5fc4c0",
|
||||
"dimension_driven": "#4f9895",
|
||||
"dimension": "#c3bdb5",
|
||||
"dimension_driven": "#9d9285",
|
||||
"error": "#f85052",
|
||||
"field": "#3c352d",
|
||||
"form_accent": "#a6a2fc",
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
"dialog_footer": "#f4eee3",
|
||||
"dialog_label": "#423e36",
|
||||
"dialog_rule": "#e0d8c9",
|
||||
"dimension": "#006a70",
|
||||
"dimension_driven": "#4a8688",
|
||||
"dimension": "#423e36",
|
||||
"dimension_driven": "#726a5d",
|
||||
"error": "#c71529",
|
||||
"field": "#fdfaf3",
|
||||
"form_accent": "#5844bc",
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
"dialog_footer": "#ececec",
|
||||
"dialog_label": "#262628",
|
||||
"dialog_rule": "#c6c6c8",
|
||||
"dimension": "#00646b",
|
||||
"dimension_driven": "#3d8087",
|
||||
"dimension": "#262628",
|
||||
"dimension_driven": "#6e6e73",
|
||||
"error": "#d53137",
|
||||
"field": "#ffffff",
|
||||
"form_accent": "#8b62bf",
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
"dialog_footer": "#25272b",
|
||||
"dialog_label": "#e0e2e5",
|
||||
"dialog_rule": "#484d55",
|
||||
"dimension": "#5fd0c8",
|
||||
"dimension_driven": "#4a9a95",
|
||||
"dimension": "#e0e2e5",
|
||||
"dimension_driven": "#a0a6b0",
|
||||
"error": "#ed7777",
|
||||
"field": "#1c1e21",
|
||||
"form_accent": "#a08fc4",
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
"dialog_footer": "#061009",
|
||||
"dialog_label": "#8def9f",
|
||||
"dialog_rule": "#173b22",
|
||||
"dimension": "#c8ff4d",
|
||||
"dimension_driven": "#7fa040",
|
||||
"dimension": "#8def9f",
|
||||
"dimension_driven": "#5b9b6a",
|
||||
"error": "#d8ffdf",
|
||||
"field": "#061009",
|
||||
"form_accent": "#1faa4e",
|
||||
|
||||
@@ -55,8 +55,8 @@
|
||||
"sketch_fixed": "#ebdbb2",
|
||||
"sketch_halo": "#141617",
|
||||
"sketch_locked": "#b8bb26",
|
||||
"dimension": "#fabd2f",
|
||||
"dimension_driven": "#b57614",
|
||||
"dimension": "#ebdbb2",
|
||||
"dimension_driven": "#928374",
|
||||
"sketch_point": "#ebdbb2",
|
||||
"sketch_point_free": "#83a598",
|
||||
"dialog_bg": "#3c3836",
|
||||
|
||||
@@ -56,8 +56,8 @@
|
||||
"sketch_fixed": "#ebdbb2",
|
||||
"sketch_halo": "#1d2021",
|
||||
"sketch_locked": "#b8bb26",
|
||||
"dimension": "#fabd2f",
|
||||
"dimension_driven": "#b57614",
|
||||
"dimension": "#ebdbb2",
|
||||
"dimension_driven": "#928374",
|
||||
"sketch_point": "#ebdbb2",
|
||||
"sketch_point_free": "#83a598",
|
||||
"dialog_bg": "#3c3836",
|
||||
|
||||
@@ -56,8 +56,8 @@
|
||||
"sketch_fixed": "#ebdbb2",
|
||||
"sketch_halo": "#1d2021",
|
||||
"sketch_locked": "#b8bb26",
|
||||
"dimension": "#fabd2f",
|
||||
"dimension_driven": "#b57614",
|
||||
"dimension": "#ebdbb2",
|
||||
"dimension_driven": "#928374",
|
||||
"sketch_point": "#ebdbb2",
|
||||
"sketch_point_free": "#83a598",
|
||||
"dialog_bg": "#3c3836",
|
||||
|
||||
@@ -55,8 +55,8 @@
|
||||
"sketch_fixed": "#282828",
|
||||
"sketch_halo": "#fbf8e3",
|
||||
"sketch_locked": "#79740e",
|
||||
"dimension": "#1f5c40",
|
||||
"dimension_driven": "#4f7d64",
|
||||
"dimension": "#3c3836",
|
||||
"dimension_driven": "#7c6f64",
|
||||
"sketch_point": "#282828",
|
||||
"sketch_point_free": "#076678",
|
||||
"dialog_bg": "#f9f5d7",
|
||||
|
||||
@@ -55,8 +55,8 @@
|
||||
"sketch_fixed": "#282828",
|
||||
"sketch_halo": "#f6edd0",
|
||||
"sketch_locked": "#79740e",
|
||||
"dimension": "#1f5c40",
|
||||
"dimension_driven": "#4f7d64",
|
||||
"dimension": "#3c3836",
|
||||
"dimension_driven": "#7c6f64",
|
||||
"sketch_point": "#282828",
|
||||
"sketch_point_free": "#076678",
|
||||
"dialog_bg": "#f2e5bc",
|
||||
|
||||
@@ -55,8 +55,8 @@
|
||||
"sketch_fixed": "#282828",
|
||||
"sketch_halo": "#fcf5d8",
|
||||
"sketch_locked": "#79740e",
|
||||
"dimension": "#1f5c40",
|
||||
"dimension_driven": "#4f7d64",
|
||||
"dimension": "#3c3836",
|
||||
"dimension_driven": "#7c6f64",
|
||||
"sketch_point": "#282828",
|
||||
"sketch_point_free": "#076678",
|
||||
"dialog_bg": "#fbf1c7",
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
"dialog_footer": "#000000",
|
||||
"dialog_label": "#ffffff",
|
||||
"dialog_rule": "#a0a0a0",
|
||||
"dimension": "#ffff66",
|
||||
"dimension_driven": "#c8c850",
|
||||
"dimension": "#ffffff",
|
||||
"dimension_driven": "#cccccc",
|
||||
"error": "#ff6666",
|
||||
"field": "#000000",
|
||||
"form_accent": "#d98af9",
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
"dialog_footer": "#2e3440",
|
||||
"dialog_label": "#eceff4",
|
||||
"dialog_rule": "#4c566a",
|
||||
"dimension": "#d08770",
|
||||
"dimension_driven": "#a87060",
|
||||
"dimension": "#eceff4",
|
||||
"dimension_driven": "#a4b0c2",
|
||||
"error": "#bf616a",
|
||||
"field": "#242933",
|
||||
"form_accent": "#cfa7c7",
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
"dialog_footer": "#282a36",
|
||||
"dialog_label": "#f8f8f2",
|
||||
"dialog_rule": "#50546d",
|
||||
"dimension": "#8be9fd",
|
||||
"dimension_driven": "#62a3b1",
|
||||
"dimension": "#f8f8f2",
|
||||
"dimension_driven": "#a1a4c4",
|
||||
"error": "#ff5555",
|
||||
"field": "#21222c",
|
||||
"form_accent": "#c096e8",
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
"dialog_footer": "#1b1e20",
|
||||
"dialog_label": "#bbbec1",
|
||||
"dialog_rule": "#2f3235",
|
||||
"dimension": "#5fc4c0",
|
||||
"dimension_driven": "#4f9895",
|
||||
"dimension": "#bbbec1",
|
||||
"dimension_driven": "#8f9296",
|
||||
"error": "#f85052",
|
||||
"field": "#2c2e31",
|
||||
"file_rail": "#0f1113",
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
"dialog_footer": "#2f343e",
|
||||
"dialog_label": "#b4bbc8",
|
||||
"dialog_rule": "#353b47",
|
||||
"dimension": "#56b6c2",
|
||||
"dimension_driven": "#3f8590",
|
||||
"dimension": "#abb2bf",
|
||||
"dimension_driven": "#7f848e",
|
||||
"error": "#e06c75",
|
||||
"field": "#1e2227",
|
||||
"form_accent": "#d283e9",
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
"dialog_footer": "#dddddd",
|
||||
"dialog_label": "#222222",
|
||||
"dialog_rule": "#aaaaaa",
|
||||
"dimension": "#00787f",
|
||||
"dimension_driven": "#4d9196",
|
||||
"dimension": "#222222",
|
||||
"dimension_driven": "#666666",
|
||||
"error": "#bb3333",
|
||||
"field": "#ffffff",
|
||||
"form_accent": "#704e81",
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
"dialog_footer": "#eee8d5",
|
||||
"dialog_label": "#586e75",
|
||||
"dialog_rule": "#b6b3a1",
|
||||
"dimension": "#136059",
|
||||
"dimension_driven": "#3d8f88",
|
||||
"dimension": "#586e75",
|
||||
"dimension_driven": "#657b83",
|
||||
"error": "#dc322f",
|
||||
"field": "#fdf6e3",
|
||||
"form_accent": "#6b70c3",
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
"dialog_footer": "#c0c0c0",
|
||||
"dialog_label": "#000000",
|
||||
"dialog_rule": "#808080",
|
||||
"dimension": "#ffffff",
|
||||
"dimension_driven": "#ffff00",
|
||||
"dimension": "#000000",
|
||||
"dimension_driven": "#808080",
|
||||
"error": "#ff0000",
|
||||
"field": "#ffffff",
|
||||
"form_accent": "#800080",
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
"dialog_footer": "#1a1b26",
|
||||
"dialog_label": "#c0caf5",
|
||||
"dialog_rule": "#414868",
|
||||
"dimension": "#73daca",
|
||||
"dimension_driven": "#4f9a90",
|
||||
"dimension": "#c0caf5",
|
||||
"dimension_driven": "#9aa5ce",
|
||||
"error": "#f7768e",
|
||||
"field": "#16161e",
|
||||
"form_accent": "#b190ec",
|
||||
|
||||
+17
-43
@@ -1,40 +1,15 @@
|
||||
//! Lists the format changes of each family of files versioned by `src/format.rs`: designs in
|
||||
//! `src/format/`, drawings in `src/drawing/format/` (#3890). One file per change, in `v<N>/` once a
|
||||
//! release fixed its number, else in `pending/` (see `src/format/pending/README.md`). The lists are
|
||||
//! Lists the design-format changes in `src/format/`: one file per change, in `v<N>/` once a
|
||||
//! release fixed its number, else in `pending/` (see `src/format/pending/README.md`). The list is
|
||||
//! generated so that a change adds a file and never edits a line another change also edits.
|
||||
|
||||
use std::fmt::Write as _;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// A family's change files: their folder, the last format before them, and the prefix of the
|
||||
/// family's lists in `format.rs`.
|
||||
struct Family {
|
||||
folder: &'static str,
|
||||
base: u32,
|
||||
list_prefix: &'static str,
|
||||
}
|
||||
|
||||
/// Every family, each read by `scripts/fold-format-pending.sh` too. A design's upgrade steps are
|
||||
/// written out in `format.rs` up to 22 (`JSON_STEPS`); a drawing's first format is 1.
|
||||
const FAMILIES: [Family; 2] =
|
||||
[Family { folder: "src/format", base: 22, list_prefix: "" }, Family { folder: "src/drawing/format", base: 1, list_prefix: "DRAWING_" }];
|
||||
/// The last format whose upgrade steps are written out in `format.rs` (`JSON_STEPS`).
|
||||
const HAND_WRITTEN: u32 = 22;
|
||||
|
||||
fn main() {
|
||||
let manifest = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
|
||||
let mut out = String::new();
|
||||
let mut mods = String::new();
|
||||
for family in &FAMILIES {
|
||||
lists(family, &manifest, &mut out, &mut mods);
|
||||
}
|
||||
out.push_str(&mods);
|
||||
let dest = PathBuf::from(std::env::var("OUT_DIR").unwrap()).join("format_parts.rs");
|
||||
std::fs::write(dest, out).unwrap();
|
||||
}
|
||||
|
||||
/// A family's `FOLDED` and `PENDING` lists; its folded formats follow on from its base.
|
||||
fn lists(family: &Family, manifest: &Path, out: &mut String, mods: &mut String) {
|
||||
let Family { folder, base, list_prefix } = family;
|
||||
let root = manifest.join(folder);
|
||||
let root = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("src/format");
|
||||
println!("cargo:rerun-if-changed={}", root.display());
|
||||
let mut folded: Vec<(u32, PathBuf)> = std::fs::read_dir(&root)
|
||||
.unwrap_or_else(|e| panic!("{}: {e}", root.display()))
|
||||
@@ -46,24 +21,23 @@ fn lists(family: &Family, manifest: &Path, out: &mut String, mods: &mut String)
|
||||
.collect();
|
||||
folded.sort();
|
||||
for (i, (version, _)) in folded.iter().enumerate() {
|
||||
let expected = base + 1 + i as u32;
|
||||
assert_eq!(*version, expected, "{folder}/: expected v{expected}/ next, found v{version}/ (versions are contiguous)");
|
||||
let expected = HAND_WRITTEN + 1 + i as u32;
|
||||
assert_eq!(*version, expected, "src/format/: expected v{expected}/ next, found v{version}/ (versions are contiguous)");
|
||||
}
|
||||
// Module names start with the family's (`drawing_pending_…`), so two families' changes of one
|
||||
// name never clash.
|
||||
let module = list_prefix.to_ascii_lowercase();
|
||||
let _ = writeln!(
|
||||
out,
|
||||
"/// The changes of each format after the base, oldest first (`{folder}/v<N>/`).\nconst {list_prefix}FOLDED: &[(u32, &[Part])] = &["
|
||||
);
|
||||
let mut out = String::new();
|
||||
let mut mods = String::new();
|
||||
out.push_str("/// The changes of each format after the hand-written ones, oldest first.\nconst FOLDED: &[(u32, &[Part])] = &[\n");
|
||||
for (version, dir) in &folded {
|
||||
let _ = writeln!(out, " ({version}, &[");
|
||||
parts(dir, &format!("{module}v{version}"), out, mods);
|
||||
parts(dir, &format!("v{version}"), &mut out, &mut mods);
|
||||
out.push_str(" ]),\n");
|
||||
}
|
||||
let _ = writeln!(out, "];\n\n/// The changes no release has a number for yet (`{folder}/pending/`).\nconst {list_prefix}PENDING: &[Part] = &[");
|
||||
parts(&root.join("pending"), &format!("{module}pending"), out, mods);
|
||||
out.push_str("];\n\n");
|
||||
out.push_str("];\n\n/// The changes no release has a number for yet (`src/format/pending/`).\nconst PENDING: &[Part] = &[\n");
|
||||
parts(&root.join("pending"), "pending", &mut out, &mut mods);
|
||||
out.push_str("];\n");
|
||||
out.push_str(&mods);
|
||||
let dest = PathBuf::from(std::env::var("OUT_DIR").unwrap()).join("format_parts.rs");
|
||||
std::fs::write(dest, out).unwrap();
|
||||
}
|
||||
|
||||
/// The `Part`s of the change files in `dir`, in file-name order, and a module for each.
|
||||
|
||||
@@ -815,8 +815,6 @@ mod tests {
|
||||
bad.boundary = GlassBoundary::Thin { thickness_mm: 10001.0 };
|
||||
assert!(bad.validate().is_err());
|
||||
}
|
||||
// The swatch shows the transmission as sRGB codes.
|
||||
assert_eq!(find("starter.glass.smoky-neutral").unwrap().preview_color(), [160, 166, 173]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -154,7 +154,10 @@ impl VisualPreset {
|
||||
match self.recipe {
|
||||
Recipe::Opaque(r) => r.color,
|
||||
// A UI swatch of the transmission values, not rendered glass or absorption.
|
||||
Recipe::Glass(r) => r.transmission_linear_rgb.map(solidcraft_geom::srgb::encode8),
|
||||
Recipe::Glass(r) => r.transmission_linear_rgb.map(|v| {
|
||||
let encoded = if v <= 0.0031308 { 12.92 * v } else { 1.055 * v.powf(1.0 / 2.4) - 0.055 };
|
||||
(encoded.clamp(0.0, 1.0) * 255.0).round() as u8
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -124,48 +124,6 @@ fn identity() -> Mat {
|
||||
IDENTITY
|
||||
}
|
||||
|
||||
/// How a component counts in the bill of materials of an assembly it is placed in. It is a part
|
||||
/// property (the `bom_type` key of the component's [`Metadata`](crate::Metadata)); a component
|
||||
/// without one is [`BomType::Normal`].
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum BomType {
|
||||
/// A row of its own; every occurrence adds to its quantity.
|
||||
#[default]
|
||||
Normal,
|
||||
/// No row of its own: what it holds counts as if placed directly in its parent.
|
||||
Phantom,
|
||||
/// Shown, but counted nowhere, and nothing inside it is counted either.
|
||||
Reference,
|
||||
/// Bought: one row, with nothing inside it counted or listed.
|
||||
Purchased,
|
||||
/// Made as one item that doesn't come apart (a welded frame): one row in the assembly it is
|
||||
/// placed in. What it holds is listed in its own bill of materials, and its purchased parts
|
||||
/// also in the parts-only list of the assembly it is placed in.
|
||||
Inseparable,
|
||||
}
|
||||
|
||||
impl BomType {
|
||||
/// Every type, in the order commands and documentation list them.
|
||||
pub const ALL: [BomType; 5] = [BomType::Normal, BomType::Phantom, BomType::Reference, BomType::Purchased, BomType::Inseparable];
|
||||
|
||||
/// The word the design file and commands use.
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
BomType::Normal => "normal",
|
||||
BomType::Phantom => "phantom",
|
||||
BomType::Reference => "reference",
|
||||
BomType::Purchased => "purchased",
|
||||
BomType::Inseparable => "inseparable",
|
||||
}
|
||||
}
|
||||
|
||||
/// The type a word names, if it names one.
|
||||
pub fn parse(word: &str) -> Option<BomType> {
|
||||
BomType::ALL.into_iter().find(|t| t.as_str() == word)
|
||||
}
|
||||
}
|
||||
|
||||
/// How deep and how many placements [`Document::placements`] walks.
|
||||
pub const MAX_PLACEMENT_DEPTH: usize = 64;
|
||||
pub const MAX_PLACEMENTS: usize = 100_000;
|
||||
|
||||
@@ -2727,9 +2727,6 @@ 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>,
|
||||
@@ -2935,7 +2932,6 @@ 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))
|
||||
@@ -2983,7 +2979,6 @@ 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(),
|
||||
|
||||
@@ -1,305 +0,0 @@
|
||||
//! A drawing (#3890): sheets of paper that draw designs saved in their own files. A drawing is a
|
||||
//! file of its own, `"format": "solidcraft-drawing/N"`, versioned by the same code as designs
|
||||
//! ([`DRAWING`]); format 1 is this record.
|
||||
//!
|
||||
//! - **Sources** are the designs it draws: an id and the design file's path, relative to the
|
||||
//! drawing's folder once the drawing is saved, else as given, with `/` between folders (a linked
|
||||
//! design's [`DesignLink`](crate::DesignLink) rule). What a design was when the drawing last saw
|
||||
//! it is not saved here.
|
||||
//! - **Sheets**: the paper, a copy of its drawing border, the resolution its pictures are rendered
|
||||
//! at, the component it documents, and whether it is left out of the sheet count or of printing.
|
||||
//! The border is a copy of the profile it was made with, never a name, so a later built-in never
|
||||
//! changes a saved drawing.
|
||||
//! - **Units** follow each view's design or are one length unit for the whole drawing. The
|
||||
//! **language** is the content's, a BCP 47 tag chosen when the drawing is made, whatever the
|
||||
//! interface's is. **Properties** follow [`Metadata`]'s rules.
|
||||
//!
|
||||
//! The rules, which every later kind of record (views, annotations, tables) keeps too:
|
||||
//! - Every object has an id of [`crate::ids`]: random, so objects added side by side never take
|
||||
//! the same one, and written as 13 characters of base32. No two objects share an id, and a run
|
||||
//! never gives a removed object's id again ([`Drawing::allocate_id`]).
|
||||
//! - Every reference between records is an id and must resolve: a file with one that doesn't is
|
||||
//! damaged and refused, never patched.
|
||||
//! - 1 to [`MAX_SHEETS`] sheets and at most [`MAX_SOURCES`] sources; names of 1 to
|
||||
//! [`MAX_NAME_BYTES`] bytes; paths of 1 to [`MAX_PATH_BYTES`] bytes, without NUL.
|
||||
//! - Nothing derived is saved, and a drawing always saves as the same bytes: fields in their
|
||||
//! order, maps sorted.
|
||||
//! - A later change to the record is one file in `src/drawing/format/pending/` (its README).
|
||||
//!
|
||||
//! [`Drawing::from_json`] reads a file in this order: at most [`MAX_DRAWING_BYTES`] of text, the
|
||||
//! family's format check, every number in range, the upgrade, the typed record, then
|
||||
//! [`Drawing::validate`].
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use solidcraft_geom::drawing_sheet::{self, DrawingBorderProfile};
|
||||
|
||||
use crate::format::DRAWING;
|
||||
use crate::ids::{self, IdSource};
|
||||
use crate::{DocError, Metadata, Result, expr};
|
||||
|
||||
/// The most sheets a drawing may have.
|
||||
pub const MAX_SHEETS: usize = 1000;
|
||||
/// The most designs a drawing may draw.
|
||||
pub const MAX_SOURCES: usize = 64;
|
||||
/// The longest name of a drawing or a sheet, in bytes.
|
||||
pub const MAX_NAME_BYTES: usize = 256;
|
||||
/// The longest path to a design, in bytes.
|
||||
pub const MAX_PATH_BYTES: usize = 4096;
|
||||
/// The longest language tag: what RFC 5646 asks every reader to take (§4.4.1).
|
||||
pub const MAX_LANGUAGE_CHARS: usize = 35;
|
||||
/// The largest drawing read, as JSON text.
|
||||
pub const MAX_DRAWING_BYTES: usize = 16 << 20;
|
||||
/// The resolution a sheet's pictures (its shaded views) are rendered at unless it says, in dots
|
||||
/// per inch.
|
||||
pub const DEFAULT_DPI: f64 = 300.0;
|
||||
/// The lowest resolution a sheet may ask for.
|
||||
pub const MIN_DPI: f64 = 25.0;
|
||||
/// The highest resolution a sheet may ask for.
|
||||
pub const MAX_DPI: f64 = 2400.0;
|
||||
|
||||
/// A drawing: its sheets and the designs they draw.
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Drawing {
|
||||
/// `solidcraft-drawing/N`: the format this build writes, whatever the file said.
|
||||
pub format: String,
|
||||
/// The changes of `format` this drawing has while they are pending
|
||||
/// ([`Family::pending_names`](crate::format::Family::pending_names)). Empty, and not saved,
|
||||
/// otherwise.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub format_pending: Vec<String>,
|
||||
pub name: String,
|
||||
/// The designs the drawing draws.
|
||||
pub sources: Vec<Source>,
|
||||
/// The drawing's properties and other tools' data, kept as they are.
|
||||
#[serde(default, skip_serializing_if = "Metadata::is_empty")]
|
||||
pub properties: Metadata,
|
||||
#[serde(default)]
|
||||
pub units: Units,
|
||||
/// The language of the drawing's content, a BCP 47 tag (`en-GB`).
|
||||
pub language: String,
|
||||
/// In order, each behind its own `Arc`, so an undo step shares every sheet it didn't change.
|
||||
pub sheets: Vec<Arc<Sheet>>,
|
||||
}
|
||||
|
||||
/// A design the drawing draws.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Source {
|
||||
#[serde(with = "crate::ids")]
|
||||
pub id: u64,
|
||||
/// The design file: relative to the drawing's folder once the drawing is saved, else as given
|
||||
/// (`/` separates folders).
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
/// The length unit a drawing gives its measurements in.
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case", deny_unknown_fields)]
|
||||
pub enum Units {
|
||||
/// Each view's design's own units.
|
||||
#[default]
|
||||
FollowModel,
|
||||
/// One length unit for the whole drawing, by its name in [`expr::UNITS`] (`mm`, `in`).
|
||||
Fixed { unit: String },
|
||||
}
|
||||
|
||||
/// One sheet of a drawing.
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Sheet {
|
||||
#[serde(with = "crate::ids")]
|
||||
pub id: u64,
|
||||
pub name: String,
|
||||
/// The paper: its size and the way it lies.
|
||||
pub sheet: drawing_sheet::Sheet,
|
||||
/// A copy of the drawing border's profile (`None`: no drawing border).
|
||||
pub border: Option<DrawingBorderProfile>,
|
||||
/// The resolution its pictures (its shaded views) are rendered at, in dots per inch.
|
||||
#[serde(default = "default_dpi")]
|
||||
pub dpi: f64,
|
||||
/// The component the sheet documents, which its title block reads.
|
||||
pub documents: Option<SheetComponent>,
|
||||
#[serde(default)]
|
||||
pub excluded: Excluded,
|
||||
}
|
||||
|
||||
/// A component of one of the drawing's designs.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct SheetComponent {
|
||||
/// The [`Source`]'s id.
|
||||
#[serde(with = "crate::ids")]
|
||||
pub source: u64,
|
||||
/// The component's id in that design, as the design writes it (0: the design itself).
|
||||
pub component: u64,
|
||||
}
|
||||
|
||||
/// What a sheet is left out of.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct Excluded {
|
||||
/// The sheet count ("sheet 2 of 3" on the other sheets).
|
||||
pub count: bool,
|
||||
/// Printing and exporting every sheet.
|
||||
pub print: bool,
|
||||
}
|
||||
|
||||
fn default_dpi() -> f64 {
|
||||
DEFAULT_DPI
|
||||
}
|
||||
|
||||
impl Drawing {
|
||||
/// A drawing of no designs yet, named `name` and written in `language`, with one sheet named
|
||||
/// `sheet`: the paper `paper` with a copy of `border`, its id from `id_source`.
|
||||
pub fn new(
|
||||
name: &str,
|
||||
language: &str,
|
||||
sheet: &str,
|
||||
paper: drawing_sheet::Sheet,
|
||||
border: Option<DrawingBorderProfile>,
|
||||
id_source: &mut IdSource,
|
||||
) -> Result<Drawing> {
|
||||
let id = id_source.next(&BTreeSet::new())?;
|
||||
let first = Sheet { id, name: sheet.into(), sheet: paper, border, dpi: DEFAULT_DPI, documents: None, excluded: Excluded::default() };
|
||||
let drawing = Drawing {
|
||||
format: DRAWING.format_string(DRAWING.current()),
|
||||
format_pending: DRAWING.pending_names(),
|
||||
name: name.into(),
|
||||
sources: Vec::new(),
|
||||
properties: Metadata::default(),
|
||||
units: Units::default(),
|
||||
language: language.into(),
|
||||
sheets: vec![Arc::new(first)],
|
||||
};
|
||||
drawing.validate()?;
|
||||
Ok(drawing)
|
||||
}
|
||||
|
||||
/// Read a drawing file's JSON (any format this build knows; older ones are upgraded). A file
|
||||
/// that breaks a rule of the record is damaged and refused, never patched.
|
||||
pub fn from_json(s: &str) -> Result<Drawing> {
|
||||
if s.len() > MAX_DRAWING_BYTES {
|
||||
return Err(DocError::Invalid(format!("drawing too large (at most {} MiB)", MAX_DRAWING_BYTES >> 20)));
|
||||
}
|
||||
let mut v: Value = serde_json::from_str(s).map_err(|e| DocError::Invalid(format!("not a SolidCraft drawing: {e}")))?;
|
||||
let (version, applied) = DRAWING.take_version(&mut v)?;
|
||||
DRAWING.check_numbers(&v, "properties")?;
|
||||
DRAWING.upgrade(&mut v, version, applied.as_deref())?;
|
||||
let d: Drawing = serde_json::from_value(v).map_err(damaged)?;
|
||||
d.validate().map_err(damaged)?;
|
||||
Ok(Drawing { format: DRAWING.format_string(DRAWING.current()), format_pending: DRAWING.pending_names(), ..d })
|
||||
}
|
||||
|
||||
/// The drawing's JSON: the same bytes for the same drawing. JSON longer than a read takes
|
||||
/// ([`MAX_DRAWING_BYTES`]) is refused, so every drawing saved opens again: other tools'
|
||||
/// properties are kept as they came, and indenting them can make a drawing that read in
|
||||
/// under the limit save over it.
|
||||
pub fn to_json(&self) -> Result<String> {
|
||||
let text = serde_json::to_string_pretty(self).map_err(|e| DocError::Invalid(format!("drawing not saved: {e}")))?;
|
||||
require(text.len() <= MAX_DRAWING_BYTES, || format!("drawing too large to save (at most {} MiB)", MAX_DRAWING_BYTES >> 20))?;
|
||||
Ok(text)
|
||||
}
|
||||
|
||||
/// An id for a new object: one no object of the drawing has, from `id_source`, which never
|
||||
/// gives one twice.
|
||||
pub fn allocate_id(&self, id_source: &mut IdSource) -> Result<u64> {
|
||||
id_source.next(&self.ids().collect())
|
||||
}
|
||||
|
||||
/// Every object's id, of every kind: what a new id must differ from. Each later kind adds its
|
||||
/// own here.
|
||||
fn ids(&self) -> impl Iterator<Item = u64> {
|
||||
self.sources.iter().map(|s| s.id).chain(self.sheets.iter().map(|s| s.id))
|
||||
}
|
||||
|
||||
/// The record's rules (the module's notes): what reading a file checks, and what every edit
|
||||
/// keeps.
|
||||
pub fn validate(&self) -> Result<()> {
|
||||
require((1..=MAX_SHEETS).contains(&self.sheets.len()), || format!("a drawing has 1 to {MAX_SHEETS} sheets"))?;
|
||||
require(self.sources.len() <= MAX_SOURCES, || format!("a drawing draws at most {MAX_SOURCES} designs"))?;
|
||||
check_name(&self.name, "the drawing's")?;
|
||||
require(is_language_tag(&self.language), || {
|
||||
format!("the language is not a BCP 47 tag of 2 to {MAX_LANGUAGE_CHARS} characters (such as `en` or `en-GB`)")
|
||||
})?;
|
||||
if let Units::Fixed { unit } = &self.units {
|
||||
// A length unit of the expression unit table, written as the table writes it.
|
||||
let length = unit.trim() == unit.as_str() && expr::unit_info(unit).is_some_and(|(kind, _)| kind == expr::Kind::Length);
|
||||
require(length, || "the drawing's unit is not a length unit (such as `mm` or `in`)".into())?;
|
||||
}
|
||||
let mut seen = BTreeSet::new();
|
||||
for id in self.ids() {
|
||||
require(id != 0, || "an object has the id 0, which is never one".into())?;
|
||||
require(seen.insert(id), || format!("two objects have the id {}", ids::encode(id)))?;
|
||||
}
|
||||
for source in &self.sources {
|
||||
let path = &source.path;
|
||||
require(!path.is_empty() && path.len() <= MAX_PATH_BYTES && !path.contains('\0'), || {
|
||||
format!("source {}: a design's path is 1 to {MAX_PATH_BYTES} bytes, without NUL", ids::encode(source.id))
|
||||
})?;
|
||||
}
|
||||
for sheet in &self.sheets {
|
||||
self.check_sheet(sheet).map_err(|e| DocError::Invalid(format!("sheet {}: {e}", ids::encode(sheet.id))))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A sheet's own rules, and that the source it documents is one of the drawing's.
|
||||
fn check_sheet(&self, sheet: &Sheet) -> Result<()> {
|
||||
check_name(&sheet.name, "its")?;
|
||||
sheet.sheet.size_mm().map_err(from_sheet)?;
|
||||
if let Some(border) = &sheet.border {
|
||||
border.validate().map_err(from_sheet)?;
|
||||
}
|
||||
require((MIN_DPI..=MAX_DPI).contains(&sheet.dpi), || format!("its pictures are {MIN_DPI} to {MAX_DPI} dpi"))?;
|
||||
match sheet.documents {
|
||||
Some(SheetComponent { source, .. }) => {
|
||||
let named = || format!("it documents source {}, which the drawing doesn't have", ids::encode(source));
|
||||
require(self.sources.iter().any(|s| s.id == source), named)
|
||||
}
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `Ok` when `ok`, else the rule `broken` names.
|
||||
fn require(ok: bool, broken: impl FnOnce() -> String) -> Result<()> {
|
||||
if ok { Ok(()) } else { Err(DocError::Invalid(broken())) }
|
||||
}
|
||||
|
||||
/// A name: 1 to [`MAX_NAME_BYTES`] bytes, not only spaces, without control characters.
|
||||
fn check_name(name: &str, whose: &str) -> Result<()> {
|
||||
require(!name.trim().is_empty() && name.len() <= MAX_NAME_BYTES && !name.chars().any(char::is_control), || {
|
||||
format!("{whose} name is 1 to {MAX_NAME_BYTES} bytes, not only spaces, without control characters")
|
||||
})
|
||||
}
|
||||
|
||||
/// A well-formed language tag, as far as a drawing needs (RFC 5646 §2.1): 2 to
|
||||
/// [`MAX_LANGUAGE_CHARS`] characters, subtags of 1 to 8 ASCII letters and digits joined by `-`,
|
||||
/// the first of 2 to 8 letters (`en`, `en-GB`, `zh-Hans-CN`). Whether the subtags are registered
|
||||
/// isn't checked, and a private-use tag alone (`x-…`) isn't taken.
|
||||
fn is_language_tag(tag: &str) -> bool {
|
||||
let mut subtags = tag.split('-');
|
||||
let first = subtags.next().unwrap_or_default();
|
||||
(2..=MAX_LANGUAGE_CHARS).contains(&tag.len())
|
||||
&& (2..=8).contains(&first.len())
|
||||
&& first.bytes().all(|b| b.is_ascii_alphabetic())
|
||||
&& subtags.all(|s| (1..=8).contains(&s.len()) && s.bytes().all(|b| b.is_ascii_alphanumeric()))
|
||||
}
|
||||
|
||||
/// A file that breaks a rule of the record.
|
||||
fn damaged(e: impl std::fmt::Display) -> DocError {
|
||||
DocError::Invalid(format!("damaged drawing: {e}"))
|
||||
}
|
||||
|
||||
fn from_sheet(e: drawing_sheet::Invalid) -> DocError {
|
||||
DocError::Invalid(e.to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "drawing_tests.rs"]
|
||||
mod tests;
|
||||
@@ -1,30 +0,0 @@
|
||||
# Pending drawing-format changes
|
||||
|
||||
One file per change to what a drawing file saves that no release has shipped yet. Drawings
|
||||
(`"format": "solidcraft-drawing/N"`, #3890) are versioned by the same code as designs
|
||||
(`crates/doc/src/format.rs`, `Family`), from their own first format, 1. Adding a file here is the
|
||||
whole format bump: never edit the `DRAWING` family in `format.rs`, another change's file or a
|
||||
`../v<N>/` folder.
|
||||
|
||||
- Name it after the change, lowercase with dashes: `sheet-views.rs`.
|
||||
- Start it with a `//!` note: what is saved, what an older drawing gets without it, and what
|
||||
the last release does with a drawing that has it (it refuses the newer format number).
|
||||
- If older drawings must be rewritten as they open, add the step to the same file:
|
||||
|
||||
```rust
|
||||
//! …the note…
|
||||
use serde_json::Value;
|
||||
|
||||
pub fn step(v: &mut Value) -> crate::Result<()> {
|
||||
// rewrite the drawing's JSON here
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
Steps run in file-name order, after the steps of every older format.
|
||||
|
||||
While this folder has a change in it, the build writes the next drawing format number (the last
|
||||
folded one + 1, which no release reads), and lists the changes a drawing has in its
|
||||
`format_pending` field, so a later build applies only the steps that drawing is missing. At
|
||||
release time `scripts/fold-format-pending.sh` moves these files into `../v<N>/`, as it does the
|
||||
design format's (`crates/doc/src/format/pending/README.md`).
|
||||
@@ -1,553 +0,0 @@
|
||||
use serde_json::json;
|
||||
use solidcraft_geom::drawing_sheet::{Margins, Orientation, PaperSize, SheetSize};
|
||||
|
||||
use super::*;
|
||||
|
||||
/// The seed every test drawing's ids come from.
|
||||
const SEED: u64 = 3890;
|
||||
|
||||
/// A profile of its own: ISO 5457's filing margin and trimming marks, which the built-in leaves off.
|
||||
fn own_border() -> DrawingBorderProfile {
|
||||
DrawingBorderProfile { filing_margin: true, trimming_mark_mm: Some([10.0, 5.0]), ..DrawingBorderProfile::iso() }
|
||||
}
|
||||
|
||||
/// Three designs on two sheets: the first sheet (A3, the ISO border) documents component 7 of the
|
||||
/// first design; the second (A4 portrait, a border of its own, 600 dpi, left out of printing)
|
||||
/// documents the second design. The third design is on no sheet yet.
|
||||
fn plate() -> Drawing {
|
||||
let mut id_source = IdSource::seeded(SEED);
|
||||
let a3 = drawing_sheet::Sheet::standard(PaperSize::A3, Orientation::Landscape);
|
||||
let mut d = Drawing::new("Plate assembly", "en-GB", "Assembly", a3, Some(DrawingBorderProfile::iso()), &mut id_source).unwrap();
|
||||
for path in ["plate.sc", "../parts/bolt.sc", "bracket.sc"] {
|
||||
let id = d.allocate_id(&mut id_source).unwrap();
|
||||
d.sources.push(Source { id, path: path.into() });
|
||||
}
|
||||
let (plate_id, bolt_id) = (d.sources[0].id, d.sources[1].id);
|
||||
let id = d.allocate_id(&mut id_source).unwrap();
|
||||
d.sheets.push(Arc::new(Sheet {
|
||||
id,
|
||||
name: "Bolt".into(),
|
||||
sheet: drawing_sheet::Sheet::standard(PaperSize::A4, Orientation::Portrait),
|
||||
border: Some(own_border()),
|
||||
dpi: 600.0,
|
||||
documents: Some(SheetComponent { source: bolt_id, component: 0 }),
|
||||
excluded: Excluded { count: false, print: true },
|
||||
}));
|
||||
Arc::make_mut(&mut d.sheets[0]).documents = Some(SheetComponent { source: plate_id, component: 7 });
|
||||
d.properties.set(crate::metadata::Owner::Design, "author", json!("Jane Doe")).unwrap();
|
||||
d.units = Units::Fixed { unit: "in".into() };
|
||||
d.validate().unwrap();
|
||||
d
|
||||
}
|
||||
|
||||
/// One A4 sheet that documents nothing, no sources: the base the caps grow from.
|
||||
fn bare() -> Drawing {
|
||||
let a4 = drawing_sheet::Sheet::standard(PaperSize::A4, Orientation::Landscape);
|
||||
Drawing::new("Bare", "en", "Sheet", a4, None, &mut IdSource::seeded(SEED)).unwrap()
|
||||
}
|
||||
|
||||
fn file(d: &Drawing) -> Value {
|
||||
serde_json::from_str(&d.to_json().unwrap()).unwrap()
|
||||
}
|
||||
|
||||
fn read(file: &Value) -> Result<Drawing> {
|
||||
Drawing::from_json(&file.to_string())
|
||||
}
|
||||
|
||||
/// Why `file` is refused, or "read".
|
||||
fn refusal(file: &Value) -> String {
|
||||
read(file).map_or_else(|e| e.to_string(), |_| "read".into())
|
||||
}
|
||||
|
||||
/// Why `d`'s file, changed by `edit`, is refused, or "read".
|
||||
fn refused(d: &Drawing, edit: impl FnOnce(&mut Value)) -> String {
|
||||
let mut v = file(d);
|
||||
edit(&mut v);
|
||||
refusal(&v)
|
||||
}
|
||||
|
||||
/// A drawing reads back as it was saved, and save, open and save again writes the same bytes, as
|
||||
/// does another run from the same seed. The file holds each field under the record's name for it,
|
||||
/// ids as their 13 characters, and no counter.
|
||||
#[test]
|
||||
fn a_drawing_reads_back_as_saved_and_saves_the_same_bytes_again() {
|
||||
let d = plate();
|
||||
let saved = d.to_json().unwrap();
|
||||
let reopened = Drawing::from_json(&saved).unwrap();
|
||||
assert_eq!(reopened, d);
|
||||
assert_eq!(reopened.to_json().unwrap(), saved);
|
||||
assert_eq!(plate().to_json().unwrap(), saved, "a seeded run makes the same drawing");
|
||||
let v = file(&d);
|
||||
let [plate_id, bolt_id, bracket_id] = [0, 1, 2].map(|i| ids::encode(d.sources[i].id));
|
||||
let [assembly_id, bolt_sheet_id] = [0, 1].map(|i| ids::encode(d.sheets[i].id));
|
||||
assert_eq!(assembly_id, "1ge30wkvq38r0", "the seed's first id, as splitmix64 gives it");
|
||||
assert_eq!(v["format"], DRAWING.format_string(DRAWING.current()));
|
||||
assert_eq!(v.get("format_pending").is_some(), !DRAWING.pending_names().is_empty());
|
||||
assert_eq!(v.get("next_id"), None);
|
||||
assert_eq!((&v["name"], &v["language"]), (&json!("Plate assembly"), &json!("en-GB")));
|
||||
assert_eq!(
|
||||
v["sources"],
|
||||
json!([{"id": plate_id, "path": "plate.sc"}, {"id": bolt_id, "path": "../parts/bolt.sc"}, {"id": bracket_id, "path": "bracket.sc"}])
|
||||
);
|
||||
assert_eq!(v["properties"], json!({"author": "Jane Doe"}));
|
||||
assert_eq!(v["units"], json!({"fixed": {"unit": "in"}}));
|
||||
assert_eq!((&v["sheets"][0]["id"], &v["sheets"][0]["dpi"]), (&json!(assembly_id), &json!(300.0)));
|
||||
assert_eq!(v["sheets"][0]["sheet"], json!({"size": {"standard": "a3"}, "orientation": "landscape"}));
|
||||
assert_eq!(v["sheets"][0]["documents"], json!({"source": plate_id, "component": 7}));
|
||||
assert_eq!(v["sheets"][0]["excluded"], json!({"count": false, "print": false}));
|
||||
assert_eq!(
|
||||
v["sheets"][1],
|
||||
json!({
|
||||
"id": bolt_sheet_id, "name": "Bolt", "sheet": {"size": {"standard": "a4"}, "orientation": "portrait"},
|
||||
"border": serde_json::to_value(own_border()).unwrap(), "dpi": 600.0,
|
||||
"documents": {"source": bolt_id, "component": 0}, "excluded": {"count": false, "print": true}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/// A hand-written file may leave out what has a default, and reads as a new drawing has it; the
|
||||
/// rest is needed.
|
||||
#[test]
|
||||
fn a_file_may_leave_out_what_has_a_default() {
|
||||
let minimal = json!({
|
||||
"format": DRAWING.format_string(1),
|
||||
"name": "Bracket",
|
||||
"sources": [],
|
||||
"language": "de",
|
||||
"sheets": [{"id": "0000000000001", "name": "Blatt 1", "sheet": {"size": {"standard": "a3"}}}]
|
||||
});
|
||||
let d = read(&minimal).unwrap();
|
||||
assert_eq!((d.sheets[0].id, &d.units, d.properties.is_empty()), (1, &Units::FollowModel, true));
|
||||
let sheet = &d.sheets[0];
|
||||
assert_eq!(sheet.sheet, drawing_sheet::Sheet::standard(PaperSize::A3, Orientation::Landscape));
|
||||
assert_eq!((sheet.border, sheet.dpi, sheet.documents, sheet.excluded), (None, DEFAULT_DPI, None, Excluded::default()));
|
||||
// The format as this build writes it, however the file spelt it.
|
||||
let mut spelt = minimal.clone();
|
||||
spelt["format"] = json!("solidcraft-drawing/+1");
|
||||
assert_eq!(read(&spelt).unwrap().format, DRAWING.format_string(DRAWING.current()));
|
||||
for key in ["name", "sources", "language", "sheets"] {
|
||||
let mut v = minimal.clone();
|
||||
v.as_object_mut().unwrap().remove(key);
|
||||
let why = refusal(&v);
|
||||
assert!(why.starts_with("damaged drawing: ") && why.contains(&format!("missing field `{key}`")), "{why}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Every cap on both sides: sheets, sources, the bytes of paths and names (two-byte characters too).
|
||||
#[test]
|
||||
fn every_cap_holds_on_both_sides() {
|
||||
let base = file(&bare());
|
||||
let with = |key: &str, value: Value| {
|
||||
let mut v = base.clone();
|
||||
v[key] = value;
|
||||
v
|
||||
};
|
||||
let sheet = base["sheets"][0].clone();
|
||||
let sheets = |n: u64| {
|
||||
let one = |id: u64| {
|
||||
let mut s = sheet.clone();
|
||||
s["id"] = json!(ids::encode(id));
|
||||
s
|
||||
};
|
||||
Value::Array((1..=n).map(one).collect())
|
||||
};
|
||||
assert_eq!(read(&with("sheets", sheets(1000))).unwrap().sheets.len(), 1000);
|
||||
for n in [0, 1001] {
|
||||
assert_eq!(refusal(&with("sheets", sheets(n))), "damaged drawing: a drawing has 1 to 1000 sheets", "{n} sheets");
|
||||
}
|
||||
// Sources from id 1: the sheet's id is the seed's, far from them.
|
||||
let sources = |n: u64| Value::Array((1..=n).map(|id| json!({"id": ids::encode(id), "path": format!("part{id}.sc")})).collect());
|
||||
assert_eq!(read(&with("sources", sources(64))).unwrap().sources.len(), 64);
|
||||
assert_eq!(refusal(&with("sources", sources(65))), "damaged drawing: a drawing draws at most 64 designs");
|
||||
let path = |p: &str| refusal(&with("sources", json!([{"id": ids::encode(1), "path": p}])));
|
||||
for fits in ["a".repeat(4096), "é".repeat(2048), "/".into()] {
|
||||
assert_eq!(path(fits.as_str()), "read", "{} bytes", fits.len());
|
||||
}
|
||||
let rule = "damaged drawing: source 0000000000001: a design's path is 1 to 4096 bytes, without NUL";
|
||||
for broken in ["a".repeat(4097), "é".repeat(2048) + "a", String::new(), "plate\0.sc".into()] {
|
||||
assert_eq!(path(broken.as_str()), rule, "{} bytes", broken.len());
|
||||
}
|
||||
let names = |name: &str| {
|
||||
let mut sheet_named = base.clone();
|
||||
sheet_named["sheets"][0]["name"] = json!(name);
|
||||
(refusal(&with("name", json!(name))), refusal(&sheet_named))
|
||||
};
|
||||
for fits in ["x".repeat(256), "é".repeat(128), "A".into(), "Blatt 1 – Übersicht".into()] {
|
||||
assert_eq!(names(fits.as_str()), ("read".to_string(), "read".to_string()), "{} bytes", fits.len());
|
||||
}
|
||||
let rule = "name is 1 to 256 bytes, not only spaces, without control characters";
|
||||
let sheet_id = ids::encode(bare().sheets[0].id);
|
||||
for broken in ["x".repeat(257), "é".repeat(128) + "a", String::new(), " ".into(), "two\nlines".into(), "bell\u{7}".into(), "nul\0".into()] {
|
||||
let expected = (format!("damaged drawing: the drawing's {rule}"), format!("damaged drawing: sheet {sheet_id}: its {rule}"));
|
||||
assert_eq!(names(broken.as_str()), expected, "{broken:?}");
|
||||
}
|
||||
}
|
||||
|
||||
/// The content language is a well-formed BCP 47 tag of 2 to 35 characters.
|
||||
#[test]
|
||||
fn the_language_is_a_bcp_47_tag_of_2_to_35_characters() {
|
||||
let base = file(&bare());
|
||||
let why = |tag: &str| {
|
||||
let mut v = base.clone();
|
||||
v["language"] = json!(tag);
|
||||
refusal(&v)
|
||||
};
|
||||
let longest = "en-abcdefgh-abcdefgh-abcdefgh-abcde";
|
||||
assert_eq!(longest.len(), 35);
|
||||
for tag in ["en", "de", "ast", "en-GB", "EN-gb", "zh-Hans-CN", "sr-Latn-RS", "es-419", longest] {
|
||||
assert_eq!(why(tag), "read", "{tag}");
|
||||
}
|
||||
let rule = "damaged drawing: the language is not a BCP 47 tag of 2 to 35 characters (such as `en` or `en-GB`)";
|
||||
let too_long = format!("{longest}f");
|
||||
let broken =
|
||||
["e", "", too_long.as_str(), "en_GB", "en-", "-en", "en--GB", "e1", "x-private", "en-abcdefghi", "abcdefghi", "日本", "en GB", "en\n"];
|
||||
for tag in broken {
|
||||
assert_eq!(why(tag), rule, "{tag:?}");
|
||||
}
|
||||
}
|
||||
|
||||
/// A fixed unit is a length unit of the expression unit table, written as the table writes it.
|
||||
#[test]
|
||||
fn a_fixed_unit_is_a_length_unit_of_the_expression_table() {
|
||||
let base = file(&bare());
|
||||
let units = |units: Value| {
|
||||
let mut v = base.clone();
|
||||
v["units"] = units;
|
||||
read(&v).map(|d| d.units)
|
||||
};
|
||||
for (unit, kind, _) in expr::UNITS {
|
||||
assert_eq!(units(json!({"fixed": {"unit": unit}})).is_ok(), kind == expr::Kind::Length, "{unit}");
|
||||
}
|
||||
assert_eq!(units(json!({"fixed": {"unit": "in"}})).unwrap(), Units::Fixed { unit: "in".into() });
|
||||
assert_eq!(units(json!("follow_model")).unwrap(), Units::FollowModel);
|
||||
let rule = "damaged drawing: the drawing's unit is not a length unit (such as `mm` or `in`)";
|
||||
for unit in ["furlong", "deg", "°", "none", "", " in", "in ", "IN"] {
|
||||
assert_eq!(units(json!({"fixed": {"unit": unit}})).unwrap_err().to_string(), rule, "{unit:?}");
|
||||
}
|
||||
for broken in [json!("follow_design"), json!({"fixed": "in"}), json!({"fixed": {}}), json!(null)] {
|
||||
assert!(units(broken.clone()).is_err(), "{broken}");
|
||||
}
|
||||
}
|
||||
|
||||
/// A sheet's border is saved as a copy of its profile, field by field, and read back as saved, so
|
||||
/// a later built-in never changes it; a profile's name in its place is refused.
|
||||
#[test]
|
||||
fn a_border_is_saved_as_a_copy_of_its_profile() {
|
||||
let v = file(&plate());
|
||||
assert_eq!(v["sheets"][0]["border"], serde_json::to_value(DrawingBorderProfile::iso()).unwrap());
|
||||
assert_eq!(v["sheets"][1]["border"], serde_json::to_value(own_border()).unwrap());
|
||||
assert_eq!(read(&v).unwrap().sheets[1].border, Some(own_border()));
|
||||
let mut edited = v.clone();
|
||||
edited["sheets"][0]["border"]["margins"]["left_mm"] = json!(15.0);
|
||||
let iso = DrawingBorderProfile::iso();
|
||||
let wider = DrawingBorderProfile { margins: Margins { left_mm: 15.0, ..iso.margins }, ..iso };
|
||||
assert_eq!(read(&edited).unwrap().sheets[0].border, Some(wider));
|
||||
edited["sheets"][0]["border"] = json!("iso");
|
||||
assert!(refusal(&edited).starts_with("damaged drawing: "));
|
||||
edited["sheets"][0]["border"] = Value::Null;
|
||||
assert_eq!(read(&edited).unwrap().sheets[0].border, None, "no drawing border");
|
||||
}
|
||||
|
||||
/// What reading refuses: each damage the rules name, refused rather than patched, and files this
|
||||
/// build can't read.
|
||||
#[test]
|
||||
fn damaged_files_are_refused_and_never_patched() {
|
||||
let d = plate();
|
||||
let plate_id = ids::encode(d.sources[0].id);
|
||||
let [assembly_id, bolt_sheet_id] = [0, 1].map(|i| ids::encode(d.sheets[i].id));
|
||||
// Two objects with one id, of one kind and of two kinds.
|
||||
assert_eq!(refused(&d, |v| v["sources"][1]["id"] = json!(plate_id)), format!("damaged drawing: two objects have the id {plate_id}"));
|
||||
assert_eq!(refused(&d, |v| v["sheets"][1]["id"] = json!(assembly_id)), format!("damaged drawing: two objects have the id {assembly_id}"));
|
||||
assert_eq!(refused(&d, |v| v["sources"][2]["id"] = json!(bolt_sheet_id)), format!("damaged drawing: two objects have the id {bolt_sheet_id}"));
|
||||
// An id that isn't one, where an object has it and where a reference names it; no number form,
|
||||
// and no counter.
|
||||
let not_an_id = "damaged drawing: an id is 13 characters of lowercase Crockford base32, not all 0";
|
||||
for text in ["0000000000000", "FZZZZZZZZZZZZ", "000000000001", "0000000000001 ", "000000000000o", "g000000000000"] {
|
||||
assert_eq!(refused(&d, |v| v["sources"][2]["id"] = json!(text)), not_an_id, "{text:?}");
|
||||
assert_eq!(refused(&d, |v| v["sheets"][1]["documents"]["source"] = json!(text)), not_an_id, "{text:?}");
|
||||
}
|
||||
assert!(refused(&d, |v| v["sources"][2]["id"] = json!(5)).starts_with("damaged drawing: invalid type"));
|
||||
assert!(refused(&d, |v| v["next_id"] = json!(6)).contains("unknown field `next_id`"));
|
||||
let mut in_memory = d.clone();
|
||||
in_memory.sources[2].id = 0;
|
||||
assert_eq!(in_memory.validate().unwrap_err().to_string(), "an object has the id 0, which is never one");
|
||||
// A reference to nothing, or to an object of another kind; a source a sheet documents removed.
|
||||
for missing in [ids::encode(99), assembly_id.clone()] {
|
||||
let expected = format!("damaged drawing: sheet {bolt_sheet_id}: it documents source {missing}, which the drawing doesn't have");
|
||||
assert_eq!(refused(&d, |v| v["sheets"][1]["documents"]["source"] = json!(missing)), expected);
|
||||
}
|
||||
let without_plate = refused(&d, |v| {
|
||||
v["sources"].as_array_mut().unwrap().remove(0);
|
||||
});
|
||||
assert_eq!(without_plate, format!("damaged drawing: sheet {assembly_id}: it documents source {plate_id}, which the drawing doesn't have"));
|
||||
// The paper, its border and its resolution, through the sheet's own rules.
|
||||
let custom = |sides: Value| refused(&d, |v| v["sheets"][0]["sheet"]["size"] = json!({"custom": {"sides_mm": sides}}));
|
||||
assert_eq!(custom(json!([5000.0, 0.5])), "read");
|
||||
for sides in [json!([5000.1, 100.0]), json!([0.0, 100.0]), json!([-1.0, 100.0])] {
|
||||
let expected = format!("damaged drawing: sheet {assembly_id}: drawing sheet: a custom side is finite, above 0 and at most 5000 mm");
|
||||
assert_eq!(custom(sides), expected);
|
||||
}
|
||||
let why = custom(json!([1e10, 100.0]));
|
||||
assert!(why.starts_with("damaged drawing: the number ") && why.ends_with(" is out of range"), "checked before the record: {why}");
|
||||
let nan = d.to_json().unwrap().replacen("600.0", "NaN", 1);
|
||||
assert!(Drawing::from_json(&nan).unwrap_err().to_string().starts_with("not a SolidCraft drawing: "), "NaN isn't JSON");
|
||||
assert_eq!(
|
||||
refused(&d, |v| v["sheets"][1]["border"]["margins"]["top_mm"] = json!(-1.0)),
|
||||
format!("damaged drawing: sheet {bolt_sheet_id}: drawing sheet: a margin is finite and 0 to 5000 mm")
|
||||
);
|
||||
for dpi in [25.0, 2400.0] {
|
||||
assert_eq!(refused(&d, |v| v["sheets"][1]["dpi"] = json!(dpi)), "read", "{dpi}");
|
||||
}
|
||||
for dpi in [24.9, 2400.1, 0.0, -300.0] {
|
||||
let expected = format!("damaged drawing: sheet {bolt_sheet_id}: its pictures are 25 to 2400 dpi");
|
||||
assert_eq!(refused(&d, |v| v["sheets"][1]["dpi"] = json!(dpi)), expected, "{dpi}");
|
||||
}
|
||||
// In memory, where NaN can be, the same rules.
|
||||
let mut in_memory = plate();
|
||||
Arc::make_mut(&mut in_memory.sheets[1]).dpi = f64::NAN;
|
||||
assert_eq!(in_memory.validate().unwrap_err().to_string(), format!("sheet {bolt_sheet_id}: its pictures are 25 to 2400 dpi"));
|
||||
let sheet = Arc::make_mut(&mut in_memory.sheets[1]);
|
||||
sheet.dpi = DEFAULT_DPI;
|
||||
sheet.sheet.size = SheetSize::Custom { sides_mm: [f64::NAN, 100.0] };
|
||||
assert!(in_memory.validate().unwrap_err().to_string().starts_with(&format!("sheet {bolt_sheet_id}: drawing sheet: a custom side")));
|
||||
// An unknown field anywhere.
|
||||
let unknown: [fn(&mut Value); 8] = [
|
||||
|v| v["views"] = json!([]),
|
||||
|v| v["sources"][0]["name"] = json!("Plate"),
|
||||
|v| v["sheets"][0]["scale"] = json!(1),
|
||||
|v| v["sheets"][0]["sheet"]["colour"] = json!("white"),
|
||||
|v| v["sheets"][0]["border"]["style"] = json!("iso"),
|
||||
|v| v["sheets"][0]["documents"]["view"] = json!(1),
|
||||
|v| v["sheets"][0]["excluded"]["export"] = json!(true),
|
||||
|v| v["units"]["fixed"]["precision"] = json!(2),
|
||||
];
|
||||
for (i, edit) in unknown.into_iter().enumerate() {
|
||||
let why = refused(&d, edit);
|
||||
assert!(why.starts_with("damaged drawing: ") && why.contains("unknown field"), "{i}: {why}");
|
||||
}
|
||||
// A newer build's file is refused as one before anything in it is read: a field this build
|
||||
// doesn't know, or a number out of its range.
|
||||
let (current, newer) = (DRAWING.current(), DRAWING.current() + 1);
|
||||
let why = refused(&d, |v| {
|
||||
v["format"] = json!(DRAWING.format_string(newer));
|
||||
v["views"] = json!([]);
|
||||
v["sheets"][0]["dpi"] = json!(1e10);
|
||||
});
|
||||
assert_eq!(
|
||||
why,
|
||||
format!(
|
||||
"this drawing was saved by a newer SolidCraft (file format {newer}; this version reads formats up to {current}): update SolidCraft to open it"
|
||||
)
|
||||
);
|
||||
// A list of pending changes means nothing beside a format made of none; beside the pending
|
||||
// number, a change this build doesn't know is a newer build's.
|
||||
let why = refused(&d, |v| v["format_pending"] = json!(["a-later-change"]));
|
||||
if DRAWING.pending_names().is_empty() {
|
||||
assert_eq!(why, "read");
|
||||
} else {
|
||||
assert!(why.contains("newer SolidCraft"), "{why}");
|
||||
}
|
||||
// A design is never read as a drawing, nor a drawing as a design.
|
||||
let design = crate::Document::new("Plate").to_json();
|
||||
let design_format = crate::format::format_string(crate::format::FORMAT);
|
||||
assert_eq!(Drawing::from_json(&design).unwrap_err().to_string(), format!("not a SolidCraft drawing (format `{design_format}`)"));
|
||||
let drawing_format = DRAWING.format_string(current);
|
||||
assert_eq!(
|
||||
crate::Document::from_json(&d.to_json().unwrap()).unwrap_err().to_string(),
|
||||
format!("not a SolidCraft design (format `{drawing_format}`)")
|
||||
);
|
||||
// Not an object, not JSON, nested too deeply, too large.
|
||||
assert_eq!(Drawing::from_json("[]").unwrap_err().to_string(), "not a SolidCraft drawing (expected a JSON object)");
|
||||
assert!(Drawing::from_json("").unwrap_err().to_string().starts_with("not a SolidCraft drawing: "));
|
||||
let deep: Value = serde_json::from_str(&format!("{}1{}", "[".repeat(100), "]".repeat(100))).unwrap();
|
||||
assert_eq!(refused(&d, |v| v["views"] = deep), "damaged drawing: nested too deeply");
|
||||
let deeper = format!("{{\"format\": \"{drawing_format}\", \"views\": {}1{}}}", "[".repeat(200), "]".repeat(200));
|
||||
assert!(Drawing::from_json(&deeper).unwrap_err().to_string().starts_with("not a SolidCraft drawing: "));
|
||||
let text = d.to_json().unwrap();
|
||||
let at_cap = text.clone() + &" ".repeat(MAX_DRAWING_BYTES - text.len());
|
||||
assert_eq!(Drawing::from_json(&at_cap).unwrap(), d);
|
||||
assert_eq!(Drawing::from_json(&(at_cap + " ")).unwrap_err().to_string(), "drawing too large (at most 16 MiB)");
|
||||
}
|
||||
|
||||
/// A run never gives an id twice: not one in use, and not a removed object's. Only another run from
|
||||
/// the same seed, which tests and replays start from the same file, can give a removed id again.
|
||||
#[test]
|
||||
fn a_run_never_gives_an_id_twice() {
|
||||
let mut id_source = IdSource::seeded(SEED);
|
||||
let a4 = drawing_sheet::Sheet::standard(PaperSize::A4, Orientation::Landscape);
|
||||
let mut d = Drawing::new("Ids", "en", "Sheet", a4, None, &mut id_source).unwrap();
|
||||
let mut given = BTreeSet::from([d.sheets[0].id]);
|
||||
for _ in 0..MAX_SOURCES {
|
||||
let id = d.allocate_id(&mut id_source).unwrap();
|
||||
d.sources.push(Source { id, path: "part.sc".into() });
|
||||
assert!(given.insert(id), "{id:#x} given twice");
|
||||
}
|
||||
let removed = d.sources.pop().unwrap().id;
|
||||
for _ in 0..10_000 {
|
||||
let id = d.allocate_id(&mut id_source).unwrap();
|
||||
assert!(id != removed && given.insert(id), "{id:#x} given twice");
|
||||
}
|
||||
// The same seed, from the start, over a file it made: every draw up to the removed id is in use.
|
||||
let reopened = Drawing::from_json(&d.to_json().unwrap()).unwrap();
|
||||
let again = reopened.allocate_id(&mut IdSource::seeded(SEED)).unwrap();
|
||||
assert!(!reopened.ids().any(|id| id == again));
|
||||
assert_eq!(again, removed);
|
||||
}
|
||||
|
||||
/// Properties follow the design metadata's rules: other tools' data and a well-known key of another
|
||||
/// kind are kept as they are, never read as numbers, while the same number elsewhere is refused.
|
||||
#[test]
|
||||
fn properties_are_kept_as_they_are() {
|
||||
let mut v = file(&plate());
|
||||
v["properties"]["com.example.pdm"] = json!({"huge": 1e300, "ids": [1, 2, 3]});
|
||||
v["properties"]["revision"] = json!(3);
|
||||
let d = read(&v).unwrap();
|
||||
assert_eq!(d.properties.get("com.example.pdm"), Some(&json!({"huge": 1e300, "ids": [1, 2, 3]})));
|
||||
assert_eq!((d.properties.revision(), d.properties.get("revision")), (None, Some(&json!(3))));
|
||||
assert_eq!(read(&file(&d)).unwrap(), d);
|
||||
v["sheets"][0]["dpi"] = json!(1e300);
|
||||
let why = refusal(&v);
|
||||
assert!(why.starts_with("damaged drawing: the number ") && why.ends_with(" is out of range"), "{why}");
|
||||
}
|
||||
|
||||
/// A drawing whose JSON would be over the read limit isn't saved. Other tools' properties are kept
|
||||
/// as they came, and indenting them can grow a file read in under the limit past it, so the save
|
||||
/// refuses rather than write a file that won't open: every drawing saved opens again.
|
||||
#[test]
|
||||
fn a_drawing_too_large_to_reopen_is_not_saved() {
|
||||
let file = |zeros: usize| {
|
||||
let items = vec!["0"; zeros].join(",");
|
||||
let sheet = r#"{"id":"0000000000001","name":"S","sheet":{"size":{"standard":"a4"}}}"#;
|
||||
let format = DRAWING.format_string(1);
|
||||
format!(
|
||||
r#"{{"format":"{format}","name":"A","sources":[],"language":"en","sheets":[{sheet}],"properties":{{"com.example":{{"items":[{items}]}}}}}}"#
|
||||
)
|
||||
};
|
||||
// The case found in review (#3891): 2,000,000 zeros read in at about 4 MB and indent past 16 MiB.
|
||||
let big = Drawing::from_json(&file(2_000_000)).unwrap();
|
||||
assert_eq!(big.to_json().unwrap_err().to_string(), "drawing too large to save (at most 16 MiB)");
|
||||
let small = Drawing::from_json(&file(100_000)).unwrap();
|
||||
assert_eq!(Drawing::from_json(&small.to_json().unwrap()).unwrap(), small);
|
||||
}
|
||||
|
||||
/// A new drawing has one sheet, its id the source's first, follows its designs' units and renders
|
||||
/// pictures at 300 dpi; a name, language or border that breaks the rules is refused.
|
||||
#[test]
|
||||
fn a_new_drawing_has_one_sheet_and_keeps_the_rules() {
|
||||
let d = bare();
|
||||
let first = IdSource::seeded(SEED).next(&BTreeSet::new()).unwrap();
|
||||
assert_eq!((d.sheets.len(), d.sheets[0].id, d.sheets[0].dpi, &d.units), (1, first, DEFAULT_DPI, &Units::FollowModel));
|
||||
assert_eq!((d.format, d.format_pending), (DRAWING.format_string(DRAWING.current()), DRAWING.pending_names()));
|
||||
let a4 = drawing_sheet::Sheet::standard(PaperSize::A4, Orientation::Landscape);
|
||||
let thin = DrawingBorderProfile { drawing_border_line_mm: 0.0, ..DrawingBorderProfile::iso() };
|
||||
let broken = [("", "en", "Sheet", None), ("Bare", "en_GB", "Sheet", None), ("Bare", "en", "\n", None), ("Bare", "en", "Sheet", Some(thin))];
|
||||
for (name, language, sheet, border) in broken {
|
||||
let made = Drawing::new(name, language, sheet, a4, border, &mut IdSource::seeded(SEED));
|
||||
assert!(made.is_err(), "{name:?} {language:?} {sheet:?} {border:?}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Hostile input never panics: a valid file's text cut, overwritten and given lies, and its JSON
|
||||
/// values replaced, repeated and dropped. Each read gives an error, or a drawing that keeps the
|
||||
/// rules and reads back as it saves.
|
||||
#[test]
|
||||
fn mutated_files_give_an_error_or_a_valid_drawing_never_a_panic() {
|
||||
let good = plate().to_json().unwrap();
|
||||
let tree: Value = serde_json::from_str(&good).unwrap();
|
||||
let nodes = count(&tree) as u64;
|
||||
let lies = [
|
||||
json!(null),
|
||||
json!(0),
|
||||
json!(-1),
|
||||
json!(1),
|
||||
json!(1e10),
|
||||
json!(u64::MAX),
|
||||
json!(""),
|
||||
json!("x".repeat(300)),
|
||||
json!("0000000000000"),
|
||||
json!("0000000000001"),
|
||||
json!("fzzzzzzzzzzzz"),
|
||||
json!("FZZZZZZZZZZZZ"),
|
||||
json!([]),
|
||||
json!({}),
|
||||
json!(true),
|
||||
json!("follow_model"),
|
||||
json!({"fixed": {"unit": "deg"}}),
|
||||
];
|
||||
let words = ["NaN", "1e400", "-0", "18446744073709551616", "\"", "{", "]", ",", "null", "\u{0}", "é", "o", "I"];
|
||||
let mut seed: u64 = 0x9e37_79b9_7f4a_7c15;
|
||||
let mut rnd = move || {
|
||||
seed ^= seed << 13;
|
||||
seed ^= seed >> 7;
|
||||
seed ^= seed << 17;
|
||||
seed
|
||||
};
|
||||
let mut read_back = 0;
|
||||
for i in 0..10_000 {
|
||||
let text = if i % 2 == 0 {
|
||||
let mut v = tree.clone();
|
||||
let lie = &lies[(rnd() % lies.len() as u64) as usize];
|
||||
mutate(&mut v, &mut ((rnd() % nodes) as usize), lie, rnd() % 3);
|
||||
v.to_string()
|
||||
} else {
|
||||
let mut s = good.clone().into_bytes();
|
||||
for _ in 0..1 + rnd() % 3 {
|
||||
// A run of up to 15 bytes taken out, overwritten by one byte or replaced by a lie.
|
||||
let at = (rnd() % (s.len() as u64 + 1)) as usize;
|
||||
let cut = ((rnd() % 16) as usize).min(s.len() - at);
|
||||
let byte = [(rnd() >> 56) as u8];
|
||||
let put: &[u8] = match rnd() % 3 {
|
||||
0 => &[],
|
||||
1 => &byte,
|
||||
_ => words[(rnd() % words.len() as u64) as usize].as_bytes(),
|
||||
};
|
||||
let next = [&s[..at], put, &s[at + cut..]].concat();
|
||||
s = next;
|
||||
if rnd() % 8 == 0 {
|
||||
s.truncate(at);
|
||||
}
|
||||
}
|
||||
String::from_utf8_lossy(&s).into_owned()
|
||||
};
|
||||
if let Ok(d) = Drawing::from_json(&text) {
|
||||
assert!(d.validate().is_ok() && d.ids().all(|id| id != 0), "{text}");
|
||||
assert_eq!(Drawing::from_json(&d.to_json().unwrap()).unwrap(), d, "{text}");
|
||||
read_back += 1;
|
||||
}
|
||||
}
|
||||
assert!(read_back > 50, "only {read_back} mutated files read: the mutations hardly reach the record's rules");
|
||||
}
|
||||
|
||||
/// The number of values in `v`, itself included.
|
||||
fn count(v: &Value) -> usize {
|
||||
1 + match v {
|
||||
Value::Array(a) => a.iter().map(count).sum::<usize>(),
|
||||
Value::Object(o) => o.values().map(count).sum(),
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Change the `pick`th value of `v`, depth first: `how` 1 repeats a list's first element (two
|
||||
/// objects with one id), 2 drops an object's first field, and otherwise `lie` takes its place.
|
||||
fn mutate(v: &mut Value, pick: &mut usize, lie: &Value, how: u64) -> bool {
|
||||
if *pick == 0 {
|
||||
match (v, how) {
|
||||
(Value::Array(a), 1) if !a.is_empty() => {
|
||||
let first = a[0].clone();
|
||||
a.push(first);
|
||||
}
|
||||
(Value::Object(o), 2) if !o.is_empty() => {
|
||||
let key = o.keys().next().cloned().unwrap_or_default();
|
||||
o.remove(&key);
|
||||
}
|
||||
(v, _) => *v = lie.clone(),
|
||||
}
|
||||
return true;
|
||||
}
|
||||
*pick -= 1;
|
||||
match v {
|
||||
Value::Array(a) => a.iter_mut().any(|x| mutate(x, pick, lie, how)),
|
||||
Value::Object(o) => o.values_mut().any(|x| mutate(x, pick, lie, how)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
+11
-23
@@ -1865,14 +1865,6 @@ 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 } => {
|
||||
@@ -2729,22 +2721,10 @@ 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<(String, usize)> = Vec::new();
|
||||
let mut grown: Vec<(&str, usize)> = Vec::new();
|
||||
for was in &before.bodies {
|
||||
match after.body(&was.name) {
|
||||
Some(now) if now.body.same_shape(&was.body) => {}
|
||||
@@ -2753,13 +2733,21 @@ pub fn join_pieces_added(before: &ModelState, after: &ModelState) -> Vec<(String
|
||||
had += old;
|
||||
have += new;
|
||||
if new > old {
|
||||
grown.push((now.name.clone(), new));
|
||||
grown.push((now.name.as_str(), new));
|
||||
}
|
||||
}
|
||||
None => had += pieces(&was.body),
|
||||
}
|
||||
}
|
||||
if have <= had { Vec::new() } else { grown }
|
||||
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)"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Add tool bodies to the model according to the operation.
|
||||
|
||||
@@ -485,7 +485,7 @@ fn mirror_y(r: &Region2) -> Region2 {
|
||||
.iter()
|
||||
.map(|s| match *s {
|
||||
Seg2::Arc { center, radius, start, sweep } => Seg2::Arc { center: f(center), radius, start: -start, sweep: -sweep },
|
||||
other => other.map_controls(f),
|
||||
other => sheet::map_controls(&other, f),
|
||||
})
|
||||
.collect();
|
||||
Loop2 { segs }.ccw()
|
||||
|
||||
+2
-38
@@ -352,14 +352,6 @@ 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;
|
||||
@@ -573,10 +565,6 @@ 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(),
|
||||
@@ -604,8 +592,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. Every value input defines a user parameter this way (#2916, #4071): until
|
||||
/// `Document::define_inline` stores the parameter, the text evaluates as its expression.
|
||||
/// to parse as before. The dimension input defines a user parameter this way (#2916); other
|
||||
/// value inputs can take the same form later.
|
||||
pub fn definition(s: &str) -> Option<(&str, &str)> {
|
||||
let (name, rhs) = s.split_once('=')?;
|
||||
let name = name.trim();
|
||||
@@ -991,9 +979,6 @@ 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 };
|
||||
@@ -1485,27 +1470,6 @@ 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() {
|
||||
|
||||
@@ -183,11 +183,6 @@ impl Document {
|
||||
return Err(DocError::Invalid("this face has no supported construction certificate; choose a fixed plane instead".into()));
|
||||
}
|
||||
let indices = self.configuration_face_indices(body, &target)?;
|
||||
// A copy a pattern or mirror made on the same body carries its seed's certificate,
|
||||
// which finds the seed's face: the copy is attached by its own history name below.
|
||||
if !indices.contains(&index) {
|
||||
return Err(DocError::Invalid("this face has no supported construction certificate; choose a fixed plane instead".into()));
|
||||
}
|
||||
self.coplanar_pieces(body, &indices, |_| true)?;
|
||||
Ok(target)
|
||||
});
|
||||
|
||||
+75
-347
@@ -232,11 +232,6 @@
|
||||
//! this build writes the next number (which no release reads), with the names of these changes
|
||||
//! in `format_pending`, so a later build applies just the steps a file is missing. At release
|
||||
//! time `scripts/fold-format-pending.sh` moves them into the next `v<N>/`.
|
||||
//!
|
||||
//! Drawings (`solidcraft-drawing/N`, #3890) are versioned by the same code ([`Family`]) from their
|
||||
//! own first format, 1 (the record in [`crate::drawing`]): each later change is one file in
|
||||
//! `src/drawing/format/`, in `v<N>/` or `pending/` as above, and the release script folds that
|
||||
//! folder too.
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
@@ -249,11 +244,11 @@ pub const FORMAT: u32 = FORMATS.current();
|
||||
/// of [`Part`]s (`src/format/`, listed by `build.rs`).
|
||||
const HAND_WRITTEN: u32 = 22;
|
||||
|
||||
/// One change to a family's file format: a file in its `v<N>/` or `pending/` folder.
|
||||
/// One change to the file format: a file in `src/format/v<N>/` or `src/format/pending/`.
|
||||
pub(crate) struct Part {
|
||||
/// The file's name, which a file written while the change was pending lists.
|
||||
pub name: &'static str,
|
||||
/// How a file from before the change is rewritten as it opens, if it is.
|
||||
/// How a design from before the change is rewritten as it opens, if it is.
|
||||
pub step: Option<JsonStep>,
|
||||
}
|
||||
|
||||
@@ -262,102 +257,13 @@ include!(concat!(env!("OUT_DIR"), "/format_parts.rs"));
|
||||
/// Every format this build knows and how to upgrade a design through them.
|
||||
const FORMATS: Formats<'static> = Formats { hand: &JSON_STEPS, folded: FOLDED, pending: PENDING };
|
||||
|
||||
/// A kind of file whose format is versioned here: designs, and drawings (#3890). Each family has
|
||||
/// its own format tag, its own numbers and its own folder of change files (`build.rs` lists them);
|
||||
/// reading a file's format, refusing a file a newer build saved and the upgrade are the same code
|
||||
/// for every family.
|
||||
pub struct Family<'a> {
|
||||
/// What a file of the family is called in messages.
|
||||
noun: &'static str,
|
||||
/// The tag before the number in a file's `format`.
|
||||
prefix: &'static str,
|
||||
/// The tag files saved before the rename carry: read, never written (docs/compatibility.md).
|
||||
legacy_prefix: Option<&'static str>,
|
||||
/// The format of a file that names none: designs saved before format strings are format 1; a
|
||||
/// drawing always names its own.
|
||||
untagged: Option<u32>,
|
||||
formats: Formats<'a>,
|
||||
}
|
||||
|
||||
/// Designs (`solidcraft/N`): the upgrade steps written out up to format 22, then the change files
|
||||
/// in `src/format/`.
|
||||
pub const DESIGN: Family<'static> =
|
||||
Family { noun: "design", prefix: "solidcraft/", legacy_prefix: Some(LEGACY_FORMAT_PREFIX), untagged: Some(1), formats: FORMATS };
|
||||
|
||||
/// Drawings (`solidcraft-drawing/N`, #3890): format 1 is the first drawing record, and every later
|
||||
/// change is a file in `src/drawing/format/`.
|
||||
pub const DRAWING: Family<'static> = Family {
|
||||
noun: "drawing",
|
||||
prefix: "solidcraft-drawing/",
|
||||
legacy_prefix: None,
|
||||
untagged: None,
|
||||
formats: Formats { hand: &[], folded: DRAWING_FOLDED, pending: DRAWING_PENDING },
|
||||
};
|
||||
|
||||
impl Family<'_> {
|
||||
/// The format this build writes: the last folded one, or the next while changes are pending.
|
||||
pub const fn current(&self) -> u32 {
|
||||
self.formats.current()
|
||||
}
|
||||
|
||||
/// The names of the pending changes this build writes into a file (`format_pending`).
|
||||
pub fn pending_names(&self) -> Vec<String> {
|
||||
self.formats.pending.iter().map(|p| p.name.to_string()).collect()
|
||||
}
|
||||
|
||||
/// The format tag of format `n`.
|
||||
pub fn format_string(&self, n: u32) -> String {
|
||||
format!("{}{n}", self.prefix)
|
||||
}
|
||||
|
||||
/// The version in one of this family's format tags; `None` for any other tag, another
|
||||
/// family's included.
|
||||
pub fn version_of(&self, s: &str) -> Option<u32> {
|
||||
s.strip_prefix(self.prefix).or_else(|| self.legacy_prefix.and_then(|p| s.strip_prefix(p)))?.parse().ok()
|
||||
}
|
||||
|
||||
/// Read a file's format and take its `format_pending` out of its JSON, before anything else
|
||||
/// in it is read: a file that isn't this family's, or that a newer build saved, is refused
|
||||
/// here, so nothing a newer build saved is judged by this build's rules.
|
||||
pub fn take_version(&self, v: &mut Value) -> Result<(u32, Option<Vec<String>>)> {
|
||||
let noun = self.noun;
|
||||
let not_ours = |what: String| DocError::Invalid(format!("not a SolidCraft {noun} ({what})"));
|
||||
let Some(obj) = v.as_object() else { return Err(not_ours("expected a JSON object".into())) };
|
||||
let version = match obj.get("format") {
|
||||
None => self.untagged.ok_or_else(|| not_ours("no format".into()))?,
|
||||
Some(Value::String(f)) => self.version_of(f).ok_or_else(|| not_ours(format!("format `{f}`")))?,
|
||||
Some(_) => return Err(not_ours("format is not a string".into())),
|
||||
};
|
||||
let applied = take_names(v, "format_pending", noun)?;
|
||||
self.formats.check(noun, version, applied.as_deref())?;
|
||||
Ok((version, applied))
|
||||
}
|
||||
|
||||
/// Bring a file of format `version` up to this build's; `applied` is its `format_pending`
|
||||
/// (both from [`Family::take_version`]).
|
||||
pub fn upgrade(&self, v: &mut Value, version: u32, applied: Option<&[String]>) -> Result<()> {
|
||||
self.formats.upgrade(self.noun, v, version, applied)
|
||||
}
|
||||
|
||||
/// Every number a file of the family holds is in range ([`check_numbers`]), but for its
|
||||
/// top-level `kept` field: other tools' data (a design's `metadata`, a drawing's `properties`),
|
||||
/// kept as it is and never read as a number (#1015); parsing already bounds its nesting.
|
||||
pub(crate) fn check_numbers(&self, v: &Value, kept: &str) -> Result<()> {
|
||||
match v {
|
||||
Value::Object(m) => m.iter().filter(|(k, _)| k.as_str() != kept).try_for_each(|(_, x)| check_numbers(x, self.noun, 1)),
|
||||
v => check_numbers(v, self.noun, 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The names of the pending changes this build writes into a design (`format_pending`).
|
||||
pub fn pending_names() -> Vec<String> {
|
||||
DESIGN.pending_names()
|
||||
FORMATS.pending.iter().map(|p| p.name.to_string()).collect()
|
||||
}
|
||||
|
||||
/// A family's upgrade path: the steps written out by hand (designs' up to [`HAND_WRITTEN`]), the
|
||||
/// folded formats after them (each a list of changes) and the pending changes, which make the
|
||||
/// next format.
|
||||
/// The upgrade path: the hand-written steps up to [`HAND_WRITTEN`], the folded formats after it
|
||||
/// (each a list of changes) and the pending changes, which make the next format.
|
||||
struct Formats<'a> {
|
||||
hand: &'a [JsonStep],
|
||||
folded: &'a [(u32, &'a [Part])],
|
||||
@@ -394,41 +300,26 @@ impl Formats<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Refuse a `noun` of format `version` that this build can't read: format 0, a newer number,
|
||||
/// or a change `applied` (its `format_pending`) names that this build doesn't have anywhere in
|
||||
/// its history, which only a newer build's file can. Changes are recognized across the entire
|
||||
/// history: a branch's pending change may have landed at a later number. A list beside a format
|
||||
/// that isn't made of changes means nothing (a tool or a test set an older number on a newer
|
||||
/// file).
|
||||
fn check(&self, noun: &str, version: u32, applied: Option<&[String]>) -> Result<()> {
|
||||
let newer = |what: String| DocError::Invalid(format!("this {noun} was saved by a newer SolidCraft ({what}): update SolidCraft to open it"));
|
||||
let current = self.current();
|
||||
if version == 0 {
|
||||
return Err(DocError::Invalid(format!("not a SolidCraft {noun} (format 0)")));
|
||||
}
|
||||
if version > current {
|
||||
return Err(newer(format!("file format {version}; this version reads formats up to {current}")));
|
||||
}
|
||||
let applied = applied.filter(|_| self.parts(version).is_some()).unwrap_or_default();
|
||||
match applied.iter().find(|name| !self.knows_part_name(self.canonical_applied_name(version, name))) {
|
||||
Some(unknown) => Err(newer(format!("file format {version} with `{unknown}`, which this version doesn't know"))),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Bring a `noun` of format `version` up to [`Formats::current`]. `applied` is the file's
|
||||
/// Bring a design of format `version` up to [`Formats::current`]. `applied` is the file's
|
||||
/// `format_pending`: the changes of its format it already has, when it was written while they
|
||||
/// were pending; the others' steps run first. A file [`Formats::check`] refuses is refused,
|
||||
/// whoever calls, before anything is changed; already-applied changes never run again at
|
||||
/// their later number.
|
||||
fn upgrade(&self, noun: &str, v: &mut Value, version: u32, applied: Option<&[String]>) -> Result<()> {
|
||||
self.check(noun, version, applied)?;
|
||||
// A list beside a format that isn't made of changes is ignored, like any field an upgrade
|
||||
// drops.
|
||||
/// were pending; the others' steps run first. Recognize changes across the entire history:
|
||||
/// a branch's pending change may have landed at a later number. Unknown changes are refused
|
||||
/// even after folding, and already-applied changes never run again at their later number.
|
||||
fn upgrade(&self, v: &mut Value, version: u32, applied: Option<&[String]>) -> Result<()> {
|
||||
if version == 0 || version > self.current() {
|
||||
return Err(DocError::Invalid(format!("not a SolidCraft design (format {version})")));
|
||||
}
|
||||
// A list beside a format that isn't made of changes means nothing (a tool or a test set
|
||||
// an older number on a newer file): it is ignored, like any field an upgrade drops.
|
||||
let applied = applied.filter(|_| self.parts(version).is_some());
|
||||
let already_applied =
|
||||
|part: &Part| applied.is_some_and(|names| names.iter().any(|name| self.canonical_applied_name(version, name) == part.name));
|
||||
if applied.is_some() {
|
||||
if let Some(applied) = applied {
|
||||
if let Some(unknown) = applied.iter().find(|name| !self.knows_part_name(self.canonical_applied_name(version, name))) {
|
||||
return Err(DocError::Invalid(format!(
|
||||
"this design was saved by a newer SolidCraft (file format {version} with `{unknown}`, which this version doesn't know): update SolidCraft to open it"
|
||||
)));
|
||||
}
|
||||
for part in self.parts(version).unwrap_or_default().iter().filter(|part| !already_applied(part)) {
|
||||
part.step.map_or(Ok(()), |step| step(v))?;
|
||||
}
|
||||
@@ -447,26 +338,25 @@ impl Formats<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Take a short list of names out of a file's JSON: its `format_pending` (the pending changes it
|
||||
/// has).
|
||||
fn take_names(v: &mut Value, key: &str, noun: &str) -> Result<Option<Vec<String>>> {
|
||||
let Some(list) = v.as_object_mut().and_then(|o| o.remove(key)) else { return Ok(None) };
|
||||
let invalid = || DocError::Invalid(format!("damaged {noun}: `{key}` is not a short list of names"));
|
||||
/// Take a file's `format_pending` (the pending changes it has) out of its JSON.
|
||||
fn take_pending(v: &mut Value) -> Result<Option<Vec<String>>> {
|
||||
let Some(list) = v.as_object_mut().and_then(|o| o.remove("format_pending")) else { return Ok(None) };
|
||||
let invalid = || DocError::Invalid("damaged design: `format_pending` is not a short list of names".into());
|
||||
let list = list.as_array().filter(|l| l.len() <= 1000).ok_or_else(invalid)?;
|
||||
list.iter().map(|n| n.as_str().filter(|n| n.len() <= 128).map(str::to_string).ok_or_else(invalid)).collect::<Result<_>>().map(Some)
|
||||
}
|
||||
|
||||
pub fn format_string(n: u32) -> String {
|
||||
DESIGN.format_string(n)
|
||||
format!("solidcraft/{n}")
|
||||
}
|
||||
|
||||
/// The format tag of designs saved before the rename. Compatibility shim (docs/compatibility.md):
|
||||
/// read, never written.
|
||||
pub const LEGACY_FORMAT_PREFIX: &str = "solvecraft/";
|
||||
|
||||
/// The version in a design's format string (`solidcraft/N`, or the legacy `solvecraft/N`).
|
||||
/// The version in a format string (`solidcraft/N`, or the legacy `solvecraft/N`).
|
||||
pub fn version_of(s: &str) -> Option<u32> {
|
||||
DESIGN.version_of(s)
|
||||
s.strip_prefix("solidcraft/").or_else(|| s.strip_prefix(LEGACY_FORMAT_PREFIX))?.parse().ok()
|
||||
}
|
||||
|
||||
/// JSON-level upgrades up to [`HAND_WRITTEN`]: entry `i` turns format `i + 1` into `i + 2`.
|
||||
@@ -676,7 +566,23 @@ fn v2_to_v3(v: &mut Value) -> Result<()> {
|
||||
/// Read a design file's JSON, upgrading older formats.
|
||||
pub fn read(s: &str) -> Result<Document> {
|
||||
let mut v: Value = serde_json::from_str(s).map_err(|e| DocError::Invalid(format!("not a SolidCraft design: {e}")))?;
|
||||
let (version, applied) = DESIGN.take_version(&mut v)?;
|
||||
let Some(obj) = v.as_object() else {
|
||||
return Err(DocError::Invalid("not a SolidCraft design (expected a JSON object)".into()));
|
||||
};
|
||||
let version = match obj.get("format") {
|
||||
None => 1,
|
||||
Some(Value::String(f)) => version_of(f).ok_or_else(|| DocError::Invalid(format!("not a SolidCraft design (format `{f}`)")))?,
|
||||
Some(_) => return Err(DocError::Invalid("not a SolidCraft design (format is not a string)".into())),
|
||||
};
|
||||
let applied = take_pending(&mut v)?;
|
||||
if version == 0 {
|
||||
return Err(DocError::Invalid("not a SolidCraft design (format 0)".into()));
|
||||
}
|
||||
if version > FORMAT {
|
||||
return Err(DocError::Invalid(format!(
|
||||
"this design was saved by a newer SolidCraft (file format {version}; this version reads formats up to {FORMAT}): update SolidCraft to open it"
|
||||
)));
|
||||
}
|
||||
check_design_numbers(&v)?;
|
||||
crate::config::validate_rules_json(&v)?;
|
||||
crate::config::validate_styles_json(&v)?;
|
||||
@@ -688,7 +594,7 @@ pub fn read(s: &str) -> Result<Document> {
|
||||
crate::format_offsets::validate_raw(&v)?;
|
||||
let legacy_wires = validate_wires(&v, version)?;
|
||||
validate_body_transfers(&v)?;
|
||||
DESIGN.upgrade(&mut v, version, applied.as_deref())?;
|
||||
FORMATS.upgrade(&mut v, version, applied.as_deref())?;
|
||||
let mut d: Document = serde_json::from_value(v).map_err(|e| DocError::Invalid(format!("design file: {e}")))?;
|
||||
check_typed_numbers(&d)?;
|
||||
let mut legacy_by_feature = std::collections::BTreeMap::<usize, Vec<usize>>::new();
|
||||
@@ -716,7 +622,6 @@ 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();
|
||||
@@ -1155,22 +1060,22 @@ const MAX_REAL: f64 = 1e9;
|
||||
/// already bounds its nesting. Engineering SI constants have their own bounded admission
|
||||
/// (e.g. E in Pa): only the exact root user-material card field bypasses the coordinate bound.
|
||||
fn check_design_numbers(v: &Value) -> Result<()> {
|
||||
let Some(root) = v.as_object() else { return check_numbers(v, "design", 0) };
|
||||
let Some(root) = v.as_object() else { return check_numbers(v, 0) };
|
||||
for (key, value) in root {
|
||||
if key == "metadata" {
|
||||
continue;
|
||||
}
|
||||
if key != "user_materials" {
|
||||
check_numbers(value, "design", 1)?;
|
||||
check_numbers(value, 1)?;
|
||||
continue;
|
||||
}
|
||||
let Some(materials) = value.as_array() else { return check_numbers(value, "design", 1) };
|
||||
let Some(materials) = value.as_array() else { return check_numbers(value, 1) };
|
||||
if materials.len() > crate::MAX_USER_MATERIALS {
|
||||
return Err(DocError::Invalid("document exceeds 1000 user materials".into()));
|
||||
}
|
||||
for material in materials {
|
||||
let Some(fields) = material.as_object() else {
|
||||
check_numbers(material, "design", 2)?;
|
||||
check_numbers(material, 2)?;
|
||||
continue;
|
||||
};
|
||||
for (field, data) in fields {
|
||||
@@ -1179,7 +1084,7 @@ fn check_design_numbers(v: &Value) -> Result<()> {
|
||||
if field == "physical_card" && !data.is_null() {
|
||||
crate::physical_material::validate_card(data)?;
|
||||
} else {
|
||||
check_numbers(data, "design", 3)?;
|
||||
check_numbers(data, 3)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1197,22 +1102,21 @@ fn check_typed_numbers(d: &Document) -> Result<()> {
|
||||
check_design_numbers(&typed)
|
||||
}
|
||||
|
||||
/// Every non-integer number in a `noun`'s file, outside separately validated engineering cards,
|
||||
/// is finite and within ±`MAX_REAL` (a damaged or hostile file is refused instead of overflowing
|
||||
/// the geometry later).
|
||||
fn check_numbers(v: &Value, noun: &str, depth: usize) -> Result<()> {
|
||||
/// Every non-integer number outside separately validated engineering cards is finite and within ±`MAX_REAL` (a damaged or hostile
|
||||
/// file is refused instead of overflowing the geometry later).
|
||||
fn check_numbers(v: &Value, depth: usize) -> Result<()> {
|
||||
if depth > 64 {
|
||||
return Err(DocError::Invalid(format!("damaged {noun}: nested too deeply")));
|
||||
return Err(DocError::Invalid("damaged design: nested too deeply".into()));
|
||||
}
|
||||
match v {
|
||||
Value::Number(n) if !(n.is_u64() || n.is_i64()) => {
|
||||
let x = n.as_f64().unwrap_or(f64::NAN);
|
||||
if !(x.is_finite() && x.abs() <= MAX_REAL) {
|
||||
return Err(DocError::Invalid(format!("damaged {noun}: the number {n} is out of range")));
|
||||
return Err(DocError::Invalid(format!("damaged design: the number {n} is out of range")));
|
||||
}
|
||||
}
|
||||
Value::Array(a) => a.iter().try_for_each(|x| check_numbers(x, noun, depth + 1))?,
|
||||
Value::Object(m) => m.values().try_for_each(|x| check_numbers(x, noun, depth + 1))?,
|
||||
Value::Array(a) => a.iter().try_for_each(|x| check_numbers(x, depth + 1))?,
|
||||
Value::Object(m) => m.values().try_for_each(|x| check_numbers(x, depth + 1))?,
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
@@ -1320,25 +1224,25 @@ mod tests {
|
||||
assert_eq!(formats.current(), next);
|
||||
for version in [1, HAND_WRITTEN, next - 1] {
|
||||
let mut v = serde_json::json!({"features": [], "assembly": {"joints": [{"flip": false}]}});
|
||||
formats.upgrade("design", &mut v, version, None).unwrap();
|
||||
formats.upgrade(&mut v, version, None).unwrap();
|
||||
assert_eq!(trace_of(&v), ["a", "b"], "from format {version}");
|
||||
// The hand-written steps ran too: format 3's joints were flipped on the way up.
|
||||
assert_eq!(v["assembly"]["joints"][0]["flip"], Value::Bool(version < 4), "from format {version}");
|
||||
}
|
||||
// A design of the pending number written before `b-second` existed gets just that step.
|
||||
let mut v = serde_json::json!({});
|
||||
formats.upgrade("design", &mut v, next, Some(&["a-first".to_string()])).unwrap();
|
||||
formats.upgrade(&mut v, next, Some(&["a-first".to_string()])).unwrap();
|
||||
assert_eq!(trace_of(&v), ["b"]);
|
||||
// One that has both, or that this build wrote, needs nothing.
|
||||
let mut v = serde_json::json!({});
|
||||
formats.upgrade("design", &mut v, next, Some(&["a-first".into(), "b-second".into()])).unwrap();
|
||||
formats.upgrade(&mut v, next, Some(&["a-first".into(), "b-second".into()])).unwrap();
|
||||
assert_eq!(trace_of(&v), Vec::<&str>::new());
|
||||
// A change this build doesn't know is a newer build's: refused, not guessed at.
|
||||
let err = formats.upgrade("design", &mut serde_json::json!({}), next, Some(&["c-later".into()])).unwrap_err();
|
||||
let err = formats.upgrade(&mut serde_json::json!({}), next, Some(&["c-later".into()])).unwrap_err();
|
||||
assert!(err.to_string().contains("newer SolidCraft"), "{err}");
|
||||
// A list on a format that isn't made of changes means nothing.
|
||||
let mut v = serde_json::json!({});
|
||||
formats.upgrade("design", &mut v, HAND_WRITTEN, Some(&["a-first".into()])).unwrap();
|
||||
formats.upgrade(&mut v, HAND_WRITTEN, Some(&["a-first".into()])).unwrap();
|
||||
assert_eq!(trace_of(&v), ["a", "b"]);
|
||||
}
|
||||
|
||||
@@ -1353,13 +1257,13 @@ mod tests {
|
||||
let formats = Formats { hand: &JSON_STEPS, folded: &folded, pending: &[] };
|
||||
assert_eq!(formats.current(), next);
|
||||
let mut old = serde_json::json!({});
|
||||
formats.upgrade("design", &mut old, HAND_WRITTEN, None).unwrap();
|
||||
formats.upgrade(&mut old, HAND_WRITTEN, None).unwrap();
|
||||
assert_eq!(trace_of(&old), ["a", "b"]);
|
||||
let mut nightly = serde_json::json!({});
|
||||
formats.upgrade("design", &mut nightly, next, Some(&["a-first".into()])).unwrap();
|
||||
formats.upgrade(&mut nightly, next, Some(&["a-first".into()])).unwrap();
|
||||
assert_eq!(trace_of(&nightly), ["b"]);
|
||||
let mut released = serde_json::json!({});
|
||||
formats.upgrade("design", &mut released, next, None).unwrap();
|
||||
formats.upgrade(&mut released, next, None).unwrap();
|
||||
assert_eq!(trace_of(&released), Vec::<&str>::new());
|
||||
}
|
||||
|
||||
@@ -1372,7 +1276,7 @@ mod tests {
|
||||
let folded = Formats { hand: &hand, folded: &[(29, &first), (30, &cards)], pending: &[] };
|
||||
for formats in [pending, folded] {
|
||||
let mut file = serde_json::json!({"trace": ["b"]});
|
||||
formats.upgrade("design", &mut file, 29, Some(&["physical-material-cards".into()])).unwrap();
|
||||
formats.upgrade(&mut file, 29, Some(&["physical-material-cards".into()])).unwrap();
|
||||
assert_eq!(trace_of(&file), ["b", "a"], "never replay existing card semantics");
|
||||
}
|
||||
}
|
||||
@@ -1384,7 +1288,7 @@ mod tests {
|
||||
let formats = Formats { hand: &hand, folded: &[(29, &first)], pending: &[] };
|
||||
let mut file = serde_json::json!({"trace": [], "sentinel": 7});
|
||||
let original = file.clone();
|
||||
let error = formats.upgrade("design", &mut file, 29, Some(&["physical-material-cards".into()])).unwrap_err();
|
||||
let error = formats.upgrade(&mut file, 29, Some(&["physical-material-cards".into()])).unwrap_err();
|
||||
assert!(error.to_string().contains("physical-material-cards"), "{error}");
|
||||
assert_eq!(file, original);
|
||||
}
|
||||
@@ -1399,7 +1303,7 @@ mod tests {
|
||||
for version in [28, 30] {
|
||||
let mut file = serde_json::json!({"trace": []});
|
||||
let original = file.clone();
|
||||
let error = formats.upgrade("design", &mut file, version, Some(&["physical-material-cards".into()])).unwrap_err();
|
||||
let error = formats.upgrade(&mut file, version, Some(&["physical-material-cards".into()])).unwrap_err();
|
||||
assert!(error.to_string().contains("physical-material-cards"), "{error}");
|
||||
assert_eq!(file, original);
|
||||
}
|
||||
@@ -1416,7 +1320,7 @@ mod tests {
|
||||
{
|
||||
let mut file = serde_json::json!({"trace": ["b"]});
|
||||
let original = file.clone();
|
||||
let error = formats.upgrade("design", &mut file, 29, Some(&names)).unwrap_err();
|
||||
let error = formats.upgrade(&mut file, 29, Some(&names)).unwrap_err();
|
||||
assert!(error.to_string().contains("unknown-card-codec"), "{error}");
|
||||
assert_eq!(file, original);
|
||||
}
|
||||
@@ -1433,7 +1337,7 @@ mod tests {
|
||||
vec!["material-engineering-cards".into(), "physical-material-cards".into()],
|
||||
] {
|
||||
let mut file = serde_json::json!({"trace": ["b"]});
|
||||
formats.upgrade("design", &mut file, 29, Some(&names)).unwrap();
|
||||
formats.upgrade(&mut file, 29, Some(&names)).unwrap();
|
||||
assert_eq!(trace_of(&file), ["b", "a"]);
|
||||
}
|
||||
}
|
||||
@@ -1446,7 +1350,7 @@ mod tests {
|
||||
let formats = Formats { hand: &hand, folded: &[(29, &first)], pending: &cards };
|
||||
let mut file = serde_json::json!({"trace": ["a"], "metadata": {"format_pending": ["physical-material-cards"]}});
|
||||
let metadata = file["metadata"].clone();
|
||||
formats.upgrade("design", &mut file, 29, None).unwrap();
|
||||
formats.upgrade(&mut file, 29, None).unwrap();
|
||||
assert_eq!(trace_of(&file), ["a", "b"]);
|
||||
assert_eq!(file["metadata"], metadata);
|
||||
}
|
||||
@@ -1461,7 +1365,7 @@ mod tests {
|
||||
let formats = Formats { hand: &[], folded: &[(2, &parts)], pending };
|
||||
let mut file = serde_json::json!({"trace": []});
|
||||
let original = file.clone();
|
||||
let err = formats.upgrade("design", &mut file, 2, Some(&["experimental-card".into()])).unwrap_err();
|
||||
let err = formats.upgrade(&mut file, 2, Some(&["experimental-card".into()])).unwrap_err();
|
||||
assert!(err.to_string().contains("experimental-card") && err.to_string().contains("update SolidCraft"), "{err}");
|
||||
assert_eq!(file, original, "refuse before applying even a known migration");
|
||||
}
|
||||
@@ -1477,190 +1381,14 @@ mod tests {
|
||||
let folded = Formats { hand: &[], folded: &[(2, &first), (3, &later)], pending: &[] };
|
||||
for formats in [pending, folded] {
|
||||
let mut experimental = serde_json::json!({"trace": ["b"]});
|
||||
formats.upgrade("design", &mut experimental, 2, Some(&["b-second".into()])).unwrap();
|
||||
formats.upgrade(&mut experimental, 2, Some(&["b-second".into()])).unwrap();
|
||||
assert_eq!(trace_of(&experimental), ["b", "a"]);
|
||||
let mut released = serde_json::json!({"trace": ["a"]});
|
||||
formats.upgrade("design", &mut released, 2, None).unwrap();
|
||||
formats.upgrade(&mut released, 2, None).unwrap();
|
||||
assert_eq!(trace_of(&released), ["a", "b"], "ordinary released files still get later migrations");
|
||||
}
|
||||
}
|
||||
|
||||
/// A design naming a pending change this build doesn't have is a newer build's, refused as
|
||||
/// such before anything else in it is checked (as a newer number is), so a newer build's
|
||||
/// fields never read as damage.
|
||||
#[test]
|
||||
fn a_newer_builds_pending_change_is_refused_before_the_design_is_checked() {
|
||||
let mut file: Value = serde_json::from_str(&Document::new("A").to_json()).unwrap();
|
||||
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();
|
||||
// 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
|
||||
/// (`v2/`, `v3/`, …), the next number is written while changes are pending, and the build lists
|
||||
/// every change file in the drawing's folder, in file-name order.
|
||||
#[test]
|
||||
fn drawing_formats_follow_on_from_one_and_every_change_file_is_listed() {
|
||||
for (i, (version, parts)) in DRAWING_FOLDED.iter().enumerate() {
|
||||
assert_eq!(*version, 2 + i as u32);
|
||||
assert!(!parts.is_empty(), "drawing format {version} has no changes");
|
||||
}
|
||||
assert_eq!(DRAWING.current(), 1 + DRAWING_FOLDED.len() as u32 + u32::from(!DRAWING_PENDING.is_empty()));
|
||||
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src/drawing/format");
|
||||
let names = |dir: &str| -> Vec<String> {
|
||||
let mut names: Vec<String> = std::fs::read_dir(root.join(dir))
|
||||
.map(|d| d.filter_map(|e| e.ok()?.file_name().to_str()?.strip_suffix(".rs").map(str::to_string)).collect())
|
||||
.unwrap_or_default();
|
||||
names.sort();
|
||||
names
|
||||
};
|
||||
assert_eq!(names("pending"), DRAWING.pending_names());
|
||||
for (version, parts) in DRAWING_FOLDED {
|
||||
assert_eq!(names(&format!("v{version}")), parts.iter().map(|p| p.name).collect::<Vec<_>>());
|
||||
}
|
||||
}
|
||||
|
||||
/// A design's format tag never reads as a drawing's, nor a drawing's as a design's, at any
|
||||
/// number: no family's tag starts another's, the legacy tag is a design's only, and each family
|
||||
/// refuses the other's files by name.
|
||||
#[test]
|
||||
fn a_design_never_reads_as_a_drawing_or_the_other_way_round() {
|
||||
let families = [&DESIGN, &DRAWING];
|
||||
for a in families {
|
||||
assert!(a.prefix.len() > 1 && a.prefix.ends_with('/'), "{}", a.prefix);
|
||||
for b in families.into_iter().filter(|b| b.prefix != a.prefix) {
|
||||
assert!(!a.prefix.starts_with(b.prefix), "{} starts {}", b.prefix, a.prefix);
|
||||
}
|
||||
}
|
||||
for n in [0, 1, FORMAT, FORMAT + 1, DRAWING.current(), DRAWING.current() + 1, u32::MAX] {
|
||||
assert_eq!(DESIGN.version_of(&DESIGN.format_string(n)), Some(n));
|
||||
assert_eq!(DRAWING.version_of(&DRAWING.format_string(n)), Some(n));
|
||||
assert_eq!(DRAWING.version_of(&DESIGN.format_string(n)), None, "{n}");
|
||||
assert_eq!(DESIGN.version_of(&DRAWING.format_string(n)), None, "{n}");
|
||||
assert_eq!(DRAWING.version_of(&format!("{LEGACY_FORMAT_PREFIX}{n}")), None, "{n}: the legacy tag is a design's");
|
||||
}
|
||||
for tag in [
|
||||
"",
|
||||
"solidcraft-drawing/",
|
||||
"solidcraft-drawing",
|
||||
"solidcraft-drawing/x",
|
||||
"solidcraft-drawing/-1",
|
||||
"solidcraft-drawing/4294967296",
|
||||
"Solidcraft-drawing/1",
|
||||
"solidcraft-drawing/1 ",
|
||||
"solidcraft-drawings/1",
|
||||
"solidcraft/drawing/1",
|
||||
] {
|
||||
assert_eq!(DRAWING.version_of(tag), None, "{tag:?}");
|
||||
}
|
||||
// The number reads as Rust reads a `u32`, a leading `+` included, as in a design's tag.
|
||||
assert_eq!(DRAWING.version_of("solidcraft-drawing/+1"), Some(1));
|
||||
let error = DRAWING.take_version(&mut serde_json::json!({"format": format_string(FORMAT)})).unwrap_err().to_string();
|
||||
assert_eq!(error, format!("not a SolidCraft drawing (format `solidcraft/{FORMAT}`)"));
|
||||
let error = read(&serde_json::json!({"format": DRAWING.format_string(1)}).to_string()).unwrap_err().to_string();
|
||||
assert_eq!(error, "not a SolidCraft design (format `solidcraft-drawing/1`)");
|
||||
}
|
||||
|
||||
/// Every refusal names its family: a design's read exactly as they did before drawings had one
|
||||
/// (the troubleshooting guide quotes them), a drawing's say "drawing". A file that names no
|
||||
/// format is a design from before format strings, never a drawing.
|
||||
#[test]
|
||||
fn refusals_name_the_family_and_a_designs_read_as_before() {
|
||||
use serde_json::json;
|
||||
fn refusal(family: &Family<'_>, mut file: Value) -> String {
|
||||
family.take_version(&mut file).map_or_else(|e| e.to_string(), |opened| format!("read as {opened:?}"))
|
||||
}
|
||||
fn tagged(family: &Family<'_>, n: u32) -> Value {
|
||||
serde_json::json!({"format": family.format_string(n)})
|
||||
}
|
||||
fn newer(noun: &str, n: u32, current: u32) -> String {
|
||||
format!(
|
||||
"this {noun} was saved by a newer SolidCraft (file format {n}; this version reads formats up to {current}): update SolidCraft to open it"
|
||||
)
|
||||
}
|
||||
assert_eq!(refusal(&DESIGN, tagged(&DESIGN, FORMAT + 1)), newer("design", FORMAT + 1, FORMAT));
|
||||
assert_eq!(read(&tagged(&DESIGN, FORMAT + 1).to_string()).unwrap_err().to_string(), newer("design", FORMAT + 1, FORMAT));
|
||||
let current = DRAWING.current();
|
||||
assert!(matches!(DRAWING.take_version(&mut tagged(&DRAWING, current)), Ok((v, None)) if v == current));
|
||||
for n in [current + 1, u32::MAX] {
|
||||
assert_eq!(refusal(&DRAWING, tagged(&DRAWING, n)), newer("drawing", n, current));
|
||||
}
|
||||
for (family, noun) in [(&DESIGN, "design"), (&DRAWING, "drawing")] {
|
||||
assert_eq!(refusal(family, tagged(family, 0)), format!("not a SolidCraft {noun} (format 0)"));
|
||||
assert_eq!(refusal(family, json!([])), format!("not a SolidCraft {noun} (expected a JSON object)"));
|
||||
assert_eq!(refusal(family, json!({"format": 1})), format!("not a SolidCraft {noun} (format is not a string)"));
|
||||
assert_eq!(refusal(family, json!({"format": ""})), format!("not a SolidCraft {noun} (format ``)"));
|
||||
// `format_pending`'s caps, on both sides of each.
|
||||
let mut file = tagged(family, 1);
|
||||
file["format_pending"] = vec!["x".repeat(128); 1000].into();
|
||||
assert!(family.take_version(&mut file).is_ok(), "{noun}: 1,000 names of 128 bytes");
|
||||
for damaged in [vec!["x".repeat(128); 1001], vec!["x".repeat(129)]] {
|
||||
let mut file = tagged(family, 1);
|
||||
file["format_pending"] = damaged.into();
|
||||
assert_eq!(refusal(family, file), format!("damaged {noun}: `format_pending` is not a short list of names"));
|
||||
}
|
||||
}
|
||||
// A change this build doesn't know, in a design of the pending number.
|
||||
let pending = [traced("a-first")];
|
||||
let design = Family { formats: Formats { hand: &JSON_STEPS, folded: FOLDED, pending: &pending }, ..DESIGN };
|
||||
let next = design.current();
|
||||
let file = json!({"format": format_string(next), "format_pending": ["c-later"]});
|
||||
assert_eq!(
|
||||
refusal(&design, file),
|
||||
format!(
|
||||
"this design was saved by a newer SolidCraft (file format {next} with `c-later`, which this version doesn't know): update SolidCraft to open it"
|
||||
)
|
||||
);
|
||||
assert!(matches!(DESIGN.take_version(&mut json!({})), Ok((1, None))));
|
||||
assert_eq!(refusal(&DRAWING, json!({})), "not a SolidCraft drawing (no format)");
|
||||
}
|
||||
|
||||
/// One drawing change pending, as a drawing's first change will be: the build writes format 2
|
||||
/// and names the change in `format_pending`. Its step runs for a drawing without the change and
|
||||
/// never for one with it; a drawing naming a change this build lacks, or of format 3, is a newer
|
||||
/// build's.
|
||||
#[test]
|
||||
fn a_pending_drawing_change_makes_format_two() {
|
||||
use serde_json::json;
|
||||
let pending = [traced("a-first")];
|
||||
let drawing = Family { formats: Formats { hand: &[], folded: &[], pending: &pending }, ..DRAWING };
|
||||
assert_eq!((drawing.current(), drawing.format_string(drawing.current())), (2, "solidcraft-drawing/2".to_string()));
|
||||
assert_eq!(drawing.pending_names(), ["a-first"]);
|
||||
let open = |mut file: Value| -> Result<Vec<String>> {
|
||||
let (version, applied) = drawing.take_version(&mut file)?;
|
||||
assert!(file.get("format_pending").is_none(), "taken out of the JSON before the record is read");
|
||||
drawing.upgrade(&mut file, version, applied.as_deref())?;
|
||||
Ok(trace_of(&file).into_iter().map(str::to_string).collect())
|
||||
};
|
||||
assert_eq!(open(json!({"format": "solidcraft-drawing/1"})).unwrap(), ["a"]);
|
||||
assert_eq!(open(json!({"format": "solidcraft-drawing/2", "format_pending": []})).unwrap(), ["a"]);
|
||||
assert_eq!(open(json!({"format": "solidcraft-drawing/2", "format_pending": ["a-first"]})).unwrap(), Vec::<String>::new());
|
||||
// A list beside format 1 means nothing: the step still runs.
|
||||
assert_eq!(open(json!({"format": "solidcraft-drawing/1", "format_pending": ["c-later"]})).unwrap(), ["a"]);
|
||||
let error = open(json!({"format": "solidcraft-drawing/2", "format_pending": ["a-first", "c-later"]})).unwrap_err().to_string();
|
||||
assert_eq!(
|
||||
error,
|
||||
"this drawing was saved by a newer SolidCraft (file format 2 with `c-later`, which this version doesn't know): update SolidCraft to open it"
|
||||
);
|
||||
let error = open(json!({"format": "solidcraft-drawing/3"})).unwrap_err().to_string();
|
||||
assert_eq!(
|
||||
error,
|
||||
"this drawing was saved by a newer SolidCraft (file format 3; this version reads formats up to 2): update SolidCraft to open it"
|
||||
);
|
||||
// An upgrade called without `take_version` refuses in the drawing's words too, format 0 included.
|
||||
let error = drawing.upgrade(&mut json!({}), 3, None).unwrap_err().to_string();
|
||||
assert!(error.starts_with("this drawing was saved by a newer SolidCraft (file format 3;"), "{error}");
|
||||
assert_eq!(drawing.upgrade(&mut json!({}), 0, None).unwrap_err().to_string(), "not a SolidCraft drawing (format 0)");
|
||||
}
|
||||
|
||||
/// `format_pending` is read and checked: a damaged list is refused, a list of changes this
|
||||
/// build doesn't have is a newer build's, and saving writes this build's own list.
|
||||
#[test]
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
//! 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).
|
||||
@@ -1,195 +0,0 @@
|
||||
//! Object ids (#1017, #3890): random, so objects added side by side, on two branches of a
|
||||
//! repository or in two copies of a file, never take the same id, and no counter changes with
|
||||
//! every addition. Drawings use them from their first format; designs keep their numbered ids
|
||||
//! until they move over too (#626 Phase 3).
|
||||
//!
|
||||
//! - In memory an id is a nonzero `u64`. 0 is never one (it stands for a design's root
|
||||
//! component).
|
||||
//! - An [`IdSource`] gives them: splitmix64 from a seed, each draw checked against the ids in
|
||||
//! use and drawn again when it is one. [`IdSource::seeded`] gives the same ids for the same
|
||||
//! seed, for tests and replays, and still a new one in a file that seed made;
|
||||
//! [`IdSource::from_entropy`] is what a session uses.
|
||||
//! - In a file an id is 13 characters of Crockford's base32, lowercase: digits and letters without
|
||||
//! i, l, o and u, the first `0` to `f` (`0000000000001`, `fzzzzzzzzzzzz`). [`encode`] writes it
|
||||
//! and [`decode`] reads only that spelling, so an id has one form in a file's text. A field
|
||||
//! holding an id says `#[serde(with = "crate::ids")]`.
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::hash_map::RandomState;
|
||||
use std::hash::BuildHasher;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
|
||||
use crate::{DocError, Result};
|
||||
|
||||
/// The characters of an id in a file: 64 bits in 5-bit digits, the first holding the top four.
|
||||
pub const ID_CHARS: usize = 13;
|
||||
/// Crockford's base32 digits, lowercase.
|
||||
const DIGITS: &[u8; 32] = b"0123456789abcdefghjkmnpqrstvwxyz";
|
||||
/// splitmix64's step: odd, so the state passes every `u64` before it comes round again.
|
||||
const GAMMA: u64 = 0x9e37_79b9_7f4a_7c15;
|
||||
|
||||
/// Gives ids from a seed.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct IdSource {
|
||||
state: u64,
|
||||
}
|
||||
|
||||
impl IdSource {
|
||||
/// The same ids for the same seed, for tests and replays.
|
||||
pub fn seeded(seed: u64) -> IdSource {
|
||||
IdSource { state: seed }
|
||||
}
|
||||
|
||||
/// Ids another run gives only by chance: seeded from the standard library's hash keys (random
|
||||
/// where the platform gives it randomness), the time, and a count of sources made, so two
|
||||
/// sources made together differ.
|
||||
pub fn from_entropy() -> IdSource {
|
||||
static MADE: AtomicU64 = AtomicU64::new(0);
|
||||
let nanos = web_time::SystemTime::now().duration_since(web_time::UNIX_EPOCH).map_or(0, |d| d.as_nanos());
|
||||
IdSource::seeded(RandomState::new().hash_one((nanos, MADE.fetch_add(1, Ordering::Relaxed))))
|
||||
}
|
||||
|
||||
/// The next id that isn't 0 and isn't `in_use`. A run never draws a number twice, so at most
|
||||
/// one draw per id in use, and one 0, come before a free one.
|
||||
pub fn next(&mut self, in_use: &BTreeSet<u64>) -> Result<u64> {
|
||||
for _ in 0..in_use.len().saturating_add(2) {
|
||||
let id = self.draw();
|
||||
if id != 0 && !in_use.contains(&id) {
|
||||
return Ok(id);
|
||||
}
|
||||
}
|
||||
Err(DocError::Invalid("no free id".into()))
|
||||
}
|
||||
|
||||
/// splitmix64: its mixing is one-to-one, so a run never gives one number twice.
|
||||
fn draw(&mut self) -> u64 {
|
||||
self.state = self.state.wrapping_add(GAMMA);
|
||||
let mut z = self.state;
|
||||
z = (z ^ (z >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9);
|
||||
z = (z ^ (z >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb);
|
||||
z ^ (z >> 31)
|
||||
}
|
||||
}
|
||||
|
||||
/// `id` as a file writes it: [`ID_CHARS`] base32 digits, the most significant first.
|
||||
pub fn encode(id: u64) -> String {
|
||||
(0..ID_CHARS).rev().map(|k| DIGITS.get(((id >> (5 * k)) & 31) as usize).map_or('0', |&d| char::from(d))).collect()
|
||||
}
|
||||
|
||||
/// The id `text` writes, if it is one in [`encode`]'s spelling: anything else, 0 included, is not.
|
||||
pub fn decode(text: &str) -> Option<u64> {
|
||||
if text.len() != ID_CHARS {
|
||||
return None;
|
||||
}
|
||||
// A first digit above `f` overflows the 64 bits.
|
||||
let id = text.bytes().try_fold(0u64, |id, b| id.checked_mul(32)?.checked_add(DIGITS.iter().position(|&d| d == b)? as u64))?;
|
||||
(id != 0).then_some(id)
|
||||
}
|
||||
|
||||
/// serde's `with` for an id field: written as [`encode`] writes it, read as [`decode`] reads it.
|
||||
pub fn serialize<S: Serializer>(id: &u64, s: S) -> std::result::Result<S::Ok, S::Error> {
|
||||
s.serialize_str(&encode(*id))
|
||||
}
|
||||
|
||||
/// See [`serialize`].
|
||||
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> std::result::Result<u64, D::Error> {
|
||||
let text = String::deserialize(d)?;
|
||||
decode(&text).ok_or_else(|| serde::de::Error::custom(format!("an id is {ID_CHARS} characters of lowercase Crockford base32, not all 0")))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn none() -> BTreeSet<u64> {
|
||||
BTreeSet::new()
|
||||
}
|
||||
|
||||
/// Every boundary of the spelling: 1, the last value of one digit and the first of two, the
|
||||
/// top digit alone and the largest id, and splitmix64's first output for seed 0.
|
||||
#[test]
|
||||
fn an_id_is_written_as_13_digits_and_read_back() {
|
||||
let pinned = [
|
||||
(1, "0000000000001"),
|
||||
(31, "000000000000z"),
|
||||
(32, "0000000000010"),
|
||||
(1 << 60, "1000000000000"),
|
||||
(u64::MAX, "fzzzzzzzzzzzz"),
|
||||
(0xe220_a839_7b1d_cdaf, "e485875xhvkdf"),
|
||||
];
|
||||
for (id, text) in pinned {
|
||||
assert_eq!((encode(id), decode(text)), (text.to_string(), Some(id)), "{id:#x}");
|
||||
}
|
||||
let mut source = IdSource::seeded(1);
|
||||
for _ in 0..10_000 {
|
||||
let id = source.next(&none()).unwrap();
|
||||
assert_eq!(decode(&encode(id)), Some(id), "{id:#x}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Only [`encode`]'s spelling is an id: the wrong length, 0, a first digit past `f`, capitals
|
||||
/// (case-sensitive, so an id has one spelling to search for), the letters Crockford leaves
|
||||
/// out, and anything not ASCII.
|
||||
#[test]
|
||||
fn only_the_written_spelling_is_an_id() {
|
||||
let not_ids = [
|
||||
"",
|
||||
"000000000001",
|
||||
"00000000000001",
|
||||
"0000000000000",
|
||||
"g000000000000",
|
||||
"zzzzzzzzzzzzz",
|
||||
"Fzzzzzzzzzzzz",
|
||||
"000000000000A",
|
||||
"000000000000i",
|
||||
"000000000000l",
|
||||
"000000000000o",
|
||||
"000000000000u",
|
||||
"00000000000é",
|
||||
"000000000000-",
|
||||
" 000000000001",
|
||||
"000000000001\n",
|
||||
];
|
||||
for text in not_ids {
|
||||
assert_eq!(decode(text), None, "{text:?}");
|
||||
}
|
||||
}
|
||||
|
||||
/// A seed gives the same ids every time (splitmix64's published first outputs for seed 0), and
|
||||
/// a run never gives one twice.
|
||||
#[test]
|
||||
fn a_seeded_source_repeats_its_ids_and_never_gives_one_twice() {
|
||||
let mut zero = IdSource::seeded(0);
|
||||
assert_eq!([zero.draw(), zero.draw(), zero.draw()], [0xe220_a839_7b1d_cdaf, 0x6e78_9e6a_a1b9_65f4, 0x06c4_5d18_8009_454f]);
|
||||
let run = |seed| {
|
||||
let mut source = IdSource::seeded(seed);
|
||||
(0..10_000).map(|_| source.next(&none()).unwrap()).collect::<Vec<_>>()
|
||||
};
|
||||
assert_eq!(run(7), run(7));
|
||||
assert_ne!(run(7), run(8));
|
||||
assert_eq!(run(7).into_iter().collect::<BTreeSet<_>>().len(), 10_000);
|
||||
}
|
||||
|
||||
/// A draw that is in use, or 0, is drawn again: a file the same seed made, reopened with that
|
||||
/// seed, collides on every one of its ids in turn and still gets a new one.
|
||||
#[test]
|
||||
fn a_taken_id_or_0_is_drawn_again() {
|
||||
let mut seven = IdSource::seeded(7);
|
||||
let made: Vec<u64> = (0..101).map(|_| seven.draw()).collect();
|
||||
let in_use: BTreeSet<u64> = made.iter().take(100).copied().collect();
|
||||
assert_eq!(IdSource::seeded(7).next(&in_use).unwrap(), made[100]);
|
||||
// The state just before 0's: its first draw is 0, and the next is seed 0's first.
|
||||
let before_zero = 0u64.wrapping_sub(GAMMA);
|
||||
assert_eq!(IdSource::seeded(before_zero).draw(), 0);
|
||||
assert_eq!(IdSource::seeded(before_zero).next(&none()).unwrap(), IdSource::seeded(0).draw());
|
||||
}
|
||||
|
||||
/// Two sources made together still give different ids.
|
||||
#[test]
|
||||
fn sources_from_entropy_differ() {
|
||||
let (mut a, mut b) = (IdSource::from_entropy(), IdSource::from_entropy());
|
||||
assert_ne!(a.next(&none()).unwrap(), b.next(&none()).unwrap());
|
||||
}
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
//! 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)
|
||||
}
|
||||
}
|
||||
+6
-11
@@ -23,7 +23,6 @@ pub mod construct;
|
||||
pub mod display_quality;
|
||||
pub mod display_units;
|
||||
mod document;
|
||||
pub mod drawing;
|
||||
mod eval;
|
||||
pub mod expr;
|
||||
pub mod face_anchor;
|
||||
@@ -35,10 +34,7 @@ mod format_offsets;
|
||||
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;
|
||||
@@ -52,7 +48,6 @@ 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;
|
||||
@@ -74,16 +69,16 @@ pub mod sheet;
|
||||
pub mod text_params;
|
||||
|
||||
pub use assembly::{
|
||||
BomType, IDENTITY, MAX_PLACEMENT_DEPTH, MAX_PLACEMENTS, Mat, Occurrence, WorldNames, apply_point, apply_vector, is_identity, map_point_expr,
|
||||
mat_inverse, mat_mul, rigid,
|
||||
IDENTITY, MAX_PLACEMENT_DEPTH, MAX_PLACEMENTS, Mat, Occurrence, WorldNames, apply_point, apply_vector, is_identity, map_point_expr, mat_inverse,
|
||||
mat_mul, rigid,
|
||||
};
|
||||
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, 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,
|
||||
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,
|
||||
};
|
||||
pub use eval::{EvalCache, KeptResults};
|
||||
pub use expr::Expr;
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
//! each may be set (the design, a component or both), how its value is checked and its label.
|
||||
//! It keeps every other key exactly as it was through open, edit and save. Other tools keep
|
||||
//! their own data under one reverse-DNS key each (`com.example.tool`), holding an object.
|
||||
//! A component's part properties have one more key: its bill of materials type (`bom_type`).
|
||||
//!
|
||||
//! Reading never validates: a well-known key holding something other than text is kept as it
|
||||
//! is and reads as absent, so a file another tool wrote always opens. Writing through
|
||||
@@ -21,7 +20,7 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
use crate::{BomType, DocError, Result};
|
||||
use crate::{DocError, Result};
|
||||
|
||||
pub const PROJECT: &str = "project";
|
||||
pub const PART_NUMBER: &str = "part_number";
|
||||
@@ -93,11 +92,6 @@ pub static KEYS: &[Key] = &[
|
||||
/// own, and the Bill of Materials window edits them.
|
||||
pub static BOM_KEYS: &[&str] = &[PART_NUMBER, DESCRIPTION, REVISION];
|
||||
|
||||
/// A component's [`BomType`] in the bill of materials (one of [`BomType::as_str`]'s words). It is
|
||||
/// a part property of a component only, not a row of [`KEYS`], so the Design Properties dialog
|
||||
/// doesn't list it and a design refuses it.
|
||||
pub const BOM_TYPE: &str = "bom_type";
|
||||
|
||||
/// Longest key, in bytes.
|
||||
pub const MAX_KEY_BYTES: usize = 128;
|
||||
/// Longest well-known text value, in bytes.
|
||||
@@ -120,10 +114,9 @@ pub fn keys(owner: Owner) -> impl Iterator<Item = &'static Key> {
|
||||
KEYS.iter().filter(move |k| k.scope.covers(owner))
|
||||
}
|
||||
|
||||
/// The names of [`keys`]`(owner)`, and a component's [`BOM_TYPE`], as refusals list them.
|
||||
/// The names of [`keys`]`(owner)`, as refusals list them.
|
||||
pub fn key_list(owner: Owner) -> String {
|
||||
let bom_type = (owner == Owner::Component).then_some(BOM_TYPE);
|
||||
keys(owner).map(|k| k.name).chain(bom_type).collect::<Vec<_>>().join(", ")
|
||||
keys(owner).map(|k| k.name).collect::<Vec<_>>().join(", ")
|
||||
}
|
||||
|
||||
/// One of SolidCraft's keys, by name.
|
||||
@@ -133,7 +126,7 @@ pub fn key(name: &str) -> Option<&'static Key> {
|
||||
|
||||
/// A key SolidCraft reads as a design or part property.
|
||||
pub fn is_well_known(name: &str) -> bool {
|
||||
key(name).is_some() || name == BOM_TYPE
|
||||
key(name).is_some()
|
||||
}
|
||||
|
||||
/// A reverse-DNS key another tool keeps its data under: two or more dot-separated labels of
|
||||
@@ -169,19 +162,6 @@ fn text(key: &str, value: Value) -> Result<Option<Value>> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The bill of materials type's check: one of [`BomType`]'s words, which empty text (like
|
||||
/// `null`) removes, so the component reads as normal again.
|
||||
fn bom_type(value: Value) -> Result<Option<Value>> {
|
||||
let Some(word) = text(BOM_TYPE, value)? else { return Ok(None) };
|
||||
let s = word.as_str().unwrap_or_default();
|
||||
if BomType::parse(s).is_none() {
|
||||
let shown: String = s.chars().take(64).collect();
|
||||
let words: Vec<&str> = BomType::ALL.iter().map(|t| t.as_str()).collect();
|
||||
return Err(invalid(format!("`{BOM_TYPE}` is one of {}, not `{shown}`", words.join(", "))));
|
||||
}
|
||||
Ok(Some(word))
|
||||
}
|
||||
|
||||
fn depth(v: &Value) -> usize {
|
||||
// Iterative, so a hostile value can't overflow the stack here.
|
||||
let mut deepest = 0;
|
||||
@@ -243,11 +223,6 @@ impl Metadata {
|
||||
pub fn license(&self) -> Option<&str> {
|
||||
self.text(LICENSE)
|
||||
}
|
||||
/// The component's bill of materials type: normal when the key is missing, or holds a word
|
||||
/// this build doesn't know (a newer SolidCraft's type reads as normal and is kept as it is).
|
||||
pub fn bom_type(&self) -> BomType {
|
||||
self.text(BOM_TYPE).and_then(BomType::parse).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// The keys SolidCraft doesn't read (other tools' data, or keys a newer SolidCraft added),
|
||||
/// kept as they are.
|
||||
@@ -285,9 +260,6 @@ impl Metadata {
|
||||
|
||||
/// `value` as `key` may hold it in `owner`'s metadata (`None`: remove the key), or why it can't.
|
||||
fn check(owner: Owner, key: &str, value: Value) -> Result<Option<Value>> {
|
||||
if key == BOM_TYPE && owner == Owner::Component {
|
||||
return bom_type(value);
|
||||
}
|
||||
if let Some(k) = self::key(key).filter(|k| k.scope.covers(owner)) {
|
||||
return (k.check)(key, value);
|
||||
}
|
||||
@@ -361,30 +333,6 @@ mod tests {
|
||||
assert_eq!(m.0.keys().collect::<Vec<_>>(), ["description"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_bom_type_takes_one_of_its_words_and_reads_as_normal_otherwise() {
|
||||
let mut m = Metadata::default();
|
||||
assert_eq!(m.bom_type(), BomType::Normal, "no key: normal");
|
||||
for t in BomType::ALL {
|
||||
assert!(m.set(Component, BOM_TYPE, json!(t.as_str())).is_ok(), "{t:?}");
|
||||
assert_eq!(m.bom_type(), t);
|
||||
assert_eq!(BomType::parse(t.as_str()), Some(t));
|
||||
}
|
||||
for bad in [json!("Phantom"), json!("kit"), json!(" phantom"), json!(1), json!({"type": "phantom"}), json!("a\u{7}")] {
|
||||
assert!(m.set(Component, BOM_TYPE, bad.clone()).is_err(), "{bad}");
|
||||
}
|
||||
assert_eq!(m.bom_type(), BomType::Inseparable, "a refused value leaves the old one");
|
||||
assert!(m.set(Component, BOM_TYPE, json!("")).unwrap(), "empty text removes it");
|
||||
assert_eq!(m.bom_type(), BomType::Normal);
|
||||
assert!(m.others().next().is_none(), "SolidCraft reads it, so it isn't another tool's key");
|
||||
// A word a newer build wrote is kept as it is and reads as normal here.
|
||||
let newer: Metadata = serde_json::from_value(json!({"bom_type": "kit"})).unwrap();
|
||||
assert_eq!(newer.bom_type(), BomType::Normal);
|
||||
assert_eq!(newer.get(BOM_TYPE), Some(&json!("kit")));
|
||||
assert!(!keys(Design).any(|k| k.name == BOM_TYPE), "the Design Properties dialog doesn't list it");
|
||||
assert!(Metadata::default().set(Design, BOM_TYPE, json!("phantom")).is_err(), "a design has no bill of materials type");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_limits_refuse_growth_but_let_an_oversized_file_shrink() {
|
||||
let mut m = Metadata::default();
|
||||
@@ -422,7 +370,7 @@ mod tests {
|
||||
}
|
||||
// Every key SolidCraft took on the design and on a component before the table still is.
|
||||
assert_eq!(key_list(Design), "project, part_number, revision, author, organization, description, license");
|
||||
assert_eq!(key_list(Component), format!("{}, {BOM_TYPE}", key_list(Design)), "a component also takes its bill of materials type");
|
||||
assert_eq!(key_list(Component), key_list(Design));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -454,7 +402,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
refusal(Component, "colour"),
|
||||
"`colour` is not a part property: SolidCraft's are project, part_number, revision, author, organization, \
|
||||
description, license, bom_type; another tool's data goes under a reverse-DNS key such as `com.example.tool`"
|
||||
description, license; another tool's data goes under a reverse-DNS key such as `com.example.tool`"
|
||||
);
|
||||
for owner in [Design, Component] {
|
||||
let word = owner.names().0;
|
||||
|
||||
@@ -306,61 +306,9 @@ pub(crate) fn warm_names<'a>(bodies: impl Iterator<Item = &'a ModelBody>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Bodies whose names are worked out one from the other, oldest first, at most this many apart
|
||||
/// (see [`warm_chain`]).
|
||||
const WARM_STEP: usize = 16;
|
||||
|
||||
/// Up to this many earlier versions of a body are walked to name it from the oldest up.
|
||||
const MAX_CHAIN: usize = 100_000;
|
||||
|
||||
/// The body `b`'s names are worked out from first: the body it is a moved copy of, else the
|
||||
/// body of its name before the feature that made it (`None`: none, or past [`MAX_DEPTH`]).
|
||||
fn predecessor(b: &ModelBody) -> Option<&ModelBody> {
|
||||
if let Some(source) = &b.names.copy_of {
|
||||
return Some(source);
|
||||
}
|
||||
let o = b.names.origin.as_ref().filter(|o| o.depth < MAX_DEPTH)?;
|
||||
o.before.body(&b.name)
|
||||
}
|
||||
|
||||
/// Work out the names of the earlier versions of `b` that are not known yet, oldest first and
|
||||
/// every [`WARM_STEP`]th, so that naming `b` never recurses through more than that many of them.
|
||||
/// A body's names are found from the names of the body it was made from, which are found the
|
||||
/// same way: a body moved or changed by a few hundred features in a row whose names nothing
|
||||
/// asked for overflowed the window thread's stack the first time something did (a fillet after
|
||||
/// them evaluated again when an earlier feature is reordered or moved: #7587, #7926). `ready` says whether a
|
||||
/// body's names are known, `name` works them out. The names are the same either way.
|
||||
fn warm_chain(b: &ModelBody, ready: fn(&ModelBody) -> bool, name: fn(&ModelBody)) {
|
||||
let mut chain: Vec<&ModelBody> = Vec::new();
|
||||
let mut at = b;
|
||||
while chain.len() < MAX_CHAIN
|
||||
&& let Some(p) = predecessor(at)
|
||||
&& !ready(p)
|
||||
{
|
||||
chain.push(p);
|
||||
at = p;
|
||||
}
|
||||
if chain.len() <= WARM_STEP {
|
||||
return;
|
||||
}
|
||||
for p in chain.iter().rev().step_by(WARM_STEP) {
|
||||
name(p);
|
||||
}
|
||||
}
|
||||
|
||||
/// A body's names, with what finding them again needs: from the history of the feature that
|
||||
/// made it where it reports one, else from geometry (unverified).
|
||||
pub(crate) fn names(b: &ModelBody) -> Arc<Names> {
|
||||
if let Some(known) = b.names.names.get() {
|
||||
return known.clone();
|
||||
}
|
||||
warm_chain(
|
||||
b,
|
||||
|x| x.names.names.get().is_some(),
|
||||
|x| {
|
||||
let _ = names(x);
|
||||
},
|
||||
);
|
||||
// A moved copy has its source's names (the source's cell keeps them for both).
|
||||
if let Some(source) = &b.names.copy_of {
|
||||
return b.names.names.get_or_init(|| names(source)).clone();
|
||||
@@ -385,16 +333,6 @@ pub(crate) fn names(b: &ModelBody) -> Arc<Names> {
|
||||
/// as those builds gave them, pieces of a split face numbered along the longest axis of their
|
||||
/// spread (`#1`, `#2`, …).
|
||||
pub(crate) fn legacy_face_names(b: &ModelBody) -> Arc<Vec<String>> {
|
||||
if let Some(known) = b.names.legacy.get() {
|
||||
return known.clone();
|
||||
}
|
||||
warm_chain(
|
||||
b,
|
||||
|x| x.names.legacy.get().is_some(),
|
||||
|x| {
|
||||
let _ = legacy_face_names(x);
|
||||
},
|
||||
);
|
||||
if let Some(source) = &b.names.copy_of {
|
||||
return legacy_face_names(source);
|
||||
}
|
||||
|
||||
@@ -225,12 +225,6 @@ 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) {
|
||||
|
||||
@@ -136,7 +136,6 @@ 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
|
||||
@@ -171,7 +170,6 @@ 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
|
||||
|
||||
@@ -1,501 +0,0 @@
|
||||
//! 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);
|
||||
}
|
||||
}
|
||||
@@ -309,41 +309,6 @@ 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 {
|
||||
|
||||
@@ -9,13 +9,6 @@ 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>> {
|
||||
@@ -60,13 +53,12 @@ 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);
|
||||
let join = join_tolerance(mesh);
|
||||
if !tol.is_finite() || !join.is_finite() {
|
||||
if !tol.is_finite() {
|
||||
return Err(DocError::Invalid("face projection has invalid bounds".into()));
|
||||
}
|
||||
let key = |p: Vec3| -> Key {
|
||||
let p = p - origin;
|
||||
((p.x / join).round() as i64, (p.y / join).round() as i64, (p.z / join).round() as i64)
|
||||
((p.x / tol).round() as i64, (p.y / tol).round() as i64, (p.z / tol).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()));
|
||||
@@ -79,9 +71,6 @@ 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 {
|
||||
@@ -118,7 +107,7 @@ fn boundary_edges(mesh: &Mesh, mut edges: Vec<usize>, faces: &BTreeSet<usize>, p
|
||||
*slot = true;
|
||||
}
|
||||
boundary.push(edge);
|
||||
if current.dist(start) <= join {
|
||||
if current.dist(start) <= tol {
|
||||
break;
|
||||
}
|
||||
// Neighbouring quantization cells cover points on either side of a rounding
|
||||
@@ -132,7 +121,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) <= join) || p.last().is_some_and(|b| b.dist(current) <= join)
|
||||
p.first().is_some_and(|a| a.dist(current) <= tol) || p.last().is_some_and(|b| b.dist(current) <= tol)
|
||||
})
|
||||
}));
|
||||
}
|
||||
@@ -159,7 +148,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) <= join;
|
||||
let closed = current.dist(start) <= tol;
|
||||
if !closed || !twice_area.is_finite() {
|
||||
return Err(DocError::Invalid("face projection has an open or invalid boundary".into()));
|
||||
}
|
||||
@@ -390,63 +379,6 @@ 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();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user