mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-06 03:51:08 +00:00
Compare commits
130 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4ea1435f7c | |||
| 18c8e31397 | |||
| 180510cdf4 | |||
| 7da12cb981 | |||
| add3e2d2c1 | |||
| 5d8bd85b7d | |||
| f0645730d9 | |||
| ba2408d6f4 | |||
| 124aa4cccd | |||
| c47669d406 | |||
| d7cad1005a | |||
| 8564006ffb | |||
| 708d5a149d | |||
| 0c094142d3 | |||
| 1939adba6b | |||
| 207679e261 | |||
| 4cbbccb935 | |||
| 22f8da2689 | |||
| 51d8842e8a | |||
| 95fd121bb3 | |||
| 1444801d96 | |||
| 28dcbbc862 | |||
| 7e96aabd6d | |||
| 52893744a5 | |||
| 4699b9cc89 | |||
| 4eb0c6f5ec | |||
| a07b60cd81 | |||
| bd3053f57b | |||
| 49ab9179a7 | |||
| 8c4caf9372 | |||
| 202b775de4 | |||
| 2c35a027f5 | |||
| 146a43f38e | |||
| e04bc060f6 | |||
| 464da90238 | |||
| 0b32d3619d | |||
| eb10552c61 | |||
| e95f65fd54 | |||
| 1fdc56835b | |||
| 3fde538320 | |||
| 283ccd0b21 | |||
| 6b222aedf7 | |||
| bcdbb91f26 | |||
| 21a1269d5c | |||
| d580c4256c | |||
| 1b52186524 | |||
| 3e6eadc118 | |||
| 87358ef724 | |||
| 3d73822442 | |||
| 51f4074506 | |||
| 796b7c40d3 | |||
| 9cdee588f2 | |||
| 90d9c6488a | |||
| edb7882815 | |||
| db3558d820 | |||
| b2f5da6ca7 | |||
| f1159a5d51 | |||
| ffc84dbe2e | |||
| 942e1540db | |||
| 6500c42c9f | |||
| 655c1f3336 | |||
| 9f2e117934 | |||
| c6fe613d32 | |||
| d5e953871b | |||
| c7dcab172f | |||
| 8312bf0cb8 | |||
| 8f20ad4532 | |||
| abdf195132 | |||
| 7e002f182d | |||
| f0d305a783 | |||
| dc33c4a24d | |||
| 2405f75c38 | |||
| de07905f78 | |||
| 1cf7ae8e5c | |||
| e9efcf4c0b | |||
| 7546e84ef4 | |||
| 278ab40a0f | |||
| ed7206cc0d | |||
| 16f3b939b3 | |||
| ad230eb4a4 | |||
| c1760d4857 | |||
| 4d69d8c87a | |||
| fa2faf99e2 | |||
| 20467e39e2 | |||
| 020e3bebe3 | |||
| 6029a38ea0 | |||
| 9c93a6c340 | |||
| 9fdb7fdefc | |||
| 718d5a4383 | |||
| 1c10f82b34 | |||
| ca4f3ed1a0 | |||
| a1d66c238e | |||
| 70603de654 | |||
| db20c8be23 | |||
| 8f4fe740c7 | |||
| aae4282f1e | |||
| 18b698ffa8 | |||
| 051e6236c3 | |||
| 8691cefbbf | |||
| 6d2465b52d | |||
| c517ca0393 | |||
| ee1bdea97b | |||
| dc54d2f229 | |||
| b916f9bf33 | |||
| 733f0b2254 | |||
| ee02080a25 | |||
| 99d96482af | |||
| a5be592b7d | |||
| d92923a964 | |||
| 71598c0354 | |||
| 06a8a9b7d2 | |||
| 4d2691e636 | |||
| 6e17874cb9 | |||
| bf22dad8e8 | |||
| 731029cd87 | |||
| 6b5c5dc6e8 | |||
| e7ce6c282a | |||
| 67c58cbd28 | |||
| 7c89f7445f | |||
| 26adcfa997 | |||
| 1e41dddae2 | |||
| a9b219bc9f | |||
| 32dc8750df | |||
| ceeff58a7f | |||
| a55286953a | |||
| 9a0846ef68 | |||
| 06749ac464 | |||
| a6e7126bf1 | |||
| 4a906699ee | |||
| a2a7102859 |
@@ -3,13 +3,13 @@
|
||||
SolidCraft is a clean-room, pure-Rust parametric 3D CAD app (sketch solver, feature timeline, B-rep kernel, an egui UI that agents drive). This file is a router: the hard rules, then the skill to read for your task. Humans: CONTRIBUTING.md.
|
||||
|
||||
## Start here
|
||||
Work: ROADMAP.md, "Where help is wanted most" in CONTRIBUTING.md, issues labelled `help wanted`, `good first issue`, `needs-triage`; architecture in `docs/architecture.md`. Act when the code, docs and rules settle a question; ask only when it changes what users see. Local `plan/` notes are optional.
|
||||
Work: ROADMAP.md, "Where help is wanted most" in CONTRIBUTING.md, issues labelled `help wanted`, `good first issue`, `needs-triage`; architecture in `docs/architecture.md`. Local `plan/` notes are optional.
|
||||
|
||||
## Hard rules (what enforces each)
|
||||
- Independent: never copy another product's code, icons, text or templates; never drive or take data from commercial CAD. `skills/solidcraft-ground-rules/SKILL.md`.
|
||||
- No GPL/LGPL/AGPL code, not even reading it (OpenCASCADE, FreeCAD, SolveSpace, CadQuery, LibreCAD, QCAD): `cargo xtask licences --check`.
|
||||
- Every asset is original or openly licensed with a row in ATTRIBUTION.md; read `assets/AGENTS.md` before icon work: `cargo xtask assets`, `cargo xtask icons`.
|
||||
- Never crash: no unwrap/expect/panic/unsafe in non-test code (unsafe allowlist in crash-safety), errors are `Result`, input is hostile: clippy `-D warnings`, `hostile_params_never_panic`.
|
||||
- Never crash: no unwrap/expect/panic/unsafe in non-test code, errors are `Result`, input is hostile: clippy `-D warnings`, `hostile_params_never_panic`.
|
||||
- Model decisions (what an operation does, which bodies it touches, which face or edge a point or name means, depths, nearness) use exact geometry; a mesh is for display, export, previews and mesh bodies (`xtask/mesh-decisions.txt` only shrinks): `cargo xtask mesh-decisions`.
|
||||
- New kernel cases go to solidcore: `cargo xtask lists`.
|
||||
- Everything is a command and programmatic calls never open dialogs: `skills/solidcraft-commands/SKILL.md`.
|
||||
@@ -21,12 +21,14 @@ Work: ROADMAP.md, "Where help is wanted most" in CONTRIBUTING.md, issues labelle
|
||||
- A design-format change is ONE new file in `crates/doc/src/format/pending/`: `scripts/test-format-pending.sh`.
|
||||
- A command has input and output schemas (will become required; `schema/unconverted.txt` only shrinks): `skills/solidcraft-commands/SKILL.md`.
|
||||
- New UI strings go in `assets/locales/pending/<slug>.json`, ATTRIBUTION rows in `attribution/pending/<slug>.md`: `cargo xtask lists`.
|
||||
- A changed default keeps the old behaviour behind a preference and fills the PR's `Changes a default:` line, and no second PR for an issue a lower-numbered one closes: `pr-checks.yml`.
|
||||
- A changed default keeps the old behaviour behind a preference and fills the PR's `Changes a default:` line: `pr-checks.yml`.
|
||||
- A regression test goes in the area's existing test file, never a per-bug file (#3226).
|
||||
- Look at open PRs first, claim with a draft PR (lowest number wins), one topic per PR, never force-push `main`, no pushes just to catch up with `main`: `skills/solidcraft-contributing/SKILL.md`.
|
||||
- Look at open PRs first, claim with a draft PR (lowest number wins), one topic per PR: `skills/solidcraft-contributing/SKILL.md`.
|
||||
- A PR's title and first sentence make sense to someone who never saw your chat or the issue: `skills/solidcraft-contributing/SKILL.md`.
|
||||
- Code, comments, commit messages and docs are in English, always: `skills/solidcraft-contributing/SKILL.md` ("Working as an agent").
|
||||
- Independent work runs in parallel, dependent work stays sequential: `skills/solidcraft-contributing/SKILL.md` ("Working as an agent").
|
||||
- Show observable changes with real screenshots or a recording, captured yourself.
|
||||
- Run `cargo xtask ci` before every commit, without switching branches while it runs. `cargo xtask skills` keeps this file and `skills/` honest.
|
||||
- Run `cargo xtask ci` before every commit. `cargo xtask skills` keeps this file and `skills/` honest.
|
||||
|
||||
## Working on X: read the skill first
|
||||
| Working on | Read |
|
||||
|
||||
+45
-4
@@ -82,7 +82,6 @@ Editable drawing code, native projects and shipped exports are collected under `
|
||||
| assets/fonts/ibm-plex-mono/IBMPlexMono-Regular.ttf | IBM Corp. (IBM Plex Mono) | https://github.com/IBM/plex (via google/fonts), unmodified; licence text in assets/fonts/ibm-plex-mono/OFL.txt; the interface monospace font | SIL OFL 1.1 |
|
||||
| assets/fonts/ibm-plex-sans/IBMPlexSans-Variable.ttf | IBM Corp. (IBM Plex Sans, variable wdth and wght) | https://github.com/IBM/plex (via google/fonts), unmodified; licence text in assets/fonts/ibm-plex-sans/OFL.txt; the default interface font | SIL OFL 1.1 |
|
||||
| docs/book/src/generated/tool-preview.png | SolidCraft contributors | rendered by `cargo xtask book` (`solidcraft-cli snapshot docs/book/scripts/tool-preview.json`; gitignored) | MIT OR Apache-2.0 |
|
||||
| assets/fonts/liberation-sans/LiberationSans-Regular.ttf | Red Hat, Inc. (Liberation Fonts 2.x; digitized data Google) | https://github.com/liberationfonts (Debian fonts-liberation), unmodified; licence text in assets/fonts/liberation-sans/OFL.txt; used for sketch Text outlines | SIL OFL 1.1 |
|
||||
| assets/fonts/biz-udp-gothic/BIZUDPGothic-Regular.ttf | The BIZ UDGothic Project Authors | https://github.com/googlefonts/morisawa-biz-ud-gothic (Google Fonts), unmodified; licence text in assets/fonts/biz-udp-gothic/OFL.txt; used for web UI Japanese font | SIL OFL 1.1 |
|
||||
| assets/fonts/craft-ui-sc/CraftUI-SC.otf | Adobe / Google Inc. (Noto Sans SC); SolidCraft subset | https://github.com/notofonts/noto-cjk Sans/SubsetOTF/SC Regular, glyph-subset for assets/locales/zh.json and renamed Craft UI SC (OFL reserved-name); licence text in assets/fonts/craft-ui-sc/OFL.txt; web UI Simplified Chinese | SIL OFL 1.1 |
|
||||
| assets/fonts/craft-ui-tc/CraftUI-TC.otf | Adobe / Google Inc. (Noto Sans TC); SolidCraft subset | https://github.com/notofonts/noto-cjk Sans/SubsetOTF/TC Regular, glyph-subset for assets/locales/zh-TW.json and renamed Craft UI TC (OFL reserved-name); licence text in assets/fonts/craft-ui-tc/OFL.txt; web UI Traditional Chinese | SIL OFL 1.1 |
|
||||
@@ -147,7 +146,6 @@ Editable drawing code, native projects and shipped exports are collected under `
|
||||
| assets/fonts/craft-ui-tc/OFL.txt | Adobe / Google Inc. (Noto CJK); SolidCraft header | Accompanying OFL for CraftUI-TC.otf | SIL OFL 1.1 |
|
||||
| assets/fonts/ibm-plex-mono/OFL.txt | IBM Corp. | Unmodified accompanying font licence | SIL OFL 1.1 |
|
||||
| assets/fonts/ibm-plex-sans/OFL.txt | IBM Corp. | Unmodified accompanying font licence | SIL OFL 1.1 |
|
||||
| assets/fonts/liberation-sans/OFL.txt | Red Hat, Inc.; Liberation Fonts contributors | Unmodified accompanying font licence | SIL OFL 1.1 |
|
||||
| assets/icons/app/linux/exports/hicolor/128x128/apps/io.tangencylabs.solidcraft.png | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
|
||||
| assets/icons/app/linux/exports/hicolor/16x16/apps/io.tangencylabs.solidcraft.png | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
|
||||
| assets/icons/app/linux/exports/hicolor/24x24/apps/io.tangencylabs.solidcraft.png | SolidCraft contributors | Generated from assets/icons/app/shared/source/app_icon.rs with the original CLI/ICO encoders | MIT OR Apache-2.0 |
|
||||
@@ -799,6 +797,50 @@ Original SolidCraft drawings, Open for owner review; no runtime assignments.
|
||||
| docs/book/src/media/ui-tour/navigate.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.29.0 English desktop UI (`--sample`, default dark theme, software renderer) orbiting, panning and zooming, with the original drawn pointer of examples/demo/overlay.py and labels set in DejaVu Sans Bold, captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| assets/locales/pending/sketch-tool-hints.json | carlymx | Original sketch tool hints and engine error translations | MIT OR Apache-2.0 |
|
||||
| assets/locales/pending/primitive-pick-field-tips.json | SolidCraft contributors | Original primitive pick-field tooltip translations | MIT OR Apache-2.0 |
|
||||
| assets/locales/pending/ribbon-panel-hover.json | SolidCraft contributors | Original ribbon panel menu preference labels and translations | MIT OR Apache-2.0 |
|
||||
| docs/screenshots/box-insert-designer.gif | Shawn Marx | original recording made with BoxInsertDesigner's own Motion export (its three.js viewer) of its example game `examples/ccg-example` (https://github.com/ShawnMarx/BoxInsertDesigner): a card game box insert whose trays were built by SolidCraft through `solidcraft-cli`; the published card, board and box art in it is pixelated beyond recognition, and no logos or game names are shown (2.5× playback, 800 px wide) | MIT OR Apache-2.0 |
|
||||
| docs/screenshots/box-insert-designer.mp4 | Shawn Marx | original recording made with BoxInsertDesigner's own Motion export (its three.js viewer) of its example game `examples/ccg-example` (https://github.com/ShawnMarx/BoxInsertDesigner): a card game box insert whose trays were built by SolidCraft through `solidcraft-cli`; the published card, board and box art in it is pixelated beyond recognition, and no logos or game names are shown (1280×720) | MIT OR Apache-2.0 |
|
||||
| assets/locales/pending/agent-http-consent.json | SolidCraft contributors | Original endpoint-specific HTTP consent messages and translations | MIT OR Apache-2.0 |
|
||||
| assets/locales/pending/ground-shadow-placement.json | SolidCraft contributors | Original ground-shadow placement preference messages and translations | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/assemblies/components.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.29.0 English desktop UI (default dark theme, software renderer) making components from two original hinge-leaf bodies, with the original drawn pointer of examples/demo/overlay.py and labels set in DejaVu Sans Bold, captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/assemblies/joint.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.29.0 English desktop UI (default dark theme, software renderer) creating a revolute joint between two hinge leaves, with the original drawn pointer of examples/demo/overlay.py and labels set in DejaVu Sans Bold, captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/assemblies/drag.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.29.0 English desktop UI (default dark theme, software renderer) dragging a jointed hinge leaf, with the original drawn pointer of examples/demo/overlay.py and labels set in DejaVu Sans Bold, captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/assemblies/drive.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.29.0 English desktop UI (default dark theme, software renderer) driving a revolute joint between two hinge leaves, with the original drawn pointer of examples/demo/overlay.py and labels set in DejaVu Sans Bold, captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/features/extrude.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.29.0 English desktop UI (default dark theme, software renderer) extruding a sketch's profile, with the original drawn pointer of examples/demo/overlay.py and labels set in DejaVu Sans Bold, captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/features/shell.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.29.0 English desktop UI (default dark theme, software renderer) shelling a block, with the original drawn pointer of examples/demo/overlay.py and labels set in DejaVu Sans Bold, captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/features/fillet.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.29.0 English desktop UI (default dark theme, software renderer) filleting four edges, with the original drawn pointer of examples/demo/overlay.py and labels set in DejaVu Sans Bold, captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/features/edit.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.29.0 English desktop UI (default dark theme, software renderer) editing a feature from the timeline, with the original drawn pointer of examples/demo/overlay.py and labels set in DejaVu Sans Bold, captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/features/hole.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.29.0 English desktop UI (default dark theme, software renderer) placing a hole, with the original drawn pointer of examples/demo/overlay.py and labels set in DejaVu Sans Bold, captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/features/pattern.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.29.0 English desktop UI (default dark theme, software renderer) patterning a hole around an axis, with the original drawn pointer of examples/demo/overlay.py and labels set in DejaVu Sans Bold, captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/parameters/dialog.png | SolidCraft contributors | screenshot of SolidCraft 0.29.0 itself (default dark theme, software renderer) showing the Parameters dialog, with original numbered callouts (digits set in DejaVu Sans Bold), captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/files/menu.png | SolidCraft contributors | screenshot of SolidCraft 0.29.0 itself (`--sample`, default dark theme, software renderer) showing the File menu, with original numbered callouts (digits set in DejaVu Sans Bold), captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/getting-started/start.png | SolidCraft contributors | screenshot of SolidCraft 0.29.0 itself (default dark theme, software renderer, 1280x800) showing the start page with its built-in sample thumbnails, with original numbered callouts (digits set in DejaVu Sans Bold), captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/getting-started/sample.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.29.0 English desktop UI (default dark theme, software renderer) opening the built-in Sample Plate from the start page, with the original drawn pointer of examples/demo/overlay.py and labels set in DejaVu Sans Bold, captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/sheet-metal/base.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.29.0 English desktop UI (default dark theme, software renderer) making a sheet metal base flange, with the original drawn pointer of examples/demo/overlay.py and labels set in DejaVu Sans Bold, captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/sheet-metal/flange.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.29.0 English desktop UI (default dark theme, software renderer) adding sheet metal edge flanges, with the original drawn pointer of examples/demo/overlay.py and labels set in DejaVu Sans Bold, captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/sheet-metal/flat.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.29.0 English desktop UI (default dark theme, software renderer) showing a sheet metal flat pattern, with the original drawn pointer of examples/demo/overlay.py and labels set in DejaVu Sans Bold, captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/sketching/create.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.29.0 English desktop UI (default dark theme, software renderer) starting a sketch on the Top plane, with the original drawn pointer of examples/demo/overlay.py and labels set in DejaVu Sans Bold, captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/sketching/draw.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.29.0 English desktop UI (default dark theme, software renderer) drawing a rectangle and a circle with typed values, with the original drawn pointer of examples/demo/overlay.py and labels set in DejaVu Sans Bold, captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/sketching/dimension.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.29.0 English desktop UI (default dark theme, software renderer) dimensioning a sketch until it is fully constrained, with the original drawn pointer of examples/demo/overlay.py and labels set in DejaVu Sans Bold, captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/sketching/parts.png | SolidCraft contributors | screenshot of SolidCraft 0.29.0 itself (default dark theme, software renderer, 1280x800) showing a fully constrained sketch, with original numbered callouts (digits set in DejaVu Sans Bold), captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/sketching/offset.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.29.0 English desktop UI (default dark theme, software renderer) offsetting a sketch outline, with the original drawn pointer of examples/demo/overlay.py and labels set in DejaVu Sans Bold, captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| docs/book/src/media/sketching/trim.mp4 | SolidCraft contributors | original screen recording of the actual SolidCraft 0.29.0 English desktop UI (default dark theme, software renderer) trimming a notch in a sketch, with the original drawn pointer of examples/demo/overlay.py and labels set in DejaVu Sans Bold, captured with examples/demo/doc_shots.py | MIT OR Apache-2.0 |
|
||||
| assets/locales/ca.json | carlymx | Catalan translation of the English catalog, contributed upstream | MIT OR Apache-2.0 |
|
||||
| assets/locales/pending/sketch-mirror-auto-line.json | SolidCraft contributors | Original Mirror preference label and translations | MIT OR Apache-2.0 |
|
||||
| docs/screenshots/7452/demo-01-extrude5-edge-frame.png | SamD98 | screenshot of the actual SolidCraft desktop UI showing the revolve axis pick on the Cat Post Topper repro design (docs/screenshots/7452/revolve-edge-frame.solidcraft), for #7452 | MIT OR Apache-2.0 |
|
||||
| docs/screenshots/7452/demo-04-preview-outer-l11.png | SamD98 | screenshot of the actual SolidCraft desktop UI showing the revolve axis pick on the Cat Post Topper repro design (docs/screenshots/7452/revolve-edge-frame.solidcraft), for #7452 | MIT OR Apache-2.0 |
|
||||
| docs/screenshots/7452/demo-05-preview-inner-l8.png | SamD98 | screenshot of the actual SolidCraft desktop UI showing the revolve axis pick on the Cat Post Topper repro design (docs/screenshots/7452/revolve-edge-frame.solidcraft), for #7452 | MIT OR Apache-2.0 |
|
||||
| docs/screenshots/7452/demo-06-result-outer-120.png | SamD98 | screenshot of the actual SolidCraft desktop UI showing the revolve axis pick on the Cat Post Topper repro design (docs/screenshots/7452/revolve-edge-frame.solidcraft), for #7452 | MIT OR Apache-2.0 |
|
||||
| docs/screenshots/7452/demo-07-preview-360.png | SamD98 | screenshot of the actual SolidCraft desktop UI showing the revolve axis pick on the Cat Post Topper repro design (docs/screenshots/7452/revolve-edge-frame.solidcraft), for #7452 | MIT OR Apache-2.0 |
|
||||
| docs/screenshots/7452/demo-08-cancelled.png | SamD98 | screenshot of the actual SolidCraft desktop UI showing the revolve axis pick on the Cat Post Topper repro design (docs/screenshots/7452/revolve-edge-frame.solidcraft), for #7452 | MIT OR Apache-2.0 |
|
||||
| docs/screenshots/7452/demo-13-colliding-id.png | SamD98 | screenshot of the actual SolidCraft desktop UI showing the revolve axis pick on the Cat Post Topper repro design (docs/screenshots/7452/revolve-edge-frame.solidcraft), for #7452 | MIT OR Apache-2.0 |
|
||||
| docs/screenshots/7452/demo-14-surface-l1.png | SamD98 | screenshot of the actual SolidCraft desktop UI showing the revolve axis pick on the Cat Post Topper repro design (docs/screenshots/7452/revolve-edge-frame.solidcraft), for #7452 | MIT OR Apache-2.0 |
|
||||
| docs/screenshots/7452/demo-15-surface-result.png | SamD98 | screenshot of the actual SolidCraft desktop UI showing the revolve axis pick on the Cat Post Topper repro design (docs/screenshots/7452/revolve-edge-frame.solidcraft), for #7452 | MIT OR Apache-2.0 |
|
||||
| docs/screenshots/7452/revolve-selected-edge.mp4 | SamD98 | screen recording of the actual SolidCraft desktop UI showing the revolve axis pick on the Cat Post Topper repro design (docs/screenshots/7452/revolve-edge-frame.solidcraft), for #7452 | MIT OR Apache-2.0 |
|
||||
| assets/locales/bg.json | Yasen-55 | Original translation | MIT OR Apache-2.0 |
|
||||
| crates/sketch/assets/fonts/liberation-sans/LiberationSans-Regular.ttf | Red Hat, Inc. (Liberation Fonts 2.x; digitized data Google) | https://github.com/liberationfonts (Debian fonts-liberation), unmodified; licence text in crates/sketch/assets/fonts/liberation-sans/OFL.txt; used for sketch Text outlines | SIL OFL 1.1 |
|
||||
| crates/sketch/assets/fonts/liberation-sans/OFL.txt | Red Hat, Inc.; Liberation Fonts contributors | Unmodified accompanying font licence | SIL OFL 1.1 |
|
||||
| assets/locales/pending/drag-to-zoom.json | SolidCraft contributors | Original drag-to-zoom labels and translations | MIT OR Apache-2.0 |
|
||||
|
||||
## Code adapted from sibling projects
|
||||
|
||||
@@ -868,11 +910,10 @@ copied into SolidCraft.
|
||||
|
||||
| Directory | Source | Licence | Changes |
|
||||
|---|---|---|---|
|
||||
| vendor/accesskit_macos | accesskit_macos 0.26.3, https://github.com/AccessKit/accesskit (The AccessKit contributors) | MIT OR Apache-2.0 | the subclassing adapter and its tests from 0.27.2: dropping the adapter keeps AppKit's key-value observers (vendor/README.md) |
|
||||
|
||||
## Vendored renderer compatibility fix
|
||||
|
||||
`vendor/egui-wgpu` is egui-wgpu 0.36.2 from https://github.com/emilk/egui
|
||||
`vendor/egui-wgpu` is egui-wgpu 0.37.0 from https://github.com/emilk/egui
|
||||
(MIT OR Apache-2.0; both original licence texts are included). Rust sources and
|
||||
its original `egui.wgsl` shader are retained; no images or other artwork are copied.
|
||||
SolidCraft's narrow patch selects a safe 1x MSAA path on the chosen OpenGL adapter
|
||||
|
||||
+1
-1
@@ -279,7 +279,7 @@ mean something else, it needs a format change. **Add one file, and edit nothing
|
||||
change's file, or any `v<N>/` folder: those are fixed once released.
|
||||
|
||||
That's all. The build counts the files: while any are pending it writes the next format number,
|
||||
which no release reads, so a release refuses a design that has your change instead of losing it.
|
||||
which no release reads, so a release warns about a design that has your change and keeps what it can't read of it.
|
||||
A file also lists the pending changes it has (`format_pending`), so a later development build
|
||||
runs only the steps that file is missing. Two pull requests that each add a change merge without
|
||||
a conflict. `crates/doc/src/format/pending/README.md` has an example.
|
||||
|
||||
Generated
+171
-165
@@ -4,9 +4,9 @@ version = 4
|
||||
|
||||
[[package]]
|
||||
name = "accesskit"
|
||||
version = "0.24.1"
|
||||
version = "0.25.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3b7f7f85a7e5f68090000ed7622545829afd484d210358702ae4cb97dd0c320"
|
||||
checksum = "4e883d1d28c5fd5568c79ed2d2bd48d9f2fd3945c8974ed0f53690ceddb051be"
|
||||
dependencies = [
|
||||
"enumn",
|
||||
"serde",
|
||||
@@ -15,12 +15,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_atspi_common"
|
||||
version = "0.18.1"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e8c61bee90b42a772d39d06a740207dc71a4e780004ace1db8d99fb1baaa954"
|
||||
checksum = "24f5baf62e34a4a6c30e3d697e4782f821a7337049c44c3b40576a27dfbb580d"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_consumer 0.36.0",
|
||||
"accesskit_consumer",
|
||||
"atspi-common",
|
||||
"phf",
|
||||
"serde",
|
||||
@@ -29,41 +29,37 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_consumer"
|
||||
version = "0.35.0"
|
||||
version = "0.39.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53cf47daed85312e763fbf85ceca136e0d7abc68e0a7e12abe11f48172bc3b10"
|
||||
checksum = "f3da8f27636015191b2264ee1df681503170a632cafd832fdc1edfdd4e3a2fd3"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"hashbrown 0.16.1",
|
||||
"hashbrown 0.17.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_consumer"
|
||||
version = "0.36.0"
|
||||
name = "accesskit_ios"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25e0d7e25d06f4dc21d1774d67146e9e80d6789216cbd4d1e88185b0095dba60"
|
||||
checksum = "a5280724673bbfa8b30a838c013500b719392989be3b97c5a9964062c252c232"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"hashbrown 0.16.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_consumer"
|
||||
version = "0.38.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d10a236f96f87d70732e44520046785431ef01d5bcd6b041317bfadd2f88245"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"hashbrown 0.16.1",
|
||||
"accesskit_consumer",
|
||||
"hashbrown 0.17.1",
|
||||
"objc2 0.5.3",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-ui-kit 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_macos"
|
||||
version = "0.26.3"
|
||||
version = "0.27.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "123828ceeec755b05255e3976cb0852993202de98a0626c97dd8d34246991f84"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_consumer 0.38.0",
|
||||
"hashbrown 0.16.1",
|
||||
"accesskit_consumer",
|
||||
"hashbrown 0.17.1",
|
||||
"objc2 0.5.3",
|
||||
"objc2-app-kit 0.2.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
@@ -71,9 +67,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_unix"
|
||||
version = "0.21.1"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b016ca8db0ea0ea2ceff29a9d6240391492d960716aa471967c00e8cc8cb197c"
|
||||
checksum = "30d5192871228c54f1b0fb4a85cef000e11a3804f25d2165f14afa0daa67f3f6"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_atspi_common",
|
||||
@@ -89,13 +85,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_windows"
|
||||
version = "0.32.1"
|
||||
version = "0.35.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eff7009f1a532e917d66970a1e80c965140c6cfbbabbdde3d64e5431e6c78e21"
|
||||
checksum = "498668b90da9995302a0055aaecad4756932333da2ddef6baee1c00844c430fc"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_consumer 0.35.0",
|
||||
"hashbrown 0.16.1",
|
||||
"accesskit_consumer",
|
||||
"hashbrown 0.17.1",
|
||||
"static_assertions",
|
||||
"windows",
|
||||
"windows-core",
|
||||
@@ -103,11 +99,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_winit"
|
||||
version = "0.32.2"
|
||||
version = "0.34.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fe9a94394896352cc4660ca2288bd4ef883d83238853c038b44070c8f134313"
|
||||
checksum = "cd4ab4437b520ff03088fbe48676a11e7d44a929535e64b31432c1073e48dbcc"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_ios",
|
||||
"accesskit_macos",
|
||||
"accesskit_unix",
|
||||
"accesskit_windows",
|
||||
@@ -203,6 +200,7 @@ dependencies = [
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"windows-sys 0.60.2",
|
||||
"wl-clipboard-rs",
|
||||
"x11rb",
|
||||
]
|
||||
|
||||
@@ -687,42 +685,18 @@ dependencies = [
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "calloop"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43b32313e937c8721c9d17ee9bf60699a6402b4b7c2cb6ac1bee0ffcaa4b4e5e"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"polling",
|
||||
"rustix 1.1.5",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "calloop-wayland-source"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20"
|
||||
dependencies = [
|
||||
"calloop 0.13.0",
|
||||
"calloop",
|
||||
"rustix 0.38.44",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "calloop-wayland-source"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa"
|
||||
dependencies = [
|
||||
"calloop 0.14.5",
|
||||
"rustix 1.1.5",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cbc"
|
||||
version = "0.1.2"
|
||||
@@ -1123,9 +1097,9 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
|
||||
|
||||
[[package]]
|
||||
name = "ecolor"
|
||||
version = "0.36.2"
|
||||
version = "0.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0656a7530b9e2841ee7a5f983965bdd9840c32f4012f759cfd44b912cced6b34"
|
||||
checksum = "31bcbef0d4263a63eb32c4834e5d711613035ce1333468e107d928923e21e6e2"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"emath",
|
||||
@@ -1134,9 +1108,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "eframe"
|
||||
version = "0.36.2"
|
||||
version = "0.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55ad41fac6e3149abb4866e93df37dbd33baa95db525c3368c944584e95a5dc2"
|
||||
checksum = "9c5642501a65ca657d05f00c643a5c14ff2346cb3818032bfe23d58e1f8e5f84"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"bytemuck",
|
||||
@@ -1159,6 +1133,7 @@ dependencies = [
|
||||
"profiling",
|
||||
"raw-window-handle",
|
||||
"ron",
|
||||
"send_wrapper",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"wasm-bindgen",
|
||||
@@ -1171,9 +1146,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "egui"
|
||||
version = "0.36.2"
|
||||
version = "0.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc938cc27cd911415e1e4d151bc56ff9df063d8db081374e87aeb805ea74b2ba"
|
||||
checksum = "bec9144f49b7a28b5f395d3147e325b4fe3e77ba686eac11d1bdf0a7da3f375f"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"ahash",
|
||||
@@ -1193,7 +1168,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "egui-wgpu"
|
||||
version = "0.36.2"
|
||||
version = "0.37.0"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"bytemuck",
|
||||
@@ -1211,32 +1186,34 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "egui-winit"
|
||||
version = "0.36.2"
|
||||
version = "0.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98466000559d66ba4db786a30448b01483946add4188e7f10f522a27b4fb89cc"
|
||||
checksum = "e4e449f4c4a2a60c7ccb4ba7fa4078417bfe1f9ddc03b5e05ce75748e33a70de"
|
||||
dependencies = [
|
||||
"accesskit_winit",
|
||||
"arboard",
|
||||
"block2 0.6.2",
|
||||
"bytemuck",
|
||||
"egui",
|
||||
"log",
|
||||
"objc2 0.6.5",
|
||||
"objc2-app-kit 0.3.2",
|
||||
"objc2-foundation 0.3.2",
|
||||
"objc2-ui-kit 0.3.2",
|
||||
"profiling",
|
||||
"raw-window-handle",
|
||||
"serde",
|
||||
"smithay-clipboard",
|
||||
"web-time",
|
||||
"webbrowser",
|
||||
"windows-sys 0.61.2",
|
||||
"winit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "egui_glow"
|
||||
version = "0.36.2"
|
||||
version = "0.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "606ed4167daf341e5753d703285c4c1d562324b2c07aa8f405cd7d066fe58f3b"
|
||||
checksum = "9275ca5cc35a6b7c94e0e8cedf22542f66cb0cf45ed8ab2657c78ff49fec7268"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"egui",
|
||||
@@ -1254,9 +1231,9 @@ checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be"
|
||||
|
||||
[[package]]
|
||||
name = "emath"
|
||||
version = "0.36.2"
|
||||
version = "0.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52c8d4141cf0f60fb2f7aefaf352d3ffa5171ef0ab09731c64391801e95e48f1"
|
||||
checksum = "e73ce90f54750be8672c02be0743fe47530e6f251e066f0ee2869b59eb297af5"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"serde",
|
||||
@@ -1330,9 +1307,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "epaint"
|
||||
version = "0.36.2"
|
||||
version = "0.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0ec0308dc23130fc1a7d4623a8534c2ed069c6dc84e258ee4f3de99615aa1a3"
|
||||
checksum = "97bac86ae3dde93fc3951e790951caea739ff39a6c89d909cdaf1d9278dbb1bd"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"bytemuck",
|
||||
@@ -1350,15 +1327,16 @@ dependencies = [
|
||||
"skrifa",
|
||||
"smallvec",
|
||||
"unicode-general-category",
|
||||
"unicode-properties",
|
||||
"unicode-segmentation",
|
||||
"vello_cpu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "epaint_default_fonts"
|
||||
version = "0.36.2"
|
||||
version = "0.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773fa9c96dd0dbef887e39d0ed6177f141cce4d68e6041df77570aa3702dfa13"
|
||||
checksum = "cfc1cc505827f4c23eedc297975a0fd038268d41293f389dd8ff3b574aefab19"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
@@ -1444,6 +1422,12 @@ version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.10"
|
||||
@@ -1455,6 +1439,12 @@ dependencies = [
|
||||
"zlib-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.2.0"
|
||||
@@ -1651,12 +1641,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "glifo"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed4a1bb24121291d27230c1b1b44e07d6a9b28cefdb32fe1581dfb84e14f940a"
|
||||
checksum = "282a26c1e23de04bdab3e34a21b6f877a479b96737792516b9b8b8f69b6661be"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"foldhash",
|
||||
"foldhash 0.2.0",
|
||||
"hashbrown 0.17.1",
|
||||
"log",
|
||||
"peniko",
|
||||
@@ -1826,6 +1816,15 @@ dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"foldhash 0.1.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
@@ -1834,7 +1833,7 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash",
|
||||
"foldhash 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1843,7 +1842,7 @@ version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||
dependencies = [
|
||||
"foldhash",
|
||||
"foldhash 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2500,6 +2499,15 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "8.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.4.3"
|
||||
@@ -2956,6 +2964,16 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "os_pipe"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking"
|
||||
version = "2.2.1"
|
||||
@@ -3005,10 +3023,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.13.1"
|
||||
name = "petgraph"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf"
|
||||
checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "010378780309880b08997fae13be7834dba947d36393bd372f2b1556deb2a2f6"
|
||||
dependencies = [
|
||||
"phf_macros",
|
||||
"phf_shared",
|
||||
@@ -3017,9 +3046,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.13.1"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737"
|
||||
checksum = "aeb62e0959d5a1bebc965f4d15d9e2b7cea002b6b0f5ba8cde6cc26738467100"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"phf_shared",
|
||||
@@ -3027,9 +3056,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "phf_macros"
|
||||
version = "0.13.1"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef"
|
||||
checksum = "5fa8d0ca26d424d27630da600c6624696e7dec8bf7b3b492b383c5dc49e5e085"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
@@ -3040,9 +3069,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.13.1"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266"
|
||||
checksum = "c6fd9027e2d9319be6349febd1db4e8d02aa544921200c9b777720ac34a3aa89"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
@@ -3762,6 +3791,12 @@ version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
|
||||
|
||||
[[package]]
|
||||
name = "send_wrapper"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.229"
|
||||
@@ -3986,8 +4021,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"calloop 0.13.0",
|
||||
"calloop-wayland-source 0.3.0",
|
||||
"calloop",
|
||||
"calloop-wayland-source",
|
||||
"cursor-icon",
|
||||
"libc",
|
||||
"log",
|
||||
@@ -4004,44 +4039,6 @@ dependencies = [
|
||||
"xkeysym",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smithay-client-toolkit"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"calloop 0.14.5",
|
||||
"calloop-wayland-source 0.4.1",
|
||||
"cursor-icon",
|
||||
"libc",
|
||||
"log",
|
||||
"memmap2",
|
||||
"rustix 1.1.5",
|
||||
"thiserror 2.0.21",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-csd-frame",
|
||||
"wayland-cursor",
|
||||
"wayland-protocols",
|
||||
"wayland-protocols-experimental",
|
||||
"wayland-protocols-misc",
|
||||
"wayland-protocols-wlr",
|
||||
"wayland-scanner",
|
||||
"xkeysym",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smithay-clipboard"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71704c03f739f7745053bde45fa203a46c58d25bc5c4efba1d9a60e9dba81226"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"smithay-client-toolkit 0.20.0",
|
||||
"wayland-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smol_str"
|
||||
version = "0.2.2"
|
||||
@@ -4054,15 +4051,16 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "solidcore"
|
||||
version = "1.0.1"
|
||||
source = "git+https://github.com/tangency-labs/solidcore?rev=8ddce901671f1cebd9214a31d21895d725e886c8#8ddce901671f1cebd9214a31d21895d725e886c8"
|
||||
source = "git+https://github.com/tangency-labs/solidcore?rev=9856ffee5ab778c6b49f24716e82305683eda1fb#9856ffee5ab778c6b49f24716e82305683eda1fb"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"thiserror 2.0.21",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solidcore-record"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/tangency-labs/solidcore?rev=f5e8e9a29e0c9b7cf6c4040a5bc8878382b3c62f#f5e8e9a29e0c9b7cf6c4040a5bc8878382b3c62f"
|
||||
source = "git+https://github.com/tangency-labs/solidcore?rev=9856ffee5ab778c6b49f24716e82305683eda1fb#9856ffee5ab778c6b49f24716e82305683eda1fb"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4663,7 +4661,6 @@ version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
@@ -4689,6 +4686,17 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree_magic_mini"
|
||||
version = "3.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"nom",
|
||||
"petgraph",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ttf-parser"
|
||||
version = "0.25.1"
|
||||
@@ -4763,6 +4771,12 @@ version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "993ad287eb20e8ba755312801cfe1413e0f25d604075863acaddca10b04c1833"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.13.3"
|
||||
@@ -4846,9 +4860,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vello_common"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2e9aed918117e8152c9eddfd8362d73c465c23f26c44786aa331707b8a64fa2"
|
||||
checksum = "bbb2141a2bca6e6d598e471fd4d1d7eed8e020aad6a28187edda07f091a325dd"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"fearless_simd",
|
||||
@@ -4861,9 +4875,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vello_cpu"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac7349e1f55f6b801c7c277958df4ea53e7f20f21e8014910ad888b2ecda93ea"
|
||||
checksum = "ace506ca414548966fcf3c283be94eb1cad8254de488791ee698bf438b0890a9"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"glifo",
|
||||
@@ -5038,32 +5052,6 @@ dependencies = [
|
||||
"wayland-scanner",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols-experimental"
|
||||
version = "20250721.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-protocols",
|
||||
"wayland-scanner",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols-misc"
|
||||
version = "0.3.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-protocols",
|
||||
"wayland-scanner",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols-plasma"
|
||||
version = "0.3.12"
|
||||
@@ -5641,7 +5629,7 @@ dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"block2 0.5.1",
|
||||
"bytemuck",
|
||||
"calloop 0.13.0",
|
||||
"calloop",
|
||||
"cfg_aliases",
|
||||
"concurrent-queue",
|
||||
"core-foundation 0.9.4",
|
||||
@@ -5662,7 +5650,7 @@ dependencies = [
|
||||
"raw-window-handle",
|
||||
"redox_syscall 0.4.1",
|
||||
"rustix 0.38.44",
|
||||
"smithay-client-toolkit 0.19.2",
|
||||
"smithay-client-toolkit",
|
||||
"smol_str",
|
||||
"tracing",
|
||||
"unicode-segmentation",
|
||||
@@ -5705,6 +5693,24 @@ version = "0.57.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
|
||||
|
||||
[[package]]
|
||||
name = "wl-clipboard-rs"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "959c7ac3b8afde864f7a7448ac16f29b2b83350b7eccbf77fb781c3999aa6848"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"os_pipe",
|
||||
"rustix 1.1.5",
|
||||
"thiserror 2.0.21",
|
||||
"tree_magic_mini",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-protocols",
|
||||
"wayland-protocols-wlr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.6.4"
|
||||
|
||||
+7
-9
@@ -36,10 +36,10 @@ solidcraft-3dx-client = { path = "crates/threedx-client" }
|
||||
# move the rev on its own commit. For a
|
||||
# local checkout, add `[patch."https://github.com/tangency-labs/solidcore"] solidcore = { path =
|
||||
# "../solidcore/crates/solidcore" }` to a `.cargo/config.toml` outside the repository.
|
||||
solidcore = { git = "https://github.com/tangency-labs/solidcore", rev = "8ddce901671f1cebd9214a31d21895d725e886c8" }
|
||||
solidcore = { git = "https://github.com/tangency-labs/solidcore", rev = "9856ffee5ab778c6b49f24716e82305683eda1fb" }
|
||||
# Geometry as data (solidcore's layers §1, "Record"): the B-rep record, its structural validation
|
||||
# and the native body file. Pinned like solidcore; for a local checkout, patch it as above.
|
||||
solidcore-record = { git = "https://github.com/tangency-labs/solidcore", rev = "f5e8e9a29e0c9b7cf6c4040a5bc8878382b3c62f" }
|
||||
solidcore-record = { git = "https://github.com/tangency-labs/solidcore", rev = "9856ffee5ab778c6b49f24716e82305683eda1fb" }
|
||||
# test tooling: a dev-dependency only (xtask/src/layers.rs)
|
||||
solidcraft-testkit = { path = "crates/testkit" }
|
||||
|
||||
@@ -103,11 +103,12 @@ tiny-skia = { version = "0.12", default-features = false, features = ["std", "si
|
||||
toml = "1"
|
||||
|
||||
# ui
|
||||
egui = "0.36"
|
||||
eframe = { version = "0.36", default-features = false, features = ["persistence"] }
|
||||
# default_fonts: the built-in Hack is the UI's symbol fallback (#2955); 0.37 no longer turns it on.
|
||||
egui = { version = "0.37", features = ["default_fonts"] }
|
||||
eframe = { version = "0.37", default-features = false, features = ["persistence"] }
|
||||
# Instant/SystemTime that also work in the browser (std panics on wasm32-unknown-unknown).
|
||||
web-time = "1"
|
||||
egui-wgpu = "0.36"
|
||||
egui-wgpu = "0.37"
|
||||
# the wgpu egui-wgpu uses, for compute (crates/gpu); backends come from the apps' features
|
||||
wgpu = { version = "30", default-features = false, features = ["std", "wgsl"] }
|
||||
naga = { version = "30", default-features = false }
|
||||
@@ -162,12 +163,9 @@ debug = "line-tables-only"
|
||||
egui-wgpu = { path = "vendor/egui-wgpu" }
|
||||
|
||||
# Deliver trackpad pinch and rotation gestures on Wayland (zwp_pointer_gestures_v1), which
|
||||
# winit 0.30 never binds and eframe 0.36 still requires winit 0.30.
|
||||
# winit 0.30 never binds and eframe 0.37 still requires winit 0.30.
|
||||
winit = { path = "vendor/winit" }
|
||||
|
||||
# macOS: dropping the accessibility adapter leaves AppKit's key-value observers intact (#7535).
|
||||
accesskit_macos = { path = "vendor/accesskit_macos" }
|
||||
|
||||
# Mutation testing (cargo-mutants, docs/mutation-testing.md): the ci profile with incremental
|
||||
# builds, since every mutant rebuilds the crates above the one it changes.
|
||||
[profile.mutants]
|
||||
|
||||
@@ -225,6 +225,12 @@ completed drawings publish atomically. The browser still exports synchronously.
|
||||
|
||||
## Render foundation
|
||||
|
||||
- Wood surface images (#1345): native CPU and GPU opaque rendering share physical
|
||||
repeat frames and filtered mip chains. Imported maps retain exact bytes and
|
||||
declared provenance in the design, reopen offline, and expose grain-angle
|
||||
controls. Scalar face appearances suppress body maps. Planar projection and
|
||||
CPU encoding/optical limits remain explicit; see [portable surfaces](docs/render-portable-surfaces-and-stages.md).
|
||||
|
||||
- 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).
|
||||
|
||||
+275
-116
@@ -1,18 +1,17 @@
|
||||
Third-party software in SolidCraft
|
||||
|
||||
SolidCraft (the desktop app, solidcraft-cli and the web app) is built from the 537 crates below.
|
||||
SolidCraft (the desktop app, solidcraft-cli and the web app) is built from the 540 crates below.
|
||||
Generated by `cargo xtask licences` from Cargo.lock and the crates' own licence files.
|
||||
SolidCraft's own licence is in LICENSE-MIT and LICENSE-APACHE, and NOTICE.
|
||||
|
||||
accesskit 0.24.1 (MIT OR Apache-2.0)
|
||||
accesskit_atspi_common 0.18.1 (MIT OR Apache-2.0)
|
||||
accesskit_consumer 0.35.0 (MIT OR Apache-2.0)
|
||||
accesskit_consumer 0.36.0 (MIT OR Apache-2.0)
|
||||
accesskit_consumer 0.38.0 (MIT OR Apache-2.0)
|
||||
accesskit_macos 0.26.3 (MIT OR Apache-2.0)
|
||||
accesskit_unix 0.21.1 (MIT OR Apache-2.0)
|
||||
accesskit_windows 0.32.1 (MIT OR Apache-2.0)
|
||||
accesskit_winit 0.32.2 (Apache-2.0)
|
||||
accesskit 0.25.2 (MIT OR Apache-2.0)
|
||||
accesskit_atspi_common 0.21.1 (MIT OR Apache-2.0)
|
||||
accesskit_consumer 0.39.2 (MIT OR Apache-2.0)
|
||||
accesskit_ios 0.2.2 (MIT OR Apache-2.0)
|
||||
accesskit_macos 0.27.2 (MIT OR Apache-2.0)
|
||||
accesskit_unix 0.24.1 (MIT OR Apache-2.0)
|
||||
accesskit_windows 0.35.2 (MIT OR Apache-2.0)
|
||||
accesskit_winit 0.34.2 (Apache-2.0)
|
||||
adler2 2.0.1 (0BSD OR MIT OR Apache-2.0)
|
||||
aes 0.8.4 (MIT OR Apache-2.0)
|
||||
ahash 0.8.12 (MIT OR Apache-2.0)
|
||||
@@ -65,9 +64,7 @@ cadmpeg-core 0.6.7 (Apache-2.0)
|
||||
cadmpeg-ir 0.6.7 (Apache-2.0)
|
||||
cadmpeg-registry 0.6.7 (Apache-2.0)
|
||||
calloop 0.13.0 (MIT)
|
||||
calloop 0.14.5 (MIT)
|
||||
calloop-wayland-source 0.3.0 (MIT)
|
||||
calloop-wayland-source 0.4.1 (MIT)
|
||||
cbc 0.1.2 (MIT OR Apache-2.0)
|
||||
cfg-if 1.0.5 (MIT OR Apache-2.0)
|
||||
cgl 0.3.2 (MIT / Apache-2.0)
|
||||
@@ -109,21 +106,21 @@ document-features 0.2.12 (MIT OR Apache-2.0)
|
||||
downcast-rs 1.2.1 (MIT/Apache-2.0)
|
||||
dpi 0.1.2 (Apache-2.0 AND MIT)
|
||||
dyn-clone 1.0.20 (MIT OR Apache-2.0)
|
||||
ecolor 0.36.2 (MIT OR Apache-2.0)
|
||||
eframe 0.36.2 (MIT OR Apache-2.0)
|
||||
egui 0.36.2 (MIT OR Apache-2.0)
|
||||
egui-wgpu 0.36.2 (MIT OR Apache-2.0)
|
||||
egui-winit 0.36.2 (MIT OR Apache-2.0)
|
||||
egui_glow 0.36.2 (MIT OR Apache-2.0)
|
||||
ecolor 0.37.0 (MIT OR Apache-2.0)
|
||||
eframe 0.37.0 (MIT OR Apache-2.0)
|
||||
egui 0.37.0 (MIT OR Apache-2.0)
|
||||
egui-wgpu 0.37.0 (MIT OR Apache-2.0)
|
||||
egui-winit 0.37.0 (MIT OR Apache-2.0)
|
||||
egui_glow 0.37.0 (MIT OR Apache-2.0)
|
||||
either 1.19.0 (MIT OR Apache-2.0)
|
||||
emath 0.36.2 (MIT OR Apache-2.0)
|
||||
emath 0.37.0 (MIT OR Apache-2.0)
|
||||
encoding_rs 0.8.42 ((Apache-2.0 OR MIT) AND BSD-3-Clause)
|
||||
endi 1.1.1 (MIT)
|
||||
enumflags2 0.7.12 (MIT OR Apache-2.0)
|
||||
enumflags2_derive 0.7.12 (MIT OR Apache-2.0)
|
||||
enumn 0.1.14 (MIT OR Apache-2.0)
|
||||
epaint 0.36.2 (MIT OR Apache-2.0)
|
||||
epaint_default_fonts 0.36.2 ((MIT OR Apache-2.0) AND OFL-1.1 AND Ubuntu-font-1.0)
|
||||
epaint 0.37.0 (MIT OR Apache-2.0)
|
||||
epaint_default_fonts 0.37.0 ((MIT OR Apache-2.0) AND OFL-1.1 AND Ubuntu-font-1.0)
|
||||
equivalent 1.0.2 (Apache-2.0 OR MIT)
|
||||
errno 0.3.14 (MIT OR Apache-2.0)
|
||||
error-code 3.4.0 (BSL-1.0)
|
||||
@@ -134,7 +131,9 @@ fastrand 2.5.0 (Apache-2.0 OR MIT)
|
||||
fax 0.2.7 (MIT)
|
||||
fdeflate 0.3.7 (MIT OR Apache-2.0)
|
||||
fearless_simd 0.4.1 (Apache-2.0 OR MIT)
|
||||
fixedbitset 0.5.7 (MIT OR Apache-2.0)
|
||||
flate2 1.1.10 (MIT OR Apache-2.0)
|
||||
foldhash 0.1.5 (Zlib)
|
||||
foldhash 0.2.0 (Zlib)
|
||||
font-types 0.12.6 (MIT OR Apache-2.0)
|
||||
foreign-types 0.5.0 (MIT/Apache-2.0)
|
||||
@@ -154,7 +153,7 @@ getrandom 0.2.17 (MIT OR Apache-2.0)
|
||||
getrandom 0.3.4 (MIT OR Apache-2.0)
|
||||
getrandom 0.4.3 (MIT OR Apache-2.0)
|
||||
gif 0.14.2 (MIT OR Apache-2.0)
|
||||
glifo 0.2.0 (Apache-2.0 OR MIT)
|
||||
glifo 0.3.0 (Apache-2.0 OR MIT)
|
||||
glow 0.17.0 (MIT OR Apache-2.0 OR Zlib)
|
||||
gltf 1.4.1 (MIT OR Apache-2.0)
|
||||
gltf-derive 1.4.1 (MIT OR Apache-2.0)
|
||||
@@ -168,6 +167,7 @@ gpu-allocator 0.28.0 (MIT OR Apache-2.0)
|
||||
guillotiere 0.7.0 (MIT/Apache-2.0)
|
||||
half 2.7.1 (MIT OR Apache-2.0)
|
||||
harfrust 0.12.0 (MIT)
|
||||
hashbrown 0.15.5 (MIT OR Apache-2.0)
|
||||
hashbrown 0.16.1 (MIT OR Apache-2.0)
|
||||
hashbrown 0.17.1 (MIT OR Apache-2.0)
|
||||
hermit-abi 0.5.3 (MIT OR Apache-2.0)
|
||||
@@ -234,6 +234,7 @@ ndk-sys 0.6.0+11769913 (MIT OR Apache-2.0)
|
||||
nix 0.29.0 (MIT)
|
||||
nix 0.31.3 (MIT)
|
||||
nohash-hasher 0.2.0 (Apache-2.0 OR MIT)
|
||||
nom 8.0.0 (MIT)
|
||||
num 0.4.3 (MIT OR Apache-2.0)
|
||||
num-bigint 0.4.8 (MIT OR Apache-2.0)
|
||||
num-complex 0.4.6 (MIT OR Apache-2.0)
|
||||
@@ -275,15 +276,17 @@ orbclient 0.3.55 (MIT)
|
||||
ordered-float 2.10.1 (MIT)
|
||||
ordered-float 5.5.0 (MIT)
|
||||
ordered-stream 0.2.0 (MIT OR Apache-2.0)
|
||||
os_pipe 1.2.3 (MIT)
|
||||
parking 2.2.1 (Apache-2.0 OR MIT)
|
||||
parking_lot 0.12.5 (MIT OR Apache-2.0)
|
||||
parking_lot_core 0.9.12 (MIT OR Apache-2.0)
|
||||
peniko 0.6.1 (Apache-2.0 OR MIT)
|
||||
percent-encoding 2.3.2 (MIT OR Apache-2.0)
|
||||
phf 0.13.1 (MIT)
|
||||
phf_generator 0.13.1 (MIT)
|
||||
phf_macros 0.13.1 (MIT)
|
||||
phf_shared 0.13.1 (MIT)
|
||||
petgraph 0.8.3 (MIT OR Apache-2.0)
|
||||
phf 0.14.0 (MIT)
|
||||
phf_generator 0.14.0 (MIT)
|
||||
phf_macros 0.14.0 (MIT)
|
||||
phf_shared 0.14.0 (MIT)
|
||||
pin-project 1.1.13 (Apache-2.0 OR MIT)
|
||||
pin-project-internal 1.1.13 (Apache-2.0 OR MIT)
|
||||
pin-project-lite 0.2.17 (Apache-2.0 OR MIT)
|
||||
@@ -352,6 +355,7 @@ security-framework 3.7.0 (MIT OR Apache-2.0)
|
||||
security-framework-sys 2.17.0 (MIT OR Apache-2.0)
|
||||
self_cell 1.3.0 (Apache-2.0 OR GPL-2.0-only)
|
||||
semver 1.0.28 (MIT OR Apache-2.0)
|
||||
send_wrapper 0.6.0 (MIT/Apache-2.0)
|
||||
serde 1.0.229 (MIT OR Apache-2.0)
|
||||
serde-value 0.7.0 (MIT)
|
||||
serde_core 1.0.229 (MIT OR Apache-2.0)
|
||||
@@ -375,8 +379,6 @@ slab 0.4.12 (MIT)
|
||||
slotmap 1.1.1 (Zlib)
|
||||
smallvec 1.16.2 (MIT OR Apache-2.0)
|
||||
smithay-client-toolkit 0.19.2 (MIT)
|
||||
smithay-client-toolkit 0.20.0 (MIT)
|
||||
smithay-clipboard 0.7.3 (MIT)
|
||||
smol_str 0.2.2 (MIT OR Apache-2.0)
|
||||
solidcore 1.0.1 (Apache-2.0)
|
||||
solidcore-record 0.1.0 (Apache-2.0)
|
||||
@@ -409,6 +411,7 @@ toml_writer 1.1.3+spec-1.1.0 (MIT OR Apache-2.0)
|
||||
tracing 0.1.44 (MIT)
|
||||
tracing-attributes 0.1.31 (MIT)
|
||||
tracing-core 0.1.36 (MIT)
|
||||
tree_magic_mini 3.2.2 (MIT)
|
||||
ttf-parser 0.25.1 (MIT OR Apache-2.0)
|
||||
twox-hash 2.1.5 (MIT)
|
||||
type-map 0.5.1 (MIT/Apache-2.0)
|
||||
@@ -419,6 +422,7 @@ uds_windows 1.2.1 (MIT)
|
||||
unicode-bidi 0.3.18 (MIT OR Apache-2.0)
|
||||
unicode-general-category 1.1.0 (Apache-2.0)
|
||||
unicode-ident 1.0.26 ((MIT OR Apache-2.0) AND Unicode-3.0)
|
||||
unicode-properties 0.1.5 ((MIT OR Apache-2.0) AND Unicode-3.0)
|
||||
unicode-segmentation 1.13.3 (MIT OR Apache-2.0)
|
||||
unicode-width 0.2.2 (MIT OR Apache-2.0)
|
||||
untrusted 0.9.0 (ISC)
|
||||
@@ -428,8 +432,8 @@ url 2.5.8 (MIT OR Apache-2.0)
|
||||
utf8-zero 0.8.1 (MIT OR Apache-2.0)
|
||||
utf8_iter 1.0.4 (Apache-2.0 OR MIT)
|
||||
uuid 1.27.0 (Apache-2.0 OR MIT)
|
||||
vello_common 0.1.0 (Apache-2.0 OR MIT)
|
||||
vello_cpu 0.1.0 (Apache-2.0 OR MIT)
|
||||
vello_common 0.2.0 (Apache-2.0 OR MIT)
|
||||
vello_cpu 0.2.0 (Apache-2.0 OR MIT)
|
||||
wasi 0.11.1+wasi-snapshot-preview1 (Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT)
|
||||
wasip2 1.0.4+wasi-0.2.12 (Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT)
|
||||
wasm-bindgen 0.2.129 (MIT OR Apache-2.0)
|
||||
@@ -442,8 +446,6 @@ wayland-client 0.31.15 (MIT)
|
||||
wayland-csd-frame 0.3.0 (MIT)
|
||||
wayland-cursor 0.31.14 (MIT)
|
||||
wayland-protocols 0.32.13 (MIT)
|
||||
wayland-protocols-experimental 20250721.0.1 (MIT)
|
||||
wayland-protocols-misc 0.3.12 (MIT)
|
||||
wayland-protocols-plasma 0.3.12 (MIT)
|
||||
wayland-protocols-wlr 0.3.12 (MIT)
|
||||
wayland-scanner 0.31.11 (MIT)
|
||||
@@ -500,6 +502,7 @@ winit 0.30.13 (Apache-2.0)
|
||||
winnow 1.0.4 (MIT)
|
||||
winreg 0.56.0 (MIT)
|
||||
wit-bindgen 0.57.1 (Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT)
|
||||
wl-clipboard-rs 0.9.5 (MIT OR Apache-2.0)
|
||||
writeable 0.6.4 (Unicode-3.0)
|
||||
x11-dl 2.21.0 (MIT)
|
||||
x11rb 0.13.2 (MIT OR Apache-2.0)
|
||||
@@ -570,13 +573,14 @@ num_enum 0.7.6, num_enum_derive 0.7.6, once_cell 1.21.4, ordered-stream 0.2.0, p
|
||||
pin-project 1.1.13, pin-project-internal 1.1.13, pin-project-lite 0.2.17, piper 0.2.5, polling
|
||||
3.11.0, portable-atomic 1.15.0, portable-atomic-util 0.2.8, proc-macro-crate 3.5.0, proc-macro2
|
||||
1.0.107, quote 1.0.47, ref-cast 1.0.27, ref-cast-impl 1.0.27, rustc-hash 1.1.0, rustc-hash 2.1.3,
|
||||
rustix 0.38.44, rustix 1.1.5, rustversion 1.0.23, semver 1.0.28, serde 1.0.229, serde_core 1.0.229,
|
||||
serde_derive 1.0.229, serde_derive_internals 0.30.0, serde_json 1.0.151, serde_path_to_error 0.1.20,
|
||||
serde_repr 0.1.21, simd_cesu8 1.2.0, smol_str 0.2.2, syn 2.0.119, syn 3.0.6, thiserror 1.0.69,
|
||||
thiserror 2.0.21, thiserror-impl 1.0.69, thiserror-impl 2.0.21, typed-path 0.13.0, typeid 1.0.3,
|
||||
unicode-ident 1.0.26, utf8-zero 0.8.1, wasi 0.11.1+wasi-snapshot-preview1, wasip2 1.0.4+wasi-0.2.12,
|
||||
wit-bindgen 0.57.1, x11-dl 2.21.0, xdg-home 1.3.0, zbus-lockstep 0.5.2, zbus-lockstep-macros 0.5.2,
|
||||
zbus_names 3.0.0, zmij 1.0.23, zvariant_utils 2.1.0, zvariant_utils 4.2.0
|
||||
rustix 0.38.44, rustix 1.1.5, rustversion 1.0.23, semver 1.0.28, send_wrapper 0.6.0, serde 1.0.229,
|
||||
serde_core 1.0.229, serde_derive 1.0.229, serde_derive_internals 0.30.0, serde_json 1.0.151,
|
||||
serde_path_to_error 0.1.20, serde_repr 0.1.21, simd_cesu8 1.2.0, smol_str 0.2.2, syn 2.0.119, syn
|
||||
3.0.6, thiserror 1.0.69, thiserror 2.0.21, thiserror-impl 1.0.69, thiserror-impl 2.0.21, typed-path
|
||||
0.13.0, typeid 1.0.3, unicode-ident 1.0.26, utf8-zero 0.8.1, wasi 0.11.1+wasi-snapshot-preview1,
|
||||
wasip2 1.0.4+wasi-0.2.12, wit-bindgen 0.57.1, x11-dl 2.21.0, xdg-home 1.3.0, zbus-lockstep 0.5.2,
|
||||
zbus-lockstep-macros 0.5.2, zbus_names 3.0.0, zmij 1.0.23, zvariant_utils 2.1.0, zvariant_utils
|
||||
4.2.0
|
||||
================================================================================
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
@@ -1086,25 +1090,26 @@ ahash 0.8.12, arrayvec 0.7.8, async-channel 2.5.0, async-executor 1.14.0, async-
|
||||
0.9.4, core-foundation-sys 0.8.7, core-graphics 0.23.2, core-graphics-types 0.1.3, core_detect
|
||||
1.0.0, crossbeam-channel 0.5.17, crossbeam-deque 0.8.8, crossbeam-epoch 0.9.21, crossbeam-utils
|
||||
0.8.23, dbus-secret-service 4.1.0, displaydoc 0.2.7, either 1.19.0, equivalent 1.0.2, errno 0.3.14,
|
||||
euclid 0.22.14, event-listener 5.4.2, event-listener-strategy 0.5.4, fastrand 2.5.0, flate2 1.1.10,
|
||||
form_urlencoded 1.2.2, futures-lite 2.6.1, gethostname 1.1.0, gif 0.14.2, glow 0.17.0, gltf 1.4.1,
|
||||
gltf-derive 1.4.1, gltf-json 1.4.1, hashbrown 0.16.1, hashbrown 0.17.1, hermit-abi 0.5.3, httparse
|
||||
1.10.1, idna 1.1.0, idna_adapter 1.2.2, indexmap 2.14.2, itertools 0.15.0, js-sys 0.3.106,
|
||||
keyboard-types 0.8.3, khronos-egl 6.0.0, lazy_static 1.5.1, linux-raw-sys 0.12.1, linux-raw-sys
|
||||
0.4.15, lock_api 0.4.14, log 0.4.34, muda 0.21.2, num 0.4.3, num-bigint 0.4.8, num-complex 0.4.6,
|
||||
num-integer 0.1.47, num-iter 0.1.46, num-rational 0.4.2, num-traits 0.2.19, once_cell 1.21.4,
|
||||
openssl-probe 0.2.1, ordered-stream 0.2.0, parking 2.2.1, parking_lot 0.12.5, parking_lot_core
|
||||
0.9.12, percent-encoding 2.3.2, piper 0.2.5, plain 0.2.3, png 0.18.1, polling 3.11.0, pollster
|
||||
0.4.0, pollster 1.0.1, rayon 1.12.0, rayon-core 1.13.0, renderdoc-sys 1.1.0, robust 1.2.0, ron
|
||||
0.12.2, roxmltree 0.21.1, rustc-hash 1.1.0, rustix 0.38.44, rustix 1.1.5, rustls 0.23.46,
|
||||
rustls-native-certs 0.8.5, scoped-tls 1.0.1, scopeguard 1.2.0, secret-service 4.0.0,
|
||||
security-framework 2.11.1, security-framework 3.7.0, security-framework-sys 2.17.0,
|
||||
signal-hook-registry 1.4.8, simd_cesu8 1.2.0, smallvec 1.16.2, smol_str 0.2.2, stable_deref_trait
|
||||
1.2.1, tempfile 3.27.0, ttf-parser 0.25.1, unicode-bidi 0.3.18, unicode-segmentation 1.13.3,
|
||||
unicode-width 0.2.2, url 2.5.8, uuid 1.27.0, wasi 0.11.1+wasi-snapshot-preview1, wasip2
|
||||
1.0.4+wasi-0.2.12, wasm-bindgen 0.2.129, wasm-bindgen-futures 0.4.79, wasm-bindgen-macro 0.2.129,
|
||||
wasm-bindgen-macro-support 0.2.129, wasm-bindgen-shared 0.2.129, web-sys 0.3.106, weezl 0.1.12,
|
||||
wit-bindgen 0.57.1
|
||||
euclid 0.22.14, event-listener 5.4.2, event-listener-strategy 0.5.4, fastrand 2.5.0, fixedbitset
|
||||
0.5.7, flate2 1.1.10, form_urlencoded 1.2.2, futures-lite 2.6.1, gethostname 1.1.0, gif 0.14.2, glow
|
||||
0.17.0, gltf 1.4.1, gltf-derive 1.4.1, gltf-json 1.4.1, hashbrown 0.15.5, hashbrown 0.16.1,
|
||||
hashbrown 0.17.1, hermit-abi 0.5.3, httparse 1.10.1, idna 1.1.0, idna_adapter 1.2.2, indexmap
|
||||
2.14.2, itertools 0.15.0, js-sys 0.3.106, keyboard-types 0.8.3, khronos-egl 6.0.0, lazy_static
|
||||
1.5.1, linux-raw-sys 0.12.1, linux-raw-sys 0.4.15, lock_api 0.4.14, log 0.4.34, muda 0.21.2, num
|
||||
0.4.3, num-bigint 0.4.8, num-complex 0.4.6, num-integer 0.1.47, num-iter 0.1.46, num-rational 0.4.2,
|
||||
num-traits 0.2.19, once_cell 1.21.4, openssl-probe 0.2.1, ordered-stream 0.2.0, parking 2.2.1,
|
||||
parking_lot 0.12.5, parking_lot_core 0.9.12, percent-encoding 2.3.2, petgraph 0.8.3, piper 0.2.5,
|
||||
plain 0.2.3, png 0.18.1, polling 3.11.0, pollster 0.4.0, pollster 1.0.1, rayon 1.12.0, rayon-core
|
||||
1.13.0, renderdoc-sys 1.1.0, robust 1.2.0, ron 0.12.2, roxmltree 0.21.1, rustc-hash 1.1.0, rustix
|
||||
0.38.44, rustix 1.1.5, rustls 0.23.46, rustls-native-certs 0.8.5, scoped-tls 1.0.1, scopeguard
|
||||
1.2.0, secret-service 4.0.0, security-framework 2.11.1, security-framework 3.7.0,
|
||||
security-framework-sys 2.17.0, send_wrapper 0.6.0, signal-hook-registry 1.4.8, simd_cesu8 1.2.0,
|
||||
smallvec 1.16.2, smol_str 0.2.2, stable_deref_trait 1.2.1, tempfile 3.27.0, ttf-parser 0.25.1,
|
||||
unicode-bidi 0.3.18, unicode-properties 0.1.5, unicode-segmentation 1.13.3, unicode-width 0.2.2, url
|
||||
2.5.8, uuid 1.27.0, wasi 0.11.1+wasi-snapshot-preview1, wasip2 1.0.4+wasi-0.2.12, wasm-bindgen
|
||||
0.2.129, wasm-bindgen-futures 0.4.79, wasm-bindgen-macro 0.2.129, wasm-bindgen-macro-support
|
||||
0.2.129, wasm-bindgen-shared 0.2.129, web-sys 0.3.106, weezl 0.1.12, wit-bindgen 0.57.1,
|
||||
wl-clipboard-rs 0.9.5
|
||||
================================================================================
|
||||
|
||||
Apache License
|
||||
@@ -1310,7 +1315,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
================================================================================
|
||||
allocator-api2 0.2.21, async-trait 0.1.92, dyn-clone 1.0.20, enumn 0.1.14, glifo 0.2.0, itoa 1.0.18,
|
||||
allocator-api2 0.2.21, async-trait 0.1.92, dyn-clone 1.0.20, enumn 0.1.14, glifo 0.3.0, itoa 1.0.18,
|
||||
libc 0.2.190, litrs 1.0.0, num_enum 0.7.6, num_enum_derive 0.7.6, proc-macro2 1.0.107, quote 1.0.47,
|
||||
ref-cast 1.0.27, ref-cast-impl 1.0.27, rustc-hash 2.1.3, rustversion 1.0.23, semver 1.0.28, serde
|
||||
1.0.229, serde_core 1.0.229, serde_derive 1.0.229, serde_derive_internals 0.30.0, serde_json
|
||||
@@ -1530,7 +1535,7 @@ android-activity 0.6.1, arboard 3.6.1, color 0.3.3, fdeflate 0.3.7, fearless_sim
|
||||
image 0.25.10, image-webp 0.2.4, linebender_resource_handle 0.1.1, miniz_oxide 0.8.9, miniz_oxide
|
||||
0.9.1, naga 30.0.1, naga-types 30.0.1, pin-project 1.1.13, pin-project-internal 1.1.13,
|
||||
pin-project-lite 0.2.17, portable-atomic 1.15.0, portable-atomic-util 0.2.8, range-alloc 0.1.5,
|
||||
raw-window-handle 0.6.2, simdutf8 0.1.5, vello_common 0.1.0, vello_cpu 0.1.0, wgpu 30.0.1, wgpu-core
|
||||
raw-window-handle 0.6.2, simdutf8 0.1.5, vello_common 0.2.0, vello_cpu 0.2.0, wgpu 30.0.1, wgpu-core
|
||||
30.0.1, wgpu-core-deps-apple 30.0.1, wgpu-core-deps-emscripten 30.0.1, wgpu-core-deps-wasm 30.0.1,
|
||||
wgpu-core-deps-windows-linux-android 30.0.1, wgpu-hal 30.0.1, wgpu-naga-bridge 30.0.1, wgpu-types
|
||||
30.0.1
|
||||
@@ -2588,7 +2593,7 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
bit-set 0.10.0, bit-vec 0.9.1, downcast-rs 1.2.1, egui-wgpu 0.36.2, presser 0.3.1
|
||||
bit-set 0.10.0, bit-vec 0.9.1, downcast-rs 1.2.1, egui-wgpu 0.37.0, presser 0.3.1
|
||||
================================================================================
|
||||
|
||||
Apache License
|
||||
@@ -3134,7 +3139,7 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
calloop 0.13.0, calloop 0.14.5, smithay-client-toolkit 0.19.2, smithay-client-toolkit 0.20.0
|
||||
calloop 0.13.0, smithay-client-toolkit 0.19.2
|
||||
================================================================================
|
||||
|
||||
Copyright (c) 2018 Victor Berger
|
||||
@@ -3158,7 +3163,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
calloop-wayland-source 0.3.0, calloop-wayland-source 0.4.1
|
||||
calloop-wayland-source 0.3.0
|
||||
================================================================================
|
||||
|
||||
Copyright (c) 2023 Kirill Chibisov
|
||||
@@ -5231,7 +5236,7 @@ have been licensed under extremely permissive terms.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
================================================================================
|
||||
egui-wgpu 0.36.2
|
||||
egui-wgpu 0.37.0
|
||||
================================================================================
|
||||
|
||||
Copyright (c) 2018-2021 Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
|
||||
@@ -5261,7 +5266,7 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
either 1.19.0, itertools 0.15.0
|
||||
either 1.19.0, itertools 0.15.0, petgraph 0.8.3
|
||||
================================================================================
|
||||
|
||||
Copyright (c) 2015
|
||||
@@ -5709,6 +5714,36 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
fixedbitset 0.5.7
|
||||
================================================================================
|
||||
|
||||
Copyright (c) 2015-2017
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
flate2 1.1.10
|
||||
================================================================================
|
||||
@@ -5740,7 +5775,7 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
foldhash 0.2.0
|
||||
foldhash 0.1.5, foldhash 0.2.0
|
||||
================================================================================
|
||||
|
||||
Copyright (c) 2024 Orson Peters
|
||||
@@ -6511,7 +6546,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
glifo 0.2.0
|
||||
glifo 0.3.0
|
||||
================================================================================
|
||||
|
||||
Copyright 2025 the Vello Authors
|
||||
@@ -7138,7 +7173,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
hashbrown 0.16.1, hashbrown 0.17.1
|
||||
hashbrown 0.15.5, hashbrown 0.16.1, hashbrown 0.17.1
|
||||
================================================================================
|
||||
|
||||
Copyright (c) 2016 Amanieu d'Antras
|
||||
@@ -9459,6 +9494,31 @@ OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
nom 8.0.0
|
||||
================================================================================
|
||||
|
||||
Copyright (c) 2014-2019 Geoffroy Couprie
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
num_enum 0.7.6, num_enum_derive 0.7.6
|
||||
================================================================================
|
||||
@@ -9547,6 +9607,30 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
os_pipe 1.2.3
|
||||
================================================================================
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
parking 2.2.1
|
||||
================================================================================
|
||||
@@ -9562,7 +9646,7 @@ option. All files in the project carrying such notice may not be
|
||||
copied, modified, or distributed except according to those terms.
|
||||
|
||||
================================================================================
|
||||
phf 0.13.1, phf_generator 0.13.1, phf_macros 0.13.1, phf_shared 0.13.1
|
||||
phf 0.14.0, phf_generator 0.14.0, phf_macros 0.14.0, phf_shared 0.14.0
|
||||
================================================================================
|
||||
|
||||
The MIT License (MIT)
|
||||
@@ -12377,30 +12461,6 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
smithay-clipboard 0.7.3
|
||||
================================================================================
|
||||
|
||||
Copyright (c) 2018 Lucas Timmins & Victor Berger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
solidcore 1.0.1
|
||||
================================================================================
|
||||
@@ -12973,6 +13033,32 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
tree_magic_mini 3.2.2
|
||||
================================================================================
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Aaron Hancock
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
ttf-parser 0.25.1
|
||||
================================================================================
|
||||
@@ -13795,7 +13881,7 @@ carrying such notice may not be copied, modified, or distributed except
|
||||
according to those terms.
|
||||
|
||||
================================================================================
|
||||
unicode-bidi 0.3.18, unicode-segmentation 1.13.3, unicode-width 0.2.2
|
||||
unicode-bidi 0.3.18, unicode-properties 0.1.5, unicode-segmentation 1.13.3, unicode-width 0.2.2
|
||||
================================================================================
|
||||
|
||||
Copyright (c) 2015 The Rust Project Developers
|
||||
@@ -13869,7 +13955,51 @@ dealings in these Data Files or Software without prior written
|
||||
authorization of the copyright holder.
|
||||
|
||||
================================================================================
|
||||
unicode-segmentation 1.13.3, unicode-width 0.2.2
|
||||
unicode-properties 0.1.5
|
||||
================================================================================
|
||||
|
||||
UNICODE LICENSE V3
|
||||
|
||||
COPYRIGHT AND PERMISSION NOTICE
|
||||
|
||||
Copyright © 1991-2026 Unicode, Inc.
|
||||
|
||||
NOTICE TO USER: Carefully read the following legal agreement. BY
|
||||
DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR
|
||||
SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE
|
||||
TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT
|
||||
DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of data files and any associated documentation (the "Data Files") or
|
||||
software and any associated documentation (the "Software") to deal in the
|
||||
Data Files or Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, and/or sell
|
||||
copies of the Data Files or Software, and to permit persons to whom the
|
||||
Data Files or Software are furnished to do so, provided that either (a)
|
||||
this copyright and permission notice appear with all copies of the Data
|
||||
Files or Software, or (b) this copyright and permission notice appear in
|
||||
associated Documentation.
|
||||
|
||||
THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
|
||||
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
||||
THIRD PARTY RIGHTS.
|
||||
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
|
||||
BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
|
||||
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA
|
||||
FILES OR SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of a copyright holder shall
|
||||
not be used in advertising or otherwise to promote the sale, use or other
|
||||
dealings in these Data Files or Software without prior written
|
||||
authorization of the copyright holder.
|
||||
|
||||
================================================================================
|
||||
unicode-properties 0.1.5, unicode-segmentation 1.13.3, unicode-width 0.2.2
|
||||
================================================================================
|
||||
|
||||
Licensed under the Apache License, Version 2.0
|
||||
@@ -14015,7 +14145,7 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
vello_common 0.1.0, vello_cpu 0.1.0
|
||||
vello_common 0.2.0, vello_cpu 0.2.0
|
||||
================================================================================
|
||||
|
||||
Copyright 2020 the Vello Authors
|
||||
@@ -14046,8 +14176,8 @@ DEALINGS IN THE SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
wayland-backend 0.3.17, wayland-client 0.31.15, wayland-cursor 0.31.14, wayland-protocols 0.32.13,
|
||||
wayland-protocols-experimental 20250721.0.1, wayland-protocols-misc 0.3.12, wayland-protocols-plasma
|
||||
0.3.12, wayland-protocols-wlr 0.3.12, wayland-scanner 0.31.11, wayland-sys 0.31.11
|
||||
wayland-protocols-plasma 0.3.12, wayland-protocols-wlr 0.3.12, wayland-scanner 0.31.11, wayland-sys
|
||||
0.31.11
|
||||
================================================================================
|
||||
|
||||
Copyright (c) 2015 Elinor Berger
|
||||
@@ -14942,6 +15072,36 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
wl-clipboard-rs 0.9.5
|
||||
================================================================================
|
||||
|
||||
Copyright (c) 2019 Ivan Molodetskikh
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
x11rb 0.13.2, x11rb-protocol 0.13.2
|
||||
================================================================================
|
||||
@@ -15967,15 +16127,14 @@ Crates whose package carries no licence file (their licence is named in Cargo.to
|
||||
texts are as in SolidCraft's own LICENSE-MIT and LICENSE-APACHE)
|
||||
================================================================================
|
||||
|
||||
accesskit 0.24.1: MIT OR Apache-2.0 (https://github.com/AccessKit/accesskit)
|
||||
accesskit_atspi_common 0.18.1: MIT OR Apache-2.0 (https://github.com/AccessKit/accesskit)
|
||||
accesskit_consumer 0.35.0: MIT OR Apache-2.0 (https://github.com/AccessKit/accesskit)
|
||||
accesskit_consumer 0.36.0: MIT OR Apache-2.0 (https://github.com/AccessKit/accesskit)
|
||||
accesskit_consumer 0.38.0: MIT OR Apache-2.0 (https://github.com/AccessKit/accesskit)
|
||||
accesskit_macos 0.26.3: MIT OR Apache-2.0 (https://github.com/AccessKit/accesskit)
|
||||
accesskit_unix 0.21.1: MIT OR Apache-2.0 (https://github.com/AccessKit/accesskit)
|
||||
accesskit_windows 0.32.1: MIT OR Apache-2.0 (https://github.com/AccessKit/accesskit)
|
||||
accesskit_winit 0.32.2: Apache-2.0 (https://github.com/AccessKit/accesskit)
|
||||
accesskit 0.25.2: MIT OR Apache-2.0 (https://github.com/AccessKit/accesskit)
|
||||
accesskit_atspi_common 0.21.1: MIT OR Apache-2.0 (https://github.com/AccessKit/accesskit)
|
||||
accesskit_consumer 0.39.2: MIT OR Apache-2.0 (https://github.com/AccessKit/accesskit)
|
||||
accesskit_ios 0.2.2: MIT OR Apache-2.0 (https://github.com/AccessKit/accesskit)
|
||||
accesskit_macos 0.27.2: MIT OR Apache-2.0 (https://github.com/AccessKit/accesskit)
|
||||
accesskit_unix 0.24.1: MIT OR Apache-2.0 (https://github.com/AccessKit/accesskit)
|
||||
accesskit_windows 0.35.2: MIT OR Apache-2.0 (https://github.com/AccessKit/accesskit)
|
||||
accesskit_winit 0.34.2: Apache-2.0 (https://github.com/AccessKit/accesskit)
|
||||
block2 0.5.1: MIT (https://github.com/madsmtm/objc2)
|
||||
block2 0.6.2: MIT (https://github.com/madsmtm/objc2)
|
||||
cadmpeg-codec-freecad 0.6.7: Apache-2.0 (https://github.com/cadmpeg/cadmpeg)
|
||||
@@ -15988,14 +16147,14 @@ cadmpeg-registry 0.6.7: Apache-2.0 (https://github.com/cadmpeg/cadmpeg)
|
||||
clipboard-win 5.4.1: BSL-1.0 (https://github.com/DoumanAsh/clipboard-win)
|
||||
dispatch 0.2.0: MIT (http://github.com/SSheldon/rust-dispatch)
|
||||
dispatch2 0.3.1: Zlib OR Apache-2.0 OR MIT (https://github.com/madsmtm/objc2)
|
||||
ecolor 0.36.2: MIT OR Apache-2.0 (https://github.com/emilk/egui)
|
||||
eframe 0.36.2: MIT OR Apache-2.0 (https://github.com/emilk/egui/tree/main/crates/eframe)
|
||||
egui 0.36.2: MIT OR Apache-2.0 (https://github.com/emilk/egui)
|
||||
egui-winit 0.36.2: MIT OR Apache-2.0 (https://github.com/emilk/egui/tree/main/crates/egui-winit)
|
||||
egui_glow 0.36.2: MIT OR Apache-2.0 (https://github.com/emilk/egui/tree/main/crates/egui_glow)
|
||||
emath 0.36.2: MIT OR Apache-2.0 (https://github.com/emilk/egui/tree/main/crates/emath)
|
||||
epaint 0.36.2: MIT OR Apache-2.0 (https://github.com/emilk/egui/tree/main/crates/epaint)
|
||||
epaint_default_fonts 0.36.2: (MIT OR Apache-2.0) AND OFL-1.1 AND Ubuntu-font-1.0 (https://github.com/emilk/egui/tree/main/crates/epaint_default_fonts)
|
||||
ecolor 0.37.0: MIT OR Apache-2.0 (https://github.com/emilk/egui)
|
||||
eframe 0.37.0: MIT OR Apache-2.0 (https://github.com/emilk/egui/tree/main/crates/eframe)
|
||||
egui 0.37.0: MIT OR Apache-2.0 (https://github.com/emilk/egui)
|
||||
egui-winit 0.37.0: MIT OR Apache-2.0 (https://github.com/emilk/egui/tree/main/crates/egui-winit)
|
||||
egui_glow 0.37.0: MIT OR Apache-2.0 (https://github.com/emilk/egui/tree/main/crates/egui_glow)
|
||||
emath 0.37.0: MIT OR Apache-2.0 (https://github.com/emilk/egui/tree/main/crates/emath)
|
||||
epaint 0.37.0: MIT OR Apache-2.0 (https://github.com/emilk/egui/tree/main/crates/epaint)
|
||||
epaint_default_fonts 0.37.0: (MIT OR Apache-2.0) AND OFL-1.1 AND Ubuntu-font-1.0 (https://github.com/emilk/egui/tree/main/crates/epaint_default_fonts)
|
||||
jni 0.22.4: MIT OR Apache-2.0 (https://github.com/jni-rs/jni-rs)
|
||||
jni-macros 0.22.4: MIT OR Apache-2.0 (https://github.com/jni-rs/jni-rs)
|
||||
jni-sys-macros 0.4.1: MIT OR Apache-2.0 (https://github.com/jni-rs/jni-sys)
|
||||
@@ -16039,7 +16198,7 @@ spirv 0.4.0+sdk-1.4.341.0: Apache-2.0 (https://github.com/gfx-rs/rspirv)
|
||||
Bundled fonts (SIL Open Font License, Version 1.1)
|
||||
================================================================================
|
||||
|
||||
- LiberationSans-Regular.ttf (assets/fonts/liberation-sans/LiberationSans-Regular.ttf)
|
||||
- LiberationSans-Regular.ttf (crates/sketch/assets/fonts/liberation-sans/LiberationSans-Regular.ttf)
|
||||
Copyright 2012 Red Hat, Inc. (Liberation Fonts 2.x; digitized data Google)
|
||||
Source: https://github.com/liberationfonts
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ use std::process::{Command, ExitCode};
|
||||
const TARGET: &str = "solidcraft-cli";
|
||||
|
||||
fn main() -> ExitCode {
|
||||
eprintln!("solvecraft-cli is deprecated and will be removed in a later release: use solidcraft-cli");
|
||||
solidcraft_engine::note!("solvecraft-cli is deprecated and will be removed in a later release: use solidcraft-cli");
|
||||
let sibling = std::env::current_exe()
|
||||
.ok()
|
||||
.and_then(|exe| exe.parent().map(|dir| dir.join(format!("{TARGET}{}", std::env::consts::EXE_SUFFIX))))
|
||||
@@ -23,7 +23,7 @@ fn main() -> ExitCode {
|
||||
None => ExitCode::FAILURE,
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("solvecraft-cli: could not run {}: {e}", program.to_string_lossy());
|
||||
solidcraft_engine::note!("solvecraft-cli: could not run {}: {e}", program.to_string_lossy());
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ pub fn one(args: &[String]) -> Result<(), String> {
|
||||
std::panic::set_hook(Box::new(|_| {
|
||||
PANICS.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||
}));
|
||||
eprintln!("design {seed}");
|
||||
let run = fuzz::run_with(seed, steps, &mut |st| eprintln!("step {}", serde_json::to_string(st).unwrap_or_default()));
|
||||
solidcraft_engine::note!("design {seed}");
|
||||
let run = fuzz::run_with(seed, steps, &mut |st| solidcraft_engine::note!("step {}", serde_json::to_string(st).unwrap_or_default()));
|
||||
let mut design = json!({
|
||||
"outcome": run.outcome,
|
||||
"steps": run.steps.iter().map(|s| s.kind).collect::<Vec<_>>(),
|
||||
@@ -45,13 +45,13 @@ pub fn one(args: &[String]) -> Result<(), String> {
|
||||
if run.outcome.is_bug() {
|
||||
design["script"] = run.script();
|
||||
if minimise {
|
||||
eprintln!("minimise");
|
||||
solidcraft_engine::note!("minimise");
|
||||
let m = fuzz::minimise(&run);
|
||||
design["minimal"] = json!({"outcome": m.outcome, "steps": m.steps.iter().map(|s| s.kind).collect::<Vec<_>>(), "script": m.script()});
|
||||
}
|
||||
}
|
||||
let (a, b) = solidcraft_engine::fuzz::boolean_shapes(seed);
|
||||
eprintln!("boolean {seed} {} {}", serde_json::to_string(&a).unwrap_or_default(), serde_json::to_string(&b).unwrap_or_default());
|
||||
solidcraft_engine::note!("boolean {seed} {} {}", serde_json::to_string(&a).unwrap_or_default(), serde_json::to_string(&b).unwrap_or_default());
|
||||
let b = boolean_case(seed);
|
||||
let bp = PANICS.swap(0, std::sync::atomic::Ordering::SeqCst);
|
||||
println!("{}", json!({"seed": seed, "design": design, "boolean": b, "boolean_panics": bp}));
|
||||
@@ -162,11 +162,14 @@ fn record(v: &Value, out_dir: Option<&str>) {
|
||||
let d = &v["design"];
|
||||
let bug = matches!(d["outcome"]["kind"].as_str(), Some("failed" | "panic" | "invalid" | "volume"));
|
||||
if bug || v.get("timeout").is_some() || v.get("crash").is_some() {
|
||||
eprintln!("seed {seed}: {}", if bug { format!("{} {}", d["outcome"]["kind"], d["outcome"]["detail"]) } else { format!("{v}") });
|
||||
solidcraft_engine::note!(
|
||||
"seed {seed}: {}",
|
||||
if bug { format!("{} {}", d["outcome"]["kind"], d["outcome"]["detail"]) } else { format!("{v}") }
|
||||
);
|
||||
}
|
||||
let bk = v["boolean"]["outcome"].as_str().unwrap_or("ok");
|
||||
if bk != "ok" {
|
||||
eprintln!("seed {seed}: boolean {bk} {}", v["boolean"]["detail"]);
|
||||
solidcraft_engine::note!("seed {seed}: boolean {bk} {}", v["boolean"]["detail"]);
|
||||
}
|
||||
if let Some(dir) = out_dir {
|
||||
use std::io::Write;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
//! solidcraft-cli oracle <case-dir>... replay recipes and compare with expected.json
|
||||
//! solidcraft-cli kernel-cases [--json] [--dumps=DIR] [--validation=MODE] <case-dir>... the kernel regression rig (tests/kernel-cases)
|
||||
//! solidcraft-cli mcp [--headless] [--in design|script] [--connect HOST:PORT [--token T]] [--allow-path DIR]... [--allow-any-path]
|
||||
//! [--param-check off|warn|strict]
|
||||
//! [--param-check off|warn|strict] [--compact]
|
||||
//! MCP server on stdio (docs/mcp.md)
|
||||
//! solidcraft-cli mcp setup [--dry-run] [--client NAME] [--live] (alias: mcp install) add it to OpenCode, Claude Desktop, Claude Code, Cursor
|
||||
//! solidcraft-cli licences SolidCraft's licence and the third-party licences
|
||||
@@ -64,7 +64,7 @@ struct Command {
|
||||
/// `run`'s lines in the usage.
|
||||
const USAGE_RUN: &str = " solidcraft-cli run <script.json|design.sc|part.step> [--in design.sc|part.step] [--out FILE]... [--save FILE] [--quiet] [--results] [--timings] [--profile] [--profile-out trace.json] [--json-out FILE] [--allow-errors] [--validation each_step|each_feature|off]\n [--param-check off|warn|strict]\n solidcraft-cli run --in <design.sc|part.step> [--out FILE]...\n (a timeline feature that fails fails the run and nothing is written; --allow-errors writes anyway)\n (--validation: when SMLib's full check runs on the features computed (diagnostics.validation), default each_feature)\n (--param-check checks each step against its command's contract: warn reports on stderr, strict stops the run)\n (--timings prints {timings: [{index, command, ms}], total_ms} for a script, with --results its results too;\n --profile adds each command's profile: {boolean_ms, tessellate_ms, other_ms}, the kernel's share)\n (--profile also records where the time goes, SMLib's calls, kernel operations, features, commands and import stages,\n and prints the table to stderr: count, total, self time and longest per span; --profile-out FILE writes the spans\n as a Chrome trace for chrome://tracing or ui.perfetto.dev; SOLIDCRAFT_PROFILE=1 does the same for any command)\n";
|
||||
/// `eval`'s lines in the usage.
|
||||
const USAGE_EVAL: &str = " solidcraft-cli eval <script.json|design.sc|part.step> [--in design.sc|part.step] [--json-out FILE] [--profile] [--profile-out trace.json] (alias: inspect)\n solidcraft-cli eval --in <script.json|design.sc|part.step> [--json-out FILE]\n (with both inputs, runs the positional script on --in before measuring; does not automatically save the modified design)\n (--json-out FILE writes the JSON that run (with --results, else the measurement) and eval print to FILE instead of stdout,\n as plain UTF-8: a shell's > can re-encode it, e.g. Windows PowerShell adds a byte-order mark)\n (--profile and --profile-out as for run)\n";
|
||||
const USAGE_EVAL: &str = " solidcraft-cli eval <script.json|design.sc|part.step> [--in design.sc|part.step] [--json-out FILE] [--profile] [--profile-out trace.json] [--validation each_step|each_feature|off] (alias: inspect)\n solidcraft-cli eval --in <script.json|design.sc|part.step> [--json-out FILE]\n (with both inputs, runs the positional script on --in before measuring; does not automatically save the modified design)\n (--json-out FILE writes the JSON that run (with --results, else the measurement) and eval print to FILE instead of stdout,\n as plain UTF-8: a shell's > can re-encode it, e.g. Windows PowerShell adds a byte-order mark)\n (--profile, --profile-out and --validation as for run)\n";
|
||||
/// `script`'s lines in the usage.
|
||||
const USAGE_SCRIPT: &str = " solidcraft-cli script <design.sc|script.json> [--out script.json]\n (the design's history as a command script that eval and run replay)\n";
|
||||
/// `bench-open`'s lines in the usage.
|
||||
@@ -91,7 +91,7 @@ const USAGE_KERNEL_CASES: &str = " solidcraft-cli kernel-cases <case-dir>... [-
|
||||
const USAGE_FUZZ: &str =
|
||||
" solidcraft-cli fuzz [--from N] [--count N] [--jobs J] [--timeout S] [--steps N] [--minimise] [--out DIR] [--report FILE]\n";
|
||||
/// `mcp`'s lines in the usage.
|
||||
const USAGE_MCP: &str = " solidcraft-cli mcp [--headless] [--in design.sc|script.json] [--connect 127.0.0.1:PORT [--token T]] [--allow-path DIR]... [--allow-any-path]\n [--param-check off|warn|strict] [--render-gpu]\n solidcraft-cli mcp setup [--dry-run] [--client opencode|claude-desktop|claude-code|cursor] [--live]\n (adds a `solidcraft` server, with this program's path, to each installed AI client; --live adds `solidcraft-live` for the running app; alias: mcp install)\n";
|
||||
const USAGE_MCP: &str = " solidcraft-cli mcp [--headless] [--in design.sc|script.json] [--connect 127.0.0.1:PORT [--token T]] [--allow-path DIR]... [--allow-any-path]\n [--param-check off|warn|strict] [--render-gpu] [--compact]\n (--compact, SOLIDCRAFT_MCP_COMPACT=1 or the app's Preferences › AI (with --connect): compact tool output for\n agents: inspect_design summarises sketches and takes `sketches` by name and a `bodies` filter, and tool JSON\n is not pretty-printed; off by default)\n solidcraft-cli mcp setup [--dry-run] [--client opencode|claude-desktop|claude-code|cursor] [--live]\n (adds a `solidcraft` server, with this program's path, to each installed AI client; --live adds `solidcraft-live` for the running app; alias: mcp install)\n";
|
||||
/// `crashes`'s lines in the usage.
|
||||
const USAGE_CRASHES: &str = " solidcraft-cli crashes [list [--json] | show <id> | replay <id> | dir | clear [<id>]]\n";
|
||||
/// `licences`'s lines in the usage.
|
||||
@@ -154,7 +154,7 @@ fn main() -> ExitCode {
|
||||
}
|
||||
};
|
||||
if let Err(e) = max_file_size(&mut args) {
|
||||
eprintln!("error: {e}");
|
||||
solidcraft_engine::note!("error: {e}");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
let r = match args.first().map(String::as_str) {
|
||||
@@ -235,7 +235,7 @@ fn main() -> ExitCode {
|
||||
match r {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(e) => {
|
||||
eprintln!("error: {e}");
|
||||
solidcraft_engine::note!("error: {e}");
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
@@ -318,6 +318,19 @@ fn load_checked(path: &str, check: solidcraft_engine::schema::ParamCheck) -> Res
|
||||
/// `load_checked`, the features computed under `validation` (`diagnostics.validation`), which
|
||||
/// the session keeps.
|
||||
fn load_with(path: &str, check: solidcraft_engine::schema::ParamCheck, validation: solidcraft_engine::kernel::Validation) -> Result<Session, String> {
|
||||
let s = read_session(path, check, validation)?;
|
||||
// A newer SolidCraft's file opens all the same; stderr says so, stdout stays the caller's.
|
||||
for warning in s.doc.newer_file.iter().flat_map(|file| file.warnings()) {
|
||||
eprintln!("{path}: warning: {warning}");
|
||||
}
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
fn read_session(
|
||||
path: &str,
|
||||
check: solidcraft_engine::schema::ParamCheck,
|
||||
validation: solidcraft_engine::kernel::Validation,
|
||||
) -> Result<Session, String> {
|
||||
let mut s = Session::default();
|
||||
s.param_check = check;
|
||||
s.set_validation(validation);
|
||||
@@ -351,7 +364,7 @@ fn run_script_warning(s: &mut Session, path: &str, script: &Value) -> Result<Vec
|
||||
/// Step `step`'s `param_warnings` on stderr, one per line, named after the script at `path`.
|
||||
fn print_param_warnings(path: &str, step: usize, r: &Value) {
|
||||
for w in r.get("param_warnings").and_then(Value::as_array).into_iter().flatten() {
|
||||
eprintln!("{path}: step {}: {}", step + 1, w.as_str().unwrap_or_default());
|
||||
solidcraft_engine::note!("{path}: step {}: {}", step + 1, w.as_str().unwrap_or_default());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -484,8 +497,10 @@ fn cmd_run(args: &[String]) -> Result<(), String> {
|
||||
// A feature that fails leaves the bodies as they were before it (a parameter change can
|
||||
// break a feature without failing the command that made it): writing them would hand out
|
||||
// the wrong part with success, so fail the run unless asked to write it anyway.
|
||||
// A feature only a newer SolidCraft can build isn't a broken design: saving the design keeps it
|
||||
// and hands out no part. Exporting bodies without it would, so that still stops.
|
||||
if !args.iter().any(|a| a == "--allow-errors") {
|
||||
timeline_ok(&s)?;
|
||||
timeline_ok(&s, flags(args, "--out").is_empty())?;
|
||||
}
|
||||
for out in flags(args, "--out") {
|
||||
s.execute("file.export", &json!({"path": out})).map_err(|e| e.to_string())?;
|
||||
@@ -561,8 +576,12 @@ fn known_args(cmd: &str, args: &[String], valued: &[&str], switches: &[&str], po
|
||||
}
|
||||
|
||||
/// Every failing timeline feature, one per line, as an error; Ok when none fails.
|
||||
fn timeline_ok(s: &Session) -> Result<(), String> {
|
||||
let failed: Vec<String> = s.model.results.iter().filter_map(|r| r.error.as_ref().map(|e| format!(" {}: {e}", r.name))).collect();
|
||||
fn timeline_ok(s: &Session, skip_newer_features: bool) -> Result<(), String> {
|
||||
let newer = |id: u64| {
|
||||
skip_newer_features && s.doc.feature(id).is_some_and(|f| matches!(f.kind, solidcraft_engine::doc::FeatureKind::NewerFileFeature { .. }))
|
||||
};
|
||||
let failed: Vec<String> =
|
||||
s.model.results.iter().filter(|r| !newer(r.id)).filter_map(|r| r.error.as_ref().map(|e| format!(" {}: {e}", r.name))).collect();
|
||||
if failed.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -573,13 +592,15 @@ fn timeline_ok(s: &Session) -> Result<(), String> {
|
||||
))
|
||||
}
|
||||
|
||||
/// Runs `solidcraft-cli eval` (and its `inspect` alias), opening the input design or script with the requested `--validation` mode and reporting both `inspect.measure` and `document.inspect`.
|
||||
fn cmd_eval(args: &[String]) -> Result<(), String> {
|
||||
let json_out = json_out(args)?;
|
||||
known_args("eval", args, &["--in", "--json-out", "--profile-out"], &["--profile"], true)?;
|
||||
let mut s = open(flag(args, "--in"), positional(args), solidcraft_engine::schema::ParamCheck::Off)?;
|
||||
known_args("eval", args, &["--in", "--json-out", "--profile-out", "--validation"], &["--profile"], true)?;
|
||||
let validation = validation_flag("eval", args)?;
|
||||
let mut s = open_validating(flag(args, "--in"), positional(args), solidcraft_engine::schema::ParamCheck::Off, validation)?;
|
||||
let m = s.execute("inspect.measure", &json!({})).map_err(|e| e.to_string())?;
|
||||
let d = s.execute("document.inspect", &json!({})).map_err(|e| e.to_string())?;
|
||||
let out = json!({
|
||||
let mut out = json!({
|
||||
"body_count": m["body_count"],
|
||||
"bodies": m["bodies"],
|
||||
"total": m["total"],
|
||||
@@ -587,6 +608,10 @@ fn cmd_eval(args: &[String]) -> Result<(), String> {
|
||||
"params": d["params"],
|
||||
"sketches": d["sketches"],
|
||||
});
|
||||
if let Some(file) = &s.doc.newer_file {
|
||||
out["newer_file"] = file.report();
|
||||
out["warnings"] = json!(file.warnings());
|
||||
}
|
||||
print_json(&out, json_out)
|
||||
}
|
||||
|
||||
@@ -598,10 +623,10 @@ fn cmd_script(args: &[String]) -> Result<(), String> {
|
||||
let out = flag(args, "--out");
|
||||
let r = s.execute("timeline.export_script", &json!({"path": out})).map_err(|e| e.to_string())?;
|
||||
for what in r["omitted"].as_array().into_iter().flatten().filter_map(Value::as_str) {
|
||||
eprintln!("note: the script leaves out {what}");
|
||||
solidcraft_engine::note!("note: the script leaves out {what}");
|
||||
}
|
||||
match out {
|
||||
Some(out) => eprintln!("{out}: {} steps", r["steps"]),
|
||||
Some(out) => solidcraft_engine::note!("{out}: {} steps", r["steps"]),
|
||||
None => println!("{}", pretty(&r["script"])),
|
||||
}
|
||||
Ok(())
|
||||
@@ -632,7 +657,7 @@ fn cmd_gpu(args: &[String]) -> Result<(), String> {
|
||||
);
|
||||
match s.lands_on {
|
||||
Some(solidcraft_gpu::select::Landing::Cpu) => {
|
||||
eprintln!("warning: {}", solidcraft_engine::capture::CPU_WARNING);
|
||||
solidcraft_engine::note!("warning: {}", solidcraft_engine::capture::CPU_WARNING);
|
||||
Ok(())
|
||||
}
|
||||
Some(_) => Ok(()),
|
||||
@@ -709,7 +734,7 @@ fn cmd_snapshot(args: &[String]) -> Result<(), String> {
|
||||
std::fs::write(out, &png.bytes).map_err(|e| format!("{out}: {e}"))?;
|
||||
let mut reply = json!({"path": out, "width": w, "height": h, "bytes": png.bytes.len(), "renderer": png.renderer});
|
||||
if let Some(w) = solidcraft_engine::capture::warning(png.renderer) {
|
||||
eprintln!("warning: {w}");
|
||||
solidcraft_engine::note!("warning: {w}");
|
||||
reply["warning"] = json!(w);
|
||||
}
|
||||
println!("{}", pretty(&reply));
|
||||
@@ -792,11 +817,15 @@ fn cmd_mcp(args: &[String]) -> Result<(), String> {
|
||||
return Err("mcp: --render-gpu installs a headless image host; a connected application owns its own device".into());
|
||||
}
|
||||
install_renderer();
|
||||
let options = solidcraft_mcp::Options::from_args_and_env(args).map_err(|e| format!("mcp: {e}"))?;
|
||||
if options.compact {
|
||||
eprintln!("solidcraft mcp: compact output (inspect_design summarises sketches; tool JSON is not pretty-printed)");
|
||||
}
|
||||
let scope = (!args.iter().any(|a| a == "--allow-any-path"))
|
||||
.then(|| solidcraft_mcp::agent_scope(flags(args, "--allow-path").into_iter().map(std::path::PathBuf::from)));
|
||||
if let Some(s) = &scope {
|
||||
let roots: Vec<String> = s.roots().iter().map(|r| r.display().to_string()).collect();
|
||||
eprintln!(
|
||||
solidcraft_engine::note!(
|
||||
"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)",
|
||||
roots.join(", ")
|
||||
);
|
||||
@@ -804,7 +833,7 @@ fn cmd_mcp(args: &[String]) -> Result<(), String> {
|
||||
.and_then(|p| std::path::Path::new(p).parent())
|
||||
.map(|d| if d.as_os_str().is_empty() { std::path::Path::new(".") } else { d });
|
||||
if let Some(why) = design_dir.and_then(solidcraft_engine::io::vfs::design_folder_refusal) {
|
||||
eprintln!("solidcraft mcp: the design's folder is left out because {why}; add it with --allow-path if you mean it");
|
||||
solidcraft_engine::note!("solidcraft mcp: the design's folder is left out because {why}; add it with --allow-path if you mean it");
|
||||
}
|
||||
}
|
||||
// How calls that break a command's parameter contract are treated, headless or bridged.
|
||||
@@ -818,7 +847,7 @@ fn cmd_mcp(args: &[String]) -> Result<(), String> {
|
||||
// Serve without the app too: the tools connect once it runs, and say it isn't until then.
|
||||
let mut remote = solidcraft_mcp::Remote::new(addr, flag(args, "--token")).with_param_check(param_check);
|
||||
if let Err(e) = remote.try_connect() {
|
||||
eprintln!(
|
||||
solidcraft_engine::note!(
|
||||
"solidcraft mcp: no SolidCraft app at {addr} yet ({e}); start it with `solidcraft --control {port}` \
|
||||
(or SOLIDCRAFT_CONTROL_PORT={port}), tool calls connect once it runs"
|
||||
);
|
||||
@@ -835,9 +864,9 @@ fn cmd_mcp(args: &[String]) -> Result<(), String> {
|
||||
(None, None) => {
|
||||
let auto = !args.iter().any(|a| a == "--headless");
|
||||
if auto && let Some(port) = discover_app() {
|
||||
eprintln!("solidcraft mcp: bridged to the running app at 127.0.0.1:{port} (--headless starts a session of its own)");
|
||||
solidcraft_engine::note!("solidcraft mcp: bridged to the running app at 127.0.0.1:{port} (--headless starts a session of its own)");
|
||||
let remote = solidcraft_mcp::Remote::new(&format!("127.0.0.1:{port}"), flag(args, "--token")).with_param_check(param_check);
|
||||
return serve_mcp(Box::new(remote), scope);
|
||||
return serve_mcp(Box::new(remote), scope, options);
|
||||
}
|
||||
let mut headless = solidcraft_mcp::Headless::default();
|
||||
headless.session.param_check = param_check;
|
||||
@@ -852,13 +881,17 @@ fn cmd_mcp(args: &[String]) -> Result<(), String> {
|
||||
}
|
||||
}
|
||||
};
|
||||
serve_mcp(backend, scope)
|
||||
serve_mcp(backend, scope, options)
|
||||
}
|
||||
|
||||
fn serve_mcp(backend: Box<dyn solidcraft_mcp::Backend>, scope: Option<solidcraft_engine::io::vfs::PathScope>) -> Result<(), String> {
|
||||
fn serve_mcp(
|
||||
backend: Box<dyn solidcraft_mcp::Backend>,
|
||||
scope: Option<solidcraft_engine::io::vfs::PathScope>,
|
||||
options: solidcraft_mcp::Options,
|
||||
) -> Result<(), String> {
|
||||
let stdin = std::io::stdin();
|
||||
let stdout = std::io::stdout();
|
||||
solidcraft_mcp::Server::new(backend).with_path_scope(scope).serve(stdin.lock(), stdout.lock()).map_err(|e| e.to_string())
|
||||
solidcraft_mcp::Server::new(backend).with_path_scope(scope).with_options(options).serve(stdin.lock(), stdout.lock()).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// The port of the running desktop app: the one its discovery file names, if something listens
|
||||
|
||||
@@ -188,3 +188,26 @@ fn max_file_size_raises_the_limit_and_refuses_bad_sizes() {
|
||||
assert!(!output.status.success() && !stderr.contains("too large"), "{stderr}");
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
/// #10111: a line for a stderr whose reader has gone (the pipe closed before it, as an agent may
|
||||
/// leave the MCP server's, and the app that one starts) is dropped, and the command ends as it
|
||||
/// would with stderr open: here a script that isn't there fails with 1, where `eprintln!`
|
||||
/// panicked with "failed printing to stderr" (101).
|
||||
#[test]
|
||||
fn a_closed_stderr_does_not_stop_the_cli() {
|
||||
for _ in 0..5 {
|
||||
let mut child = Command::new(env!("CARGO_BIN_EXE_solidcraft-cli"))
|
||||
.args(["run", "no-such-script.json"])
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.unwrap();
|
||||
drop(child.stderr.take());
|
||||
assert_eq!(child.wait().unwrap().code(), Some(1));
|
||||
}
|
||||
// The old name's alias writes its deprecation notice first: an agent still configured with
|
||||
// `solvecraft-cli mcp` must get the server, not that panic.
|
||||
let mut child = Command::new(env!("CARGO_BIN_EXE_solvecraft-cli")).arg("--help").stdout(Stdio::null()).stderr(Stdio::piped()).spawn().unwrap();
|
||||
drop(child.stderr.take());
|
||||
assert_eq!(child.wait().unwrap().code(), Some(0));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
//! `mcp --compact` and `SOLIDCRAFT_MCP_COMPACT` turn on compact tool output; without them the
|
||||
//! server's tools are as they always were.
|
||||
|
||||
use std::io::Write;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
use serde_json::{Value, json};
|
||||
|
||||
/// `inspect_design`'s input schema as `solidcraft-cli mcp` with `args` and `env` lists it, or
|
||||
/// the server's stderr when it does not start.
|
||||
fn inspect_schema(args: &[&str], env: Option<&str>) -> Result<Value, String> {
|
||||
let mut cmd = Command::new(env!("CARGO_BIN_EXE_solidcraft-cli"));
|
||||
// --headless: never bridge to (and read the preference of) an app the developer has running.
|
||||
cmd.args(["mcp", "--headless", "--allow-any-path"]).args(args).stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped());
|
||||
match env {
|
||||
Some(v) => cmd.env("SOLIDCRAFT_MCP_COMPACT", v),
|
||||
None => cmd.env_remove("SOLIDCRAFT_MCP_COMPACT"),
|
||||
};
|
||||
let mut child = cmd.spawn().unwrap();
|
||||
{
|
||||
let mut stdin = child.stdin.take().unwrap();
|
||||
// The server may refuse to start and close stdin first.
|
||||
let _ = writeln!(stdin, "{}", json!({"jsonrpc": "2.0", "id": 1, "method": "tools/list"}));
|
||||
}
|
||||
let out = child.wait_with_output().unwrap();
|
||||
if !out.status.success() {
|
||||
return Err(String::from_utf8_lossy(&out.stderr).into_owned());
|
||||
}
|
||||
let reply: Value = serde_json::from_slice(&out.stdout).unwrap();
|
||||
let tools = reply["result"]["tools"].as_array().cloned().unwrap_or_default();
|
||||
Ok(tools.into_iter().find(|t| t["name"] == "inspect_design").map(|t| t["inputSchema"].clone()).unwrap_or(Value::Null))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compact_output_is_off_unless_asked() {
|
||||
let plain = inspect_schema(&[], None).unwrap();
|
||||
assert_eq!(plain["properties"]["sketches"]["type"], "boolean", "{plain}");
|
||||
assert!(plain["properties"].get("bodies").is_none(), "{plain}");
|
||||
assert_eq!(inspect_schema(&[], Some("0")).unwrap(), plain);
|
||||
|
||||
for (args, env) in [(&["--compact"][..], None), (&[][..], Some("1")), (&[][..], Some("true"))] {
|
||||
let compact = inspect_schema(args, env).unwrap();
|
||||
assert!(compact["properties"]["bodies"].is_object(), "{args:?} {env:?}: {compact}");
|
||||
}
|
||||
|
||||
let e = inspect_schema(&[], Some("maybe")).unwrap_err();
|
||||
assert!(e.contains("SOLIDCRAFT_MCP_COMPACT"), "{e}");
|
||||
}
|
||||
@@ -60,15 +60,25 @@ fn mcp_connect_serves_before_the_app_runs_and_reaches_it_once_it_does() {
|
||||
let listener = TcpListener::bind(&addr).unwrap();
|
||||
let app = std::thread::spawn(move || {
|
||||
let (stream, _) = listener.accept().unwrap();
|
||||
let mut line = String::new();
|
||||
BufReader::new(stream.try_clone().unwrap()).read_line(&mut line).unwrap();
|
||||
let req: Value = serde_json::from_str(&line).unwrap();
|
||||
let reply = if req["token"] == json!(token) {
|
||||
json!({"id": req["id"], "ok": true, "result": {"bodies": [], "method": req["method"]}})
|
||||
} else {
|
||||
json!({"id": req["id"], "ok": false, "error": format!("unauthorized: token {}", req["token"])})
|
||||
};
|
||||
writeln!(&stream, "{reply}").unwrap();
|
||||
let mut lines = BufReader::new(stream.try_clone().unwrap());
|
||||
loop {
|
||||
let mut line = String::new();
|
||||
lines.read_line(&mut line).unwrap();
|
||||
let req: Value = serde_json::from_str(&line).unwrap();
|
||||
// The server first asks for the agents' output options (the compact preference: off).
|
||||
let options = req["method"] == "mcp.options";
|
||||
let reply = if req["token"] != json!(token) {
|
||||
json!({"id": req["id"], "ok": false, "error": format!("unauthorized: token {}", req["token"])})
|
||||
} else if options {
|
||||
json!({"id": req["id"], "ok": true, "result": {"compact": false}})
|
||||
} else {
|
||||
json!({"id": req["id"], "ok": true, "result": {"bodies": [], "method": req["method"]}})
|
||||
};
|
||||
writeln!(&stream, "{reply}").unwrap();
|
||||
if !options {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
let r = ask(4, "tools/call", measure);
|
||||
assert_ne!(r["result"]["isError"], true, "{r}");
|
||||
@@ -111,10 +121,21 @@ fn mcp_without_arguments_finds_the_running_app() {
|
||||
break (stream, line);
|
||||
}
|
||||
};
|
||||
let req: Value = serde_json::from_str(&line).unwrap();
|
||||
let ok = req["token"] == json!(token);
|
||||
let reply = json!({"id": req["id"], "ok": ok, "result": {"bodies": [], "method": req["method"]}, "error": "unauthorized"});
|
||||
writeln!(&stream, "{reply}").unwrap();
|
||||
let mut lines = BufReader::new(stream.try_clone().unwrap());
|
||||
let mut line = line;
|
||||
// The server first asks for the agents' output options (the compact preference: off).
|
||||
loop {
|
||||
let req: Value = serde_json::from_str(&line).unwrap();
|
||||
let ok = req["token"] == json!(token);
|
||||
let options = req["method"] == "mcp.options";
|
||||
let result = if options { json!({"compact": false}) } else { json!({"bodies": [], "method": req["method"]}) };
|
||||
writeln!(&stream, "{}", json!({"id": req["id"], "ok": ok, "result": result, "error": "unauthorized"})).unwrap();
|
||||
if !options {
|
||||
break;
|
||||
}
|
||||
line.clear();
|
||||
lines.read_line(&mut line).unwrap();
|
||||
}
|
||||
});
|
||||
let mut child = Command::new(env!("CARGO_BIN_EXE_solidcraft-cli"))
|
||||
.args(["mcp", "--allow-any-path"])
|
||||
|
||||
@@ -159,7 +159,7 @@ fn cli_loads_both_design_encodings_by_contents() {
|
||||
}
|
||||
|
||||
/// #1369: a pending concept that did not land in the numbered release must not be silently
|
||||
/// dropped on open/resave, in either encoding or before replacing an existing destination.
|
||||
/// dropped on open/resave, in either encoding: it opens with a warning, and saving keeps it.
|
||||
#[test]
|
||||
fn unknown_pending_concepts_preserve_the_open_design_and_output_files() {
|
||||
use solidcraft_engine::{Session, doc, io};
|
||||
@@ -173,34 +173,29 @@ fn unknown_pending_concepts_preserve_the_open_design_and_output_files() {
|
||||
let source = dir.0.join(format!("experimental-{version}.{extension}"));
|
||||
std::fs::write(&source, &bytes).unwrap();
|
||||
let mut session = Session::default();
|
||||
session.execute("solid.box", &json!({"length": 2, "width": 3, "height": 4})).unwrap();
|
||||
let original = session.doc.clone();
|
||||
let original_path = session.path.clone();
|
||||
let undo = session.undo.len();
|
||||
let dirty = session.is_dirty();
|
||||
let err = session.execute("doc.open", &json!({"path": source})).unwrap_err().to_string();
|
||||
assert!(err.contains("experimental-card") && err.contains("update SolidCraft"), "{err}");
|
||||
assert_eq!(session.doc, original);
|
||||
assert_eq!(session.path, original_path);
|
||||
assert_eq!(session.undo.len(), undo);
|
||||
assert_eq!(session.is_dirty(), dirty);
|
||||
// It opens, and the reply says what it is.
|
||||
let reply = session.execute("doc.open", &json!({"path": source})).unwrap();
|
||||
let notice = reply["newer_file"]["notice"].as_str().unwrap().to_string();
|
||||
assert!(notice.contains("experimental-card") && notice.starts_with("Saved by a newer SolidCraft"), "{notice}");
|
||||
assert_eq!(reply["warnings"][0], notice.as_str());
|
||||
assert_eq!(session.doc.name, "Experimental");
|
||||
|
||||
for existing in [false, true] {
|
||||
let destination = dir.0.join(format!("output-{version}-{extension}-{existing}.{extension}"));
|
||||
let backup = io::backup_path(&destination);
|
||||
if existing {
|
||||
std::fs::write(&destination, b"existing design").unwrap();
|
||||
std::fs::write(&backup, b"existing backup").unwrap();
|
||||
}
|
||||
let output = cli().args(["run", "--in"]).arg(&source).arg("--save").arg(&destination).output().unwrap();
|
||||
assert_eq!(output.status.code(), Some(1));
|
||||
let error = String::from_utf8_lossy(&output.stderr);
|
||||
assert!(error.contains("experimental-card") && error.contains("update SolidCraft"), "{error}");
|
||||
assert_eq!(output.status.code(), Some(0), "{error}");
|
||||
assert!(error.contains("warning: Saved by a newer SolidCraft") && error.contains("experimental-card"), "{error}");
|
||||
// Saved, it still says it has the change, and opens the same way.
|
||||
let saved = io::read_design(&std::fs::read(&destination).unwrap()).unwrap();
|
||||
assert!(saved.format_pending.iter().any(|name| name == "experimental-card"));
|
||||
assert!(saved.newer_file.is_some());
|
||||
if existing {
|
||||
assert_eq!(std::fs::read(&destination).unwrap(), b"existing design");
|
||||
assert_eq!(std::fs::read(&backup).unwrap(), b"existing backup");
|
||||
} else {
|
||||
assert!(!destination.exists() && !backup.exists());
|
||||
assert_eq!(std::fs::read(&backup).unwrap(), b"existing design");
|
||||
}
|
||||
assert_eq!(std::fs::read(&source).unwrap(), bytes);
|
||||
}
|
||||
@@ -208,6 +203,43 @@ fn unknown_pending_concepts_preserve_the_open_design_and_output_files() {
|
||||
}
|
||||
}
|
||||
|
||||
/// `eval` and `run` on a design one format newer, with a feature kind this version doesn't have:
|
||||
/// the rest builds, the reply names it, and the saved file still holds it, under the file's number.
|
||||
#[test]
|
||||
fn the_cli_opens_a_newer_design_and_saving_keeps_what_it_cant_read() {
|
||||
use solidcraft_engine::{Session, doc, io};
|
||||
|
||||
let dir = Scratch::new();
|
||||
let mut session = Session::default();
|
||||
session.execute("solid.box", &json!({"length": 2, "width": 3, "height": 4})).unwrap();
|
||||
let mut file: Value = serde_json::from_slice(&io::write_design(&session.doc)).unwrap();
|
||||
let newer = doc::format::format_string(doc::format::FORMAT + 1);
|
||||
file["format"] = json!(newer);
|
||||
let future = json!({"id": 900, "name": "Lattice1", "type": "gyroid_lattice", "cell": 2.5});
|
||||
file["features"].as_array_mut().unwrap().push(future.clone());
|
||||
let source = dir.0.join("newer.json");
|
||||
std::fs::write(&source, file.to_string()).unwrap();
|
||||
|
||||
let output = cli().args(["eval"]).arg(&source).output().unwrap();
|
||||
let error = String::from_utf8_lossy(&output.stderr);
|
||||
assert_eq!(output.status.code(), Some(0), "{error}");
|
||||
assert!(error.contains("warning: Saved by a newer SolidCraft") && error.contains("Lattice1"), "{error}");
|
||||
let reply: Value = serde_json::from_slice(&output.stdout).unwrap();
|
||||
assert_eq!(reply["body_count"], 1);
|
||||
assert!(reply["newer_file"]["not_understood"][0].as_str().unwrap().contains("gyroid_lattice"), "{reply}");
|
||||
assert!(reply["warnings"][0].as_str().unwrap().starts_with("Saved by a newer SolidCraft"), "{reply}");
|
||||
|
||||
for extension in ["json", "sc"] {
|
||||
let destination = dir.0.join(format!("saved.{extension}"));
|
||||
let output = cli().args(["run", "--in"]).arg(&source).arg("--save").arg(&destination).output().unwrap();
|
||||
assert_eq!(output.status.code(), Some(0), "{}", String::from_utf8_lossy(&output.stderr));
|
||||
let saved = io::read_design(&std::fs::read(&destination).unwrap()).unwrap();
|
||||
assert_eq!(saved.format, newer, "never a lower number");
|
||||
let again: Value = serde_json::from_slice(&io::write_design(&saved)).unwrap();
|
||||
assert_eq!(again["features"].as_array().unwrap().last().unwrap(), &future);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn known_pending_concepts_and_released_files_round_trip_with_inert_metadata() {
|
||||
use solidcraft_engine::{doc, io};
|
||||
|
||||
@@ -676,7 +676,7 @@ pub fn take_drops(files: &Rc<Files>, raw: &mut egui::RawInput, theme: bool) {
|
||||
/// any later page.
|
||||
fn hold_page_lock(page: &str) {
|
||||
let Some(locks) = web_locks() else { return };
|
||||
let Ok(request) = Reflect::get(&locks, &"request".into()).and_then(|f| f.dyn_into::<Function>().map_err(JsValue::from)) else { return };
|
||||
let Ok(request) = Reflect::get(&locks, &"request".into()).and_then(|f| f.dyn_into::<Function>()) else { return };
|
||||
// A promise that never settles: the lock is let go only when the page goes.
|
||||
let hold = Closure::<dyn FnMut(JsValue) -> js_sys::Promise>::new(|_lock: JsValue| js_sys::Promise::new(&mut |_, _| {}));
|
||||
let name = format!("{}{page}", keep::LOCK_PREFIX);
|
||||
|
||||
@@ -23,9 +23,9 @@ fn prompt() {
|
||||
let _ = js_sys::Reflect::set(&window, &OFFER.into(), &JsValue::NULL);
|
||||
}
|
||||
let shown = js_sys::Reflect::get(&offer, &"prompt".into())
|
||||
.and_then(|f| f.dyn_into::<js_sys::Function>().map_err(JsValue::from))
|
||||
.and_then(|f| f.dyn_into::<js_sys::Function>())
|
||||
.and_then(|f| f.call0(&offer))
|
||||
.and_then(|p| p.dyn_into::<js_sys::Promise>().map_err(JsValue::from));
|
||||
.and_then(|p| p.dyn_into::<js_sys::Promise>());
|
||||
match shown {
|
||||
// Accepted or dismissed, the browser handles it; a refusal is only logged.
|
||||
Ok(p) => wasm_bindgen_futures::spawn_local(async move {
|
||||
|
||||
@@ -48,7 +48,7 @@ impl Download {
|
||||
}
|
||||
if solidcraft_ui_egui::documents::any_dirty(app) {
|
||||
let mut answer = None;
|
||||
let closed = ui_kit::Window::new(egui::Id::new("open_link"), "status_warning", solidcraft_ui_egui::i18n::tr("web.open_link"))
|
||||
let closed = ui_kit::Window::new(egui::Id::unique("open_link"), "status_warning", solidcraft_ui_egui::i18n::tr("web.open_link"))
|
||||
.required()
|
||||
.show_body(ctx, |ui| {
|
||||
ui.label(solidcraft_ui_egui::i18n::tr("web.open_link_confirm"));
|
||||
|
||||
@@ -726,7 +726,7 @@ mod web {
|
||||
return;
|
||||
}
|
||||
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")
|
||||
let closed = solidcraft_ui_egui::ui_kit::Window::new(egui::Id::unique("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)
|
||||
.show_body(ctx, |ui| {
|
||||
@@ -828,7 +828,9 @@ mod web {
|
||||
self.files.panel.set(false);
|
||||
}
|
||||
"insert" => {
|
||||
self.app.insert_path(&path);
|
||||
if !solidcraft_ui_egui::mesh_units::ask(&mut self.app, &path, true) {
|
||||
self.app.insert_path(&path);
|
||||
}
|
||||
self.files.panel.set(false);
|
||||
}
|
||||
"download" => match vfs::read(&path, solidcraft_engine::io::MAX_DESIGN_BYTES) {
|
||||
@@ -873,10 +875,16 @@ mod web {
|
||||
*r = new.clone();
|
||||
}
|
||||
}
|
||||
if let Some(at) = self.app.home.opened_at.remove(&old) {
|
||||
self.app.home.opened_at.insert(new.clone(), at);
|
||||
}
|
||||
// A forgotten design follows its new name.
|
||||
if self.app.home.forgotten.remove(&old) {
|
||||
self.app.home.forgotten.insert(new);
|
||||
self.app.home.forgotten.insert(new.clone());
|
||||
}
|
||||
// An open tab of that file keeps its path and, when the name came
|
||||
// from the file, the title that matches the file list (#11375).
|
||||
solidcraft_ui_egui::documents::follow_file_rename(&mut self.app, &old, &new);
|
||||
}
|
||||
Err(e) => self.app.set_status(e, true),
|
||||
}
|
||||
@@ -890,7 +898,7 @@ mod web {
|
||||
fn recovery_prompt(&mut self, ctx: &egui::Context) {
|
||||
let Some((name, path)) = self.files.recovery.borrow().clone() else { return };
|
||||
let mut answer = None;
|
||||
let _ = solidcraft_ui_egui::ui_kit::Window::new(egui::Id::new("sc_browser_recovery"), "restore", "Recover unsaved work?")
|
||||
let _ = solidcraft_ui_egui::ui_kit::Window::new(egui::Id::unique("sc_browser_recovery"), "restore", "Recover unsaved work?")
|
||||
.required()
|
||||
.show_body(ctx, |ui| {
|
||||
ui.label(format!("“{name}” had unsaved changes when this page last closed."));
|
||||
|
||||
@@ -68,7 +68,9 @@ pub fn start(listen_on: control_auth::Listen, ctx: egui::Context) -> Receiver<Co
|
||||
Ok(l) => {
|
||||
if let Some(port) = listen_on_listener(l, token.clone(), tx, ctx, false) {
|
||||
if matches!(listen_on, control_auth::Listen::Default) {
|
||||
eprintln!("solidcraft: control channel on the default port 127.0.0.1:{port} (Preferences > Assistant turns it off)");
|
||||
solidcraft_engine::note!(
|
||||
"solidcraft: control channel on the default port 127.0.0.1:{port} (Preferences > Assistant turns it off)"
|
||||
);
|
||||
}
|
||||
// The discovery file: where `solidcraft-cli mcp` finds this app with no arguments.
|
||||
match control_auth::write_current_port(port) {
|
||||
@@ -77,14 +79,14 @@ pub fn start(listen_on: control_auth::Listen, ctx: egui::Context) -> Receiver<Co
|
||||
*p = Some((port, token));
|
||||
}
|
||||
}
|
||||
Err(e) => eprintln!("solidcraft: could not write the control discovery file ({e})"),
|
||||
Err(e) => solidcraft_engine::note!("solidcraft: could not write the control discovery file ({e})"),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => eprintln!("solidcraft: control server failed to bind 127.0.0.1: {e}"),
|
||||
Err(e) => solidcraft_engine::note!("solidcraft: control server failed to bind 127.0.0.1: {e}"),
|
||||
}
|
||||
}
|
||||
None => eprintln!("solidcraft: control server not started: no random token available"),
|
||||
None => solidcraft_engine::note!("solidcraft: control server not started: no random token available"),
|
||||
}
|
||||
rx
|
||||
}
|
||||
@@ -95,13 +97,13 @@ pub fn start(listen_on: control_auth::Listen, ctx: egui::Context) -> Receiver<Co
|
||||
/// requests go to `tx`, where the chat's gate reads them; one may wait long for the user's Allow.
|
||||
pub fn listen_agent(tx: Sender<ControlRequest>, ctx: egui::Context) -> Option<u16> {
|
||||
let Some(token) = new_token() else {
|
||||
eprintln!("solidcraft: agent chat endpoint not started: no random token available");
|
||||
solidcraft_engine::note!("solidcraft: agent chat endpoint not started: no random token available");
|
||||
return None;
|
||||
};
|
||||
let listener = match TcpListener::bind(("127.0.0.1", 0)) {
|
||||
Ok(l) => l,
|
||||
Err(e) => {
|
||||
eprintln!("solidcraft: control server failed to bind 127.0.0.1: {e}");
|
||||
solidcraft_engine::note!("solidcraft: control server failed to bind 127.0.0.1: {e}");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
@@ -115,19 +117,21 @@ fn listen_on_listener(listener: TcpListener, token: String, tx: Sender<ControlRe
|
||||
match control_auth::token_path(port) {
|
||||
Some(path) => match control_auth::write_token_file(&path, &token) {
|
||||
Ok(()) => {
|
||||
eprintln!("solidcraft: control server listening on 127.0.0.1:{port} (token in {})", path.display());
|
||||
solidcraft_engine::note!("solidcraft: control server listening on 127.0.0.1:{port} (token in {})", path.display());
|
||||
if let Ok(mut files) = TOKEN_FILES.lock() {
|
||||
files.push((path, token.clone()));
|
||||
}
|
||||
}
|
||||
Err(e) => eprintln!(
|
||||
Err(e) => solidcraft_engine::note!(
|
||||
"solidcraft: control server listening on 127.0.0.1:{port}, but its token file {} could not be written ({e}); \
|
||||
clients need SOLIDCRAFT_CONTROL_TOKEN",
|
||||
path.display()
|
||||
),
|
||||
},
|
||||
None => {
|
||||
eprintln!("solidcraft: control server listening on 127.0.0.1:{port}; no data folder for its token, clients need SOLIDCRAFT_CONTROL_TOKEN")
|
||||
solidcraft_engine::note!(
|
||||
"solidcraft: control server listening on 127.0.0.1:{port}; no data folder for its token, clients need SOLIDCRAFT_CONTROL_TOKEN"
|
||||
)
|
||||
}
|
||||
}
|
||||
let token = std::sync::Arc::new(token);
|
||||
|
||||
@@ -112,7 +112,7 @@ pub fn restart(software: bool, lost_at: u64, reason: &str) {
|
||||
pub fn end() -> ! {
|
||||
let watchdog = std::thread::Builder::new().name("solidcraft-exit".into()).spawn(|| {
|
||||
std::thread::sleep(END_GRACE);
|
||||
eprintln!("SolidCraft graphics: still closing after the device was lost; aborting");
|
||||
solidcraft_engine::note!("SolidCraft graphics: still closing after the device was lost; aborting");
|
||||
std::process::abort();
|
||||
});
|
||||
if let Err(e) = watchdog {
|
||||
|
||||
+14
-13
@@ -79,7 +79,7 @@ fn mark(step: &str) {
|
||||
static START: std::sync::OnceLock<(std::time::Instant, bool)> = std::sync::OnceLock::new();
|
||||
let (start, on) = START.get_or_init(|| (std::time::Instant::now(), solidcraft_engine::geom::env::var_os("PROFILE_STARTUP").is_some()));
|
||||
if *on {
|
||||
eprintln!("startup {:>6.0} ms {step}", start.elapsed().as_secs_f64() * 1000.0);
|
||||
solidcraft_engine::note!("startup {:>6.0} ms {step}", start.elapsed().as_secs_f64() * 1000.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,7 +368,7 @@ impl App {
|
||||
let Some(gpu) = self.gpu.as_mut().filter(|g| g.lost_at.is_none() && g.lost.is_lost()) else { return };
|
||||
let reason = gpu.lost.reason().unwrap_or_default();
|
||||
log::error!("graphics: the device was lost ({reason}); closing to start again");
|
||||
eprintln!("SolidCraft graphics: the device was lost ({reason}); starting again");
|
||||
solidcraft_engine::note!("SolidCraft graphics: the device was lost ({reason}); starting again");
|
||||
solidcraft_engine::crash::phase(&format!("graphics device lost: {reason}"));
|
||||
gpu.lost_at = Some(device_lost::now_secs());
|
||||
gpu.reason = reason;
|
||||
@@ -993,7 +993,7 @@ fn main() -> eframe::Result {
|
||||
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}");
|
||||
solidcraft_engine::note!("SolidCraft: {error}");
|
||||
return Err(eframe::Error::AppCreation(std::io::Error::other(error).into()));
|
||||
}
|
||||
}
|
||||
@@ -1025,7 +1025,7 @@ fn main() -> eframe::Result {
|
||||
let fallback_note = std::env::var(startup::FALLBACK_NOTE_ENV).ok().map(|s| s.chars().take(4096).collect::<String>());
|
||||
if let Some(note) = &fallback_note {
|
||||
log::warn!("{note}");
|
||||
eprintln!("SolidCraft: {note}");
|
||||
solidcraft_engine::note!("SolidCraft: {note}");
|
||||
notes.push(note.clone());
|
||||
}
|
||||
let saved_samples = if safe_mode { None } else { msaa() };
|
||||
@@ -1138,13 +1138,13 @@ fn main() -> eframe::Result {
|
||||
// Windows: the taskbar button shows the mark even when slow first frames keep the
|
||||
// taskbar waiting for the window's icon.
|
||||
if !native_frame::set_class_icon(cc) {
|
||||
eprintln!("SolidCraft window: no class icon; the taskbar may show a generic one");
|
||||
solidcraft_engine::note!("SolidCraft window: no class icon; the taskbar may show a generic one");
|
||||
}
|
||||
// The OS hit test offers the resize border, at the window's DPI, from the first
|
||||
// message (#1381).
|
||||
app.native_resize_border = custom_titlebar && native_frame::install(cc);
|
||||
if custom_titlebar && !app.native_resize_border {
|
||||
eprintln!("SolidCraft window: no native resize border; egui edge zones instead");
|
||||
solidcraft_engine::note!("SolidCraft window: no native resize border; egui edge zones instead");
|
||||
}
|
||||
}
|
||||
app.integrated_titlebar = cfg!(target_os = "macos");
|
||||
@@ -1175,9 +1175,10 @@ fn main() -> eframe::Result {
|
||||
.collect();
|
||||
let info = rs.adapter.get_info();
|
||||
if !app.preferences.graphics_adapter.is_empty() && !info.name.eq_ignore_ascii_case(app.preferences.graphics_adapter.as_str()) {
|
||||
eprintln!(
|
||||
solidcraft_engine::note!(
|
||||
"SolidCraft graphics: preferred adapter {:?} was not used; using {:?} instead",
|
||||
app.preferences.graphics_adapter, info.name
|
||||
app.preferences.graphics_adapter,
|
||||
info.name
|
||||
);
|
||||
}
|
||||
let samples = scene_msaa_request(saved_samples);
|
||||
@@ -1185,7 +1186,7 @@ fn main() -> eframe::Result {
|
||||
eframe::egui_wgpu::supported_msaa_samples(&rs.adapter, &rs.device, rs.target_format, depth)
|
||||
});
|
||||
let scene_samples = scene_samples(info.backend, u32::from(samples), &supported);
|
||||
eprintln!("SolidCraft graphics: {:?}, MSAA {scene_samples}x (requested {}x)", rs.adapter.get_info().backend, samples);
|
||||
solidcraft_engine::note!("SolidCraft graphics: {:?}, MSAA {scene_samples}x (requested {}x)", rs.adapter.get_info().backend, samples);
|
||||
solidcraft_engine::crash::phase("viewport pipelines (shader compile)");
|
||||
let scene_samples = app.set_wgpu_split(rs, depth_bits, STENCIL_BITS, scene_samples);
|
||||
app.msaa_supported = supported;
|
||||
@@ -1346,7 +1347,7 @@ fn wgpu_options(cpu: bool, preference: Option<&str>) -> eframe::egui_wgpu::WgpuC
|
||||
let retry = std::env::var(startup::RETRY_BACKEND_ENV).ok();
|
||||
let preference = if cpu { preference } else { retry.as_deref().or(preference) };
|
||||
if let Some(backends) = startup::choose_backends(cfg!(target_os = "windows"), env_set, cpu, preference, startup::has_hardware_adapter) {
|
||||
eprintln!("SolidCraft graphics: starting {backends:?}");
|
||||
solidcraft_engine::note!("SolidCraft graphics: starting {backends:?}");
|
||||
setup.instance_descriptor.backends = backends;
|
||||
}
|
||||
let intel_mac = cfg!(all(target_os = "macos", target_arch = "x86_64"));
|
||||
@@ -1372,7 +1373,7 @@ fn wgpu_options(cpu: bool, preference: Option<&str>) -> eframe::egui_wgpu::WgpuC
|
||||
/// event loop or a failed device; each retry advances through the APIs, then software once.
|
||||
fn no_window(error: eframe::Error, tried_cpu: bool, failed: eframe::wgpu::Backends, settings: Option<&std::path::Path>) -> eframe::Result {
|
||||
log::error!("the window could not open: {error}");
|
||||
eprintln!("SolidCraft: the window could not open: {error}");
|
||||
solidcraft_engine::note!("SolidCraft: the window could not open: {error}");
|
||||
// This failure was handled, so neither the marker nor the journal should call it a crash.
|
||||
if let Some(dir) = settings {
|
||||
startup::started(dir);
|
||||
@@ -1385,7 +1386,7 @@ fn no_window(error: eframe::Error, tried_cpu: bool, failed: eframe::wgpu::Backen
|
||||
let previous = std::env::var(startup::FALLBACK_NOTE_ENV).unwrap_or_default();
|
||||
let note = startup::fallback_note(&previous, failed, next, &error.to_string());
|
||||
log::warn!("{note}");
|
||||
eprintln!("SolidCraft: {note}");
|
||||
solidcraft_engine::note!("SolidCraft: {note}");
|
||||
let mut command = std::process::Command::new(exe);
|
||||
command.args(std::env::args_os().skip(1)).env(startup::FALLBACK_NOTE_ENV, note);
|
||||
if next == "cpu" {
|
||||
@@ -1791,7 +1792,7 @@ mod viewport_tests {
|
||||
viewport.fullscreen = Some(fullscreen);
|
||||
viewport.native_pixels_per_point = Some(retina);
|
||||
super::fullscreen_content_insets(&mut raw, 32.0, zoom);
|
||||
let _ = ctx.run_ui(raw, |ui| super::content_ui(app, ui));
|
||||
ctx.run_ui(raw, |ui| super::content_ui(app, ui)).textures_delta.clear();
|
||||
};
|
||||
// Enter, leave, and enter again: no stale inset when returning to a normal window.
|
||||
for fullscreen in [false, true, false, true] {
|
||||
|
||||
@@ -434,7 +434,7 @@ mod tests {
|
||||
let mut app = SolveApp::new(solidcraft_engine::Session::default(), solidcraft_ui_egui::Services::default());
|
||||
app.home.open = false;
|
||||
let ctx = Context::default();
|
||||
let _ = ctx.run_ui(egui::RawInput { focused: true, ..Default::default() }, |_| {});
|
||||
ctx.run_ui(egui::RawInput { focused: true, ..Default::default() }, |_| {}).textures_delta.clear();
|
||||
let mut bridge = NavBridge { shared: Arc::new(Mutex::new(Shared::default())) };
|
||||
let queue = |bridge: &NavBridge| {
|
||||
let mut shared = bridge.shared.lock().unwrap();
|
||||
|
||||
@@ -1914,6 +1914,7 @@ mod tests {
|
||||
|
||||
fn wheel() -> egui::Event {
|
||||
egui::Event::MouseWheel {
|
||||
source: egui::MouseWheelSource::Unknown,
|
||||
unit: egui::MouseWheelUnit::Line,
|
||||
delta: egui::vec2(0.0, -1.0),
|
||||
modifiers: Default::default(),
|
||||
|
||||
@@ -357,7 +357,7 @@ pub fn relaunch_with_version_compat() -> Result<bool, String> {
|
||||
return Ok(false);
|
||||
}
|
||||
log::info!("startup: OCLP detected; re-launching with {}=1", SYSTEM_VERSION_COMPAT_ENV);
|
||||
eprintln!("SolidCraft: OCLP detected; re-launching with {}=1", SYSTEM_VERSION_COMPAT_ENV);
|
||||
solidcraft_engine::note!("SolidCraft: OCLP detected; re-launching with {}=1", SYSTEM_VERSION_COMPAT_ENV);
|
||||
let exe = std::env::current_exe().map_err(|e| format!("could not find own executable: {e}"))?;
|
||||
let status = std::process::Command::new(&exe)
|
||||
.args(std::env::args_os().skip(1))
|
||||
|
||||
+4
-2
@@ -23,7 +23,7 @@ assets/
|
||||
appearances/library.rs # built-in visual presets
|
||||
materials/library.rs # physical defaults and colours
|
||||
shaders/viewport.wgsl # original viewport shader
|
||||
fonts/{liberation-sans,biz-udp-gothic}/ # unmodified fonts and OFL licences
|
||||
fonts/biz-udp-gothic/ # unmodified font and OFL licence
|
||||
locales/{en,de,ja,ko}.json # shared offline catalogs
|
||||
platforms/{macos,linux,windows}/ # bundle, freedesktop and WiX templates
|
||||
platforms/macos/dmg/ # disk image window: layout, dmgbuild settings, background art
|
||||
@@ -87,7 +87,9 @@ The CLI still supports arbitrary PNG/ICO exports with `solidcraft-cli icon`.
|
||||
|
||||
Resources remain compiled by their owning layer through existing APIs:
|
||||
`ui-egui::icons::paint_icon` and `Tokens` at L5, model presets in L2 `doc`, and sketch
|
||||
fonts at L1. State, projection, interaction, commands and GPU bindings stay in
|
||||
fonts at L1. The sketch crate owns its built-in Liberation Sans font and OFL
|
||||
licence under `crates/sketch/assets/fonts/liberation-sans/`, so Cargo source
|
||||
packages and vendored builds include both files. State, projection, interaction, commands and GPU bindings stay in
|
||||
their current modules. Embedded data uses `CARGO_MANIFEST_DIR`; application code
|
||||
needs no runtime resource registry or repository lookup.
|
||||
|
||||
|
||||
@@ -55,8 +55,7 @@ pub(crate) fn face_text(painter: &egui::Painter, face: &Facet, ink: Color32, fon
|
||||
painter.ctx().add_plugin(LabelAtlas);
|
||||
let text = Shape::Text(egui::epaint::TextShape::new(Pos2::ZERO, galley, ink));
|
||||
let atlas = painter.ctx().fonts(|f| f.font_image_size());
|
||||
let primitives =
|
||||
painter.ctx().tessellate(vec![egui::epaint::ClippedShape { clip_rect: Rect::EVERYTHING, shape: text }], painter.ctx().pixels_per_point());
|
||||
let primitives = painter.ctx().tessellate(vec![egui::epaint::ClippedShape::new(Rect::EVERYTHING, text)], painter.ctx().pixels_per_point());
|
||||
for primitive in primitives {
|
||||
if let egui::epaint::Primitive::Mesh(mut mesh) = primitive.primitive {
|
||||
mesh.texture_id = LABEL_PIXELS;
|
||||
|
||||
+327
-8
@@ -1,4 +1,9 @@
|
||||
{
|
||||
" Keys": " الإطارات المفتاحية",
|
||||
" Maximum": " الحد الأقصى",
|
||||
" Minimum": " الحد الأدنى",
|
||||
" Of": " من",
|
||||
" Value": " القيمة",
|
||||
"(active rule)": "(القاعدة النشطة)",
|
||||
"0 turns it off": "القيمة 0 تُوقفه",
|
||||
"1. Open GitHub draft below, then paste the report copied to your clipboard.": "1. افتح مسودة GitHub أدناه، ثم الصق التقرير المنسوخ إلى الحافظة.",
|
||||
@@ -10,7 +15,18 @@
|
||||
"A new configuration, a copy of the active one": "تكوين جديد، نسخة من التكوين النشط",
|
||||
"A placed Single hole has a handle to drag it, and picking its face again moves the hole there instead of taking it out": "للثقب المفرد الموضوع مقبض لسحبه، واختيار وجهه مرة أخرى ينقل الثقب إلى هناك بدلًا من إزالته",
|
||||
"A short title for the problem (optional)": "عنوان قصير للمشكلة (اختياري)",
|
||||
"ABS - Black": "ABS - أسود",
|
||||
"ABS - Grey": "ABS - رمادي",
|
||||
"ABS - White": "ABS - أبيض",
|
||||
"ALIGN": "محاذاة",
|
||||
"ARRANGE": "ترتيب",
|
||||
"AS-BUILT JOINT": "مفصل حسب الوضع الحالي",
|
||||
"ASSIGN PLASTIC RULE": "تعيين قاعدة البلاستيك",
|
||||
"AXIS PERPENDICULAR TO FACE": "محور عمودي على وجه",
|
||||
"AXIS THROUGH CYLINDER": "محور عبر أسطوانة",
|
||||
"AXIS THROUGH EDGE": "محور عبر حافة",
|
||||
"AXIS THROUGH TWO PLANES": "محور عبر مستويين",
|
||||
"AXIS THROUGH TWO POINTS": "محور عبر نقطتين",
|
||||
"About SolidCraft": "حول SolidCraft",
|
||||
"Acceleration": "التسارع",
|
||||
"Access": "الوصول",
|
||||
@@ -27,6 +43,7 @@
|
||||
"Add bend": "إضافة ثني",
|
||||
"Add condition": "إضافة شرط",
|
||||
"Add or edit rule": "إضافة قاعدة أو تعديلها",
|
||||
"Add shortcut": "إضافة اختصار",
|
||||
"Add to New Folder": "إضافة إلى مجلد جديد",
|
||||
"Add visible bodies": "إضافة الأجسام المرئية",
|
||||
"Advanced": "متقدم",
|
||||
@@ -37,13 +54,17 @@
|
||||
"All components": "كل المكوّنات",
|
||||
"All open designs": "كل التصاميم المفتوحة",
|
||||
"Allowed expressions, separated by semicolons": "التعابير المسموح بها، مفصولة بفواصل منقوطة",
|
||||
"Allowed values": "القيم المسموح بها",
|
||||
"Also deletes": "يحذف أيضًا",
|
||||
"Also place them from the origin (fully constrained)": "وضعها أيضًا انطلاقًا من نقطة الأصل (مقيّدة بالكامل)",
|
||||
"Aluminum - Satin": "ألومنيوم - ساتان",
|
||||
"Anchor": "نقطة الإرساء",
|
||||
"Angle 2": "الزاوية 2",
|
||||
"Angle Display": "عرض الزاوية",
|
||||
"Animate": "تحريك",
|
||||
"Anodized Black": "أسود مؤكسد",
|
||||
"Another bend on the far edge of the last flange": "ثني آخر على الحافة البعيدة لآخر حافة مثنية",
|
||||
"Any": "أي",
|
||||
"App/system information and command settings are included. Review private details before sharing.": "تُضمَّن معلومات التطبيق والنظام وإعدادات الأوامر. راجع التفاصيل الخاصة قبل المشاركة.",
|
||||
"Appearance name": "اسم المظهر",
|
||||
"Applied Radius": "نصف القطر المطبّق",
|
||||
@@ -58,6 +79,8 @@
|
||||
"Assign Rule": "تعيين قاعدة",
|
||||
"Attachment": "الارتباط",
|
||||
"BLEND SURFACE": "سطح مزج",
|
||||
"BOSS": "نتوء",
|
||||
"BOUNDARY FILL": "تعبئة الحدود",
|
||||
"BOUNDARY SURFACE": "سطح حدودي",
|
||||
"BROWSER": "المستعرض",
|
||||
"Back to the original order": "العودة إلى الترتيب الأصلي",
|
||||
@@ -85,13 +108,21 @@
|
||||
"Bottom Right": "أسفل اليمين",
|
||||
"Boundary": "الحد",
|
||||
"Bounding box": "الصندوق المحيط",
|
||||
"Bridge": "جسر",
|
||||
"Brass": "نحاس أصفر",
|
||||
"Bridge": "تجسير",
|
||||
"Built-in": "مدمج",
|
||||
"By definition": "حسب التعريف",
|
||||
"By part number": "حسب رقم القطعة",
|
||||
"CAM": "كامة",
|
||||
"CHECK": "تحقق",
|
||||
"COMPONENTS": "المكوّنات",
|
||||
"CONFIGURATIONS": "التكوينات",
|
||||
"CONSTRAIN": "القيود",
|
||||
"CONTACT": "التلامس",
|
||||
"CONTACT SET": "مجموعة تلامس",
|
||||
"CONVERT": "تحويل",
|
||||
"CONVERT MESH": "تحويل الشبكة",
|
||||
"CONVERT TO SHEET METAL": "تحويل إلى صفائح معدنية",
|
||||
"CSV or JSON: updates existing parameters, adds new ones": "CSV أو JSON: يحدّث المعاملات الموجودة ويضيف الجديدة",
|
||||
"Capture All Positions": "تثبيت كل المواضع",
|
||||
"Capture Pos.": "تثبيت الموضع",
|
||||
@@ -111,11 +142,15 @@
|
||||
"Choose Variable radius or Spline in Type.": "اختر نصف قطر متغير أو منحنى شريحي في النوع.",
|
||||
"Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.": "اختر مستوى جديدًا. تبقى هندسة الرسم وأبعاده دون تغيير في إحداثياته ثنائية الأبعاد.",
|
||||
"Choose…": "اختيار…",
|
||||
"Chord length": "طول الوتر",
|
||||
"Chrome": "كروم",
|
||||
"Circle center": "مركز الدائرة",
|
||||
"Circle or sphere": "دائرة أو كرة",
|
||||
"Circular": "دائري",
|
||||
"Circumference": "المحيط",
|
||||
"Clockwise": "باتجاه عقارب الساعة",
|
||||
"Close All Tabs": "إغلاق كل الألسنة",
|
||||
"Close Other Tabs": "إغلاق الألسنة الأخرى",
|
||||
"Close draft": "إغلاق المسودة",
|
||||
"Collapse": "طي",
|
||||
"Collisions": "التصادمات",
|
||||
@@ -129,6 +164,7 @@
|
||||
"Config Table": "جدول التكوين",
|
||||
"Configuration": "التكوين",
|
||||
"Configuration rules": "قواعد التكوين",
|
||||
"Connect AI assistants…": "ربط مساعدي الذكاء الاصطناعي…",
|
||||
"Constant radius": "نصف قطر ثابت",
|
||||
"Contact": "التلامس",
|
||||
"Contact Sets": "مجموعات التلامس",
|
||||
@@ -137,6 +173,7 @@
|
||||
"Continuity": "الاستمرارية",
|
||||
"Control Radius": "نصف قطر التحكم",
|
||||
"Copies the definition only. Assign it from Physical Material when ready.": "يُنسخ التعريف فقط. عيّنه من «المادة الفيزيائية» عندما تكون جاهزًا.",
|
||||
"Copper": "نحاس",
|
||||
"Copy Error Message": "نسخ رسالة الخطأ",
|
||||
"Copy Warning Message": "نسخ رسالة التحذير",
|
||||
"Copy all licences": "نسخ كل التراخيص",
|
||||
@@ -150,6 +187,7 @@
|
||||
"Counterbore Ø": "قطر التوسيع الأسطواني Ø",
|
||||
"Countersink angle": "زاوية التوسيع المخروطي",
|
||||
"Countersink Ø": "قطر التوسيع المخروطي Ø",
|
||||
"Crash Reports…": "تقارير الأعطال…",
|
||||
"Create Hole": "إنشاء ثقب",
|
||||
"Create Issue — review draft": "إنشاء بلاغ — مراجعة المسودة",
|
||||
"Create Issue…": "إنشاء بلاغ…",
|
||||
@@ -162,12 +200,17 @@
|
||||
"Curv. Comb": "مشط الانحناء",
|
||||
"Curvature (G2)": "الانحناء (G2)",
|
||||
"Curvature change (G3)": "تغيّر الانحناء (G3)",
|
||||
"Curvature continuous (G2)": "استمرارية الانحناء (G2)",
|
||||
"Custom": "مخصص",
|
||||
"Customize Toolbar": "تخصيص شريط الأدوات",
|
||||
"Customize…": "تخصيص…",
|
||||
"Cylindrical": "أسطواني",
|
||||
"Cylindrical face": "وجه أسطواني",
|
||||
"DELETE FACE": "حذف وجه",
|
||||
"DELETE FEATURE": "حذف الميزة",
|
||||
"DRIVE JOINTS": "تحريك المفاصل",
|
||||
"Deboss": "نقش غائر",
|
||||
"Default material": "المادة الافتراضية",
|
||||
"Delete": "حذف",
|
||||
"Delete Folders and Features…": "حذف المجلدات مع ميزاتها…",
|
||||
"Delete Selected Features…": "حذف الميزات المحددة…",
|
||||
@@ -180,20 +223,26 @@
|
||||
"Dependents": "المعتمدات",
|
||||
"Describe the problem you saw": "صف المشكلة التي رأيتها",
|
||||
"Describe what happened before continuing.": "صف ما حدث قبل المتابعة.",
|
||||
"Design Properties…": "خصائص التصميم…",
|
||||
"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": "إضافة أبعاد للأشكال الجديدة",
|
||||
"Direct interaction": "تفاعل مباشر",
|
||||
"Direction": "الاتجاه",
|
||||
"Discard": "تجاهل",
|
||||
"Distance": "المسافة",
|
||||
"Distance 1": "المسافة 1",
|
||||
"Distance X": "المسافة X",
|
||||
"Distance Y": "المسافة Y",
|
||||
"Distance and Angle": "المسافة والزاوية",
|
||||
"Do not export hidden edges": "عدم تصدير الحواف المخفية",
|
||||
"Don't ask again": "عدم السؤال مرة أخرى",
|
||||
"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": "النقر المزدوج بالزر الأوسط في العرض: «الرئيسي» (العرض الرئيسي، مؤطرًا ما هو ظاهر)، أو «ملاءمة» (تأطير ما هو ظاهر مع الحفاظ على الاتجاه)، أو لا شيء",
|
||||
"Download design": "تنزيل التصميم",
|
||||
"Drag to move the boss across the model. It snaps to sketch points on the face.": "اسحب لتحريك النتوء على النموذج. يلتقط نقاط الرسم على الوجه.",
|
||||
"Drag to move the hole; click to remove it": "اسحب لتحريك الثقب؛ انقر لإزالته",
|
||||
"Drag to move the rest across the model. It snaps to sketch points on the face.": "اسحب لتحريك المسند على النموذج. يلتقط نقاط الرسم على الوجه.",
|
||||
@@ -201,7 +250,16 @@
|
||||
"Drag to point the catch in another direction.": "اسحب لتوجيه لسان الكبس في اتجاه آخر.",
|
||||
"Drag to turn the rest in 5° steps. Hold Alt or Ctrl to turn it freely.": "اسحب لتدوير المسند بخطوات 5°. اضغط Alt أو Ctrl لتدويره بحرية.",
|
||||
"Drive Joint": "تحريك المفصل",
|
||||
"Drive parameter": "تشغيل المعامل",
|
||||
"Drive suppression": "تشغيل التعطيل",
|
||||
"Duration": "المدة",
|
||||
"EDIT DIMENSION": "تحرير البُعد",
|
||||
"EDIT JOINT": "تعديل المفصل",
|
||||
"EDIT MOTION STUDY": "تحرير دراسة الحركة",
|
||||
"EDIT TEXT": "تعديل النص",
|
||||
"EMBOSS": "نقش بارز",
|
||||
"EXPLODED VIEW": "عرض مفكك",
|
||||
"EXTEND": "توسيع",
|
||||
"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": "حافة",
|
||||
@@ -215,10 +273,12 @@
|
||||
"End bulge": "انتفاخ النهاية",
|
||||
"End continuity": "استمرارية النهاية",
|
||||
"Equal Distance": "مسافة متساوية",
|
||||
"Exact": "دقيق",
|
||||
"Expand": "توسيع",
|
||||
"Expected result (optional)": "النتيجة المتوقعة (اختياري)",
|
||||
"Explode": "تفكيك",
|
||||
"Explode Text": "تفكيك النص",
|
||||
"Export 3MF…": "تصدير 3MF…",
|
||||
"Export Animation…": "تصدير الحركة…",
|
||||
"Export Bend Table…": "تصدير جدول الثني…",
|
||||
"Export CSV…": "تصدير CSV…",
|
||||
@@ -226,7 +286,12 @@
|
||||
"Export Current View as DXF…": "تصدير العرض الحالي بصيغة DXF…",
|
||||
"Export DXF": "تصدير DXF",
|
||||
"Export DXF…": "تصدير DXF…",
|
||||
"Export Design as JSON…": "تصدير التصميم كـ JSON…",
|
||||
"Export JSON…": "تصدير JSON…",
|
||||
"Export OBJ…": "تصدير OBJ…",
|
||||
"Export STEP…": "تصدير STEP…",
|
||||
"Export STL…": "تصدير STL…",
|
||||
"Export URDF…": "تصدير URDF…",
|
||||
"Export design library...": "تصدير مكتبة التصميم...",
|
||||
"Export hidden edges as dashed lines": "تصدير الحواف المخفية كخطوط متقطعة",
|
||||
"Exported by SolidWorks, which usually saves Y up": "صدّره SolidWorks، وهو يحفظ عادةً والمحور Y للأعلى",
|
||||
@@ -234,13 +299,19 @@
|
||||
"Extend to meet": "تمديد حتى الالتقاء",
|
||||
"Extents": "الامتدادات",
|
||||
"FILES": "الملفات",
|
||||
"FLANGE": "حافة مثنية",
|
||||
"FLAT": "مسطّح",
|
||||
"FLAT PATTERN": "النمط المسطح",
|
||||
"FOLD": "طي",
|
||||
"FRAME MEMBER": "عنصر هيكلي",
|
||||
"Face": "الوجه",
|
||||
"Face Appearance": "مظهر الوجه",
|
||||
"Face attachment lost": "فُقد ارتباط الوجه",
|
||||
"Face center": "مركز الوجه",
|
||||
"Faces": "الأوجه",
|
||||
"Faces / edges / vertices": "الأوجه / الحواف / الرؤوس",
|
||||
"Faces on Block": "أوجه على الكتلة",
|
||||
"Faces on Ring": "أوجه على الحلقة",
|
||||
"Faces to split": "الأوجه المراد تقسيمها",
|
||||
"Faces/Body": "الأوجه/الجسم",
|
||||
"Factor must be a positive number": "يجب أن يكون المعامل عددًا موجبًا",
|
||||
@@ -265,20 +336,30 @@
|
||||
"Flat size": "الحجم المسطّح",
|
||||
"Flat size X": "الحجم المسطّح X",
|
||||
"Flat size Y": "الحجم المسطّح Y",
|
||||
"Flipped": "معكوس",
|
||||
"Follow camera (Look At sketches)": "تتبّع الكاميرا (النظر إلى الرسومات)",
|
||||
"Follow inherited appearance": "اتباع المظهر الموروث",
|
||||
"Follow material": "اتباع المادة",
|
||||
"Follow the camera (Look At sketches)": "تتبّع الكاميرا (النظر إلى الرسومات)",
|
||||
"Font": "الخط",
|
||||
"Free Move": "تحريك حر",
|
||||
"From": "من",
|
||||
"From Bodies": "من الأجسام",
|
||||
"From Object": "من كائن",
|
||||
"From Snap": "من نقطة الالتقاط",
|
||||
"From edges": "من الحواف",
|
||||
"Full Length": "الطول الكامل",
|
||||
"Full thread depth": "سن لولبي بكامل العمق",
|
||||
"Further Bends": "ثنيات إضافية",
|
||||
"G2 corners require a constant radius and Exact fit. G2 corner construction is not supported yet.": "زوايا G2 تتطلب نصف قطر ثابتًا وملاءمة دقيقة. إنشاء زوايا G2 غير مدعوم بعد.",
|
||||
"GEAR": "ترس",
|
||||
"GROOVE": "أخدود",
|
||||
"Gap": "الفجوة",
|
||||
"Glass - Clear": "زجاج - شفاف",
|
||||
"Glass - Smoked": "زجاج - مدخن",
|
||||
"Grid": "الشبكة",
|
||||
"Groove": "أخدود",
|
||||
"HDR file changed since selection; inspect and select it again before saving": "تغيّر ملف HDR منذ اختياره؛ افحصه واختره مجددًا قبل الحفظ",
|
||||
"HISTORY": "السجل",
|
||||
"Halo under sketch curves": "هالة أسفل منحنيات الرسم",
|
||||
"Height": "الارتفاع",
|
||||
@@ -287,6 +368,7 @@
|
||||
"Hidden edges": "الحواف المخفية",
|
||||
"History layout": "تخطيط السجل",
|
||||
"History names": "أسماء السجل",
|
||||
"Hold click": "ضغط مطوّل",
|
||||
"Hole (optional)": "الثقب (اختياري)",
|
||||
"Hole Depth": "عمق الثقب",
|
||||
"Hole Diameter": "قطر الثقب",
|
||||
@@ -296,6 +378,8 @@
|
||||
"Horiz./Vert.": "أفقي/رأسي",
|
||||
"I-Beam": "كمرة I",
|
||||
"IMAGES": "الصور",
|
||||
"INSERT PART": "إدراج قطعة",
|
||||
"INSERT SVG": "إدراج SVG",
|
||||
"If you are offline or GitHub does not open, use Copy report and keep the text to submit later.": "إذا كنت غير متصل بالإنترنت أو تعذّر فتح GitHub، فاستخدم «نسخ التقرير» واحتفظ بالنص لإرساله لاحقًا.",
|
||||
"Import material into design": "استيراد المادة إلى التصميم",
|
||||
"Import…": "استيراد…",
|
||||
@@ -304,6 +388,8 @@
|
||||
"In plane": "في المستوى",
|
||||
"Included": "الزاوية المحصورة",
|
||||
"Inner Sharp": "حاد من الداخل",
|
||||
"Insert Mesh…": "إدراج شبكة…",
|
||||
"Insert STEP…": "إدراج STEP…",
|
||||
"Inside": "داخل",
|
||||
"Instant": "فوري",
|
||||
"Interface font": "خط الواجهة",
|
||||
@@ -312,6 +398,8 @@
|
||||
"Isocurve": "خطوط المعامِل",
|
||||
"Issue title": "عنوان البلاغ",
|
||||
"Items": "العناصر",
|
||||
"JOINT": "مفصل",
|
||||
"JOINT ORIGIN": "أصل المفصل",
|
||||
"JOINTS": "المفاصل",
|
||||
"Joint 1": "المفصل 1",
|
||||
"Joint 2": "المفصل 2",
|
||||
@@ -348,9 +436,15 @@
|
||||
"M5 or M5x0.5": "M5 أو M5x0.5",
|
||||
"M5, M5x0.5, 1/4-20 UNC": "M5, M5x0.5, 1/4-20 UNC",
|
||||
"MANAGE": "إدارة",
|
||||
"MANAGE PLASTIC RULES": "إدارة قواعد البلاستيك",
|
||||
"MATERIALS": "المواد",
|
||||
"MIDPLANE": "المستوى الأوسط",
|
||||
"MODEL": "النموذج",
|
||||
"MOTION": "الحركة",
|
||||
"MOTION LINK": "ربط الحركة",
|
||||
"MOTION STUDY": "دراسة الحركة",
|
||||
"MOTION TRACE": "أثر الحركة",
|
||||
"MOVE/COPY TEXT": "نقل/نسخ النص",
|
||||
"Margin": "الهامش",
|
||||
"Mass": "الكتلة",
|
||||
"Mass Center": "مركز الكتلة",
|
||||
@@ -361,6 +455,7 @@
|
||||
"Max": "الأقصى",
|
||||
"Max Thickness": "أقصى سماكة",
|
||||
"Maximum": "الحد الأقصى",
|
||||
"Mesh": "شبكة",
|
||||
"Mesh body": "جسم شبكي",
|
||||
"Mid Radius": "نصف القطر الأوسط",
|
||||
"Middle double-click": "النقر المزدوج بالزر الأوسط",
|
||||
@@ -383,6 +478,7 @@
|
||||
"Move holes by clicking or dragging": "التحريك بالنقر أو السحب",
|
||||
"Move later": "نقل إلى الأحدث",
|
||||
"Move up": "نقل للأعلى",
|
||||
"NEW COMPONENT": "مكوّن جديد",
|
||||
"Name": "الاسم",
|
||||
"Name on import": "الاسم عند الاستيراد",
|
||||
"Nearest": "الأقرب",
|
||||
@@ -400,11 +496,19 @@
|
||||
"Nominal Radius": "نصف القطر الاسمي",
|
||||
"Nominal diameter": "القطر الاسمي",
|
||||
"None": "لا شيء",
|
||||
"Not": "ليس",
|
||||
"Not available yet": "غير متاح بعد",
|
||||
"Not saved yet": "لم يُحفظ بعد",
|
||||
"Notch": "حزّ",
|
||||
"Nylon - Natural": "نايلون - طبيعي",
|
||||
"OFFSET FACE": "إزاحة وجه",
|
||||
"OFFSET SURFACE": "إزاحة سطح",
|
||||
"OUTPUT": "المخرجات",
|
||||
"Oak": "بلوط",
|
||||
"Of": "من",
|
||||
"Offset X": "الإزاحة X",
|
||||
"Offset Y": "الإزاحة Y",
|
||||
"Offset Z": "الإزاحة Z",
|
||||
"On your computer": "على جهازك",
|
||||
"Opacity": "العتامة",
|
||||
"Open GitHub": "فتح GitHub",
|
||||
@@ -427,10 +531,30 @@
|
||||
"Outer radius": "نصف القطر الخارجي",
|
||||
"Outside": "خارج",
|
||||
"Overall length": "الطول الكلي",
|
||||
"PATCH": "رقعة",
|
||||
"PERPENDICULAR PLANE": "مستوى عمودي",
|
||||
"PHYSICAL MATERIAL": "المادة الفيزيائية",
|
||||
"PICK CONTEXT": "اختيار السياق",
|
||||
"PLANE ALONG PATH": "مستوى على طول مسار",
|
||||
"PLANE THROUGH THREE POINTS": "مستوى عبر ثلاث نقاط",
|
||||
"PLANE THROUGH TWO EDGES": "مستوى عبر حافتين",
|
||||
"POINT ALONG PATH": "نقطة على طول مسار",
|
||||
"POINT AT CENTER": "نقطة في المركز",
|
||||
"POINT AT EDGE AND PLANE": "نقطة عند حافة ومستوى",
|
||||
"POINT AT VERTEX": "نقطة عند رأس",
|
||||
"POINT THROUGH THREE PLANES": "نقطة عبر ثلاثة مستويات",
|
||||
"POINT THROUGH TWO EDGES": "نقطة عبر حافتين",
|
||||
"Paint - Blue": "طلاء - أزرق",
|
||||
"Paint - Green": "طلاء - أخضر",
|
||||
"Paint - Orange": "طلاء - برتقالي",
|
||||
"Paint - Purple": "طلاء - بنفسجي",
|
||||
"Paint - Red": "طلاء - أحمر",
|
||||
"Paint - Yellow": "طلاء - أصفر",
|
||||
"Param. Graph": "مخطط المعاملات",
|
||||
"Parameter": "المعامل",
|
||||
"Part length must be greater than zero and at most 2000 mm": "يجب أن يكون طول القطعة أكبر من صفر وألا يزيد على 2000 mm",
|
||||
"Parts": "القطع",
|
||||
"Parts only": "القطع فقط",
|
||||
"Pass through": "المرور عبر",
|
||||
"Path": "المسار",
|
||||
"Pattern": "نمط",
|
||||
@@ -446,16 +570,21 @@
|
||||
"Place at the origin instead": "الوضع عند نقطة الأصل بدلًا من ذلك",
|
||||
"Planar": "مستوٍ",
|
||||
"Plastic Rules": "قواعد البلاستيك",
|
||||
"Play": "تشغيل",
|
||||
"Play the joint's motion in the view": "تشغيل حركة المفصل في العرض",
|
||||
"Playback": "التشغيل",
|
||||
"Plot": "رسم بياني",
|
||||
"Point on line": "نقطة على خط",
|
||||
"Point on plane": "نقطة على مستوى",
|
||||
"Point to Point": "من نقطة إلى نقطة",
|
||||
"Point to Position": "من نقطة إلى موضع",
|
||||
"Polycarbonate - Clear": "بولي كربونات - شفاف",
|
||||
"Position": "الموضع",
|
||||
"Position (0 to 1)": "الموضع (من 0 إلى 1)",
|
||||
"Position (G0)": "الموضع (G0)",
|
||||
"Position along the edge, 0 at the start end, 1 at the other": "الموضع على طول الحافة، 0 عند طرف البداية و1 عند الطرف الآخر",
|
||||
"Preferences › Design turns the offer back on": "التفضيلات › التصميم تعيد تفعيل هذا العرض",
|
||||
"Preferences…": "التفضيلات…",
|
||||
"Preview": "معاينة",
|
||||
"Preview Row": "معاينة الصف",
|
||||
"Preview leaves the current design unchanged.": "المعاينة لا تغيّر التصميم الحالي.",
|
||||
@@ -464,12 +593,21 @@
|
||||
"Profiles/Curves": "المقاطع/المنحنيات",
|
||||
"Quantity": "الكمية",
|
||||
"Quantity must be at least 2": "يجب ألا تقل الكمية عن 2",
|
||||
"RACK AND PINION": "جريدة مسننة وترس",
|
||||
"RECENT": "الأخيرة",
|
||||
"REFERENCE": "مرجع",
|
||||
"REFOLD": "إعادة الطي",
|
||||
"REMOVE": "إزالة",
|
||||
"RENAME": "إعادة تسمية",
|
||||
"REPLACE FACE": "استبدال وجه",
|
||||
"REST": "مسند",
|
||||
"REVERSE NORMAL": "عكس الاتجاه العمودي",
|
||||
"RIGID GROUP": "مجموعة صلبة",
|
||||
"RULED SURFACE": "سطح مسطّر",
|
||||
"RULES": "القواعد",
|
||||
"Radius": "نصف القطر",
|
||||
"Radius Fit": "ملاءمة نصف القطر",
|
||||
"Range": "النطاق",
|
||||
"Ratio": "النسبة",
|
||||
"Read-only list: shortcuts can't be changed here.": "قائمة للقراءة فقط: لا يمكن تغيير الاختصارات هنا.",
|
||||
"Reattach selected face": "إعادة ربط الوجه المحدد",
|
||||
@@ -477,6 +615,7 @@
|
||||
"Recover unsaved design?": "استعادة التصميم غير المحفوظ؟",
|
||||
"Rectangular": "مستطيلي",
|
||||
"Rectangular Tube": "أنبوب مستطيل",
|
||||
"Reduce to Fit": "تصغير للملاءمة",
|
||||
"Reference face": "الوجه المرجعي",
|
||||
"Reference plane": "المستوى المرجعي",
|
||||
"Relief Depth": "عمق التفريغ",
|
||||
@@ -500,6 +639,7 @@
|
||||
"Repair Part": "إصلاح الجزء",
|
||||
"Replace existing library file": "استبدال ملف المكتبة الموجود",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "الإبلاغ عن خطأ — راجع لقطة الشاشة وافتح مسودة GitHub",
|
||||
"Report an Issue…": "الإبلاغ عن مشكلة…",
|
||||
"Report copied. Keep this text to submit on GitHub when you are online.": "تم نسخ التقرير. احتفظ بهذا النص لإرساله على GitHub عند الاتصال بالإنترنت.",
|
||||
"Report text copied. Paste it into GitHub and submit your report there.": "نُسخ نص البلاغ. الصقه في GitHub وأرسل بلاغك هناك.",
|
||||
"Reports are public. A GitHub account is required.": "البلاغات عامة، ويلزم حساب GitHub.",
|
||||
@@ -532,16 +672,32 @@
|
||||
"Ring of tools on the right button": "حلقة أدوات على الزر الأيمن",
|
||||
"Roll Back Here": "نقل مؤشر الخط الزمني إلى هنا",
|
||||
"Roll History Marker Here": "نقل مؤشر السجل إلى هنا",
|
||||
"Rolling": "متدحرج",
|
||||
"Root radius": "نصف قطر الجذر",
|
||||
"Rotate": "تدوير",
|
||||
"Round": "دائري",
|
||||
"Round Bar": "قضيب دائري",
|
||||
"Round Tube": "أنبوب دائري",
|
||||
"Rubber - Black": "مطاط - أسود",
|
||||
"Rule": "القاعدة",
|
||||
"Rule name": "اسم القاعدة",
|
||||
"Rules": "القواعد",
|
||||
"Rules run from top to bottom. Matched drives change effective values.": "تُنفَّذ القواعد من الأعلى إلى الأسفل. المحرّكات المطابقة تغيّر القيم الفعلية.",
|
||||
"SCREW": "لولب",
|
||||
"SECTION": "المقطع",
|
||||
"SHEET METAL RULES": "قواعد الصفائح المعدنية",
|
||||
"SNAP FIT": "وصلة كبس",
|
||||
"SPLIT FACE": "تقسيم وجه",
|
||||
"STITCH": "خياطة",
|
||||
"STOCK": "خامة",
|
||||
"SURFACE EXTRUDE": "بثق سطح",
|
||||
"SURFACE LOFT": "تشكيل انتقالي لسطح",
|
||||
"SURFACE QUALITY": "جودة السطح",
|
||||
"SURFACE REVOLVE": "دوران سطح",
|
||||
"SURFACE SWEEP": "كنس سطح",
|
||||
"Same way": "الاتجاه نفسه",
|
||||
"Save As JSON…": "حفظ باسم JSON…",
|
||||
"Save As…": "حفظ باسم…",
|
||||
"Save Rule": "حفظ القاعدة",
|
||||
"Save View": "حفظ العرض",
|
||||
"Save screenshot…": "حفظ لقطة الشاشة…",
|
||||
@@ -550,6 +706,9 @@
|
||||
"Scale factor": "معامل التحجيم",
|
||||
"Scene": "المشهد",
|
||||
"Screenshot (optional)": "لقطة الشاشة (اختياري)",
|
||||
"Screenshot captured locally. Review it before attaching it on GitHub.": "تم التقاط لقطة الشاشة محليًا. راجعها قبل إرفاقها على GitHub.",
|
||||
"Scroll left": "التمرير لليسار",
|
||||
"Scroll right": "التمرير لليمين",
|
||||
"Search": "بحث",
|
||||
"Search commands": "البحث في الأوامر",
|
||||
"Search this library": "البحث في هذه المكتبة",
|
||||
@@ -581,6 +740,7 @@
|
||||
"Send your report on GitHub": "أرسل تقريرك عبر GitHub",
|
||||
"Shaded": "مظلل",
|
||||
"Shape": "الشكل",
|
||||
"Sheet Metal": "صفائح معدنية",
|
||||
"Sheet Rules": "قواعد الصفائح",
|
||||
"Show Agent Cursor": "إظهار مؤشر الوكيل",
|
||||
"Show ghost": "إظهار الطيف",
|
||||
@@ -608,6 +768,7 @@
|
||||
"Speed": "السرعة",
|
||||
"Splitting tool": "أداة التقسيم",
|
||||
"Square": "مربع",
|
||||
"Stainless Steel": "فولاذ مقاوم للصدأ",
|
||||
"Stand it up": "إيقافه قائمًا",
|
||||
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "إيقافه قائمًا: ربع دورة حول X، كميزة تحريك واحدة يمكنك التراجع عنها.",
|
||||
"Stand this assembly up?": "تعديل وضع هذا التجميع ليصبح قائمًا؟",
|
||||
@@ -616,6 +777,8 @@
|
||||
"Start bulge": "انتفاخ البداية",
|
||||
"Start continuity": "استمرارية البداية",
|
||||
"Stationary Side": "الجانب الثابت",
|
||||
"Steel": "فولاذ",
|
||||
"Steel - Brushed": "فولاذ - مصقول بالفرشاة",
|
||||
"Step": "الخطوة",
|
||||
"Steps": "الخطوات",
|
||||
"Steps to reproduce (optional)": "خطوات إعادة المشكلة (اختياري)",
|
||||
@@ -626,12 +789,19 @@
|
||||
"Style": "النمط",
|
||||
"Summary": "الملخص",
|
||||
"Suppress": "تعطيل",
|
||||
"Suppress Features": "تعطيل الميزات",
|
||||
"Suppress Selected Folders": "تعطيل المجلدات المحددة",
|
||||
"Suppressed": "مُعطّل",
|
||||
"Suppression": "التعطيل",
|
||||
"Surface": "السطح",
|
||||
"Surface or face": "سطح أو وجه",
|
||||
"Surface or faces": "سطح أو أوجه",
|
||||
"Surface, or faces to turn over": "السطح، أو الأوجه المراد قلبها",
|
||||
"Surfaces": "الأسطح",
|
||||
"Swap Ends": "تبديل الطرفين",
|
||||
"TANGENT PLANE": "مستوى مماس",
|
||||
"THICKEN": "تثخين",
|
||||
"TRANSFORM": "تحويل",
|
||||
"TRIM": "قص",
|
||||
"Tangent": "مماس",
|
||||
"Tangent (G1)": "مماس (G1)",
|
||||
@@ -639,9 +809,11 @@
|
||||
"Tangent chain": "سلسلة مماسية",
|
||||
"Tangent to Edge": "مماس للحافة",
|
||||
"Tap drill diameter": "قطر ثقب التسنين",
|
||||
"Taper to Fit": "تضييق للملاءمة",
|
||||
"Target": "الهدف",
|
||||
"Target Body": "الجسم الهدف",
|
||||
"Target Face (optional)": "الوجه الهدف (اختياري)",
|
||||
"Target edges": "الحواف المستهدفة",
|
||||
"Tear": "تمزيق",
|
||||
"Technical details": "التفاصيل التقنية",
|
||||
"The dashed sketch is a saved plane guide. Select a new face to repair the attachment.": "الرسم المتقطع دليل مستوى محفوظ. حدد وجهًا جديدًا لإصلاح الارتباط.",
|
||||
@@ -649,6 +821,7 @@
|
||||
"The direction must be an axis or a straight sketch curve that lies in the sketch plane": "يجب أن يكون الاتجاه محورًا أو منحنى رسم مستقيمًا يقع في مستوى الرسم",
|
||||
"The mirror line cannot also be mirrored": "لا يمكن عكس خط المرآة نفسه",
|
||||
"The mirror line must be a straight curve of this sketch, not an origin axis": "يجب أن يكون خط المرآة منحنى مستقيمًا من هذا الرسم، وليس أحد محاور نقطة الأصل",
|
||||
"The origin axis is perpendicular to the sketch plane, so it has no line in the plane": "محور الأصل عمودي على مستوى الرسم، لذا ليس له خط في المستوى",
|
||||
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "نسخة المؤلف المحفوظة مفقودة. حدد موضع مؤلف صالحًا قبل إعادة الربط.",
|
||||
"Then": "ثم",
|
||||
"These features will be deleted:": "ستُحذف هذه الميزات:",
|
||||
@@ -680,16 +853,21 @@
|
||||
"Top Left": "أعلى اليسار",
|
||||
"Top Middle": "أعلى الوسط",
|
||||
"Top Right": "أعلى اليمين",
|
||||
"Top level": "المستوى الأعلى",
|
||||
"Trimming tool (plane, face or curves)": "أداة القص (مستوى أو وجه أو منحنيات)",
|
||||
"Turn off section analysis before exporting the view.": "أوقف تحليل المقطع قبل تصدير العرض.",
|
||||
"Two Distances": "مسافتان",
|
||||
"Type": "النوع",
|
||||
"Type a distance or an angle to move by": "اكتب مسافة أو زاوية للتحريك بها",
|
||||
"UNFOLD": "فرد",
|
||||
"UNSTITCH": "فك الخياطة",
|
||||
"UNTRIM": "إلغاء القص",
|
||||
"Unit": "الوحدة",
|
||||
"Unsuppress": "إلغاء التعطيل",
|
||||
"Unsuppress Selected Folders": "إلغاء تعطيل المجلدات المحددة",
|
||||
"Updating the preview": "جارٍ تحديث المعاينة",
|
||||
"Use active author placement": "استخدام موضع المؤلف النشط",
|
||||
"Use axis direction only": "استخدام اتجاه المحور فقط",
|
||||
"Use fixed plane": "استخدام مستوى ثابت",
|
||||
"Use saved fixed plane": "استخدام المستوى الثابت المحفوظ",
|
||||
"Use selected face as fixed plane": "استخدام الوجه المحدد كمستوى ثابت",
|
||||
@@ -699,13 +877,16 @@
|
||||
"VIEWS": "العروض",
|
||||
"Value": "القيمة",
|
||||
"Variable Radius": "نصف قطر متغير",
|
||||
"Variable radius": "نصف قطر متغير",
|
||||
"Velocity": "السرعة المتجهة",
|
||||
"Vertices": "الرؤوس",
|
||||
"View": "عرض",
|
||||
"View navigator": "متصفح العرض",
|
||||
"Volume": "الحجم",
|
||||
"Wall": "الجدار",
|
||||
"Wall Location": "موضع الجدار",
|
||||
"Wall Thickness": "سماكة الجدار",
|
||||
"Walnut": "جوز",
|
||||
"Web thickness": "سماكة الجذع",
|
||||
"What did you do just before the problem? A short list is enough": "ماذا فعلت قبل المشكلة مباشرة؟ تكفي قائمة قصيرة",
|
||||
"What did you expect to see?": "ماذا توقعت أن ترى؟",
|
||||
@@ -740,6 +921,9 @@
|
||||
"a picked edge's body is gone": "جسم الحافة المحددة لم يعد موجودًا",
|
||||
"add a key for a joint first": "أضف أولًا مفتاحًا لمفصل",
|
||||
"ai.connected_toast": "أصبح SolidCraft متاحًا في {clients}. أعد تشغيلها لاستخدامه (التفضيلات > المساعد).",
|
||||
"ai.http.allow": "السماح باتصال HTTP غير آمن",
|
||||
"ai.http.confirm": "السماح لهذه النقطة الطرفية",
|
||||
"ai.http.warning": "سيُرسَل مفتاح API والمحادثة دون تشفير. يمكن لأي شخص لديه وصول إلى الشبكة قراءتهما أو تغييرهما. استخدم هذا الخيار فقط على شبكة تثق بها.",
|
||||
"ai.prefs.allow_control": "السماح لوكلاء الذكاء الاصطناعي بالاتصال",
|
||||
"ai.prefs.allow_control_note": "قناة محلية (MCP). لا شيء يغادر هذا الحاسوب.\n\nيفتح قناة تحكم على هذا الحاسوب فقط (127.0.0.1، المنفذ 27878 أو أول منفذ متاح بعده). على البرنامج الذي يتصل أن يقدّم أيضًا رمزًا سريًا لا يقرؤه إلا حساب المستخدم الخاص بك، ويُنشأ من جديد عند كل تشغيل. يُطبّق في المرة القادمة التي يبدأ فيها SolidCraft. عند الإيقاف: لا شيء يستمع ما لم تشغّل SolidCraft مع --control.",
|
||||
"ai.prefs.availability": "التوفر",
|
||||
@@ -751,6 +935,8 @@
|
||||
"ai.prefs.follow_camera": "تتبّع الكاميرا",
|
||||
"ai.prefs.follow_camera_off": "تتبّع الكاميرا التلقائي متوقف.",
|
||||
"ai.prefs.follow_camera_on": "تتجه الكاميرا نحو الرسومات التي ينشئها الوكيل.",
|
||||
"ai.prefs.instructions": "تعليمات مخصصة",
|
||||
"ai.prefs.instructions_note": "تُرسَل إلى الوكيل في بداية كل محادثة، بعد تعليمات SolidCraft الخاصة: طريقتك المفضلة في العمل والقواعد التي يجب الالتزام بها. يسري أي تغيير من المحادثة التالية (اتصال أو محادثة جديدة).",
|
||||
"ai.prefs.intro": "وكلاء الذكاء الاصطناعي في SolidCraft.",
|
||||
"ai.prefs.make_default": "تعيين افتراضيًا",
|
||||
"ai.prefs.no_agents": "لا يوجد وكلاء بعد. أضف وكيلًا أو حدّث القائمة للعثور على الوكلاء المثبّتين.",
|
||||
@@ -1035,6 +1221,7 @@
|
||||
"cmd.occurrence_move": "تحريك النسخة",
|
||||
"cmd.occurrence_properties": "الخصائص الفيزيائية للنسخة",
|
||||
"cmd.occurrence_reparent": "نقل إلى مكوّن",
|
||||
"cmd.panel.action": "تشغيل إجراء رأس اللوحة",
|
||||
"cmd.panel.activate": "تنشيط علامة تبويب اللوحة",
|
||||
"cmd.panel.close": "إغلاق اللوحة",
|
||||
"cmd.panel.collapse": "طي قسم اللوحة",
|
||||
@@ -1081,6 +1268,9 @@
|
||||
"cmd.render.compare.divider": "تحريك فاصل مقارنة التصيير",
|
||||
"cmd.render.result.discard": "تجاهل نتيجة التصيير",
|
||||
"cmd.render.result.select": "تحديد نتيجة التصيير",
|
||||
"cmd.render.surface.import": "استيراد خرائط السطح",
|
||||
"cmd.render.texture.list": "عرض صور السطح المضمّنة",
|
||||
"cmd.render.texture.remove": "إزالة صورة السطح المضمّنة",
|
||||
"cmd.render_aging_describe": "تقادم السطح بالطبقات",
|
||||
"cmd.render_backends": "محركات التصيير",
|
||||
"cmd.render_batch": "تصيير كل اللقطات",
|
||||
@@ -1219,6 +1409,7 @@
|
||||
"cmd.sketch_move": "تحريك/نسخ عناصر الرسم",
|
||||
"cmd.sketch_move3d": "تحريك نقطة ثلاثية الأبعاد",
|
||||
"cmd.sketch_move_point": "سحب نقطة الرسم",
|
||||
"cmd.sketch_move_points": "تحريك نقاط الرسم",
|
||||
"cmd.sketch_offset_break": "فك ارتباط الإزاحة",
|
||||
"cmd.sketch_options": "خيارات لوحة الرسم",
|
||||
"cmd.sketch_paste": "لصق عناصر الرسم",
|
||||
@@ -1324,11 +1515,13 @@
|
||||
"cmd.view_previous_tab": "علامة التبويب السابقة",
|
||||
"cmd.view_rename": "إعادة تسمية عرض مسمّى",
|
||||
"cmd.view_render_frame": "أدلة إطار التصيير",
|
||||
"cmd.view_ribbon_tabs_only": "علامات تبويب الشريط فقط",
|
||||
"cmd.view_right": "العرض الأيمن",
|
||||
"cmd.view_save": "عرض مسمّى جديد",
|
||||
"cmd.view_shots_strip": "شريط اللقطات",
|
||||
"cmd.view_spin": "تدوير مستمر",
|
||||
"cmd.view_tool": "أداة التنقل",
|
||||
"cmd.view_tool_zoom": "التكبير والتصغير بالسحب",
|
||||
"cmd.view_top": "العرض العلوي",
|
||||
"cmd.view_visual_style": "نمط العرض",
|
||||
"color.blue": "أزرق",
|
||||
@@ -1419,6 +1612,9 @@
|
||||
"document.changed_on_disk": "تغيّر {name} على القرص",
|
||||
"document.changed_on_disk_detail": "غيّر برنامج أو حاسوب آخر {name} منذ أن فتحته أو حفظته آخر مرة. الكتابة فوقه تستبدل تلك التغييرات بتغييراتك؛ وحفظ نسخة يحتفظ بالاثنين.",
|
||||
"document.copy_name": "نسخة من {name}",
|
||||
"document.newer_file": "تم الحفظ بإصدار أحدث من SolidCraft (صيغة الملف {format}، وهذا الإصدار يقرأ حتى {reads}). إذا كان هناك شيء مفقود، فحدّث البرنامج.",
|
||||
"document.newer_file_change": "تم الحفظ بإصدار أحدث من SolidCraft (صيغة الملف {format} مع `{change}`، وهو ما لا يعرفه هذا الإصدار). إذا كان هناك شيء مفقود، فحدّث البرنامج.",
|
||||
"document.newer_file_not_read": "لم يُقرأ: {what}. يبقى في الملف عند الحفظ.",
|
||||
"document.open_anyway": "افتح على أي حال",
|
||||
"document.open_elsewhere": "{name} مفتوح بالفعل",
|
||||
"document.open_elsewhere_detail": "{name} مفتوح في SolidCraft {app} على {who}، فُتح {when}. إذا حفظتما كلاكما، فسيخسر أحدكما عمله. افتحه للقراءة فقط لتطّلع عليه دون الحفظ فوقه.",
|
||||
@@ -1454,8 +1650,11 @@
|
||||
"file.open.portal_fallback.xwayland": "منتقي الملفات الذي فُتح ليس تابعًا لسطح المكتب: في XWayland ترفض البوابة النافذة الأصلية.",
|
||||
"file_load.built": "بُني حتى الآن: {built} من {total} ميزة.",
|
||||
"file_load.converting": "تحويل الجسم الصلب {done} من {total}",
|
||||
"file_load.inserted": "تم إدراج {name}",
|
||||
"file_load.inserting": "إدراج الهندسة",
|
||||
"file_load.link_item": "{name} ({done} من {total})",
|
||||
"file_load.opened": "تم فتح {name}",
|
||||
"file_load.opened_in_tab": "تم فتح {name} في علامة تبويب جديدة",
|
||||
"file_load.opening": "فتح التصميم",
|
||||
"file_load.preparing_thumbnail": "تجهيز الصورة المصغّرة...",
|
||||
"file_load.preparing_view": "تجهيز العرض...",
|
||||
@@ -1691,7 +1890,7 @@
|
||||
"help.sketch.insert_svg": "يقرأ مسارات مخطط SVG إلى رسم بالمقياس والدوران والموضع الذي تحدده. استخدمه لتحويل شعار أو محيط متجهي إلى منحنيات يمكنك بثقها.",
|
||||
"help.sketch.inspect": "يفتح تقريرًا عن الرسم المفتوح: عدد درجات الحرية المتبقية وهل هو مقيد بالكامل. استخدمه لمعرفة ما لا يزال يتحرك قبل إنهاء الرسم.",
|
||||
"help.sketch.intersect": "يضيف المنحنيات أو النقاط التي تقطع فيها أجسام أو أوجه أو حواف أو رسوم أخرى مستوى الرسم. تبقى مرتبطة وتتبع النموذج عندما يتغير.",
|
||||
"help.sketch.intersection_curve": "ينشئ منحنيات ثلاثية الأبعاد حيث يلتقي جسمان أو وجهان، مرتبطة بهما. استخدمها مسارات أو قضبان توجيه تتبع النموذج.",
|
||||
"help.sketch.intersection_curve": "ينشئ منحنيات ثلاثية الأبعاد حيث يلتقي جسمان أو وجهان أو منحنيات رسوم أخرى، مرتبطة بها. استخدمها مسارات أو قضبان توجيه تتبع النموذج.",
|
||||
"help.sketch.isoparametric_curve": "ينشئ منحنى ثلاثي الأبعاد على اتجاه U أو V للوجه عبر نقطة تختارها، مرتبطًا بالوجه. استخدمه مسارًا أو خط تقسيم يتبع السطح.",
|
||||
"help.sketch.line": "يرسم خطوطًا متصلة نقطة بنقطة، كسلسلة مفتوحة أو محيط مغلق. هو الأداة الأساسية لمعظم المقاطع.",
|
||||
"help.sketch.line.midpoint": "يرسم خطًا من منتصفه إلى أحد طرفيه، ويعكس الطرف الآخر. استخدمه للخطوط المتمركزة على نقطة، مثل حافة متناظرة.",
|
||||
@@ -1797,6 +1996,7 @@
|
||||
"help.view.navigate": "يدير العرض أو يُزيحه أو يغيّر تكبيره بخطوات، مثل ربع دورة إلى اليسار. استخدمه لتحريك الكاميرا بدقة بدلًا من السحب.",
|
||||
"help.view.navigation_bar": "يُظهر شريط التنقل أسفل منطقة العرض أو يخفيه. أخفِه لمساحة أكبر، وأظهره للوصول إلى أدوات العرض بالفأرة.",
|
||||
"help.view.rename": "يمنح عرضًا مسمّى محفوظًا اسمًا جديدًا. يبقى العرض نفسه كما هو.",
|
||||
"help.view.ribbon_tabs_only": "يطوي الشريط إلى صف علامات التبويب، فتحصل منطقة العرض على المساحة التي كانت تشغلها لوحاته. انقر علامة تبويب لإظهار لوحاتها فوق منطقة العرض؛ واختر هذا الأمر مرة أخرى لإبقاء الشريط كاملًا ظاهرًا.",
|
||||
"help.view.save": "يحفظ موضع الكاميرا الحالي كعرض مسمّى في التصميم. استخدمه للعودة إلى الزاوية نفسها لاحقًا، مثلًا للقطات الشاشة.",
|
||||
"help.view.spin": "يدوّر العرض حول النموذج باستمرار حتى النقرة أو المفتاح التالي. استخدمه لعرض النموذج من جميع الجهات.",
|
||||
"help.view.tool": "يثبّت أداة كاميرا، التدوير أو الإزاحة أو التكبير، بحيث يحرّك السحب بزر الفأرة الأيسر العرض حتى توقفها. استخدمه على لوحة اللمس أو بدون زر فأرة أوسط.",
|
||||
@@ -2042,7 +2242,7 @@
|
||||
"nav.blender.help": "السحب بالزر الأوسط يدوّر العرض، وShift مع الأوسط يُزيحه، وCtrl مع الأوسط يكبّره؛ والسحب بالأيمن لا يحرك الكاميرا",
|
||||
"nav.creo.help": "السحب بالزر الأوسط يدوّر العرض، وShift مع الأوسط يُزيحه، وCtrl مع السحب العمودي بالأوسط يكبّره أو يصغّره (السحب لأسفل للتكبير)؛ وتدوير العجلة يكبّره أو يصغّره",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.drag_keeps_mode.help": "يستمر السحب بالزر الأوسط أو الأيمن في التدوير أو التحريك أو التكبير كما بدأ حتى تحرير الزر، مهما ضُغطت المفاتيح أو حُرّرت في أثناء ذلك. عند الإيقاف: تقرر المفاتيح المضغوطة في كل لحظة من السحب.",
|
||||
"nav.drag_keeps_mode.help": "يستمر السحب بالزر الأوسط أو الأيمن في التدوير أو الإزاحة أو التكبير كما بدأ حتى تحرير الزر، مهما ضُغطت المفاتيح أو حُرّرت في أثناء ذلك. عند الإيقاف: تقرر المفاتيح المضغوطة في كل لحظة من السحب.",
|
||||
"nav.drag_keeps_mode.label": "يحتفظ السحب بطريقة التنقل التي بدأ بها",
|
||||
"nav.free_orbit.help": "الفأرة والفأرة ثلاثية الأبعاد تدوّران العرض عبر القطبين وتحافظان على اللف. يُعطَّل التقاط المحاور بـ Shift.",
|
||||
"nav.free_orbit.label": "تدوير حر",
|
||||
@@ -2163,6 +2363,7 @@
|
||||
"navbar.value_tip": "{name} · {value}",
|
||||
"navbar.visual_style": "نمط العرض",
|
||||
"navbar.zoom": "تكبير",
|
||||
"navbar.zoom_hold_tip": "التكبير والتصغير (أبقِ مفتاح الاختصار مضغوطًا)",
|
||||
"needs two joints": "يحتاج إلى مفصلين",
|
||||
"network.allow_once": "السماح مرة واحدة",
|
||||
"network.always_allow": "السماح لهذا الخادم دائمًا",
|
||||
@@ -2305,7 +2506,7 @@
|
||||
"prefs.browser_eye_left_note": "تعرض صفوف المستعرض أيقونة الإظهار/الإخفاء بجانب سهم الطي كما كانت من قبل، بدلًا من عمود عند نهاية الصفوف.",
|
||||
"prefs.browser_reveal_on_hover": "إظهار عند مرور المؤشر",
|
||||
"prefs.browser_rows_start_closed": "صفوف المستعرض تبدأ مغلقة",
|
||||
"prefs.browser_rows_start_closed_note": "تبدأ المواضع والمجلدات في المستعرض مغلقة، فيعرض التصميم الذي يضم مكونات كثيرة صفوفه العليا. يُفتح كل صف بسهمه أو بـ«توسيع الكل» أو بـ«البحث في المستعرض» الذي يفتح الصفوف المؤدية إلى الجسم فقط، ويغلق من جديد الصفوف التي فتحها البحث السابق. اختيار جسم في العرض ثلاثي الأبعاد يترك المستعرض كما هو. عند إيقافه تبدأ المجلدات مفتوحة، وتبدأ المواضع كما يحدّد خيار «المكوّنات مفتوحة من البداية»، ويفتح الاختيار في العرض ثلاثي الأبعاد المستعرضَ حتى صف الجسم، كما كان من قبل.",
|
||||
"prefs.browser_rows_start_closed_note": "تبدأ المواضع والمجلدات في المستعرض مغلقة، فيعرض التصميم الذي يضم مكوّنات كثيرة صفوفه العليا. يُفتح كل صف بسهمه أو بـ«توسيع الكل» أو بـ«البحث في المستعرض» الذي يفتح الصفوف المؤدية إلى الجسم فقط، ويغلق من جديد الصفوف التي فتحها البحث السابق. اختيار جسم في العرض ثلاثي الأبعاد يترك المستعرض كما هو. عند إيقافه تبدأ المجلدات مفتوحة، وتبدأ المواضع كما يحدّد خيار «المكوّنات مفتوحة من البداية»، ويفتح الاختيار في العرض ثلاثي الأبعاد المستعرضَ حتى صف الجسم، كما كان من قبل.",
|
||||
"prefs.browser_show_note": "«ظاهر» يُبقي المستعرض على الشاشة. «إظهار عند مرور المؤشر» يُخفيه حتى يمر المؤشر فوق مكانه أو فوق الشريط الرفيع عند حافته اليسرى، ويبقى مفتوحًا ما دام المؤشر داخله أو كانت قائمة منه مفتوحة.",
|
||||
"prefs.browser_shown": "ظاهر",
|
||||
"prefs.built_in": "مدمج",
|
||||
@@ -2316,6 +2517,7 @@
|
||||
"prefs.carousel_previous": "السابق",
|
||||
"prefs.choose_font": "اختيار ملف خط…",
|
||||
"prefs.click": "نقر",
|
||||
"prefs.command_dialog_values": "مربعات حوار الأوامر",
|
||||
"prefs.contact_shading": "تظليل التلامس الناعم",
|
||||
"prefs.contact_shading_note": "تظليل ناعم حيث تقترب القطع من بعضها.",
|
||||
"prefs.context_menu": "القائمة السياقية",
|
||||
@@ -2362,6 +2564,8 @@
|
||||
"prefs.edges": "الحواف والتظليل",
|
||||
"prefs.extend": "توسيع",
|
||||
"prefs.extra_navigation": "Ctrl مع السحب بالزر الأيسر في مساحة فارغة يدوّر العرض، واضغط مطولًا على Space للإزاحة.",
|
||||
"prefs.extrude_arrow_face_stops": "يتوقف سهم البثق عند الأوجه",
|
||||
"prefs.extrude_arrow_face_stops_note": "عند سحب سهم مسافة البثق يتوقف عند الأوجه المستوية الموازية للمقطع، مثل الجانب البعيد من كتلة يجري قطعها، ويحدد الوجه. عند الإيقاف: يتحرك السهم بخطوات مستديرة فقط.",
|
||||
"prefs.extrude_view_isometric": "التحويل إلى العرض المتساوي القياس عند فتح البثق",
|
||||
"prefs.extrude_view_return": "العودة إلى العرض السابق عند إغلاق البثق",
|
||||
"prefs.follows_os": "يتبع النظام",
|
||||
@@ -2411,13 +2615,15 @@
|
||||
"prefs.graphics_page.surfaces_edges": "الأسطح والحواف",
|
||||
"prefs.graphics_page.view_bar": "أيضًا في شريط العرض",
|
||||
"prefs.graphics_presets_note": "الإعدادات المسبقة تضبط الخيارات أدناه معًا.",
|
||||
"prefs.ground_shadow_follows_model": "ظل الأرض يتبع النموذج",
|
||||
"prefs.ground_shadow_follows_model_help": "يضع الظل أسفل أدنى جسم مرئي. عند الإيقاف، يبقى ظل النماذج المرفوعة على مستوى الأرض المار بنقطة الأصل.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "إخفاء رموز القيود عند التصغير",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out_note": "تختفي رموز القيود من العرض عندما يُصغَّر الرسم بقدر يجعلها تزحمه. معطّل: تبقى بحجم ثابت على الشاشة عند أي مستوى تكبير.",
|
||||
"prefs.history": "السجل",
|
||||
"prefs.history_origin_bodies": "إظهار الأصل والأجسام في الأعلى",
|
||||
"prefs.history_origin_bodies_note": "يبدأ السجل على اليسار أو تحت المستعرض بمجلد الأجسام للمكوّن النشط وبالأصل، مع أيقونات الإظهار والقوائم كما في المستعرض. لا يتغير السجل في الأسفل.",
|
||||
"prefs.history_play_button": "زر تشغيل السجل في عناصر تحكم السجل",
|
||||
"prefs.history_play_button_note": "يضيف تشغيل السجل بين الرجوع خطوة والتقدم خطوة في عناصر تحكم السجل. يمكنك أيضًا النقر بزر الماوس الأيمن على عناصر التحكم لإظهاره أو إخفائه.",
|
||||
"prefs.history_play_button_note": "يضيف تشغيل السجل بين الرجوع خطوة والتقدم خطوة في عناصر تحكم السجل. يمكنك أيضًا النقر بزر الفأرة الأيمن على عناصر التحكم لإظهاره أو إخفائه.",
|
||||
"prefs.history_sketches": "الرسومات في السجل",
|
||||
"prefs.history_sketches_in_order": "بالترتيب",
|
||||
"prefs.history_sketches_note": "عند ضبط الرسومات على «تحت ميزتها»، يعرض السجل على اليسار كل رسم بمستوى واحد للداخل، تحت أول ميزة بُنيت منه، متى كان عريضًا بما يكفي لإظهار الأسماء. أما الخط الزمني في الأسفل فيحتفظ دائمًا بترتيب الإنشاء.",
|
||||
@@ -2438,7 +2644,7 @@
|
||||
"prefs.invert_navigator_arrows": "عكس أسهم تدوير مكعب العرض",
|
||||
"prefs.invert_navigator_arrows_note": "تدوّر الأسهم المحيطة بالمكعب الكاميرا في الاتجاه الذي تشير إليه، كما في Fusion وInventor: السهم الموجود على اليسار يشير إلى اليمين ويدوّر الكاميرا إلى اليمين. عند الإيقاف: يدوّر السهم المكعب نحو الجهة التي يقع فيها، فيدوّر السهم الأيسر الكاميرا إلى اليسار. لا يتأثر سهما الدوران المنحنيان.",
|
||||
"prefs.keep_unsaved_also_asks": "السؤال أيضًا عند إغلاق علامة التبويب",
|
||||
"prefs.keep_unsaved_also_asks_tip": "عند إيقاف هذا الخيار، تُغلق علامة التبويب دون مربع المتصفح بعد تحديث نسخة كل تصميم غير محفوظ.",
|
||||
"prefs.keep_unsaved_also_asks_tip": "عند إيقاف هذا الخيار، تُغلق علامة التبويب دون مربع حوار المتصفح بعد تحديث نسخة كل تصميم غير محفوظ.",
|
||||
"prefs.keep_unsaved_in_browser": "الاحتفاظ بالعمل غير المحفوظ في هذا المتصفح",
|
||||
"prefs.keep_unsaved_in_browser_tip": "تُحدَّث التغييرات غير المحفوظة لكل تصميم مفتوح باستمرار في التخزين الخاص بهذا المتصفح. وتعرض الزيارة التالية استعادتها.",
|
||||
"prefs.keep_work_on_tab_switch": "إبقاء مربعات الحوار والأدوات مفتوحة عند التبديل بين التصاميم",
|
||||
@@ -2452,6 +2658,8 @@
|
||||
"prefs.layout_page.card": "بطاقة",
|
||||
"prefs.layout_page.column": "عمود",
|
||||
"prefs.layout_page.hover": "عند مرور المؤشر",
|
||||
"prefs.layout_page.hover_panel_menus": "تبديل قوائم اللوحات عند مرور المؤشر",
|
||||
"prefs.layout_page.hover_panel_menus_help": "انقر على اسم لوحة في الشريط لفتح قائمتها. مرّر المؤشر فوق اسم لوحة أخرى للتبديل. حرّك المؤشر خارج الشريط والقائمة المفتوحة لإغلاقها.",
|
||||
"prefs.layout_page.icons_labels": "الأيقونات + التسميات",
|
||||
"prefs.layout_page.ok_cancel": "موافق وإلغاء",
|
||||
"prefs.layout_page.on_hover": "عند مرور المؤشر",
|
||||
@@ -2490,6 +2698,8 @@
|
||||
"prefs.mouse": "الفأرة ولوحة اللمس",
|
||||
"prefs.mouse_heading": "الفأرة",
|
||||
"prefs.mouse_mapping": "تعيين أزرار الفأرة",
|
||||
"prefs.move_drag_highlights": "تمييز ما تحت المؤشر أثناء سحب مكوّن بأسهم أداة التحريك",
|
||||
"prefs.move_drag_highlights_tip": "عند الإيقاف: لا يُميَّز أي شيء تحت المؤشر أثناء سحب مكوّن بأسهم أداة التحريك، فتبقى التجميعات الكبيرة سلسة. مع عرض مقطع أو تحليل يبقى التمييز كما هو، ولا يُميَّز شيء أبدًا عند سحب جزء على امتداد وصلاته.",
|
||||
"prefs.navigation": "التنقل",
|
||||
"prefs.navigation.axes_signed": "المحاور",
|
||||
"prefs.navigation.cube_compact": "المكعب",
|
||||
@@ -2518,6 +2728,22 @@
|
||||
"prefs.persistent_ribbon_pressed": "تمييز أوامر الشريط النشطة",
|
||||
"prefs.persistent_ribbon_pressed_help": "تمييز أوامر الشريط أثناء تفعيل تأثيراتها. أوقف هذا الخيار للاحتفاظ بالمظهر المحايد السابق ومساعدة الأوامر.",
|
||||
"prefs.perspective_fov": "مجال الرؤية",
|
||||
"prefs.picker.carousel": "عرض دوّار",
|
||||
"prefs.picker.change": "تغيير",
|
||||
"prefs.picker.clear": "مسح عوامل التصفية",
|
||||
"prefs.picker.day": "سمة النهار",
|
||||
"prefs.picker.empty": "لا توجد سمة تطابق عوامل التصفية هذه.",
|
||||
"prefs.picker.follow": "اتباع النظام",
|
||||
"prefs.picker.grid": "شبكة",
|
||||
"prefs.picker.high": "تباين عالٍ",
|
||||
"prefs.picker.high_note": "{source} · تباين عالٍ",
|
||||
"prefs.picker.hint": "تتغير تلقائيًا مع نظام التشغيل.",
|
||||
"prefs.picker.import": "استيراد ملف سمة…",
|
||||
"prefs.picker.imported": "مستوردة",
|
||||
"prefs.picker.night": "سمة الليل",
|
||||
"prefs.picker.one": "سمة واحدة",
|
||||
"prefs.picker.pick": "اختيار من الأسفل",
|
||||
"prefs.picker.standard": "قياسي",
|
||||
"prefs.placement": "الموضع",
|
||||
"prefs.plugins_enabled": "مفعّل",
|
||||
"prefs.plugins_installed": "المثبّتة",
|
||||
@@ -2554,6 +2780,8 @@
|
||||
"prefs.quality_smoother": "أنعم",
|
||||
"prefs.quick_measure": "قياس سريع في زاوية العرض",
|
||||
"prefs.quick_measure_note": "مقاس العناصر المحددة في الزاوية اليمنى السفلية من العرض: طول الحافة (أو Ø وR للحافة الدائرية)، ومساحة الوجه، وموضع النقطة، والمسافة والزاوية بين عنصرين، ومجموع عدة حواف أو أوجه. النقر ينسخه.",
|
||||
"prefs.remember_dialog_values": "تذكّر قيم مربعات حوار الأوامر",
|
||||
"prefs.remember_dialog_values.help": "معطّل افتراضيًا. عند تفعيله، بعد نجاح الأمر، يُعاد استخدام قيمه وخياراته الآمنة في المرة التالية التي تفتح فيها الأمر نفسه، حتى إغلاق SolidCraft.",
|
||||
"prefs.render_previous_geometry_budget": "استخدام ميزانية الهندسة السابقة لمعاينة التصيير",
|
||||
"prefs.render_previous_geometry_budget_help": "استعادة حد الهندسة الثابت السابق (512 ميبيبايت على سطح المكتب 64 بت، و128 ميبيبايت على المنصات الأخرى) بدلًا من الميزانية المعتمدة على الجهاز.",
|
||||
"prefs.reports_saved": "{n} محفوظة",
|
||||
@@ -2562,6 +2790,8 @@
|
||||
"prefs.ribbon_click_ends_tool_note": "النقر على زر الشريط المضيء لأداة الرسم المستخدمة ينهي الأداة، فيعود النقر في العرض إلى التحديد. عند الإيقاف: يبدأ النقر الأداة من جديد.",
|
||||
"prefs.ribbon_reveal": "إظهار مكان نتائج البحث في الشريط",
|
||||
"prefs.ribbon_reveal_note": "عند تمرير المؤشر فوق نتيجة في «البحث في الأوامر» يظهر مكان الأمر في الشريط: تبويبه أو زره أو صفه في القائمة المنسدلة للوحة. ويومض هناك الأمر الذي بدأ من البحث، ويُعلِّم الأمر الجاري من قائمة منسدلة لوحتَه.",
|
||||
"prefs.ribbon_tabs_only": "إظهار علامات تبويب الشريط فقط",
|
||||
"prefs.ribbon_tabs_only_note": "يُطوى الشريط إلى صف علامات التبويب. يؤدي النقر على علامة تبويب إلى إظهار لوحاتها فوق منطقة العرض حتى يبدأ أمر أو يغادرها المؤشر. يبدّل الأمر «علامات تبويب الشريط فقط» في تبويب العرض، أو اختصاره، هذا الخيار.",
|
||||
"prefs.right_click": "نقر أيمن",
|
||||
"prefs.saving": "البدء والحفظ",
|
||||
"prefs.scale_note": "أو Ctrl + / Ctrl − في النافذة. الأزرار 28 px عند 100%.",
|
||||
@@ -2633,6 +2863,7 @@
|
||||
"prefs.sketch_exit_view_note": "أين ينتقل العرض بعد إنهاء الرسم أو الإلغاء أو الضغط على Esc مرتين.",
|
||||
"prefs.sketch_face_activates_part": "الرسم على وجه جزء يجعل ذلك الجزء نشطًا",
|
||||
"prefs.sketch_face_activates_part_note": "الرسم الذي يبدأ على وجه جسم في مكوّن آخر ينشّط ذلك المكوّن أولًا، فينتمي الرسم إليه ويبقى مع ذلك الجزء في المستعرض. ويبقى الجزء نشطًا بعد ذلك. عند الإيقاف: ينتمي الرسم إلى المكوّن النشط.",
|
||||
"prefs.sketch_mirror_auto_line": "استخدام الخط الإنشائي المحدد مسبقًا كمحور للانعكاس",
|
||||
"prefs.sketch_open_look_at": "توجيه العرض نحو الرسم عند فتحه",
|
||||
"prefs.sketch_open_look_at_note": "عند الإيقاف: إنشاء الرسم أو تحريره يُبقي العرض كما هو. يوجّه «النظر إلى» العرض نحو الرسم عند الحاجة.",
|
||||
"prefs.sketch_recent_snap": "الالتقاط التلقائي إلى المراكز ونقاط المنتصف الأخيرة",
|
||||
@@ -3675,7 +3906,7 @@
|
||||
"render.page_effects": "تأثيرات العدسة واللمسات النهائية",
|
||||
"render.page_environment": "البيئة",
|
||||
"render.page_lighting": "الإضاءة",
|
||||
"render.page_output": "الإخراج",
|
||||
"render.page_output": "المخرجات",
|
||||
"render.page_scene": "المشهد",
|
||||
"render.paint_label": "تلوين",
|
||||
"render.painting_with": "التلوين بـ {name}",
|
||||
@@ -3914,6 +4145,7 @@
|
||||
"render.stage_panels.extent": "الامتداد",
|
||||
"render.stage_panels.fit_save": "ملاءمة وحفظ",
|
||||
"render.stage_panels.gpu_required": "يتطلب تتبّع المسار على GPU",
|
||||
"render.stage_panels.grain_angle": "زاوية اتجاه النسيج",
|
||||
"render.stage_panels.keep": "احتفاظ",
|
||||
"render.stage_panels.kept": "ما يُحتفظ به",
|
||||
"render.stage_panels.kept_fields": "الكاميرا، وأخذ العيّنات، والأبعاد، والروابط البصرية، وهندسة CAD، والخصائص الهندسية.",
|
||||
@@ -4032,6 +4264,19 @@
|
||||
"render.subsurface_enable": "مسار عشوائي للتشتت الداخلي",
|
||||
"render.subsurface_help": "للعوازل المغلقة فقط. التشتت وامتصاص RGB معاملان منفصلان لكل mm. تتشتت المسارات داخل الهندسة الفعلية، ثم تنكسر أو تنعكس عند الحد. عطّل نفاذ الظل المستقيم وإزالة تشويش السطح والحجم الخارجي؛ واستخدم طبقة فرينل واحدة. ليس مظلل انتشار ولا شبكة كثافة ولا مادة مقيسة. الأوساط الكثيفة قد تحتاج مزيدًا من العيّنات وعمق المسار.",
|
||||
"render.subsurface_rate": "معدل اصطدامات التشتت",
|
||||
"render.surface_import.author": "المؤلف",
|
||||
"render.surface_import.busy": "جارٍ استيراد الصورة…",
|
||||
"render.surface_import.complete": "اختر صورة وأدخل مؤلفها ومصدرها وترخيصها.",
|
||||
"render.surface_import.embedded": "صورة مستوردة",
|
||||
"render.surface_import.failed": "لم يكتمل استيراد الصورة.",
|
||||
"render.surface_import.license": "الترخيص / الملكية",
|
||||
"render.surface_import.raytrace_required": "تتطلب الصور تتبّع الأشعة على CPU أو GPU. اختره في المخرجات.",
|
||||
"render.surface_import.repeat_u": "تكرار U",
|
||||
"render.surface_import.repeat_v": "تكرار V",
|
||||
"render.surface_import.save": "حفظ الصورة على الجسم",
|
||||
"render.surface_import.saved_cpu": "احفظ الخرائط لاستخدامها في التصيير على CPU.",
|
||||
"render.surface_import.source": "المصدر",
|
||||
"render.surface_import.title": "استيراد صورة",
|
||||
"render.tone_aces_fitted": "ACES مُلاءَم",
|
||||
"render.tone_reinhard": "راينهارد",
|
||||
"render.transmission_scope": "يدعم زجاج GPU الأوساط المنفصلة المعتمدة. لا تتوفر الأوساط المتداخلة غير المقيدة ولا مقدّر نفاذ خالٍ من التشويش.",
|
||||
@@ -4108,6 +4353,8 @@
|
||||
"sample.plate.name": "لوح نموذجي",
|
||||
"sample.vents.about": "شقوق في نمط مستطيلي: الصفوف والأعمدة والخطوة معاملات",
|
||||
"sample.vents.name": "لوح تهوية",
|
||||
"scale.per_axis": "لكل محور",
|
||||
"scale.uniform": "موحّد",
|
||||
"search.command_keys": "Enter تشغيل Shift+Enter تشغيل مع الإبقاء مفتوحًا Esc إغلاق",
|
||||
"search.commands": "الأوامر",
|
||||
"search.design": "في هذا التصميم",
|
||||
@@ -4780,6 +5027,7 @@
|
||||
"ui.click_selects_whole_bodies": "النقر يحدد الأجسام كاملة",
|
||||
"ui.click_then_press_key": "انقر ثم اضغط المفتاح الجديد",
|
||||
"ui.close": "إغلاق",
|
||||
"ui.close_tab": "إغلاق علامة التبويب",
|
||||
"ui.coil": "ملف حلزوني",
|
||||
"ui.coincident": "متطابق",
|
||||
"ui.collapse": "طي",
|
||||
@@ -4997,6 +5245,11 @@
|
||||
"ui.factor": "المعامل",
|
||||
"ui.fade": "تلاشٍ",
|
||||
"ui.fade_length": "طول التلاشي",
|
||||
"ui.fair_iterations": "عدد التكرارات",
|
||||
"ui.fair_keep_edges": "الحفاظ على الحواف",
|
||||
"ui.fair_strength": "شدة التنعيم",
|
||||
"ui.fair_surface_title": "تنعيم السطح",
|
||||
"ui.fair_tolerance_default": "0.001 من حجم الجسم",
|
||||
"ui.feature_history": "سجل الميزات",
|
||||
"ui.feature_options": "خيارات الميزة",
|
||||
"ui.features": "الميزات",
|
||||
@@ -5182,6 +5435,7 @@
|
||||
"ui.inspect": "فحص",
|
||||
"ui.instances": "النسخ",
|
||||
"ui.intersect": "تقاطع",
|
||||
"ui.intersection_curve_second_pick": "منحنى التقاطع: انقر على الوجه أو المنحنى الثاني (انقر بزر الفأرة الأيمن للبدء من جديد)",
|
||||
"ui.iso_4753": "ISO 4753",
|
||||
"ui.isolate": "عزل",
|
||||
"ui.join": "ضم",
|
||||
@@ -5210,6 +5464,7 @@
|
||||
"ui.manipulator_free_alt": "السحب الحر للمُعالج أيضًا مع Alt/Option",
|
||||
"ui.manipulator_free_alt_tip": "عند التفعيل: Alt/Option أو Ctrl/Cmd على أسهم البثق / أداة التحويل يعطّل التقاط القيم. عند الإيقاف: Ctrl/Cmd فقط.",
|
||||
"ui.manufacture": "التصنيع",
|
||||
"ui.match_surface_title": "مطابقة السطح",
|
||||
"ui.material_review.axes": "محاور محلية للمادة؛ لم يُحدد اتجاه للجسم.",
|
||||
"ui.material_review.claims": "المصادر والتراخيص ادعاءات غير موثّقة. القيم المفقودة تبقى مجهولة.",
|
||||
"ui.material_review.compressive_strength": "مقاومة الانضغاط",
|
||||
@@ -5232,6 +5487,9 @@
|
||||
"ui.material_review.title": "مراجعة بيانات المادة الفيزيائية",
|
||||
"ui.material_review.unknown": "مجهول",
|
||||
"ui.material_review.yield_strength": "مقاومة الخضوع",
|
||||
"ui.mcp_agents": "الوكلاء عبر MCP",
|
||||
"ui.mcp_compact": "مخرجات مضغوطة",
|
||||
"ui.mcp_compact_note": "للوكلاء المتصلين عبر خادم MCP (solidcraft-cli mcp): يلخّص inspect_design الرسومات، ولا تُنسَّق نتائج الأدوات، فتستهلك رموزًا أقل. يسري على جلسات الوكلاء التي تبدأ بعد التغيير؛ ويفعّله أيضًا --compact أو SOLIDCRAFT_MCP_COMPACT=1.",
|
||||
"ui.measure": "قياس",
|
||||
"ui.mesh_format_3mf": "3MF",
|
||||
"ui.mesh_format_3mf_tagged": "3MF مع وسوم الأوجه",
|
||||
@@ -5273,6 +5531,7 @@
|
||||
"ui.options": "الخيارات",
|
||||
"ui.orbit": "تدوير العرض",
|
||||
"ui.orbit_around": "التدوير حول",
|
||||
"ui.organize": "تنظيم",
|
||||
"ui.orientation": "الاتجاه",
|
||||
"ui.origin": "نقطة الأصل",
|
||||
"ui.palette_construction_mode": "وضع الخطوط المساعدة (X)",
|
||||
@@ -5280,6 +5539,65 @@
|
||||
"ui.palette_sketch_options": "خيارات الرسم",
|
||||
"ui.pan": "إزاحة العرض",
|
||||
"ui.pan_zoom_orbit": "إزاحة، تكبير، تدوير",
|
||||
"ui.panel_fixture.apply": "تطبيق",
|
||||
"ui.panel_fixture.cancel": "إلغاء",
|
||||
"ui.panel_fixture.caption": "نموذج لوحة تجريبي",
|
||||
"ui.panel_fixture.draft": "مسودة محفوظة",
|
||||
"ui.panel_fixture.editor": "محرر تجريبي",
|
||||
"ui.panel_fixture.explanation": "يحتفظ هذا المحتوى التجريبي بمسودته عند إخفائه أو نقله.",
|
||||
"ui.panel_fixture.fill": "نموذج ملء الارتفاع",
|
||||
"ui.panel_fixture.finish": "إنهاء",
|
||||
"ui.panel_fixture.finished": "انتهت المعاملة التجريبية",
|
||||
"ui.panel_fixture.footer": "تترك الإجراءات التجريبية التصميم دون تغيير.",
|
||||
"ui.panel_fixture.guard": "رفض الإلغاء أثناء نشاط المعاملة",
|
||||
"ui.panel_fixture.inline": "نموذج بسطر واحد",
|
||||
"ui.panel_fixture.live": "المعاملة التجريبية نشطة",
|
||||
"ui.panel_fixture.log": "سجل تجريبي",
|
||||
"ui.panel_fixture.pin": "نموذج تجريبي مثبت",
|
||||
"ui.panel_fixture.title": "{title} · {id}",
|
||||
"ui.panel_fixture.updates": "تحديثات الخدمة: {count}",
|
||||
"ui.panel_host.attention": "يحتاج إلى انتباه",
|
||||
"ui.panel_host.bottom": "أسفل",
|
||||
"ui.panel_host.close": "إغلاق اللوحة",
|
||||
"ui.panel_host.collapse": "طي اللوحة",
|
||||
"ui.panel_host.expand": "توسيع اللوحة",
|
||||
"ui.panel_host.floating": "عائم",
|
||||
"ui.panel_host.focus": "تركيز اللوحة",
|
||||
"ui.panel_host.focus_target": "التركيز على {title}",
|
||||
"ui.panel_host.header": "رأس اللوحة",
|
||||
"ui.panel_host.join": "ضم علامات التبويب",
|
||||
"ui.panel_host.join_target": "ضم علامات التبويب مع {title}",
|
||||
"ui.panel_host.left": "يسار",
|
||||
"ui.panel_host.menu": "إجراءات اللوحة",
|
||||
"ui.panel_host.move": "نقل إلى",
|
||||
"ui.panel_host.overflow": "المزيد من اللوحات",
|
||||
"ui.panel_host.popout": "فتح في نافذة",
|
||||
"ui.panel_host.resize": "تغيير حجم اللوحة",
|
||||
"ui.panel_host.right": "يمين",
|
||||
"ui.panel_host.rule.availability": "هذه اللوحة غير متاحة.",
|
||||
"ui.panel_host.rule.bounds": "لا تتسع اللوحات ضمن حدود أحجامها.",
|
||||
"ui.panel_host.rule.capability": "لا يدعم هذا المضيف هذا الإجراء.",
|
||||
"ui.panel_host.rule.collection_limit": "بلغ تخطيط اللوحات حده الأقصى.",
|
||||
"ui.panel_host.rule.duplicate_id": "هوية هذه اللوحة مستخدمة بالفعل.",
|
||||
"ui.panel_host.rule.exactly_once": "يجب أن تظهر كل لوحة مرة واحدة.",
|
||||
"ui.panel_host.rule.feature_disabled": "هذا الإجراء معطل لهذه اللوحة.",
|
||||
"ui.panel_host.rule.id": "هوية اللوحة غير صالحة.",
|
||||
"ui.panel_host.rule.incompatible_tabs": "لا يمكن لهذه اللوحات مشاركة مجموعة علامات تبويب.",
|
||||
"ui.panel_host.rule.lifecycle": "تعذر على اللوحة إكمال هذا الإجراء.",
|
||||
"ui.panel_host.rule.missing_target": "لم يعد هدف اللوحة هذا متاحا.",
|
||||
"ui.panel_host.rule.no_space": "لا توجد مساحة كافية لتخطيط اللوحات هذا.",
|
||||
"ui.panel_host.rule.pin_conflict": "هناك لوحة أخرى مثبتة بالفعل على هذه الحافة.",
|
||||
"ui.panel_host.rule.pin_order": "يجب أن تبقى اللوحة المثبتة أولا على حافتها.",
|
||||
"ui.panel_host.rule.placement": "لا يمكن نقل هذه اللوحة إلى ذلك الموضع.",
|
||||
"ui.panel_host.rule.revision": "تغير التخطيط. حاول الإجراء مجددا.",
|
||||
"ui.panel_host.rule.schema": "طلب اللوحة غير صالح.",
|
||||
"ui.panel_host.rule.version": "إصدار تخطيط اللوحات هذا غير مدعوم.",
|
||||
"ui.panel_host.sash": "تغيير حجم اللوحات المتجاورة",
|
||||
"ui.panel_host.scroll_horizontal": "تمرير أعمدة اللوحات",
|
||||
"ui.panel_host.scroll_vertical": "تمرير مجموعات اللوحات",
|
||||
"ui.panel_host.surface": "لوحة",
|
||||
"ui.panel_host.tab": "علامة تبويب لوحة",
|
||||
"ui.panel_host.tabs": "كل علامات التبويب",
|
||||
"ui.parallel": "متوازي",
|
||||
"ui.parameters": "المعاملات",
|
||||
"ui.part_size_needs_a_part": "حدد قطعة قياسية مُدرجة (برغي أو صامولة أو حلقة مسطحة أو مسمار) لتغيير مقاسها. لتكبير جسم أو تصغيره، استخدم «تحجيم».",
|
||||
@@ -5317,6 +5635,7 @@
|
||||
"ui.point_cloud_add_filter": "إضافة مرشح…",
|
||||
"ui.point_cloud_auto_range": "نطاق تلقائي",
|
||||
"ui.point_cloud_color_by": "التلوين حسب",
|
||||
"ui.point_cloud_dots": "نقاط منفردة",
|
||||
"ui.point_cloud_file_colors": "ألوان الملف",
|
||||
"ui.point_cloud_filter_by": "التصفية حسب",
|
||||
"ui.point_cloud_height_high": "أعلى ارتفاع",
|
||||
@@ -5399,7 +5718,7 @@
|
||||
"ui.reverse_horizontal_pan_tip": "التمرير الأفقي بإصبعين وعجلة الإبهام يزيحان في الاتجاه المعاكس",
|
||||
"ui.reverse_trackpad_orbit_direction": "عكس اتجاه تدوير العرض بلوحة اللمس",
|
||||
"ui.reverse_trackpad_orbit_tip": "التمرير بإصبعين مع Shift يدوّر العرض في الاتجاه المعاكس",
|
||||
"ui.reverse_vertical_pan_direction": "عكس اتجاه التحريك الرأسي",
|
||||
"ui.reverse_vertical_pan_direction": "عكس اتجاه الإزاحة الرأسية",
|
||||
"ui.reverse_vertical_pan_tip": "التمرير الرأسي بإصبعين يحرّك العرض في الاتجاه المعاكس",
|
||||
"ui.reverse_zoom_direction": "عكس اتجاه التكبير",
|
||||
"ui.revolutions": "عدد اللفات",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1621,7 +1621,7 @@
|
||||
"help.sketch.insert_svg": "Učitava putanje SVG crteža u skicu, skalirane, zakrenute i postavljene gdje zadate. Koristite ga da logotip ili vektorski obris pretvorite u krive koje možete ekstrudirati.",
|
||||
"help.sketch.inspect": "Otvara izvještaj o otvorenoj skici: koliko je stepeni slobode preostalo i je li potpuno ograničena. Koristite ga da saznate šta se još pomjera prije nego što završite skicu.",
|
||||
"help.sketch.intersect": "Dodaje krive ili tačke gdje tijela, lica, ivice ili druge skice sijeku ravan skice. Ostaju povezane i prate model kada se promijeni.",
|
||||
"help.sketch.intersection_curve": "Pravi 3D krive tamo gdje se dva tijela ili lica sastaju, povezane s njima. Koristite ih kao putanje ili vodilice koje prate model.",
|
||||
"help.sketch.intersection_curve": "Pravi 3D krive tamo gdje se dva tijela, lica ili krive drugih skica sastaju, povezane s njima. Koristite ih kao putanje ili vodilice koje prate model.",
|
||||
"help.sketch.isoparametric_curve": "Pravi 3D krivu u smjeru U ili V lica kroz tačku koju izaberete, povezanu s licem. Koristite je kao putanju ili liniju razdvajanja koja prati površ.",
|
||||
"help.sketch.line": "Crta povezane linije tačku po tačku, kao otvoreni lanac ili zatvoren obris. To je osnovni alat za većinu profila.",
|
||||
"help.sketch.line.midpoint": "Crta liniju od njene središnje tačke do jednog kraja, a drugi kraj se preslikava. Koristite je za linije centrirane na tački, npr. simetričnu ivicu.",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+354
-176
File diff suppressed because it is too large
Load Diff
@@ -1621,7 +1621,7 @@
|
||||
"help.sketch.insert_svg": "Læser stierne i en SVG-tegning ind i en skitse, skaleret, drejet og placeret, som du angiver. Brug den til at gøre et logo eller en vektorkontur til kurver, du kan ekstrudere.",
|
||||
"help.sketch.inspect": "Åbner en rapport om den åbne skitse: hvor mange frihedsgrader der er tilbage, og om den er fuldt bundet. Brug den til at finde ud af, hvad der stadig kan bevæge sig, før du afslutter skitsen.",
|
||||
"help.sketch.intersect": "Tilføjer de kurver eller punkter, hvor emner, flader, kanter eller andre skitser krydser skitseplanet. De forbliver linkede og følger modellen, når den ændres.",
|
||||
"help.sketch.intersection_curve": "Opretter 3D-kurver, hvor to emner eller flader mødes, linket til dem. Brug dem som baner eller styrekurver, der følger modellen.",
|
||||
"help.sketch.intersection_curve": "Opretter 3D-kurver, hvor to emner, flader eller kurver fra andre skitser mødes, linket til dem. Brug dem som baner eller styrekurver, der følger modellen.",
|
||||
"help.sketch.isoparametric_curve": "Opretter en 3D-kurve langs en flades U- eller V-retning gennem et punkt, du vælger, linket til fladen. Brug den som bane eller delelinje, der følger overfladen.",
|
||||
"help.sketch.line": "Tegner forbundne linjer punkt for punkt som en åben kæde eller en lukket kontur. Det er det grundlæggende værktøj til de fleste profiler.",
|
||||
"help.sketch.line.midpoint": "Tegner en linje fra dens midtpunkt ud til den ene ende, og den anden ende spejles. Brug den til linjer centreret om et punkt, f.eks. en symmetrisk kant.",
|
||||
@@ -3807,6 +3807,8 @@
|
||||
"sample.plate.name": "Eksempelplade",
|
||||
"sample.vents.about": "Slidser i et rektangulært mønster: Rækker, kolonner og deling er parametre",
|
||||
"sample.vents.name": "Ventilationsplade",
|
||||
"scale.per_axis": "Per akse",
|
||||
"scale.uniform": "Ensartet",
|
||||
"search.command_keys": "Enter kør Shift+Enter kør og hold åben Esc luk",
|
||||
"search.commands": "Kommandoer",
|
||||
"search.design": "I dette design",
|
||||
|
||||
+118
-1
@@ -628,6 +628,8 @@
|
||||
"Suppress Selected Folders": "Ausgewählte Ordner unterdrücken",
|
||||
"Suppressed": "Unterdrückt",
|
||||
"Surface": "Flächenkörper",
|
||||
"Surface or face": "Flächenkörper oder Fläche",
|
||||
"Surface or faces": "Flächenkörper oder Flächen",
|
||||
"Surface, or faces to turn over": "Fläche oder umzudrehende Flächen",
|
||||
"Surfaces": "Flächen",
|
||||
"Swap Ends": "Enden vertauschen",
|
||||
@@ -641,6 +643,7 @@
|
||||
"Target": "Ziel",
|
||||
"Target Body": "Zielkörper",
|
||||
"Target Face (optional)": "Zielfläche (optional)",
|
||||
"Target edges": "Zielkanten",
|
||||
"Tear": "Reißen",
|
||||
"Technical details": "Technische Details",
|
||||
"The dashed sketch is a saved plane guide. Select a new face to repair the attachment.": "Die gestrichelte Skizze ist eine gespeicherte Ebenenführung. Wählen Sie eine neue Fläche, um die Anbindung zu reparieren.",
|
||||
@@ -648,6 +651,7 @@
|
||||
"The direction must be an axis or a straight sketch curve that lies in the sketch plane": "Die Richtung muss eine Achse oder eine gerade Skizzenkurve in der Skizzenebene sein",
|
||||
"The mirror line cannot also be mirrored": "Die Spiegellinie kann nicht gleichzeitig gespiegelt werden",
|
||||
"The mirror line must be a straight curve of this sketch, not an origin axis": "Die Spiegellinie muss eine gerade Kurve dieser Skizze sein, keine Ursprungsachse",
|
||||
"The origin axis is perpendicular to the sketch plane, so it has no line in the plane": "Die Ursprungsachse steht senkrecht auf der Skizzenebene und hat daher keine Linie in ihr",
|
||||
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "Das gespeicherte Autorvorkommen fehlt. Wählen Sie vor dem erneuten Anbinden eine gültige Autorplatzierung.",
|
||||
"Then": "Then",
|
||||
"These features will be deleted:": "Diese Merkmale werden gelöscht:",
|
||||
@@ -689,6 +693,7 @@
|
||||
"Unsuppress Selected Folders": "Unterdrückung der ausgewählten Ordner aufheben",
|
||||
"Updating the preview": "Vorschau wird aktualisiert",
|
||||
"Use active author placement": "Aktive Autorplatzierung verwenden",
|
||||
"Use axis direction only": "Nur Achsenrichtung verwenden",
|
||||
"Use fixed plane": "Feste Ebene verwenden",
|
||||
"Use saved fixed plane": "Gespeicherte feste Ebene verwenden",
|
||||
"Use selected face as fixed plane": "Ausgewählte Fläche als feste Ebene verwenden",
|
||||
@@ -739,6 +744,9 @@
|
||||
"a picked edge's body is gone": "der Körper einer gewählten Kante existiert nicht mehr",
|
||||
"add a key for a joint first": "zuerst einen Schlüssel für ein Gelenk hinzufügen",
|
||||
"ai.connected_toast": "SolidCraft ist jetzt in {clients} verfügbar. Starten Sie diese neu, um es zu nutzen (Einstellungen > Assistent).",
|
||||
"ai.http.allow": "Unsicheres HTTP erlauben",
|
||||
"ai.http.confirm": "Für diesen Endpunkt erlauben",
|
||||
"ai.http.warning": "Der API-Schlüssel und die Unterhaltung werden unverschlüsselt gesendet. Jeder mit Zugriff auf das Netzwerk kann sie lesen oder verändern. Verwenden Sie dies nur in einem vertrauenswürdigen Netzwerk.",
|
||||
"ai.prefs.allow_control": "KI-Agenten verbinden lassen",
|
||||
"ai.prefs.allow_control_note": "Ein lokaler Kanal (MCP). Nichts verlässt diesen Computer.\n\nÖffnet einen Steuerkanal nur auf diesem Computer (127.0.0.1, Port 27878 oder der nächste freie). Ein verbindendes Programm muss zusätzlich ein geheimes Token vorweisen, das nur Ihr Benutzerkonto lesen kann und bei jedem Start neu erzeugt wird. Wirkt ab dem nächsten Start von SolidCraft. Aus: Es wartet nichts auf Verbindungen, außer Sie starten SolidCraft mit --control.",
|
||||
"ai.prefs.availability": "Verfügbarkeit",
|
||||
@@ -1028,6 +1036,7 @@
|
||||
"cmd.occurrence_move": "Instanz verschieben",
|
||||
"cmd.occurrence_properties": "Physikalische Eigenschaften der Instanz",
|
||||
"cmd.occurrence_reparent": "In andere Komponente verschieben",
|
||||
"cmd.panel.action": "Panelaktion ausführen",
|
||||
"cmd.panel.activate": "Panel-Registerkarte aktivieren",
|
||||
"cmd.panel.close": "Panel schließen",
|
||||
"cmd.panel.collapse": "Panel-Bereich einklappen",
|
||||
@@ -1074,6 +1083,9 @@
|
||||
"cmd.render.compare.divider": "Render-Vergleichstrenner verschieben",
|
||||
"cmd.render.result.discard": "Renderergebnis verwerfen",
|
||||
"cmd.render.result.select": "Renderergebnis auswählen",
|
||||
"cmd.render.surface.import": "Oberflächentexturen importieren",
|
||||
"cmd.render.texture.list": "Eingebettete Oberflächenbilder auflisten",
|
||||
"cmd.render.texture.remove": "Eingebettetes Oberflächenbild entfernen",
|
||||
"cmd.render_aging_describe": "Mehrschichtige Oberflächenalterung",
|
||||
"cmd.render_backends": "Render-Backends",
|
||||
"cmd.render_batch": "Alle Aufnahmen rendern",
|
||||
@@ -1322,6 +1334,7 @@
|
||||
"cmd.view_shots_strip": "Aufnahmenleiste",
|
||||
"cmd.view_spin": "Ansicht kontinuierlich drehen",
|
||||
"cmd.view_tool": "Navigationswerkzeug",
|
||||
"cmd.view_tool_zoom": "Durch Ziehen zoomen",
|
||||
"cmd.view_top": "Draufsicht",
|
||||
"cmd.view_visual_style": "Darstellungsstil",
|
||||
"color.blue": "Blau",
|
||||
@@ -1446,8 +1459,11 @@
|
||||
"file.open.portal_fallback.xwayland": "Die geöffnete Dateiauswahl ist nicht die des Desktops: Unter XWayland lehnt das Portal das Elternfenster ab.",
|
||||
"file_load.built": "Bisher erstellt: {built} von {total} Merkmalen.",
|
||||
"file_load.converting": "Volumenkörper {done} von {total} wird konvertiert",
|
||||
"file_load.inserted": "{name} eingefügt",
|
||||
"file_load.inserting": "Geometrie wird eingefügt",
|
||||
"file_load.link_item": "{name} ({done} von {total})",
|
||||
"file_load.opened": "{name} geöffnet",
|
||||
"file_load.opened_in_tab": "{name} in neuem Tab geöffnet",
|
||||
"file_load.opening": "Design wird geöffnet",
|
||||
"file_load.preparing_thumbnail": "Vorschaubild wird vorbereitet...",
|
||||
"file_load.preparing_view": "Ansicht wird vorbereitet...",
|
||||
@@ -1683,7 +1699,7 @@
|
||||
"help.sketch.insert_svg": "Liest die Pfade einer SVG-Zeichnung in eine Skizze ein, skaliert, gedreht und an der gewünschten Stelle. Damit machen Sie aus einem Logo oder einer Vektorkontur Kurven, die sich extrudieren lassen.",
|
||||
"help.sketch.inspect": "Öffnet einen Bericht zur offenen Skizze: wie viele Freiheitsgrade übrig sind und ob sie vollständig bestimmt ist. Damit sehen Sie, was sich noch bewegt, bevor Sie die Skizze abschließen.",
|
||||
"help.sketch.intersect": "Fügt die Kurven oder Punkte hinzu, an denen Körper, Flächen, Kanten oder andere Skizzen die Skizzenebene schneiden. Sie bleiben verknüpft und folgen Änderungen am Modell.",
|
||||
"help.sketch.intersection_curve": "Erzeugt 3D-Kurven, wo sich zwei Körper oder Flächen treffen, mit diesen verknüpft. Als Pfade oder Leitkurven, die dem Modell folgen.",
|
||||
"help.sketch.intersection_curve": "Erzeugt 3D-Kurven, wo sich zwei Körper, Flächen oder Kurven anderer Skizzen treffen, mit diesen verknüpft. Als Pfade oder Leitkurven, die dem Modell folgen.",
|
||||
"help.sketch.isoparametric_curve": "Erzeugt eine 3D-Kurve in U- oder V-Richtung einer Fläche durch einen gewählten Punkt, mit der Fläche verknüpft. Als Pfad oder Trennlinie, die der Fläche folgt.",
|
||||
"help.sketch.line": "Zeichnet verbundene Linien Punkt für Punkt, als offene Kette oder geschlossene Kontur. Das Grundwerkzeug für die meisten Profile.",
|
||||
"help.sketch.line.midpoint": "Zeichnet eine Linie von ihrem Mittelpunkt zu einem Ende, das andere Ende gespiegelt. Für Linien, die auf einem Punkt mittig liegen, etwa eine symmetrische Kante.",
|
||||
@@ -2143,6 +2159,7 @@
|
||||
"navbar.value_tip": "{name} · {value}",
|
||||
"navbar.visual_style": "Darstellung",
|
||||
"navbar.zoom": "Zoomen",
|
||||
"navbar.zoom_hold_tip": "Zoomen (Tastenkürzel gedrückt halten)",
|
||||
"needs two joints": "benötigt zwei Gelenke",
|
||||
"network.allow_once": "Einmalig erlauben",
|
||||
"network.always_allow": "Diesen Server immer erlauben",
|
||||
@@ -2293,6 +2310,7 @@
|
||||
"prefs.carousel_previous": "Zurück",
|
||||
"prefs.choose_font": "Schriftdatei auswählen…",
|
||||
"prefs.click": "Klicken",
|
||||
"prefs.command_dialog_values": "Befehlsdialoge",
|
||||
"prefs.contact_shading": "Weiche Kontaktschattierung",
|
||||
"prefs.contact_shading_note": "Erzeugt weiche Schatten an Stellen, an denen sich Bauteile nahekommen.",
|
||||
"prefs.context_menu": "Kontextmenü",
|
||||
@@ -2339,6 +2357,8 @@
|
||||
"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.extrude_arrow_face_stops": "Extrusionspfeil hält an Flächen",
|
||||
"prefs.extrude_arrow_face_stops_note": "Beim Ziehen des Abstandspfeils von Extrusion hält er an ebenen Flächen parallel zum Profil, etwa an der Rückseite eines Blocks, in den geschnitten wird, und hebt die Fläche hervor. Aus: Der Pfeil bewegt sich nur in runden Schritten.",
|
||||
"prefs.extrude_view_isometric": "Beim Öffnen von Extrudieren isometrisch ausrichten",
|
||||
"prefs.extrude_view_return": "Beim Schließen von Extrudieren zur vorigen Ansicht zurück",
|
||||
"prefs.follows_os": "Wie im Betriebssystem",
|
||||
@@ -2388,6 +2408,8 @@
|
||||
"prefs.graphics_page.surfaces_edges": "Flächen und Kanten",
|
||||
"prefs.graphics_page.view_bar": "Auch in der Ansichtsleiste",
|
||||
"prefs.graphics_presets_note": "Mit den Profilen werden die unten aufgeführten Optionen gemeinsam eingestellt.",
|
||||
"prefs.ground_shadow_follows_model": "Bodenschatten folgt dem Modell",
|
||||
"prefs.ground_shadow_follows_model_help": "Den Schatten unter dem niedrigsten sichtbaren Körper platzieren. Wenn deaktiviert, bleibt der Schatten angehobener Modelle auf der Bodenebene durch den Ursprung.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Bindungssymbole bei starkem Herauszoomen ausblenden",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out_note": "Bindungssymbole verschwinden, wenn die Skizze so weit herausgezoomt ist, dass sie sie überladen würden. Aus: sie behalten bei jedem Zoom eine feste Bildschirmgröße.",
|
||||
"prefs.history": "Verlauf",
|
||||
@@ -2423,6 +2445,8 @@
|
||||
"prefs.layout_page.card": "Karte",
|
||||
"prefs.layout_page.column": "Spalte",
|
||||
"prefs.layout_page.hover": "Bei Mauszeiger",
|
||||
"prefs.layout_page.hover_panel_menus": "Bereichsmenüs beim Darüberfahren wechseln",
|
||||
"prefs.layout_page.hover_panel_menus_help": "Klicken Sie auf einen Bereichsnamen, um das Menü zu öffnen. Bewegen Sie den Zeiger auf einen anderen Bereichsnamen, um das Menü zu wechseln. Außerhalb des Menübands und des geöffneten Menüs schließt es sich.",
|
||||
"prefs.layout_page.icons_labels": "Symbole + Beschriftungen",
|
||||
"prefs.layout_page.ok_cancel": "OK und Abbrechen",
|
||||
"prefs.layout_page.on_hover": "Bei Mauszeiger",
|
||||
@@ -2461,6 +2485,8 @@
|
||||
"prefs.mouse": "Maus und Trackpad",
|
||||
"prefs.mouse_heading": "Maus",
|
||||
"prefs.mouse_mapping": "Mausbelegung",
|
||||
"prefs.move_drag_highlights": "Hervorheben, was unter dem Zeiger liegt, während die Pfeile von Verschieben eine Komponente ziehen",
|
||||
"prefs.move_drag_highlights_tip": "Aus: Solange die Pfeile von Verschieben eine Komponente ziehen, wird nichts unter dem Zeiger hervorgehoben; so bleiben große Baugruppen flüssig. Mit einem Schnitt oder einer Analyse wird es trotzdem hervorgehoben, und ein entlang seiner Gelenke gezogenes Teil hebt nie etwas hervor.",
|
||||
"prefs.navigation": "Navigation",
|
||||
"prefs.navigation.axes_signed": "Achsen",
|
||||
"prefs.navigation.cube_compact": "Würfel",
|
||||
@@ -2525,6 +2551,8 @@
|
||||
"prefs.quality_smoother": "Stärker geglättet",
|
||||
"prefs.quick_measure": "Schnellmessung in der Ecke der Ansicht",
|
||||
"prefs.quick_measure_note": "Zeigt unten rechts in der Ansicht die Größe der Auswahl an: bei einer Kante die Länge, bei einer kreisförmigen Kante zusätzlich Durchmesser (Ø) und Radius (R), bei einer Fläche den Flächeninhalt, bei einem Punkt die Position, bei zwei Elementen Abstand und Winkel sowie bei mehreren Kanten oder Flächen die Gesamtlänge bzw. Gesamtfläche. Ein Klick kopiert den Messwert.",
|
||||
"prefs.remember_dialog_values": "Befehlsdialogwerte merken",
|
||||
"prefs.remember_dialog_values.help": "Standardmäßig aus. Wenn aktiviert, werden nach erfolgreicher Ausführung eines Befehls seine sicheren Werte und Optionen beim nächsten Öffnen desselben Befehls wiederverwendet, bis SolidCraft geschlossen wird.",
|
||||
"prefs.render_previous_geometry_budget": "Bisheriges Geometriebudget für die Render-Vorschau verwenden",
|
||||
"prefs.render_previous_geometry_budget_help": "Stellt die bisherige feste Obergrenze für die Geometrie wieder her (512 MiB auf 64-Bit-Desktop-Systemen, andernfalls 128 MiB) anstelle des geräteabhängigen Budgets.",
|
||||
"prefs.reports_saved": "{n} gespeichert",
|
||||
@@ -2601,6 +2629,7 @@
|
||||
"prefs.sketch_exit_view_note": "Legt fest, zu welcher Ansicht nach „Skizze beenden“, „Abbrechen“ oder zweimaligem Drücken der Esc-Taste gewechselt wird.",
|
||||
"prefs.sketch_face_activates_part": "Skizzieren auf der Fläche eines Teils aktiviert dieses Teil",
|
||||
"prefs.sketch_face_activates_part_note": "Eine Skizze, die auf einer Fläche am Körper einer anderen Komponente begonnen wird, aktiviert zuerst diese Komponente, sodass die Skizze zu ihr gehört und im Browser bei diesem Teil bleibt. Das Teil bleibt danach aktiv. Aus: Die Skizze gehört zur aktiven Komponente.",
|
||||
"prefs.sketch_mirror_auto_line": "Vorausgewählte Konstruktionslinie zum Spiegeln verwenden",
|
||||
"prefs.sketch_open_look_at": "Beim Öffnen einer Skizze auf sie ausrichten",
|
||||
"prefs.sketch_open_look_at_note": "Aus: Beim Erstellen oder Bearbeiten einer Skizze bleibt die Ansicht, wie sie ist. „Ansehen“ richtet die Ansicht bei Bedarf auf die Skizze aus.",
|
||||
"prefs.sketch_recent_snap": "Automatisch an zuletzt verwendete Mittelpunkte fangen",
|
||||
@@ -3876,6 +3905,7 @@
|
||||
"render.stage_panels.extent": "Ausdehnung",
|
||||
"render.stage_panels.fit_save": "Einpassen und speichern",
|
||||
"render.stage_panels.gpu_required": "Benötigt GPU-Pfadverfolgung",
|
||||
"render.stage_panels.grain_angle": "Maserungswinkel",
|
||||
"render.stage_panels.keep": "Behalten",
|
||||
"render.stage_panels.kept": "Beibehalten",
|
||||
"render.stage_panels.kept_fields": "Kamera, Abtastung, Abmessungen, optische Bindungen, CAD-Geometrie und technische Eigenschaften.",
|
||||
@@ -3994,6 +4024,19 @@
|
||||
"render.subsurface_enable": "Random Walk für innere Streuung",
|
||||
"render.subsurface_help": "Nur für geschlossene Dielektrika. Streuung und RGB-Absorption sind getrennte Koeffizienten pro mm. Pfade streuen innerhalb der tatsächlichen Geometrie und werden dann an der Grenzfläche gebrochen oder reflektiert. Deaktivieren Sie die Transmission gerader Schattenstrahlen, das Entrauschen von Oberflächen und das äußere Volumen; verwenden Sie eine Fresnel-Schicht. Kein Diffusions-Shader, kein Dichtegitter und kein gemessenes Material. Dichte Medien können mehr Samples und Pfadtiefe benötigen.",
|
||||
"render.subsurface_rate": "Streukollisionsrate",
|
||||
"render.surface_import.author": "Autor",
|
||||
"render.surface_import.busy": "Bild wird importiert…",
|
||||
"render.surface_import.complete": "Wählen Sie ein Bild und geben Sie Autor, Quelle und Lizenz an.",
|
||||
"render.surface_import.embedded": "Importiertes Bild",
|
||||
"render.surface_import.failed": "Der Bildimport wurde nicht abgeschlossen.",
|
||||
"render.surface_import.license": "Lizenz / Eigentum",
|
||||
"render.surface_import.raytrace_required": "Bilder erfordern CPU- oder GPU-Raytracing. Wählen Sie es unter Ausgabe.",
|
||||
"render.surface_import.repeat_u": "Wiederholung U",
|
||||
"render.surface_import.repeat_v": "Wiederholung V",
|
||||
"render.surface_import.save": "Bild am Körper speichern",
|
||||
"render.surface_import.saved_cpu": "Speichern Sie Texturen für das CPU-Rendering.",
|
||||
"render.surface_import.source": "Quelle",
|
||||
"render.surface_import.title": "Bild importieren",
|
||||
"render.tone_aces_fitted": "ACES angepasst",
|
||||
"render.tone_reinhard": "Reinhard",
|
||||
"render.transmission_scope": "GPU-Glas unterstützt zertifizierte getrennte Medien. Unbeschränkt verschachtelte Medien und ein rauschfreier Transmissionsschätzer werden nicht bereitgestellt.",
|
||||
@@ -4070,6 +4113,8 @@
|
||||
"sample.plate.name": "Beispielplatte",
|
||||
"sample.vents.about": "Langlöcher in einem Rechteckmuster: Zeilen, Spalten und Abstand sind Parameter",
|
||||
"sample.vents.name": "Lüftungsplatte",
|
||||
"scale.per_axis": "Je Achse",
|
||||
"scale.uniform": "Gleichmäßig",
|
||||
"search.command_keys": "Eingabe ausführen Umschalt+Eingabe ausführen und offen lassen Esc schließen",
|
||||
"search.commands": "Befehle",
|
||||
"search.design": "In diesem Design",
|
||||
@@ -4712,6 +4757,7 @@
|
||||
"ui.click_selects_whole_bodies": "Klick wählt ganze Körper aus",
|
||||
"ui.click_then_press_key": "Klicken, dann die neue Taste drücken",
|
||||
"ui.close": "Schließen",
|
||||
"ui.close_tab": "Tab schließen",
|
||||
"ui.coil": "Schraube",
|
||||
"ui.coincident": "Koinzident",
|
||||
"ui.collapse": "Einklappen",
|
||||
@@ -4927,6 +4973,11 @@
|
||||
"ui.factor": "Faktor",
|
||||
"ui.fade": "Auslauf",
|
||||
"ui.fade_length": "Auslauflänge",
|
||||
"ui.fair_iterations": "Iterationen",
|
||||
"ui.fair_keep_edges": "Kanten behalten",
|
||||
"ui.fair_strength": "Stärke",
|
||||
"ui.fair_surface_title": "FLÄCHENBEGRADIGUNG",
|
||||
"ui.fair_tolerance_default": "0,001 der Körpergröße",
|
||||
"ui.feature_history": "Feature-Verlauf",
|
||||
"ui.feature_options": "Funktionsoptionen",
|
||||
"ui.features": "Funktionen",
|
||||
@@ -5109,6 +5160,7 @@
|
||||
"ui.inspect": "Prüfen",
|
||||
"ui.instances": "Instanzen",
|
||||
"ui.intersect": "Schnitt",
|
||||
"ui.intersection_curve_second_pick": "Schnittkurve: zweite Fläche oder Kurve anklicken (Rechtsklick zum Neubeginn)",
|
||||
"ui.isolate": "Isolieren",
|
||||
"ui.join": "Verbinden",
|
||||
"ui.joint_play": "▶ Abspielen",
|
||||
@@ -5136,6 +5188,7 @@
|
||||
"ui.manipulator_free_alt": "Freies Ziehen des Manipulators auch mit Alt/Option",
|
||||
"ui.manipulator_free_alt_tip": "An: Alt/Option oder Strg/Cmd auf Extrusions-/Gizmo-Pfeilen deaktiviert das Einrasten von Werten. Aus: nur Strg/Cmd.",
|
||||
"ui.manufacture": "Fertigung",
|
||||
"ui.match_surface_title": "FLÄCHENANPASSUNG",
|
||||
"ui.material_review.axes": "Materiallokale Achsen; keine Körperausrichtung zugewiesen.",
|
||||
"ui.material_review.claims": "Quellen und Lizenzen sind ungeprüfte Angaben. Fehlende Werte bleiben unbekannt.",
|
||||
"ui.material_review.compressive_strength": "Druckfestigkeit",
|
||||
@@ -5158,6 +5211,9 @@
|
||||
"ui.material_review.title": "Physikalische Materialdaten prüfen",
|
||||
"ui.material_review.unknown": "Unbekannt",
|
||||
"ui.material_review.yield_strength": "Streckgrenze",
|
||||
"ui.mcp_agents": "Agenten über MCP",
|
||||
"ui.mcp_compact": "Kompakte Ausgabe",
|
||||
"ui.mcp_compact_note": "Für Agenten, die über den MCP-Server (solidcraft-cli mcp) verbunden sind: inspect_design fasst Skizzen zusammen und Werkzeugergebnisse werden nicht formatiert, sodass sie weniger Tokens verbrauchen. Gilt für Agentensitzungen, die nach der Änderung beginnen; --compact oder SOLIDCRAFT_MCP_COMPACT=1 schalten es ebenfalls ein.",
|
||||
"ui.measure": "Messen",
|
||||
"ui.mesh_format_3mf": "3MF",
|
||||
"ui.mesh_format_3mf_tagged": "3MF mit Flächenmarken",
|
||||
@@ -5199,6 +5255,7 @@
|
||||
"ui.options": "Optionen",
|
||||
"ui.orbit": "Drehen",
|
||||
"ui.orbit_around": "Umkreisen um",
|
||||
"ui.organize": "Organisieren",
|
||||
"ui.orientation": "Ausrichtung",
|
||||
"ui.origin": "Ursprung",
|
||||
"ui.palette_construction_mode": "Konstruktionsmodus (X)",
|
||||
@@ -5206,6 +5263,65 @@
|
||||
"ui.palette_sketch_options": "Skizzenoptionen",
|
||||
"ui.pan": "Verschieben",
|
||||
"ui.pan_zoom_orbit": "Verschieben, zoomen, umkreisen",
|
||||
"ui.panel_fixture.apply": "Anwenden",
|
||||
"ui.panel_fixture.cancel": "Abbrechen",
|
||||
"ui.panel_fixture.caption": "Synthetisches Panelmuster",
|
||||
"ui.panel_fixture.draft": "Beibehaltener Entwurf",
|
||||
"ui.panel_fixture.editor": "Mustereditor",
|
||||
"ui.panel_fixture.explanation": "Dieser synthetische Inhalt behält seinen Entwurf beim Ausblenden oder Verschieben.",
|
||||
"ui.panel_fixture.fill": "Füllendes Muster",
|
||||
"ui.panel_fixture.finish": "Abschließen",
|
||||
"ui.panel_fixture.finished": "Synthetische Transaktion beendet",
|
||||
"ui.panel_fixture.footer": "Musteraktionen lassen den Entwurf unverändert.",
|
||||
"ui.panel_fixture.guard": "Abbruch bei laufender Transaktion verweigern",
|
||||
"ui.panel_fixture.inline": "Einzeiliges Muster",
|
||||
"ui.panel_fixture.live": "Synthetische Transaktion läuft",
|
||||
"ui.panel_fixture.log": "Musterprotokoll",
|
||||
"ui.panel_fixture.pin": "Angeheftetes Muster",
|
||||
"ui.panel_fixture.title": "{title} · {id}",
|
||||
"ui.panel_fixture.updates": "Dienstaktualisierungen: {count}",
|
||||
"ui.panel_host.attention": "Aufmerksamkeit erforderlich",
|
||||
"ui.panel_host.bottom": "Unten",
|
||||
"ui.panel_host.close": "Panel schließen",
|
||||
"ui.panel_host.collapse": "Panel einklappen",
|
||||
"ui.panel_host.expand": "Panel ausklappen",
|
||||
"ui.panel_host.floating": "Schwebend",
|
||||
"ui.panel_host.focus": "Panel fokussieren",
|
||||
"ui.panel_host.focus_target": "{title} fokussieren",
|
||||
"ui.panel_host.header": "Panelkopf",
|
||||
"ui.panel_host.join": "Tabs verbinden",
|
||||
"ui.panel_host.join_target": "Tabs mit {title} verbinden",
|
||||
"ui.panel_host.left": "Links",
|
||||
"ui.panel_host.menu": "Panelaktionen",
|
||||
"ui.panel_host.move": "Verschieben nach",
|
||||
"ui.panel_host.overflow": "Weitere Panels",
|
||||
"ui.panel_host.popout": "In einem Fenster öffnen",
|
||||
"ui.panel_host.resize": "Panelgröße ändern",
|
||||
"ui.panel_host.right": "Rechts",
|
||||
"ui.panel_host.rule.availability": "Dieses Panel ist nicht verfügbar.",
|
||||
"ui.panel_host.rule.bounds": "Die Panels passen nicht in ihre Größenbegrenzungen.",
|
||||
"ui.panel_host.rule.capability": "Dieser Host unterstützt diese Aktion nicht.",
|
||||
"ui.panel_host.rule.collection_limit": "Das Panellayout hat seine Grenze erreicht.",
|
||||
"ui.panel_host.rule.duplicate_id": "Diese Panelkennung wird bereits verwendet.",
|
||||
"ui.panel_host.rule.exactly_once": "Jedes Panel muss einmal erscheinen.",
|
||||
"ui.panel_host.rule.feature_disabled": "Diese Aktion ist für dieses Panel deaktiviert.",
|
||||
"ui.panel_host.rule.id": "Die Panelkennung ist ungültig.",
|
||||
"ui.panel_host.rule.incompatible_tabs": "Diese Panels können keine Tabgruppe teilen.",
|
||||
"ui.panel_host.rule.lifecycle": "Das Panel konnte diese Aktion nicht abschließen.",
|
||||
"ui.panel_host.rule.missing_target": "Dieses Panelziel ist nicht mehr verfügbar.",
|
||||
"ui.panel_host.rule.no_space": "Für dieses Panellayout ist kein Platz.",
|
||||
"ui.panel_host.rule.pin_conflict": "An dieser Kante ist bereits ein anderes Panel angeheftet.",
|
||||
"ui.panel_host.rule.pin_order": "Ein angeheftetes Panel muss an seiner Kante zuerst bleiben.",
|
||||
"ui.panel_host.rule.placement": "Dieses Panel kann nicht an diese Position verschoben werden.",
|
||||
"ui.panel_host.rule.revision": "Das Layout wurde geändert. Versuchen Sie die Aktion erneut.",
|
||||
"ui.panel_host.rule.schema": "Die Panelanfrage ist ungültig.",
|
||||
"ui.panel_host.rule.version": "Diese Panellayoutversion wird nicht unterstützt.",
|
||||
"ui.panel_host.sash": "Benachbarte Panels skalieren",
|
||||
"ui.panel_host.scroll_horizontal": "Panelspalten scrollen",
|
||||
"ui.panel_host.scroll_vertical": "Panelstapel scrollen",
|
||||
"ui.panel_host.surface": "Panel",
|
||||
"ui.panel_host.tab": "Paneltab",
|
||||
"ui.panel_host.tabs": "Alle Tabs",
|
||||
"ui.parallel": "Parallel",
|
||||
"ui.parameters": "Parameter",
|
||||
"ui.part_size_needs_a_part": "Wählen Sie ein eingefügtes Standardbauteil (Schraube, Mutter, Scheibe oder Stift) aus, um seine Größe zu ändern. Einen Körper vergrößern oder verkleinern Sie mit Skalieren.",
|
||||
@@ -5243,6 +5359,7 @@
|
||||
"ui.point_cloud_add_filter": "Filter hinzufügen…",
|
||||
"ui.point_cloud_auto_range": "Bereich automatisch",
|
||||
"ui.point_cloud_color_by": "Färben nach",
|
||||
"ui.point_cloud_dots": "Einzelpunkte",
|
||||
"ui.point_cloud_file_colors": "Farben der Datei",
|
||||
"ui.point_cloud_filter_by": "Filtern nach",
|
||||
"ui.point_cloud_height_high": "Oberste Höhe",
|
||||
|
||||
@@ -235,6 +235,8 @@
|
||||
"render.studio_area_help": "Up to four one-sided rectangles. Orthogonal half-edges U and V define size; U × V faces the lit side. Coordinates are world-space millimetres.",
|
||||
"render.studio_center": "Centre X, Y, Z (mm)",
|
||||
"render.studio_hdr_help": "Radiance .hdr, 2:1 panorama, up to 2048 × 1024. EXR is unavailable. The colour assumption applies only when primaries are unspecified.",
|
||||
"render.surface_import.complete": "Choose an image and enter its author, source and licence.",
|
||||
"render.surface_import.license": "Licence / ownership",
|
||||
"sketch.hint.arc.center_point": "Arc: click centre and start, go round either way and click the end, or type a signed sweep",
|
||||
"sketch.hint.circle.center": "Circle: click the centre, then a point on the circle",
|
||||
"sketch.hint.constraint.midpoint": "MidPoint: pick a point and a line, or two lines to centre the second on the first",
|
||||
@@ -266,6 +268,7 @@
|
||||
"ui.layout_compact": "Compact",
|
||||
"ui.layout_floating": "Floating",
|
||||
"ui.material_review.claims": "Sources and licences are unverified claims. Missing values remain unknown.",
|
||||
"ui.organize": "Organise",
|
||||
"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",
|
||||
|
||||
+321
-2
@@ -1,4 +1,9 @@
|
||||
{
|
||||
" Keys": " Keys",
|
||||
" Maximum": " Maximum",
|
||||
" Minimum": " Minimum",
|
||||
" Of": " Of",
|
||||
" Value": " Value",
|
||||
"(active rule)": "(active rule)",
|
||||
"0 turns it off": "0 turns it off",
|
||||
"1. Open GitHub draft below, then paste the report copied to your clipboard.": "1. Open GitHub draft below, then paste the report copied to your clipboard.",
|
||||
@@ -10,7 +15,18 @@
|
||||
"A new configuration, a copy of the active one": "A new configuration, a copy of the active one",
|
||||
"A placed Single hole has a handle to drag it, and picking its face again moves the hole there instead of taking it out": "A placed Single hole has a handle to drag it, and picking its face again moves the hole there instead of taking it out",
|
||||
"A short title for the problem (optional)": "A short title for the problem (optional)",
|
||||
"ABS - Black": "ABS - Black",
|
||||
"ABS - Grey": "ABS - Grey",
|
||||
"ABS - White": "ABS - White",
|
||||
"ALIGN": "ALIGN",
|
||||
"ARRANGE": "ARRANGE",
|
||||
"AS-BUILT JOINT": "AS-BUILT JOINT",
|
||||
"ASSIGN PLASTIC RULE": "ASSIGN PLASTIC RULE",
|
||||
"AXIS PERPENDICULAR TO FACE": "AXIS PERPENDICULAR TO FACE",
|
||||
"AXIS THROUGH CYLINDER": "AXIS THROUGH CYLINDER",
|
||||
"AXIS THROUGH EDGE": "AXIS THROUGH EDGE",
|
||||
"AXIS THROUGH TWO PLANES": "AXIS THROUGH TWO PLANES",
|
||||
"AXIS THROUGH TWO POINTS": "AXIS THROUGH TWO POINTS",
|
||||
"About SolidCraft": "About SolidCraft",
|
||||
"Acceleration": "Acceleration",
|
||||
"Access": "Access",
|
||||
@@ -27,6 +43,7 @@
|
||||
"Add bend": "Add bend",
|
||||
"Add condition": "Add condition",
|
||||
"Add or edit rule": "Add or edit rule",
|
||||
"Add shortcut": "Add shortcut",
|
||||
"Add to New Folder": "Add to New Folder",
|
||||
"Add visible bodies": "Add visible bodies",
|
||||
"Advanced": "Advanced",
|
||||
@@ -37,13 +54,17 @@
|
||||
"All components": "All components",
|
||||
"All open designs": "All open designs",
|
||||
"Allowed expressions, separated by semicolons": "Allowed expressions, separated by semicolons",
|
||||
"Allowed values": "Allowed values",
|
||||
"Also deletes": "Also deletes",
|
||||
"Also place them from the origin (fully constrained)": "Also place them from the origin (fully constrained)",
|
||||
"Aluminum - Satin": "Aluminum - Satin",
|
||||
"Anchor": "Anchor",
|
||||
"Angle 2": "Angle 2",
|
||||
"Angle Display": "Angle Display",
|
||||
"Animate": "Animate",
|
||||
"Anodized Black": "Anodized Black",
|
||||
"Another bend on the far edge of the last flange": "Another bend on the far edge of the last flange",
|
||||
"Any": "Any",
|
||||
"App/system information and command settings are included. Review private details before sharing.": "App/system information and command settings are included. Review private details before sharing.",
|
||||
"Appearance name": "Appearance name",
|
||||
"Applied Radius": "Applied Radius",
|
||||
@@ -58,6 +79,8 @@
|
||||
"Assign Rule": "Assign Rule",
|
||||
"Attachment": "Attachment",
|
||||
"BLEND SURFACE": "BLEND SURFACE",
|
||||
"BOSS": "BOSS",
|
||||
"BOUNDARY FILL": "BOUNDARY FILL",
|
||||
"BOUNDARY SURFACE": "BOUNDARY SURFACE",
|
||||
"BROWSER": "BROWSER",
|
||||
"Back to the original order": "Back to the original order",
|
||||
@@ -85,13 +108,21 @@
|
||||
"Bottom Right": "Bottom Right",
|
||||
"Boundary": "Boundary",
|
||||
"Bounding box": "Bounding box",
|
||||
"Brass": "Brass",
|
||||
"Bridge": "Bridge",
|
||||
"Built-in": "Built-in",
|
||||
"By definition": "By definition",
|
||||
"By part number": "By part number",
|
||||
"CAM": "CAM",
|
||||
"CHECK": "CHECK",
|
||||
"COMPONENTS": "COMPONENTS",
|
||||
"CONFIGURATIONS": "CONFIGURATIONS",
|
||||
"CONSTRAIN": "CONSTRAIN",
|
||||
"CONTACT": "CONTACT",
|
||||
"CONTACT SET": "CONTACT SET",
|
||||
"CONVERT": "CONVERT",
|
||||
"CONVERT MESH": "CONVERT MESH",
|
||||
"CONVERT TO SHEET METAL": "CONVERT TO SHEET METAL",
|
||||
"CSV or JSON: updates existing parameters, adds new ones": "CSV or JSON: updates existing parameters, adds new ones",
|
||||
"Capture All Positions": "Capture All Positions",
|
||||
"Capture Pos.": "Capture Pos.",
|
||||
@@ -111,11 +142,15 @@
|
||||
"Choose Variable radius or Spline in Type.": "Choose Variable radius or Spline in Type.",
|
||||
"Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.": "Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.",
|
||||
"Choose…": "Choose…",
|
||||
"Chord length": "Chord length",
|
||||
"Chrome": "Chrome",
|
||||
"Circle center": "Circle center",
|
||||
"Circle or sphere": "Circle or sphere",
|
||||
"Circular": "Circular",
|
||||
"Circumference": "Circumference",
|
||||
"Clockwise": "Clockwise",
|
||||
"Close All Tabs": "Close All Tabs",
|
||||
"Close Other Tabs": "Close Other Tabs",
|
||||
"Close draft": "Close draft",
|
||||
"Collapse": "Collapse",
|
||||
"Collisions": "Collisions",
|
||||
@@ -129,6 +164,7 @@
|
||||
"Config Table": "Config Table",
|
||||
"Configuration": "Configuration",
|
||||
"Configuration rules": "Configuration rules",
|
||||
"Connect AI assistants…": "Connect AI assistants…",
|
||||
"Constant radius": "Constant radius",
|
||||
"Contact": "Contact",
|
||||
"Contact Sets": "Contact Sets",
|
||||
@@ -137,6 +173,7 @@
|
||||
"Continuity": "Continuity",
|
||||
"Control Radius": "Control Radius",
|
||||
"Copies the definition only. Assign it from Physical Material when ready.": "Copies the definition only. Assign it from Physical Material when ready.",
|
||||
"Copper": "Copper",
|
||||
"Copy Error Message": "Copy Error Message",
|
||||
"Copy Warning Message": "Copy Warning Message",
|
||||
"Copy all licences": "Copy all licenses",
|
||||
@@ -150,6 +187,7 @@
|
||||
"Counterbore Ø": "Counterbore Ø",
|
||||
"Countersink angle": "Countersink angle",
|
||||
"Countersink Ø": "Countersink Ø",
|
||||
"Crash Reports…": "Crash Reports…",
|
||||
"Create Hole": "Create Hole",
|
||||
"Create Issue — review draft": "Create Issue — review draft",
|
||||
"Create Issue…": "Create Issue…",
|
||||
@@ -162,12 +200,17 @@
|
||||
"Curv. Comb": "Curv. Comb",
|
||||
"Curvature (G2)": "Curvature (G2)",
|
||||
"Curvature change (G3)": "Curvature change (G3)",
|
||||
"Curvature continuous (G2)": "Curvature continuous (G2)",
|
||||
"Custom": "Custom",
|
||||
"Customize Toolbar": "Customize Toolbar",
|
||||
"Customize…": "Customize…",
|
||||
"Cylindrical": "Cylindrical",
|
||||
"Cylindrical face": "Cylindrical face",
|
||||
"DELETE FACE": "DELETE FACE",
|
||||
"DELETE FEATURE": "DELETE FEATURE",
|
||||
"DRIVE JOINTS": "DRIVE JOINTS",
|
||||
"Deboss": "Deboss",
|
||||
"Default material": "Default material",
|
||||
"Delete": "Delete",
|
||||
"Delete Folders and Features…": "Delete Folders and Features…",
|
||||
"Delete Selected Features…": "Delete Selected Features…",
|
||||
@@ -180,20 +223,26 @@
|
||||
"Dependents": "Dependents",
|
||||
"Describe the problem you saw": "Describe the problem you saw",
|
||||
"Describe what happened before continuing.": "Describe what happened before continuing.",
|
||||
"Design Properties…": "Design Properties…",
|
||||
"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",
|
||||
"Direct interaction": "Direct interaction",
|
||||
"Direction": "Direction",
|
||||
"Discard": "Discard",
|
||||
"Distance": "Distance",
|
||||
"Distance 1": "Distance 1",
|
||||
"Distance X": "Distance X",
|
||||
"Distance Y": "Distance Y",
|
||||
"Distance and Angle": "Distance and Angle",
|
||||
"Do not export hidden edges": "Do not export hidden edges",
|
||||
"Don't ask again": "Don't ask again",
|
||||
"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": "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",
|
||||
"Download design": "Download design",
|
||||
"Drag to move the boss across the model. It snaps to sketch points on the face.": "Drag to move the boss across the model. It snaps to sketch points on the face.",
|
||||
"Drag to move the hole; click to remove it": "Drag to move the hole; click to remove it",
|
||||
"Drag to move the rest across the model. It snaps to sketch points on the face.": "Drag to move the rest across the model. It snaps to sketch points on the face.",
|
||||
@@ -201,7 +250,16 @@
|
||||
"Drag to point the catch in another direction.": "Drag to point the catch in another direction.",
|
||||
"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",
|
||||
"Drive parameter": "Drive parameter",
|
||||
"Drive suppression": "Drive suppression",
|
||||
"Duration": "Duration",
|
||||
"EDIT DIMENSION": "EDIT DIMENSION",
|
||||
"EDIT JOINT": "EDIT JOINT",
|
||||
"EDIT MOTION STUDY": "EDIT MOTION STUDY",
|
||||
"EDIT TEXT": "EDIT TEXT",
|
||||
"EMBOSS": "EMBOSS",
|
||||
"EXPLODED VIEW": "EXPLODED VIEW",
|
||||
"EXTEND": "EXTEND",
|
||||
"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",
|
||||
@@ -215,10 +273,12 @@
|
||||
"End bulge": "End bulge",
|
||||
"End continuity": "End continuity",
|
||||
"Equal Distance": "Equal Distance",
|
||||
"Exact": "Exact",
|
||||
"Expand": "Expand",
|
||||
"Expected result (optional)": "Expected result (optional)",
|
||||
"Explode": "Explode",
|
||||
"Explode Text": "Explode Text",
|
||||
"Export 3MF…": "Export 3MF…",
|
||||
"Export Animation…": "Export Animation…",
|
||||
"Export Bend Table…": "Export Bend Table…",
|
||||
"Export CSV…": "Export CSV…",
|
||||
@@ -226,7 +286,12 @@
|
||||
"Export Current View as DXF…": "Export Current View as DXF…",
|
||||
"Export DXF": "Export DXF",
|
||||
"Export DXF…": "Export DXF…",
|
||||
"Export Design as JSON…": "Export Design as JSON…",
|
||||
"Export JSON…": "Export JSON…",
|
||||
"Export OBJ…": "Export OBJ…",
|
||||
"Export STEP…": "Export STEP…",
|
||||
"Export STL…": "Export STL…",
|
||||
"Export URDF…": "Export URDF…",
|
||||
"Export design library...": "Export design library...",
|
||||
"Export hidden edges as dashed lines": "Export hidden edges as dashed lines",
|
||||
"Exported by SolidWorks, which usually saves Y up": "Exported by SolidWorks, which usually saves Y up",
|
||||
@@ -234,13 +299,19 @@
|
||||
"Extend to meet": "Extend to meet",
|
||||
"Extents": "Extents",
|
||||
"FILES": "FILES",
|
||||
"FLANGE": "FLANGE",
|
||||
"FLAT": "FLAT",
|
||||
"FLAT PATTERN": "FLAT PATTERN",
|
||||
"FOLD": "FOLD",
|
||||
"FRAME MEMBER": "FRAME MEMBER",
|
||||
"Face": "Face",
|
||||
"Face Appearance": "Face Appearance",
|
||||
"Face attachment lost": "Face attachment lost",
|
||||
"Face center": "Face center",
|
||||
"Faces": "Faces",
|
||||
"Faces / edges / vertices": "Faces / edges / vertices",
|
||||
"Faces on Block": "Faces on Block",
|
||||
"Faces on Ring": "Faces on Ring",
|
||||
"Faces to split": "Faces to split",
|
||||
"Faces/Body": "Faces/Body",
|
||||
"Factor must be a positive number": "Factor must be a positive number",
|
||||
@@ -265,20 +336,30 @@
|
||||
"Flat size": "Flat size",
|
||||
"Flat size X": "Flat size X",
|
||||
"Flat size Y": "Flat size Y",
|
||||
"Flipped": "Flipped",
|
||||
"Follow camera (Look At sketches)": "Follow camera (Look At sketches)",
|
||||
"Follow inherited appearance": "Follow inherited appearance",
|
||||
"Follow material": "Follow material",
|
||||
"Follow the camera (Look At sketches)": "Follow the camera (Look At sketches)",
|
||||
"Font": "Font",
|
||||
"Free Move": "Free Move",
|
||||
"From": "From",
|
||||
"From Bodies": "From Bodies",
|
||||
"From Object": "From Object",
|
||||
"From Snap": "From Snap",
|
||||
"From edges": "From edges",
|
||||
"Full Length": "Full Length",
|
||||
"Full thread depth": "Full thread depth",
|
||||
"Further Bends": "Further Bends",
|
||||
"G2 corners require a constant radius and Exact fit. G2 corner construction is not supported yet.": "G2 corners require a constant radius and Exact fit. G2 corner construction is not supported yet.",
|
||||
"GEAR": "GEAR",
|
||||
"GROOVE": "GROOVE",
|
||||
"Gap": "Gap",
|
||||
"Glass - Clear": "Glass - Clear",
|
||||
"Glass - Smoked": "Glass - Smoked",
|
||||
"Grid": "Grid",
|
||||
"Groove": "Groove",
|
||||
"HDR file changed since selection; inspect and select it again before saving": "HDR file changed since selection; inspect and select it again before saving",
|
||||
"HISTORY": "HISTORY",
|
||||
"Halo under sketch curves": "Halo under sketch curves",
|
||||
"Height": "Height",
|
||||
@@ -287,6 +368,7 @@
|
||||
"Hidden edges": "Hidden edges",
|
||||
"History layout": "History layout",
|
||||
"History names": "History names",
|
||||
"Hold click": "Hold click",
|
||||
"Hole (optional)": "Hole (optional)",
|
||||
"Hole Depth": "Hole Depth",
|
||||
"Hole Diameter": "Hole Diameter",
|
||||
@@ -296,6 +378,8 @@
|
||||
"Horiz./Vert.": "Horiz./Vert.",
|
||||
"I-Beam": "I-Beam",
|
||||
"IMAGES": "IMAGES",
|
||||
"INSERT PART": "INSERT PART",
|
||||
"INSERT SVG": "INSERT SVG",
|
||||
"If you are offline or GitHub does not open, use Copy report and keep the text to submit later.": "If you are offline or GitHub does not open, use Copy report and keep the text to submit later.",
|
||||
"Import material into design": "Import material into design",
|
||||
"Import…": "Import…",
|
||||
@@ -304,6 +388,8 @@
|
||||
"In plane": "In plane",
|
||||
"Included": "Included",
|
||||
"Inner Sharp": "Inner Sharp",
|
||||
"Insert Mesh…": "Insert Mesh…",
|
||||
"Insert STEP…": "Insert STEP…",
|
||||
"Inside": "Inside",
|
||||
"Instant": "Instant",
|
||||
"Interface font": "Interface font",
|
||||
@@ -312,6 +398,8 @@
|
||||
"Isocurve": "Isocurve",
|
||||
"Issue title": "Issue title",
|
||||
"Items": "Items",
|
||||
"JOINT": "JOINT",
|
||||
"JOINT ORIGIN": "JOINT ORIGIN",
|
||||
"JOINTS": "JOINTS",
|
||||
"Joint 1": "Joint 1",
|
||||
"Joint 2": "Joint 2",
|
||||
@@ -348,9 +436,15 @@
|
||||
"M5 or M5x0.5": "M5 or M5x0.5",
|
||||
"M5, M5x0.5, 1/4-20 UNC": "M5, M5x0.5, 1/4-20 UNC",
|
||||
"MANAGE": "MANAGE",
|
||||
"MANAGE PLASTIC RULES": "MANAGE PLASTIC RULES",
|
||||
"MATERIALS": "MATERIALS",
|
||||
"MIDPLANE": "MIDPLANE",
|
||||
"MODEL": "MODEL",
|
||||
"MOTION": "MOTION",
|
||||
"MOTION LINK": "MOTION LINK",
|
||||
"MOTION STUDY": "MOTION STUDY",
|
||||
"MOTION TRACE": "MOTION TRACE",
|
||||
"MOVE/COPY TEXT": "MOVE/COPY TEXT",
|
||||
"Margin": "Margin",
|
||||
"Mass": "Mass",
|
||||
"Mass Center": "Mass Center",
|
||||
@@ -361,6 +455,7 @@
|
||||
"Max": "Max",
|
||||
"Max Thickness": "Max Thickness",
|
||||
"Maximum": "Maximum",
|
||||
"Mesh": "Mesh",
|
||||
"Mesh body": "Mesh body",
|
||||
"Mid Radius": "Mid Radius",
|
||||
"Middle double-click": "Middle double-click",
|
||||
@@ -383,6 +478,7 @@
|
||||
"Move holes by clicking or dragging": "Move by click or drag",
|
||||
"Move later": "Move later",
|
||||
"Move up": "Move up",
|
||||
"NEW COMPONENT": "NEW COMPONENT",
|
||||
"Name": "Name",
|
||||
"Name on import": "Name on import",
|
||||
"Nearest": "Nearest",
|
||||
@@ -400,11 +496,19 @@
|
||||
"Nominal Radius": "Nominal Radius",
|
||||
"Nominal diameter": "Nominal diameter",
|
||||
"None": "None",
|
||||
"Not": "Not",
|
||||
"Not available yet": "Not available yet",
|
||||
"Not saved yet": "Not saved yet",
|
||||
"Notch": "Notch",
|
||||
"Nylon - Natural": "Nylon - Natural",
|
||||
"OFFSET FACE": "OFFSET FACE",
|
||||
"OFFSET SURFACE": "OFFSET SURFACE",
|
||||
"OUTPUT": "OUTPUT",
|
||||
"Oak": "Oak",
|
||||
"Of": "Of",
|
||||
"Offset X": "Offset X",
|
||||
"Offset Y": "Offset Y",
|
||||
"Offset Z": "Offset Z",
|
||||
"On your computer": "On your computer",
|
||||
"Opacity": "Opacity",
|
||||
"Open GitHub": "Open GitHub",
|
||||
@@ -427,10 +531,30 @@
|
||||
"Outer radius": "Outer radius",
|
||||
"Outside": "Outside",
|
||||
"Overall length": "Overall length",
|
||||
"PATCH": "PATCH",
|
||||
"PERPENDICULAR PLANE": "PERPENDICULAR PLANE",
|
||||
"PHYSICAL MATERIAL": "PHYSICAL MATERIAL",
|
||||
"PICK CONTEXT": "PICK CONTEXT",
|
||||
"PLANE ALONG PATH": "PLANE ALONG PATH",
|
||||
"PLANE THROUGH THREE POINTS": "PLANE THROUGH THREE POINTS",
|
||||
"PLANE THROUGH TWO EDGES": "PLANE THROUGH TWO EDGES",
|
||||
"POINT ALONG PATH": "POINT ALONG PATH",
|
||||
"POINT AT CENTER": "POINT AT CENTER",
|
||||
"POINT AT EDGE AND PLANE": "POINT AT EDGE AND PLANE",
|
||||
"POINT AT VERTEX": "POINT AT VERTEX",
|
||||
"POINT THROUGH THREE PLANES": "POINT THROUGH THREE PLANES",
|
||||
"POINT THROUGH TWO EDGES": "POINT THROUGH TWO EDGES",
|
||||
"Paint - Blue": "Paint - Blue",
|
||||
"Paint - Green": "Paint - Green",
|
||||
"Paint - Orange": "Paint - Orange",
|
||||
"Paint - Purple": "Paint - Purple",
|
||||
"Paint - Red": "Paint - Red",
|
||||
"Paint - Yellow": "Paint - Yellow",
|
||||
"Param. Graph": "Param. Graph",
|
||||
"Parameter": "Parameter",
|
||||
"Part length must be greater than zero and at most 2000 mm": "Part length must be greater than zero and at most 2000 mm",
|
||||
"Parts": "Parts",
|
||||
"Parts only": "Parts only",
|
||||
"Pass through": "Pass through",
|
||||
"Path": "Path",
|
||||
"Pattern": "Pattern",
|
||||
@@ -446,16 +570,21 @@
|
||||
"Place at the origin instead": "Place at the origin instead",
|
||||
"Planar": "Planar",
|
||||
"Plastic Rules": "Plastic Rules",
|
||||
"Play": "Play",
|
||||
"Play the joint's motion in the view": "Play the joint's motion in the view",
|
||||
"Playback": "Playback",
|
||||
"Plot": "Plot",
|
||||
"Point on line": "Point on line",
|
||||
"Point on plane": "Point on plane",
|
||||
"Point to Point": "Point to Point",
|
||||
"Point to Position": "Point to Position",
|
||||
"Polycarbonate - Clear": "Polycarbonate - Clear",
|
||||
"Position": "Position",
|
||||
"Position (0 to 1)": "Position (0 to 1)",
|
||||
"Position (G0)": "Position (G0)",
|
||||
"Position along the edge, 0 at the start end, 1 at the other": "Position along the edge, 0 at the start end, 1 at the other",
|
||||
"Preferences › Design turns the offer back on": "Preferences › Design turns the offer back on",
|
||||
"Preferences…": "Preferences…",
|
||||
"Preview": "Preview",
|
||||
"Preview Row": "Preview Row",
|
||||
"Preview leaves the current design unchanged.": "Preview leaves the current design unchanged.",
|
||||
@@ -464,12 +593,21 @@
|
||||
"Profiles/Curves": "Profiles/Curves",
|
||||
"Quantity": "Quantity",
|
||||
"Quantity must be at least 2": "Quantity must be at least 2",
|
||||
"RACK AND PINION": "RACK AND PINION",
|
||||
"RECENT": "RECENT",
|
||||
"REFERENCE": "REFERENCE",
|
||||
"REFOLD": "REFOLD",
|
||||
"REMOVE": "REMOVE",
|
||||
"RENAME": "RENAME",
|
||||
"REPLACE FACE": "REPLACE FACE",
|
||||
"REST": "REST",
|
||||
"REVERSE NORMAL": "REVERSE NORMAL",
|
||||
"RIGID GROUP": "RIGID GROUP",
|
||||
"RULED SURFACE": "RULED SURFACE",
|
||||
"RULES": "RULES",
|
||||
"Radius": "Radius",
|
||||
"Radius Fit": "Radius Fit",
|
||||
"Range": "Range",
|
||||
"Ratio": "Ratio",
|
||||
"Read-only list: shortcuts can't be changed here.": "Read-only list: shortcuts can't be changed here.",
|
||||
"Reattach selected face": "Reattach selected face",
|
||||
@@ -477,6 +615,7 @@
|
||||
"Recover unsaved design?": "Recover unsaved design?",
|
||||
"Rectangular": "Rectangular",
|
||||
"Rectangular Tube": "Rectangular Tube",
|
||||
"Reduce to Fit": "Reduce to Fit",
|
||||
"Reference face": "Reference face",
|
||||
"Reference plane": "Reference plane",
|
||||
"Relief Depth": "Relief Depth",
|
||||
@@ -500,6 +639,7 @@
|
||||
"Repair Part": "Repair Part",
|
||||
"Replace existing library file": "Replace existing library file",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "Report a bug — review a screenshot and open a GitHub draft",
|
||||
"Report an Issue…": "Report an Issue…",
|
||||
"Report copied. Keep this text to submit on GitHub when you are online.": "Report copied. Keep this text to submit on GitHub when you are online.",
|
||||
"Report text copied. Paste it into GitHub and submit your report there.": "Report text copied. Paste it into GitHub and submit your report there.",
|
||||
"Reports are public. A GitHub account is required.": "Reports are public. A GitHub account is required.",
|
||||
@@ -532,16 +672,32 @@
|
||||
"Ring of tools on the right button": "Ring of tools on the right button",
|
||||
"Roll Back Here": "Roll Back Here",
|
||||
"Roll History Marker Here": "Roll History Marker Here",
|
||||
"Rolling": "Rolling",
|
||||
"Root radius": "Root radius",
|
||||
"Rotate": "Rotate",
|
||||
"Round": "Round",
|
||||
"Round Bar": "Round Bar",
|
||||
"Round Tube": "Round Tube",
|
||||
"Rubber - Black": "Rubber - Black",
|
||||
"Rule": "Rule",
|
||||
"Rule name": "Rule name",
|
||||
"Rules": "Rules",
|
||||
"Rules run from top to bottom. Matched drives change effective values.": "Rules run from top to bottom. Matched drives change effective values.",
|
||||
"SCREW": "SCREW",
|
||||
"SECTION": "SECTION",
|
||||
"SHEET METAL RULES": "SHEET METAL RULES",
|
||||
"SNAP FIT": "SNAP FIT",
|
||||
"SPLIT FACE": "SPLIT FACE",
|
||||
"STITCH": "STITCH",
|
||||
"STOCK": "STOCK",
|
||||
"SURFACE EXTRUDE": "SURFACE EXTRUDE",
|
||||
"SURFACE LOFT": "SURFACE LOFT",
|
||||
"SURFACE QUALITY": "SURFACE QUALITY",
|
||||
"SURFACE REVOLVE": "SURFACE REVOLVE",
|
||||
"SURFACE SWEEP": "SURFACE SWEEP",
|
||||
"Same way": "Same way",
|
||||
"Save As JSON…": "Save As JSON…",
|
||||
"Save As…": "Save As…",
|
||||
"Save Rule": "Save Rule",
|
||||
"Save View": "Save View",
|
||||
"Save screenshot…": "Save screenshot…",
|
||||
@@ -550,6 +706,9 @@
|
||||
"Scale factor": "Scale factor",
|
||||
"Scene": "Scene",
|
||||
"Screenshot (optional)": "Screenshot (optional)",
|
||||
"Screenshot captured locally. Review it before attaching it on GitHub.": "Screenshot captured locally. Review it before attaching it on GitHub.",
|
||||
"Scroll left": "Scroll left",
|
||||
"Scroll right": "Scroll right",
|
||||
"Search": "Search",
|
||||
"Search commands": "Search commands",
|
||||
"Search this library": "Search this library",
|
||||
@@ -581,6 +740,7 @@
|
||||
"Send your report on GitHub": "Send your report on GitHub",
|
||||
"Shaded": "Shaded",
|
||||
"Shape": "Shape",
|
||||
"Sheet Metal": "Sheet Metal",
|
||||
"Sheet Rules": "Sheet Rules",
|
||||
"Show Agent Cursor": "Show Agent Cursor",
|
||||
"Show ghost": "Show ghost",
|
||||
@@ -608,6 +768,7 @@
|
||||
"Speed": "Speed",
|
||||
"Splitting tool": "Splitting tool",
|
||||
"Square": "Square",
|
||||
"Stainless Steel": "Stainless Steel",
|
||||
"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?",
|
||||
@@ -616,6 +777,8 @@
|
||||
"Start bulge": "Start bulge",
|
||||
"Start continuity": "Start continuity",
|
||||
"Stationary Side": "Stationary Side",
|
||||
"Steel": "Steel",
|
||||
"Steel - Brushed": "Steel - Brushed",
|
||||
"Step": "Step",
|
||||
"Steps": "Steps",
|
||||
"Steps to reproduce (optional)": "Steps to reproduce (optional)",
|
||||
@@ -626,12 +789,19 @@
|
||||
"Style": "Style",
|
||||
"Summary": "Summary",
|
||||
"Suppress": "Suppress",
|
||||
"Suppress Features": "Suppress Features",
|
||||
"Suppress Selected Folders": "Suppress Selected Folders",
|
||||
"Suppressed": "Suppressed",
|
||||
"Suppression": "Suppression",
|
||||
"Surface": "Surface",
|
||||
"Surface or face": "Surface or face",
|
||||
"Surface or faces": "Surface or faces",
|
||||
"Surface, or faces to turn over": "Surface, or faces to turn over",
|
||||
"Surfaces": "Surfaces",
|
||||
"Swap Ends": "Swap Ends",
|
||||
"TANGENT PLANE": "TANGENT PLANE",
|
||||
"THICKEN": "THICKEN",
|
||||
"TRANSFORM": "TRANSFORM",
|
||||
"TRIM": "TRIM",
|
||||
"Tangent": "Tangent",
|
||||
"Tangent (G1)": "Tangent (G1)",
|
||||
@@ -639,9 +809,11 @@
|
||||
"Tangent chain": "Tangent chain",
|
||||
"Tangent to Edge": "Tangent to Edge",
|
||||
"Tap drill diameter": "Tap drill diameter",
|
||||
"Taper to Fit": "Taper to Fit",
|
||||
"Target": "Target",
|
||||
"Target Body": "Target Body",
|
||||
"Target Face (optional)": "Target Face (optional)",
|
||||
"Target edges": "Target edges",
|
||||
"Tear": "Tear",
|
||||
"Technical details": "Technical details",
|
||||
"The dashed sketch is a saved plane guide. Select a new face to repair the attachment.": "The dashed sketch is a saved plane guide. Select a new face to repair the attachment.",
|
||||
@@ -649,6 +821,7 @@
|
||||
"The direction must be an axis or a straight sketch curve that lies in the sketch plane": "The direction must be an axis or a straight sketch curve that lies in the sketch plane",
|
||||
"The mirror line cannot also be mirrored": "The mirror line cannot also be mirrored",
|
||||
"The mirror line must be a straight curve of this sketch, not an origin axis": "The mirror line must be a straight curve of this sketch, not an origin axis",
|
||||
"The origin axis is perpendicular to the sketch plane, so it has no line in the plane": "The origin axis is perpendicular to the sketch plane, so it has no line in the plane",
|
||||
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "The saved author occurrence is missing. Select a valid author placement before rebinding.",
|
||||
"Then": "Then",
|
||||
"These features will be deleted:": "These features will be deleted:",
|
||||
@@ -680,16 +853,21 @@
|
||||
"Top Left": "Top Left",
|
||||
"Top Middle": "Top Middle",
|
||||
"Top Right": "Top Right",
|
||||
"Top level": "Top level",
|
||||
"Trimming tool (plane, face or curves)": "Trimming tool (plane, face or curves)",
|
||||
"Turn off section analysis before exporting the view.": "Turn off section analysis before exporting the view.",
|
||||
"Two Distances": "Two Distances",
|
||||
"Type": "Type",
|
||||
"Type a distance or an angle to move by": "Type a distance or an angle to move by",
|
||||
"UNFOLD": "UNFOLD",
|
||||
"UNSTITCH": "UNSTITCH",
|
||||
"UNTRIM": "UNTRIM",
|
||||
"Unit": "Unit",
|
||||
"Unsuppress": "Unsuppress",
|
||||
"Unsuppress Selected Folders": "Unsuppress Selected Folders",
|
||||
"Updating the preview": "Updating the preview",
|
||||
"Use active author placement": "Use active author placement",
|
||||
"Use axis direction only": "Use axis direction only",
|
||||
"Use fixed plane": "Use fixed plane",
|
||||
"Use saved fixed plane": "Use saved fixed plane",
|
||||
"Use selected face as fixed plane": "Use selected face as fixed plane",
|
||||
@@ -699,13 +877,16 @@
|
||||
"VIEWS": "VIEWS",
|
||||
"Value": "Value",
|
||||
"Variable Radius": "Variable Radius",
|
||||
"Variable radius": "Variable radius",
|
||||
"Velocity": "Velocity",
|
||||
"Vertices": "Vertices",
|
||||
"View": "View",
|
||||
"View navigator": "View navigator",
|
||||
"Volume": "Volume",
|
||||
"Wall": "Wall",
|
||||
"Wall Location": "Wall Location",
|
||||
"Wall Thickness": "Wall Thickness",
|
||||
"Walnut": "Walnut",
|
||||
"Web thickness": "Web thickness",
|
||||
"What did you do just before the problem? A short list is enough": "What did you do just before the problem? A short list is enough",
|
||||
"What did you expect to see?": "What did you expect to see?",
|
||||
@@ -740,6 +921,9 @@
|
||||
"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.http.allow": "Allow insecure HTTP",
|
||||
"ai.http.confirm": "Allow for this endpoint",
|
||||
"ai.http.warning": "The API key and conversation will be sent unencrypted. Anyone with access to the network may read or change them. Use this only on a network you trust.",
|
||||
"ai.prefs.allow_control": "Let AI agents connect",
|
||||
"ai.prefs.allow_control_note": "A local channel (MCP). Nothing leaves this computer.\n\nOpens a control channel on this computer only (127.0.0.1, port 27878 or the next free one). A program that connects must also show a secret token that only your user account can read, made fresh at every start. Takes effect the next time SolidCraft starts. Off: nothing listens unless you start SolidCraft with --control.",
|
||||
"ai.prefs.availability": "Availability",
|
||||
@@ -751,6 +935,8 @@
|
||||
"ai.prefs.follow_camera": "Follow the camera",
|
||||
"ai.prefs.follow_camera_off": "Automatic camera following is off.",
|
||||
"ai.prefs.follow_camera_on": "The camera turns to face sketches the agent creates.",
|
||||
"ai.prefs.instructions": "Custom instructions",
|
||||
"ai.prefs.instructions_note": "Sent to the agent at the start of every chat, after SolidCraft's own instructions: how you like to work, rules to keep to. A change applies from the next chat (Connect or New chat).",
|
||||
"ai.prefs.intro": "AI agents in SolidCraft.",
|
||||
"ai.prefs.make_default": "Make default",
|
||||
"ai.prefs.no_agents": "No agents yet. Add an agent or refresh to find installed agents.",
|
||||
@@ -1035,6 +1221,7 @@
|
||||
"cmd.occurrence_move": "Move Occurrence",
|
||||
"cmd.occurrence_properties": "Occurrence Physical Properties",
|
||||
"cmd.occurrence_reparent": "Move to Component",
|
||||
"cmd.panel.action": "Run Panel Header Action",
|
||||
"cmd.panel.activate": "Activate Panel Tab",
|
||||
"cmd.panel.close": "Close Panel",
|
||||
"cmd.panel.collapse": "Collapse Panel Section",
|
||||
@@ -1081,6 +1268,9 @@
|
||||
"cmd.render.compare.divider": "Move Render Comparison Divider",
|
||||
"cmd.render.result.discard": "Discard Render Result",
|
||||
"cmd.render.result.select": "Select Render Result",
|
||||
"cmd.render.surface.import": "Import Surface Maps",
|
||||
"cmd.render.texture.list": "List Embedded Surface Images",
|
||||
"cmd.render.texture.remove": "Remove Embedded Surface Image",
|
||||
"cmd.render_aging_describe": "Layered Surface Aging",
|
||||
"cmd.render_backends": "Render Backends",
|
||||
"cmd.render_batch": "Render All Shots",
|
||||
@@ -1219,6 +1409,7 @@
|
||||
"cmd.sketch_move": "Move/Copy Sketch Entities",
|
||||
"cmd.sketch_move3d": "Move 3D Point",
|
||||
"cmd.sketch_move_point": "Drag Sketch Point",
|
||||
"cmd.sketch_move_points": "Move Sketch Points",
|
||||
"cmd.sketch_offset_break": "Break Offset Association",
|
||||
"cmd.sketch_options": "Sketch Palette Options",
|
||||
"cmd.sketch_paste": "Paste Sketch Entities",
|
||||
@@ -1324,11 +1515,13 @@
|
||||
"cmd.view_previous_tab": "Previous Tab",
|
||||
"cmd.view_rename": "Rename Named View",
|
||||
"cmd.view_render_frame": "Render Frame Guides",
|
||||
"cmd.view_ribbon_tabs_only": "Ribbon Tabs Only",
|
||||
"cmd.view_right": "Right View",
|
||||
"cmd.view_save": "New Named View",
|
||||
"cmd.view_shots_strip": "Shots Strip",
|
||||
"cmd.view_spin": "Continuous Orbit",
|
||||
"cmd.view_tool": "Navigation Tool",
|
||||
"cmd.view_tool_zoom": "Drag to Zoom",
|
||||
"cmd.view_top": "Top View",
|
||||
"cmd.view_visual_style": "Visual Style",
|
||||
"color.blue": "Blue",
|
||||
@@ -1419,6 +1612,9 @@
|
||||
"document.changed_on_disk": "{name} changed on disk",
|
||||
"document.changed_on_disk_detail": "{name} was changed by another program or computer since you opened or last saved it. Overwrite replaces those changes with yours; Save a Copy keeps both.",
|
||||
"document.copy_name": "{name} copy",
|
||||
"document.newer_file": "Saved by a newer SolidCraft (format {format}, this version reads up to {reads}). If something's missing, update.",
|
||||
"document.newer_file_change": "Saved by a newer SolidCraft (format {format} with `{change}`, which this version doesn't know). If something's missing, update.",
|
||||
"document.newer_file_not_read": "Not read: {what}. Saving keeps it.",
|
||||
"document.open_anyway": "Open Anyway",
|
||||
"document.open_elsewhere": "{name} is already open",
|
||||
"document.open_elsewhere_detail": "{name} is open in SolidCraft {app} on {who}, opened {when}. If you both save, one of you loses work. Open it read-only to look at it without saving over it.",
|
||||
@@ -1454,8 +1650,11 @@
|
||||
"file.open.portal_fallback.xwayland": "The file chooser that opened is not the desktop's own: under XWayland the portal refuses the parent window.",
|
||||
"file_load.built": "Built so far: {built} of {total} features.",
|
||||
"file_load.converting": "Converting solid {done} of {total}",
|
||||
"file_load.inserted": "Inserted {name}",
|
||||
"file_load.inserting": "Inserting geometry",
|
||||
"file_load.link_item": "{name} ({done} of {total})",
|
||||
"file_load.opened": "Opened {name}",
|
||||
"file_load.opened_in_tab": "Opened {name} in a new tab",
|
||||
"file_load.opening": "Opening design",
|
||||
"file_load.preparing_thumbnail": "Preparing the thumbnail...",
|
||||
"file_load.preparing_view": "Preparing the view...",
|
||||
@@ -1507,7 +1706,7 @@
|
||||
"help.construct.axis.two_planes": "Creates a construction axis where two planes or flat faces meet. Use it when the axis you need is the line between two faces.",
|
||||
"help.construct.axis.two_points": "Creates a construction axis through two points, vertices or sketch points. Use it for an axis that runs between two known positions.",
|
||||
"help.construct.plane.along_path": "Creates a construction plane square to an edge or curve, at a position along it. Use it to sketch a profile for a sweep or a section of a path.",
|
||||
"help.construct.plane.at_angle": "Creates a construction plane turned by an angle from a plane or face, about an axis or edge. Use it for sloped sketches, such as an angled bracket.",
|
||||
"help.construct.plane.at_angle": "Creates a construction plane turned by an angle from a plane or face, about an axis or edge. Turn on Use axis direction only to project the axis onto the base and pivot about a point on that plane (the base origin when none is picked). Use it for sloped sketches, such as an angled bracket.",
|
||||
"help.construct.plane.axis_point": "Creates a construction plane containing an axis and a point off that axis. Pick an origin or construction axis, a straight edge or sketch line, then a vertex, sketch point or construction point. A point on the axis cannot define a unique plane.",
|
||||
"help.construct.plane.midplane": "Creates a construction plane halfway between two planes or flat faces, or bisecting them where they meet at an angle. Use it as a symmetry plane for mirroring.",
|
||||
"help.construct.plane.offset": "Creates a construction plane parallel to a plane or flat face, at a distance or through a point. Use it to sketch at a set height above a face.",
|
||||
@@ -1691,7 +1890,7 @@
|
||||
"help.sketch.insert_svg": "Reads the paths of an SVG drawing into a sketch, scaled, turned and placed where you set. Use it to turn a logo or vector outline into curves you can extrude.",
|
||||
"help.sketch.inspect": "Opens a report on the open sketch: how many degrees of freedom are left and whether it is fully constrained. Use it to find out what still moves before you finish the sketch.",
|
||||
"help.sketch.intersect": "Adds the curves or points where bodies, faces, edges or other sketches cross the sketch plane. They stay linked and follow the model when it changes.",
|
||||
"help.sketch.intersection_curve": "Creates 3D curves where two bodies or faces meet, linked to them. Use them as paths or guide rails that follow the model.",
|
||||
"help.sketch.intersection_curve": "Creates 3D curves where two bodies, faces or curves of other sketches meet, linked to them. Use them as paths or guide rails that follow the model.",
|
||||
"help.sketch.isoparametric_curve": "Creates a 3D curve along a face's U or V direction through a point you pick, linked to the face. Use it as a path or split line that follows the surface.",
|
||||
"help.sketch.line": "Draws connected lines point by point, as an open chain or a closed outline. It is the basic tool for most profiles.",
|
||||
"help.sketch.line.midpoint": "Draws a line from its midpoint out to one end, with the other end mirrored. Use it for lines centred on a point, such as a symmetric edge.",
|
||||
@@ -1797,6 +1996,7 @@
|
||||
"help.view.navigate": "Turns, pans or zooms the view by steps, such as a quarter turn left. Use it to move the camera precisely instead of dragging.",
|
||||
"help.view.navigation_bar": "Shows or hides the navigation bar at the bottom of the viewport. Hide it for more room, show it to reach the view tools by mouse.",
|
||||
"help.view.rename": "Gives a saved named view a new name. The view itself stays the same.",
|
||||
"help.view.ribbon_tabs_only": "Folds the ribbon to its tab row, so the view gets the room its panels took. Click a tab to show its panels over the view; choose this again to keep the whole ribbon shown.",
|
||||
"help.view.save": "Saves the current camera position as a named view in the design. Use it to come back to the same angle later, for example for screenshots.",
|
||||
"help.view.spin": "Orbits the view around the model continuously until the next click or key. Use it to present a model from all sides.",
|
||||
"help.view.tool": "Latches a camera tool, orbit, pan or zoom, so that dragging with the left mouse button moves the view until you switch it off. Use it on a trackpad or without a middle mouse button.",
|
||||
@@ -2163,6 +2363,7 @@
|
||||
"navbar.value_tip": "{name} · {value}",
|
||||
"navbar.visual_style": "Visual style",
|
||||
"navbar.zoom": "Zoom",
|
||||
"navbar.zoom_hold_tip": "Zoom (hold shortcut)",
|
||||
"needs two joints": "needs two joints",
|
||||
"network.allow_once": "Allow once",
|
||||
"network.always_allow": "Always allow this server",
|
||||
@@ -2316,6 +2517,7 @@
|
||||
"prefs.carousel_previous": "Previous",
|
||||
"prefs.choose_font": "Choose font file…",
|
||||
"prefs.click": "Click",
|
||||
"prefs.command_dialog_values": "Command dialogs",
|
||||
"prefs.contact_shading": "Soft contact shading",
|
||||
"prefs.contact_shading_note": "Soft shading where parts come close.",
|
||||
"prefs.context_menu": "Context menu",
|
||||
@@ -2362,6 +2564,8 @@
|
||||
"prefs.edges": "Edges and shading",
|
||||
"prefs.extend": "Extend",
|
||||
"prefs.extra_navigation": "Ctrl+left drag on empty space orbits; hold Space to pan.",
|
||||
"prefs.extrude_arrow_face_stops": "Extrude arrow stops at faces",
|
||||
"prefs.extrude_arrow_face_stops_note": "Dragging Extrude's distance arrow stops at flat faces parallel to the profile, such as the far side of a block being cut, and outlines the face. Off: the arrow moves in round steps only.",
|
||||
"prefs.extrude_view_isometric": "Turn the view to isometric when Extrude opens",
|
||||
"prefs.extrude_view_return": "Go back to the earlier view when Extrude closes",
|
||||
"prefs.follows_os": "Follows the OS",
|
||||
@@ -2411,6 +2615,8 @@
|
||||
"prefs.graphics_page.surfaces_edges": "Surfaces and edges",
|
||||
"prefs.graphics_page.view_bar": "Also in the view bar",
|
||||
"prefs.graphics_presets_note": "Presets set the choices below together.",
|
||||
"prefs.ground_shadow_follows_model": "Ground shadow follows the model",
|
||||
"prefs.ground_shadow_follows_model_help": "Place the shadow below the lowest visible body. When off, raised models keep their shadow on the origin ground plane.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Hide constraint glyphs when zoomed out",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out_note": "Constraint glyphs leave the view when the sketch is zoomed out far enough that they would clutter it. Off: they stay at a fixed screen size at every zoom.",
|
||||
"prefs.history": "History",
|
||||
@@ -2452,6 +2658,8 @@
|
||||
"prefs.layout_page.card": "Card",
|
||||
"prefs.layout_page.column": "Column",
|
||||
"prefs.layout_page.hover": "Hover",
|
||||
"prefs.layout_page.hover_panel_menus": "Switch panel menus on hover",
|
||||
"prefs.layout_page.hover_panel_menus_help": "Click a ribbon panel name to open its menu. Hover another panel name to switch. Move outside the ribbon and open menu to close it.",
|
||||
"prefs.layout_page.icons_labels": "Icons + labels",
|
||||
"prefs.layout_page.ok_cancel": "OK and Cancel",
|
||||
"prefs.layout_page.on_hover": "On hover",
|
||||
@@ -2490,6 +2698,8 @@
|
||||
"prefs.mouse": "Mouse and trackpad",
|
||||
"prefs.mouse_heading": "Mouse",
|
||||
"prefs.mouse_mapping": "Mouse mapping",
|
||||
"prefs.move_drag_highlights": "Highlight what is under the pointer while Move's arrows drag a component",
|
||||
"prefs.move_drag_highlights_tip": "Off: while Move's arrows drag a component, nothing under the pointer is highlighted, which keeps large assemblies smooth. With a section or an analysis shown it is highlighted all the same, and a part dragged along its joints never highlights it.",
|
||||
"prefs.navigation": "Navigation",
|
||||
"prefs.navigation.axes_signed": "Axes",
|
||||
"prefs.navigation.cube_compact": "Cube",
|
||||
@@ -2518,6 +2728,22 @@
|
||||
"prefs.persistent_ribbon_pressed": "Highlight active ribbon commands",
|
||||
"prefs.persistent_ribbon_pressed_help": "Highlight ribbon commands while their effects are on. Turn off to keep the previous neutral appearance and command help.",
|
||||
"prefs.perspective_fov": "Field of view",
|
||||
"prefs.picker.carousel": "Carousel",
|
||||
"prefs.picker.change": "Change",
|
||||
"prefs.picker.clear": "Clear filters",
|
||||
"prefs.picker.day": "Day theme",
|
||||
"prefs.picker.empty": "No theme matches these filters.",
|
||||
"prefs.picker.follow": "Follow system",
|
||||
"prefs.picker.grid": "Grid",
|
||||
"prefs.picker.high": "High contrast",
|
||||
"prefs.picker.high_note": "{source} · High contrast",
|
||||
"prefs.picker.hint": "Switches with your operating system.",
|
||||
"prefs.picker.import": "Import theme file…",
|
||||
"prefs.picker.imported": "Imported",
|
||||
"prefs.picker.night": "Night theme",
|
||||
"prefs.picker.one": "One theme",
|
||||
"prefs.picker.pick": "Pick from below",
|
||||
"prefs.picker.standard": "Standard",
|
||||
"prefs.placement": "Placement",
|
||||
"prefs.plugins_enabled": "Enabled",
|
||||
"prefs.plugins_installed": "Installed",
|
||||
@@ -2554,6 +2780,8 @@
|
||||
"prefs.quality_smoother": "smoother",
|
||||
"prefs.quick_measure": "Quick measurement in the view's corner",
|
||||
"prefs.quick_measure_note": "The selection's size in the view's bottom right corner: an edge's length, or Ø and R for a round one, a face's area, a point's position, two items' distance and angle, several edges' or faces' total. A click copies it.",
|
||||
"prefs.remember_dialog_values": "Remember command dialog values",
|
||||
"prefs.remember_dialog_values.help": "Off by default. When on, after a command succeeds, its safe values and options are reused the next time you open the same command, until SolidCraft closes.",
|
||||
"prefs.render_previous_geometry_budget": "Use previous Render preview geometry budget",
|
||||
"prefs.render_previous_geometry_budget_help": "Restore the previous fixed geometry limit (512 MiB on 64-bit desktop, 128 MiB elsewhere) instead of the device-based budget.",
|
||||
"prefs.reports_saved": "{n} saved",
|
||||
@@ -2562,6 +2790,8 @@
|
||||
"prefs.ribbon_click_ends_tool_note": "A click on the lit ribbon button of the sketch tool in use ends the tool, so clicks in the view select again. Off: the click starts the tool again.",
|
||||
"prefs.ribbon_reveal": "Show search results in the ribbon",
|
||||
"prefs.ribbon_reveal_note": "Hovering a result in Search commands shows where the command sits in the ribbon: its tab, its button or its row in a panel's drop-down. A command started from the search blinks there, and a running command from a drop-down marks its panel.",
|
||||
"prefs.ribbon_tabs_only": "Show only the ribbon's tabs",
|
||||
"prefs.ribbon_tabs_only_note": "The ribbon folds to its tab row. Clicking a tab shows its panels over the view until a command starts or the pointer leaves them. Ribbon Tabs Only in the View tab, or its shortcut, switches this.",
|
||||
"prefs.right_click": "Right click",
|
||||
"prefs.saving": "Start and save",
|
||||
"prefs.scale_note": "Also Ctrl + / Ctrl − in the window. Buttons are 28 px at 100%.",
|
||||
@@ -2633,6 +2863,7 @@
|
||||
"prefs.sketch_exit_view_note": "Where the view goes after Finish Sketch, Cancel or a double Esc.",
|
||||
"prefs.sketch_face_activates_part": "Sketching on a part's face makes that part active",
|
||||
"prefs.sketch_face_activates_part_note": "A sketch started on a face of another component's body activates that component first, so the sketch belongs to it and stays with that part in the browser. The part stays active afterwards. Off: the sketch belongs to the active component.",
|
||||
"prefs.sketch_mirror_auto_line": "Use the preselected construction line for Mirror",
|
||||
"prefs.sketch_open_look_at": "Turn to face a sketch when it opens",
|
||||
"prefs.sketch_open_look_at_note": "Off: creating or editing a sketch leaves the view as it is. Look At faces the sketch when you want it.",
|
||||
"prefs.sketch_recent_snap": "Auto snap to recent centers and midpoints",
|
||||
@@ -3914,6 +4145,7 @@
|
||||
"render.stage_panels.extent": "Extent",
|
||||
"render.stage_panels.fit_save": "Fit and save",
|
||||
"render.stage_panels.gpu_required": "Needs GPU path tracing",
|
||||
"render.stage_panels.grain_angle": "Grain angle",
|
||||
"render.stage_panels.keep": "Keep",
|
||||
"render.stage_panels.kept": "Kept",
|
||||
"render.stage_panels.kept_fields": "Camera, sampling, dimensions, optical bindings, CAD geometry and engineering properties.",
|
||||
@@ -4032,6 +4264,19 @@
|
||||
"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",
|
||||
"render.surface_import.author": "Author",
|
||||
"render.surface_import.busy": "Importing image…",
|
||||
"render.surface_import.complete": "Choose an image and enter its author, source and license.",
|
||||
"render.surface_import.embedded": "Imported image",
|
||||
"render.surface_import.failed": "The image import did not finish.",
|
||||
"render.surface_import.license": "License / ownership",
|
||||
"render.surface_import.raytrace_required": "Images require CPU or GPU ray tracing. Choose it in Output.",
|
||||
"render.surface_import.repeat_u": "Repeat U",
|
||||
"render.surface_import.repeat_v": "Repeat V",
|
||||
"render.surface_import.save": "Save image on body",
|
||||
"render.surface_import.saved_cpu": "Save maps to use them with CPU rendering.",
|
||||
"render.surface_import.source": "Source",
|
||||
"render.surface_import.title": "Import image",
|
||||
"render.tone_aces_fitted": "ACES fitted",
|
||||
"render.tone_reinhard": "Reinhard",
|
||||
"render.transmission_scope": "GPU glass supports certified disjoint media. Unrestricted nested media and a noise-free transmission estimator are not provided.",
|
||||
@@ -4108,6 +4353,8 @@
|
||||
"sample.plate.name": "Sample Plate",
|
||||
"sample.vents.about": "Slots in a rectangular pattern: rows, columns and pitch are parameters",
|
||||
"sample.vents.name": "Vent Plate",
|
||||
"scale.per_axis": "Per axis",
|
||||
"scale.uniform": "Uniform",
|
||||
"search.command_keys": "Enter run Shift+Enter run and keep open Esc close",
|
||||
"search.commands": "Commands",
|
||||
"search.design": "In this design",
|
||||
@@ -4780,6 +5027,7 @@
|
||||
"ui.click_selects_whole_bodies": "Click selects whole bodies",
|
||||
"ui.click_then_press_key": "Click, then press the new key",
|
||||
"ui.close": "Close",
|
||||
"ui.close_tab": "Close tab",
|
||||
"ui.coil": "Coil",
|
||||
"ui.coincident": "Coincident",
|
||||
"ui.collapse": "Collapse",
|
||||
@@ -4997,6 +5245,11 @@
|
||||
"ui.factor": "Factor",
|
||||
"ui.fade": "Fade",
|
||||
"ui.fade_length": "Fade Length",
|
||||
"ui.fair_iterations": "Iterations",
|
||||
"ui.fair_keep_edges": "Keep edges",
|
||||
"ui.fair_strength": "Strength",
|
||||
"ui.fair_surface_title": "FAIR SURFACE",
|
||||
"ui.fair_tolerance_default": "0.001 of body size",
|
||||
"ui.feature_history": "Feature history",
|
||||
"ui.feature_options": "Feature Options",
|
||||
"ui.features": "Features",
|
||||
@@ -5182,6 +5435,7 @@
|
||||
"ui.inspect": "Inspect",
|
||||
"ui.instances": "Instances",
|
||||
"ui.intersect": "Intersect",
|
||||
"ui.intersection_curve_second_pick": "Intersection curve: click the second face or curve (right-click to start over)",
|
||||
"ui.iso_4753": "ISO 4753",
|
||||
"ui.isolate": "Isolate",
|
||||
"ui.join": "Join",
|
||||
@@ -5210,6 +5464,7 @@
|
||||
"ui.manipulator_free_alt": "Manipulator free drag also on Alt/Option",
|
||||
"ui.manipulator_free_alt_tip": "On: Alt/Option or Ctrl/Cmd on extrude / gizmo arrows disables value snapping. Off: Ctrl/Cmd only.",
|
||||
"ui.manufacture": "Manufacture",
|
||||
"ui.match_surface_title": "MATCH SURFACE",
|
||||
"ui.material_review.axes": "Material-local axes; no body orientation is assigned.",
|
||||
"ui.material_review.claims": "Sources and licenses are unverified claims. Missing values remain unknown.",
|
||||
"ui.material_review.compressive_strength": "Compressive strength",
|
||||
@@ -5232,6 +5487,9 @@
|
||||
"ui.material_review.title": "Review Physical Material Data",
|
||||
"ui.material_review.unknown": "Unknown",
|
||||
"ui.material_review.yield_strength": "Yield strength",
|
||||
"ui.mcp_agents": "Agents through MCP",
|
||||
"ui.mcp_compact": "Compact output",
|
||||
"ui.mcp_compact_note": "For agents connected through the MCP server (solidcraft-cli mcp): inspect_design summarises sketches and tool results are not pretty-printed, so they use fewer tokens. Applies to agent sessions started after the change; --compact or SOLIDCRAFT_MCP_COMPACT=1 also turn it on.",
|
||||
"ui.measure": "Measure",
|
||||
"ui.mesh_format_3mf": "3MF",
|
||||
"ui.mesh_format_3mf_tagged": "3MF with face tags",
|
||||
@@ -5273,6 +5531,7 @@
|
||||
"ui.options": "Options",
|
||||
"ui.orbit": "Orbit",
|
||||
"ui.orbit_around": "Orbit around",
|
||||
"ui.organize": "Organize",
|
||||
"ui.orientation": "Orientation",
|
||||
"ui.origin": "Origin",
|
||||
"ui.palette_construction_mode": "Construction mode (X)",
|
||||
@@ -5280,6 +5539,65 @@
|
||||
"ui.palette_sketch_options": "Sketch options",
|
||||
"ui.pan": "Pan",
|
||||
"ui.pan_zoom_orbit": "Pan, zoom, orbit",
|
||||
"ui.panel_fixture.apply": "Apply",
|
||||
"ui.panel_fixture.cancel": "Cancel",
|
||||
"ui.panel_fixture.caption": "Synthetic panel specimen",
|
||||
"ui.panel_fixture.draft": "Retained draft",
|
||||
"ui.panel_fixture.editor": "Specimen editor",
|
||||
"ui.panel_fixture.explanation": "This synthetic content retains its draft while hidden or moved.",
|
||||
"ui.panel_fixture.fill": "Fill specimen",
|
||||
"ui.panel_fixture.finish": "Finish",
|
||||
"ui.panel_fixture.finished": "Synthetic transaction ended",
|
||||
"ui.panel_fixture.footer": "Synthetic actions leave the design unchanged.",
|
||||
"ui.panel_fixture.guard": "Refuse cancellation while the transaction is live",
|
||||
"ui.panel_fixture.inline": "Inline specimen",
|
||||
"ui.panel_fixture.live": "Synthetic transaction live",
|
||||
"ui.panel_fixture.log": "Specimen log",
|
||||
"ui.panel_fixture.pin": "Pinned specimen",
|
||||
"ui.panel_fixture.title": "{title} · {id}",
|
||||
"ui.panel_fixture.updates": "Service updates: {count}",
|
||||
"ui.panel_host.attention": "Needs attention",
|
||||
"ui.panel_host.bottom": "Bottom",
|
||||
"ui.panel_host.close": "Close panel",
|
||||
"ui.panel_host.collapse": "Collapse panel",
|
||||
"ui.panel_host.expand": "Expand panel",
|
||||
"ui.panel_host.floating": "Floating",
|
||||
"ui.panel_host.focus": "Focus panel",
|
||||
"ui.panel_host.focus_target": "Focus {title}",
|
||||
"ui.panel_host.header": "Panel header",
|
||||
"ui.panel_host.join": "Join tabs",
|
||||
"ui.panel_host.join_target": "Join tabs with {title}",
|
||||
"ui.panel_host.left": "Left",
|
||||
"ui.panel_host.menu": "Panel actions",
|
||||
"ui.panel_host.move": "Move to",
|
||||
"ui.panel_host.overflow": "More panels",
|
||||
"ui.panel_host.popout": "Open in a window",
|
||||
"ui.panel_host.resize": "Resize panel",
|
||||
"ui.panel_host.right": "Right",
|
||||
"ui.panel_host.rule.availability": "This panel is unavailable.",
|
||||
"ui.panel_host.rule.bounds": "The panels do not fit within their size limits.",
|
||||
"ui.panel_host.rule.capability": "This host does not support that action.",
|
||||
"ui.panel_host.rule.collection_limit": "The panel layout has reached its limit.",
|
||||
"ui.panel_host.rule.duplicate_id": "This panel identity is already in use.",
|
||||
"ui.panel_host.rule.exactly_once": "Each panel must appear once.",
|
||||
"ui.panel_host.rule.feature_disabled": "This action is disabled for this panel.",
|
||||
"ui.panel_host.rule.id": "The panel identity is invalid.",
|
||||
"ui.panel_host.rule.incompatible_tabs": "These panels cannot share a tab group.",
|
||||
"ui.panel_host.rule.lifecycle": "The panel could not complete this action.",
|
||||
"ui.panel_host.rule.missing_target": "This panel target is no longer available.",
|
||||
"ui.panel_host.rule.no_space": "There is no space for this panel layout.",
|
||||
"ui.panel_host.rule.pin_conflict": "Another panel is already pinned to this edge.",
|
||||
"ui.panel_host.rule.pin_order": "A pinned panel must remain first on its edge.",
|
||||
"ui.panel_host.rule.placement": "This panel cannot move to that location.",
|
||||
"ui.panel_host.rule.revision": "The layout changed. Try the action again.",
|
||||
"ui.panel_host.rule.schema": "The panel request is invalid.",
|
||||
"ui.panel_host.rule.version": "This panel layout version is unsupported.",
|
||||
"ui.panel_host.sash": "Resize neighbouring panels",
|
||||
"ui.panel_host.scroll_horizontal": "Scroll panel columns",
|
||||
"ui.panel_host.scroll_vertical": "Scroll panel stacks",
|
||||
"ui.panel_host.surface": "Panel",
|
||||
"ui.panel_host.tab": "Panel tab",
|
||||
"ui.panel_host.tabs": "All tabs",
|
||||
"ui.parallel": "Parallel",
|
||||
"ui.parameters": "Parameters",
|
||||
"ui.part_size_needs_a_part": "Select an inserted standard part (a screw, nut, washer or pin) to change its size. To make a body larger or smaller, use Scale.",
|
||||
@@ -5317,6 +5635,7 @@
|
||||
"ui.point_cloud_add_filter": "Add filter…",
|
||||
"ui.point_cloud_auto_range": "Automatic Range",
|
||||
"ui.point_cloud_color_by": "Color By",
|
||||
"ui.point_cloud_dots": "Dots",
|
||||
"ui.point_cloud_file_colors": "File Colors",
|
||||
"ui.point_cloud_filter_by": "Filter by",
|
||||
"ui.point_cloud_height_high": "Highest",
|
||||
|
||||
@@ -2349,6 +2349,8 @@
|
||||
"prefs.edges": "Aristas y sombreado",
|
||||
"prefs.extend": "Extensiones",
|
||||
"prefs.extra_navigation": "Ctrl + arrastrar con el botón izquierdo sobre un espacio vacío orbita; mantén Espacio para desplazar.",
|
||||
"prefs.extrude_arrow_face_stops": "La flecha de Extruir se detiene en las caras",
|
||||
"prefs.extrude_arrow_face_stops_note": "Al arrastrar la flecha de distancia de Extruir, se detiene en las caras planas paralelas al perfil, como el lado opuesto de un bloque que se corta, y resalta la cara. Desactivado: la flecha solo avanza en pasos redondos.",
|
||||
"prefs.extrude_view_isometric": "Girar la vista a isométrica al abrir Extrusión",
|
||||
"prefs.extrude_view_return": "Volver a la vista anterior al cerrar Extrusión",
|
||||
"prefs.follows_os": "Sigue al sistema operativo",
|
||||
@@ -2473,6 +2475,8 @@
|
||||
"prefs.mouse": "Ratón y panel táctil",
|
||||
"prefs.mouse_heading": "Ratón",
|
||||
"prefs.mouse_mapping": "Asignación del ratón",
|
||||
"prefs.move_drag_highlights": "Resaltar lo que hay bajo el puntero mientras las flechas de Mover arrastran un componente",
|
||||
"prefs.move_drag_highlights_tip": "Desactivado: mientras las flechas de Mover arrastran un componente no se resalta nada bajo el puntero, y los ensamblajes grandes se mueven con fluidez. Con un corte o un análisis visible se resalta igualmente, y una pieza arrastrada a lo largo de sus uniones nunca lo resalta.",
|
||||
"prefs.navigation": "Navegación",
|
||||
"prefs.navigation.axes_signed": "Ejes",
|
||||
"prefs.navigation.cube_compact": "Cubo",
|
||||
@@ -4079,6 +4083,8 @@
|
||||
"sample.plate.name": "Placa de ejemplo",
|
||||
"sample.vents.about": "Ranuras en un patrón rectangular: las filas, las columnas y el paso son parámetros",
|
||||
"sample.vents.name": "Placa de ventilación",
|
||||
"scale.per_axis": "Por eje",
|
||||
"scale.uniform": "Uniforme",
|
||||
"search.command_keys": "Intro ejecutar Mayús+Intro ejecutar y mantener abierto Esc cerrar",
|
||||
"search.commands": "Comandos",
|
||||
"search.design": "En este diseño",
|
||||
@@ -4750,6 +4756,7 @@
|
||||
"ui.click_selects_whole_bodies": "Un clic selecciona cuerpos completos",
|
||||
"ui.click_then_press_key": "Haz clic y pulsa la nueva tecla",
|
||||
"ui.close": "Cerrar",
|
||||
"ui.close_tab": "Cerrar pestaña",
|
||||
"ui.coil": "Hélice",
|
||||
"ui.coincident": "Coincidente",
|
||||
"ui.collapse": "Contraer",
|
||||
|
||||
@@ -4077,6 +4077,8 @@
|
||||
"sample.plate.name": "Esimerkkilevy",
|
||||
"sample.vents.about": "Urat suorakulmaisessa kuviossa: rivit, sarakkeet ja jako ovat parametreja",
|
||||
"sample.vents.name": "Tuuletuslevy",
|
||||
"scale.per_axis": "Akseleittain",
|
||||
"scale.uniform": "Tasainen",
|
||||
"search.command_keys": "Enter suorita Shift+Enter suorita ja pidä auki Esc sulje",
|
||||
"search.commands": "Komennot",
|
||||
"search.design": "Tässä mallissa",
|
||||
|
||||
@@ -1624,7 +1624,7 @@
|
||||
"help.sketch.insert_svg": "Lit les tracés d'un dessin SVG dans une esquisse, mis à l'échelle, tournés et placés comme vous le définissez. À utiliser pour transformer un logo ou un contour vectoriel en courbes que vous pouvez extruder.",
|
||||
"help.sketch.inspect": "Ouvre un rapport sur l'esquisse ouverte : combien de degrés de liberté il reste et si elle est entièrement contrainte. À utiliser pour savoir ce qui bouge encore avant de terminer l'esquisse.",
|
||||
"help.sketch.intersect": "Ajoute les courbes ou points où des corps, faces, arêtes ou d'autres esquisses traversent le plan d'esquisse. Ils restent liés et suivent le modèle lorsqu'il change.",
|
||||
"help.sketch.intersection_curve": "Crée des courbes 3D là où deux corps ou faces se rencontrent, liées à eux. À utiliser comme trajectoires ou rails guides qui suivent le modèle.",
|
||||
"help.sketch.intersection_curve": "Crée des courbes 3D là où deux corps, faces ou courbes d'autres esquisses se rencontrent, liées à eux. À utiliser comme trajectoires ou rails guides qui suivent le modèle.",
|
||||
"help.sketch.isoparametric_curve": "Crée une courbe 3D le long de la direction U ou V d'une face, par un point que vous choisissez, liée à la face. À utiliser comme trajectoire ou ligne de partage qui suit la surface.",
|
||||
"help.sketch.line": "Dessine des lignes connectées point par point, en chaîne ouverte ou en contour fermé. C'est l'outil de base de la plupart des profils.",
|
||||
"help.sketch.line.midpoint": "Dessine une ligne de son milieu vers une extrémité, l'autre extrémité étant son symétrique. À utiliser pour des lignes centrées sur un point, comme une arête symétrique.",
|
||||
@@ -2201,6 +2201,8 @@
|
||||
"prefs.edges": "Arêtes et ombrage",
|
||||
"prefs.extend": "Étendre",
|
||||
"prefs.extra_navigation": "Ctrl+glisser gauche dans le vide fait orbiter ; maintenir Espace pour un panoramique.",
|
||||
"prefs.extrude_arrow_face_stops": "La flèche d'extrusion s'arrête aux faces",
|
||||
"prefs.extrude_arrow_face_stops_note": "En faisant glisser la flèche de distance d'Extrusion, elle s'arrête aux faces planes parallèles au profil, comme l'autre côté d'un bloc qu'on découpe, et souligne la face. Désactivé : la flèche avance seulement par pas ronds.",
|
||||
"prefs.extrude_view_isometric": "Passer la vue en isométrique à l'ouverture de l'extrusion",
|
||||
"prefs.extrude_view_return": "Revenir à la vue précédente à la fermeture de l'extrusion",
|
||||
"prefs.follows_os": "Suit le système",
|
||||
@@ -2280,6 +2282,8 @@
|
||||
"prefs.mouse": "Souris et pavé tactile",
|
||||
"prefs.mouse_heading": "Souris",
|
||||
"prefs.mouse_mapping": "Affectation des boutons de la souris",
|
||||
"prefs.move_drag_highlights": "Mettre en évidence ce qui est sous le pointeur pendant que les flèches de Déplacer font glisser un composant",
|
||||
"prefs.move_drag_highlights_tip": "Désactivé : pendant que les flèches de Déplacer font glisser un composant, rien n’est mis en évidence sous le pointeur, ce qui garde les grands assemblages fluides. Avec une coupe ou une analyse affichée, la mise en évidence reste, et une pièce glissée le long de ses liaisons n’en montre jamais.",
|
||||
"prefs.navigation": "Navigation",
|
||||
"prefs.navigation_note": "Comment la souris, le pavé tactile et la souris 3D déplacent la vue.",
|
||||
"prefs.navigator_fit": "Cadrer le modèle après un changement de vue du repère de navigation",
|
||||
@@ -3814,6 +3818,8 @@
|
||||
"sample.plate.name": "Plaque d'exemple",
|
||||
"sample.vents.about": "Rainures en répétition rectangulaire : lignes, colonnes et pas sont des paramètres",
|
||||
"sample.vents.name": "Plaque d'aération",
|
||||
"scale.per_axis": "Par axe",
|
||||
"scale.uniform": "Uniforme",
|
||||
"search.command_keys": "Entrée exécuter Maj+Entrée exécuter et garder ouvert Échap fermer",
|
||||
"search.commands": "Commandes",
|
||||
"search.design": "Dans cette conception",
|
||||
@@ -4443,6 +4449,7 @@
|
||||
"ui.click_selects_whole_bodies": "Un clic sélectionne des corps entiers",
|
||||
"ui.click_then_press_key": "Cliquer, puis appuyer sur la nouvelle touche",
|
||||
"ui.close": "Fermer",
|
||||
"ui.close_tab": "Fermer l’onglet",
|
||||
"ui.coil": "Hélice",
|
||||
"ui.coincident": "Coïncidence",
|
||||
"ui.collapse": "Réduire",
|
||||
@@ -4926,6 +4933,7 @@
|
||||
"ui.point_cloud_add_filter": "Ajouter un filtre…",
|
||||
"ui.point_cloud_auto_range": "Plage automatique",
|
||||
"ui.point_cloud_color_by": "Colorer par",
|
||||
"ui.point_cloud_dots": "Points isolés",
|
||||
"ui.point_cloud_file_colors": "Couleurs du fichier",
|
||||
"ui.point_cloud_filter_by": "Filtrer par",
|
||||
"ui.point_cloud_height_high": "Plus haut",
|
||||
|
||||
@@ -1621,7 +1621,7 @@
|
||||
"help.sketch.insert_svg": "Le os trazados dun debuxo SVG nun bosquexo, escalados, xirados e colocados onde indiques. Úsao para converter un logotipo ou un contorno vectorial en curvas que podes extruír.",
|
||||
"help.sketch.inspect": "Abre un informe do bosquexo aberto: cantos graos de liberdade quedan e se está totalmente restrinxido. Úsao para saber o que aínda se move antes de rematar o bosquexo.",
|
||||
"help.sketch.intersect": "Engade as curvas ou os puntos onde corpos, caras, arestas ou outros bosquexos cruzan o plano do bosquexo. Quedan ligados e seguen o modelo cando cambia.",
|
||||
"help.sketch.intersection_curve": "Crea curvas 3D onde se atopan dous corpos ou caras, ligadas a eles. Úsaas como traxectorias ou guías que seguen o modelo.",
|
||||
"help.sketch.intersection_curve": "Crea curvas 3D onde se atopan dous corpos, caras ou curvas doutros bosquexos, ligadas a eles. Úsaas como traxectorias ou guías que seguen o modelo.",
|
||||
"help.sketch.isoparametric_curve": "Crea unha curva 3D na dirección U ou V dunha cara por un punto que escollas, ligada á cara. Úsaa como traxectoria ou liña de división que segue a superficie.",
|
||||
"help.sketch.line": "Debuxa liñas conectadas punto a punto, como cadea aberta ou como contorno pechado. É a ferramenta básica para a maioría dos perfís.",
|
||||
"help.sketch.line.midpoint": "Debuxa unha liña desde o seu punto medio ata un extremo, co outro extremo reflectido. Úsao para liñas centradas nun punto, como unha aresta simétrica.",
|
||||
@@ -3807,6 +3807,8 @@
|
||||
"sample.plate.name": "Placa de exemplo",
|
||||
"sample.vents.about": "Ranuras nun patrón rectangular: filas, columnas e paso son parámetros",
|
||||
"sample.vents.name": "Placa de ventilación",
|
||||
"scale.per_axis": "Por eixo",
|
||||
"scale.uniform": "Uniforme",
|
||||
"search.command_keys": "Intro executar Maiús+Intro executar e manter aberto Esc pechar",
|
||||
"search.commands": "Ordes",
|
||||
"search.design": "Neste deseño",
|
||||
|
||||
+320
-1
@@ -1,4 +1,9 @@
|
||||
{
|
||||
" Keys": " נקודות מפתח",
|
||||
" Maximum": " מקסימום",
|
||||
" Minimum": " מינימום",
|
||||
" Of": " של",
|
||||
" Value": " ערך",
|
||||
"(active rule)": "(כלל פעיל)",
|
||||
"0 turns it off": "הערך 0 מכבה",
|
||||
"1. Open GitHub draft below, then paste the report copied to your clipboard.": "1. יש לפתוח למטה את הטיוטה ב־GitHub, ואז להדביק את הדוח שהועתק ללוח.",
|
||||
@@ -10,7 +15,18 @@
|
||||
"A new configuration, a copy of the active one": "תצורה חדשה, עותק של התצורה הפעילה",
|
||||
"A placed Single hole has a handle to drag it, and picking its face again moves the hole there instead of taking it out": "לחור יחיד שהוצב יש ידית לגרירה, ובחירה חוזרת בפאה שלו מזיזה את החור לשם במקום להסיר אותו",
|
||||
"A short title for the problem (optional)": "כותרת קצרה לבעיה (לא חובה)",
|
||||
"ABS - Black": "ABS - שחור",
|
||||
"ABS - Grey": "ABS - אפור",
|
||||
"ABS - White": "ABS - לבן",
|
||||
"ALIGN": "יישור",
|
||||
"ARRANGE": "סידור",
|
||||
"AS-BUILT JOINT": "מפרק במצב הקיים",
|
||||
"ASSIGN PLASTIC RULE": "הקצאת כלל פלסטיק",
|
||||
"AXIS PERPENDICULAR TO FACE": "ציר ניצב לפאה",
|
||||
"AXIS THROUGH CYLINDER": "ציר דרך גליל",
|
||||
"AXIS THROUGH EDGE": "ציר דרך מקצוע",
|
||||
"AXIS THROUGH TWO PLANES": "ציר דרך שני מישורים",
|
||||
"AXIS THROUGH TWO POINTS": "ציר דרך שתי נקודות",
|
||||
"About SolidCraft": "אודות SolidCraft",
|
||||
"Acceleration": "תאוצה",
|
||||
"Access": "נגישות",
|
||||
@@ -27,6 +43,7 @@
|
||||
"Add bend": "הוספת כיפוף",
|
||||
"Add condition": "הוספת תנאי",
|
||||
"Add or edit rule": "הוספה או עריכה של כלל",
|
||||
"Add shortcut": "הוספת קיצור דרך",
|
||||
"Add to New Folder": "הוספה לתיקייה חדשה",
|
||||
"Add visible bodies": "הוספת הגופים הגלויים",
|
||||
"Advanced": "מתקדם",
|
||||
@@ -37,13 +54,17 @@
|
||||
"All components": "כל הרכיבים",
|
||||
"All open designs": "כל התכנים הפתוחים",
|
||||
"Allowed expressions, separated by semicolons": "ביטויים מותרים, מופרדים בנקודה ופסיק",
|
||||
"Allowed values": "ערכים מותרים",
|
||||
"Also deletes": "נמחקים גם",
|
||||
"Also place them from the origin (fully constrained)": "מיקום גם ביחס לראשית (מוגבל לחלוטין)",
|
||||
"Aluminum - Satin": "אלומיניום - סאטן",
|
||||
"Anchor": "נקודת עיגון",
|
||||
"Angle 2": "זווית 2",
|
||||
"Angle Display": "תצוגת זווית",
|
||||
"Animate": "הנפשה",
|
||||
"Anodized Black": "אנודייז שחור",
|
||||
"Another bend on the far edge of the last flange": "כיפוף נוסף על המקצוע הרחוק של האוגן האחרון",
|
||||
"Any": "כלשהו",
|
||||
"App/system information and command settings are included. Review private details before sharing.": "נכללים פרטי האפליקציה והמערכת והגדרות הפקודות. יש לבדוק פרטים פרטיים לפני השיתוף.",
|
||||
"Appearance name": "שם המראה",
|
||||
"Applied Radius": "רדיוס מוחל",
|
||||
@@ -58,6 +79,8 @@
|
||||
"Assign Rule": "הקצאת כלל",
|
||||
"Attachment": "הצמדה",
|
||||
"BLEND SURFACE": "משטח מעבר",
|
||||
"BOSS": "בליטה",
|
||||
"BOUNDARY FILL": "מילוי לפי גבולות",
|
||||
"BOUNDARY SURFACE": "משטח גבול",
|
||||
"BROWSER": "עץ התכן",
|
||||
"Back to the original order": "חזרה לסדר המקורי",
|
||||
@@ -85,13 +108,21 @@
|
||||
"Bottom Right": "למטה מימין",
|
||||
"Boundary": "גבול",
|
||||
"Bounding box": "תיבה תוחמת",
|
||||
"Brass": "פליז",
|
||||
"Bridge": "גשר",
|
||||
"Built-in": "מובנה",
|
||||
"By definition": "לפי הגדרה",
|
||||
"By part number": "לפי מספר חלק",
|
||||
"CAM": "פיקה",
|
||||
"CHECK": "בדיקה",
|
||||
"COMPONENTS": "רכיבים",
|
||||
"CONFIGURATIONS": "תצורות",
|
||||
"CONSTRAIN": "אילוץ",
|
||||
"CONTACT": "מגע",
|
||||
"CONTACT SET": "ערכת מגע",
|
||||
"CONVERT": "המרה",
|
||||
"CONVERT MESH": "המרת רשת משולשים",
|
||||
"CONVERT TO SHEET METAL": "המרה לפח",
|
||||
"CSV or JSON: updates existing parameters, adds new ones": "CSV או JSON: מעדכן פרמטרים קיימים ומוסיף חדשים",
|
||||
"Capture All Positions": "שמירת כל המיקומים",
|
||||
"Capture Pos.": "לכידת מיקום",
|
||||
@@ -111,11 +142,15 @@
|
||||
"Choose Variable radius or Spline in Type.": "יש לבחור \"רדיוס משתנה\" או \"ספליין\" בשדה \"סוג\".",
|
||||
"Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.": "יש לבחור מישור חדש. הגאומטריה והמידות של הסקיצה נשארות ללא שינוי בקואורדינטות הדו־ממדיות שלה.",
|
||||
"Choose…": "בחירה…",
|
||||
"Chord length": "אורך מיתר",
|
||||
"Chrome": "כרום",
|
||||
"Circle center": "מרכז המעגל",
|
||||
"Circle or sphere": "מעגל או כדור",
|
||||
"Circular": "מעגלי",
|
||||
"Circumference": "היקף",
|
||||
"Clockwise": "בכיוון השעון",
|
||||
"Close All Tabs": "סגירת כל הלשוניות",
|
||||
"Close Other Tabs": "סגירת לשוניות אחרות",
|
||||
"Close draft": "סגירת הטיוטה",
|
||||
"Collapse": "כיווץ",
|
||||
"Collisions": "התנגשויות",
|
||||
@@ -129,6 +164,7 @@
|
||||
"Config Table": "טבלת תצורות",
|
||||
"Configuration": "תצורה",
|
||||
"Configuration rules": "כללי תצורה",
|
||||
"Connect AI assistants…": "חיבור עוזרי AI…",
|
||||
"Constant radius": "רדיוס קבוע",
|
||||
"Contact": "מגע",
|
||||
"Contact Sets": "קבוצות מגע",
|
||||
@@ -137,6 +173,7 @@
|
||||
"Continuity": "רציפות",
|
||||
"Control Radius": "רדיוס בקרה",
|
||||
"Copies the definition only. Assign it from Physical Material when ready.": "מועתקת ההגדרה בלבד. יש להקצות אותה מתוך חומר פיזיקלי כשמוכנים.",
|
||||
"Copper": "נחושת",
|
||||
"Copy Error Message": "העתקת הודעת השגיאה",
|
||||
"Copy Warning Message": "העתקת הודעת האזהרה",
|
||||
"Copy all licences": "העתקת כל הרישיונות",
|
||||
@@ -150,6 +187,7 @@
|
||||
"Counterbore Ø": "Ø שקע גלילי",
|
||||
"Countersink angle": "זווית שקע קוני",
|
||||
"Countersink Ø": "Ø שקע קוני",
|
||||
"Crash Reports…": "דוחות קריסה…",
|
||||
"Create Hole": "יצירת חור",
|
||||
"Create Issue — review draft": "יצירת דיווח — בדיקת הטיוטה",
|
||||
"Create Issue…": "יצירת דיווח…",
|
||||
@@ -162,12 +200,17 @@
|
||||
"Curv. Comb": "מסרק עקמומיות",
|
||||
"Curvature (G2)": "עקמומיות (G2)",
|
||||
"Curvature change (G3)": "שינוי עקמומיות (G3)",
|
||||
"Curvature continuous (G2)": "רציפות עקמומיות (G2)",
|
||||
"Custom": "מותאם אישית",
|
||||
"Customize Toolbar": "התאמת סרגל הכלים",
|
||||
"Customize…": "התאמה אישית…",
|
||||
"Cylindrical": "גלילי",
|
||||
"Cylindrical face": "פאה גלילית",
|
||||
"DELETE FACE": "מחיקת פאה",
|
||||
"DELETE FEATURE": "מחיקת תכונה",
|
||||
"DRIVE JOINTS": "הנעת מפרקים",
|
||||
"Deboss": "הטבעה",
|
||||
"Default material": "חומר ברירת מחדל",
|
||||
"Delete": "מחיקה",
|
||||
"Delete Folders and Features…": "מחיקת תיקיות עם הפיצ'רים שבהן…",
|
||||
"Delete Selected Features…": "מחיקת הפיצ'רים שנבחרו…",
|
||||
@@ -180,20 +223,26 @@
|
||||
"Dependents": "תלויים",
|
||||
"Describe the problem you saw": "תיאור הבעיה שראית",
|
||||
"Describe what happened before continuing.": "יש לתאר מה קרה לפני שממשיכים.",
|
||||
"Design Properties…": "מאפייני תכן…",
|
||||
"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": "הוספת מידות לצורות חדשות",
|
||||
"Direct interaction": "אינטראקציה ישירה",
|
||||
"Direction": "כיוון",
|
||||
"Discard": "השלכה",
|
||||
"Distance": "מרחק",
|
||||
"Distance 1": "מרחק 1",
|
||||
"Distance X": "מרחק X",
|
||||
"Distance Y": "מרחק Y",
|
||||
"Distance and Angle": "מרחק וזווית",
|
||||
"Do not export hidden edges": "לא לייצא מקצועות נסתרים",
|
||||
"Don't ask again": "לא לשאול שוב",
|
||||
"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": "לחיצה כפולה על הכפתור האמצעי באזור התצוגה: בית (תצוגת הבית, הממסגרת את המוצג), התאמה (מסגור המוצג תוך שמירת הכיוון) או ללא פעולה",
|
||||
"Download design": "הורדת התכן",
|
||||
"Drag to move the boss across the model. It snaps to sketch points on the face.": "יש לגרור כדי להזיז את הבליטה על פני המודל. היא נצמדת לנקודות סקיצה על הפאה.",
|
||||
"Drag to move the hole; click to remove it": "יש לגרור כדי להזיז את החור; לחיצה מסירה אותו",
|
||||
"Drag to move the rest across the model. It snaps to sketch points on the face.": "יש לגרור כדי להזיז את המשען על פני המודל. הוא נצמד לנקודות סקיצה על הפאה.",
|
||||
@@ -201,7 +250,16 @@
|
||||
"Drag to point the catch in another direction.": "יש לגרור כדי לכוון את התפס לכיוון אחר.",
|
||||
"Drag to turn the rest in 5° steps. Hold Alt or Ctrl to turn it freely.": "יש לגרור כדי לסובב את המשען בצעדים של 5°. יש להחזיק Alt או Ctrl לסיבוב חופשי.",
|
||||
"Drive Joint": "הנעת מפרק",
|
||||
"Drive parameter": "הנעת פרמטר",
|
||||
"Drive suppression": "הנעת השבתה",
|
||||
"Duration": "משך",
|
||||
"EDIT DIMENSION": "עריכת מידה",
|
||||
"EDIT JOINT": "עריכת מפרק",
|
||||
"EDIT MOTION STUDY": "עריכת חקר תנועה",
|
||||
"EDIT TEXT": "עריכת טקסט",
|
||||
"EMBOSS": "הבלטה",
|
||||
"EXPLODED VIEW": "תצוגה מפוצצת",
|
||||
"EXTEND": "הרחבה",
|
||||
"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": "מקצוע",
|
||||
@@ -215,10 +273,12 @@
|
||||
"End bulge": "תפיחה בסוף",
|
||||
"End continuity": "רציפות בסוף",
|
||||
"Equal Distance": "מרחק שווה",
|
||||
"Exact": "מדויק",
|
||||
"Expand": "הרחבה",
|
||||
"Expected result (optional)": "התוצאה הצפויה (אופציונלי)",
|
||||
"Explode": "פירוק",
|
||||
"Explode Text": "פירוק טקסט",
|
||||
"Export 3MF…": "ייצוא 3MF…",
|
||||
"Export Animation…": "ייצוא הנפשה…",
|
||||
"Export Bend Table…": "ייצוא טבלת כיפוף…",
|
||||
"Export CSV…": "ייצוא CSV…",
|
||||
@@ -226,7 +286,12 @@
|
||||
"Export Current View as DXF…": "ייצוא התצוגה הנוכחית כ־DXF…",
|
||||
"Export DXF": "ייצוא DXF",
|
||||
"Export DXF…": "ייצוא DXF…",
|
||||
"Export Design as JSON…": "ייצוא התכן כ-JSON…",
|
||||
"Export JSON…": "ייצוא JSON…",
|
||||
"Export OBJ…": "ייצוא OBJ…",
|
||||
"Export STEP…": "ייצוא STEP…",
|
||||
"Export STL…": "ייצוא STL…",
|
||||
"Export URDF…": "ייצוא URDF…",
|
||||
"Export design library...": "ייצוא ספריית התכן...",
|
||||
"Export hidden edges as dashed lines": "ייצוא מקצועות נסתרים כקווים מקווקווים",
|
||||
"Exported by SolidWorks, which usually saves Y up": "יוצא מ־SolidWorks, ששומרת בדרך כלל עם Y למעלה",
|
||||
@@ -234,13 +299,19 @@
|
||||
"Extend to meet": "הארכה עד למפגש",
|
||||
"Extents": "תחומים",
|
||||
"FILES": "קבצים",
|
||||
"FLANGE": "אוגן",
|
||||
"FLAT": "פריסה",
|
||||
"FLAT PATTERN": "פריסה",
|
||||
"FOLD": "קיפול",
|
||||
"FRAME MEMBER": "איבר מסגרת",
|
||||
"Face": "פאה",
|
||||
"Face Appearance": "מראה פאה",
|
||||
"Face attachment lost": "ההצמדה לפאה אבדה",
|
||||
"Face center": "מרכז הפאה",
|
||||
"Faces": "פאות",
|
||||
"Faces / edges / vertices": "פאות / מקצועות / קודקודים",
|
||||
"Faces on Block": "פאות על בלוק",
|
||||
"Faces on Ring": "פאות על טבעת",
|
||||
"Faces to split": "פאות לפיצול",
|
||||
"Faces/Body": "פאות/גוף",
|
||||
"Factor must be a positive number": "המקדם חייב להיות מספר חיובי",
|
||||
@@ -265,20 +336,30 @@
|
||||
"Flat size": "מידות הפריסה",
|
||||
"Flat size X": "מידות הפריסה X",
|
||||
"Flat size Y": "מידות הפריסה Y",
|
||||
"Flipped": "הפוך",
|
||||
"Follow camera (Look At sketches)": "מעקב אחר המצלמה (הבטה אל סקיצות)",
|
||||
"Follow inherited appearance": "לפי מראה בירושה",
|
||||
"Follow material": "לפי החומר",
|
||||
"Follow the camera (Look At sketches)": "מעקב אחר המצלמה (הבטה אל סקיצות)",
|
||||
"Font": "גופן",
|
||||
"Free Move": "הזזה חופשית",
|
||||
"From": "מ־",
|
||||
"From Bodies": "מגופים",
|
||||
"From Object": "מאובייקט",
|
||||
"From Snap": "מנקודת הצמדה",
|
||||
"From edges": "ממקצועות",
|
||||
"Full Length": "לכל האורך",
|
||||
"Full thread depth": "הברגה לכל העומק",
|
||||
"Further Bends": "כיפופים נוספים",
|
||||
"G2 corners require a constant radius and Exact fit. G2 corner construction is not supported yet.": "פינות G2 דורשות רדיוס קבוע והתאמה מדויקת. בניית פינות G2 עדיין אינה נתמכת.",
|
||||
"GEAR": "גלגל שיניים",
|
||||
"GROOVE": "חריץ",
|
||||
"Gap": "פער",
|
||||
"Glass - Clear": "זכוכית - שקופה",
|
||||
"Glass - Smoked": "זכוכית - מעושנת",
|
||||
"Grid": "רשת",
|
||||
"Groove": "חריץ",
|
||||
"HDR file changed since selection; inspect and select it again before saving": "קובץ ה-HDR השתנה מאז שנבחר; יש לבדוק ולבחור אותו מחדש לפני השמירה",
|
||||
"HISTORY": "ציר הזמן",
|
||||
"Halo under sketch curves": "הילה מתחת לעקומות סקיצה",
|
||||
"Height": "גובה",
|
||||
@@ -287,6 +368,7 @@
|
||||
"Hidden edges": "מקצועות נסתרים",
|
||||
"History layout": "פריסת ציר הזמן",
|
||||
"History names": "שמות בציר הזמן",
|
||||
"Hold click": "לחיצה ממושכת",
|
||||
"Hole (optional)": "חור (אופציונלי)",
|
||||
"Hole Depth": "עומק החור",
|
||||
"Hole Diameter": "קוטר החור",
|
||||
@@ -296,6 +378,8 @@
|
||||
"Horiz./Vert.": "אופקי/אנכי",
|
||||
"I-Beam": "קורת I",
|
||||
"IMAGES": "תמונות",
|
||||
"INSERT PART": "הוספת חלק",
|
||||
"INSERT SVG": "הוספת SVG",
|
||||
"If you are offline or GitHub does not open, use Copy report and keep the text to submit later.": "אם אין חיבור לאינטרנט או ש-GitHub אינו נפתח, יש להשתמש באפשרות «העתקת הדיווח» ולשמור את הטקסט לשליחה מאוחר יותר.",
|
||||
"Import material into design": "ייבוא חומר לתכן",
|
||||
"Import…": "ייבוא…",
|
||||
@@ -304,6 +388,8 @@
|
||||
"In plane": "במישור",
|
||||
"Included": "זווית כלואה",
|
||||
"Inner Sharp": "חד פנימי",
|
||||
"Insert Mesh…": "הוספת רשת…",
|
||||
"Insert STEP…": "הוספת STEP…",
|
||||
"Inside": "בפנים",
|
||||
"Instant": "מיידי",
|
||||
"Interface font": "גופן הממשק",
|
||||
@@ -312,6 +398,8 @@
|
||||
"Isocurve": "איזו־עקומות",
|
||||
"Issue title": "כותרת הדיווח",
|
||||
"Items": "פריטים",
|
||||
"JOINT": "מפרק",
|
||||
"JOINT ORIGIN": "ראשית מפרק",
|
||||
"JOINTS": "מפרקים",
|
||||
"Joint 1": "מפרק 1",
|
||||
"Joint 2": "מפרק 2",
|
||||
@@ -348,9 +436,15 @@
|
||||
"M5 or M5x0.5": "M5 או M5x0.5",
|
||||
"M5, M5x0.5, 1/4-20 UNC": "M5, M5x0.5, 1/4-20 UNC",
|
||||
"MANAGE": "ניהול",
|
||||
"MANAGE PLASTIC RULES": "ניהול כללי פלסטיק",
|
||||
"MATERIALS": "חומרים",
|
||||
"MIDPLANE": "מישור אמצע",
|
||||
"MODEL": "מודל",
|
||||
"MOTION": "תנועה",
|
||||
"MOTION LINK": "קישור תנועה",
|
||||
"MOTION STUDY": "חקר תנועה",
|
||||
"MOTION TRACE": "עקבת תנועה",
|
||||
"MOVE/COPY TEXT": "הזזה/העתקה של טקסט",
|
||||
"Margin": "שוליים",
|
||||
"Mass": "מסה",
|
||||
"Mass Center": "מרכז מסה",
|
||||
@@ -361,6 +455,7 @@
|
||||
"Max": "מקסימום",
|
||||
"Max Thickness": "עובי מרבי",
|
||||
"Maximum": "מקסימום",
|
||||
"Mesh": "רשת",
|
||||
"Mesh body": "גוף רשת משולשים",
|
||||
"Mid Radius": "רדיוס ביניים",
|
||||
"Middle double-click": "לחיצה כפולה אמצעית",
|
||||
@@ -383,6 +478,7 @@
|
||||
"Move holes by clicking or dragging": "הזזה בלחיצה או בגרירה",
|
||||
"Move later": "דחייה",
|
||||
"Move up": "הזזה למעלה",
|
||||
"NEW COMPONENT": "רכיב חדש",
|
||||
"Name": "שם",
|
||||
"Name on import": "שם בעת הייבוא",
|
||||
"Nearest": "הקרוב ביותר",
|
||||
@@ -400,11 +496,19 @@
|
||||
"Nominal Radius": "רדיוס נומינלי",
|
||||
"Nominal diameter": "קוטר נומינלי",
|
||||
"None": "ללא",
|
||||
"Not": "לא",
|
||||
"Not available yet": "עדיין לא זמין",
|
||||
"Not saved yet": "עדיין לא נשמר",
|
||||
"Notch": "חריץ",
|
||||
"Nylon - Natural": "ניילון - טבעי",
|
||||
"OFFSET FACE": "היסט פאה",
|
||||
"OFFSET SURFACE": "היסט משטח",
|
||||
"OUTPUT": "פלט",
|
||||
"Oak": "אלון",
|
||||
"Of": "של",
|
||||
"Offset X": "היסט X",
|
||||
"Offset Y": "היסט Y",
|
||||
"Offset Z": "היסט Z",
|
||||
"On your computer": "במחשב שלך",
|
||||
"Opacity": "אטימות",
|
||||
"Open GitHub": "פתיחת GitHub",
|
||||
@@ -427,10 +531,30 @@
|
||||
"Outer radius": "רדיוס חיצוני",
|
||||
"Outside": "בחוץ",
|
||||
"Overall length": "אורך כולל",
|
||||
"PATCH": "טלאי",
|
||||
"PERPENDICULAR PLANE": "מישור ניצב",
|
||||
"PHYSICAL MATERIAL": "חומר פיזיקלי",
|
||||
"PICK CONTEXT": "בחירת הקשר",
|
||||
"PLANE ALONG PATH": "מישור לאורך מסלול",
|
||||
"PLANE THROUGH THREE POINTS": "מישור דרך שלוש נקודות",
|
||||
"PLANE THROUGH TWO EDGES": "מישור דרך שני מקצועות",
|
||||
"POINT ALONG PATH": "נקודה לאורך מסלול",
|
||||
"POINT AT CENTER": "נקודה במרכז",
|
||||
"POINT AT EDGE AND PLANE": "נקודה במפגש מקצוע ומישור",
|
||||
"POINT AT VERTEX": "נקודה בקודקוד",
|
||||
"POINT THROUGH THREE PLANES": "נקודה דרך שלושה מישורים",
|
||||
"POINT THROUGH TWO EDGES": "נקודה דרך שני מקצועות",
|
||||
"Paint - Blue": "צבע - כחול",
|
||||
"Paint - Green": "צבע - ירוק",
|
||||
"Paint - Orange": "צבע - כתום",
|
||||
"Paint - Purple": "צבע - סגול",
|
||||
"Paint - Red": "צבע - אדום",
|
||||
"Paint - Yellow": "צבע - צהוב",
|
||||
"Param. Graph": "גרף פרמטרים",
|
||||
"Parameter": "פרמטר",
|
||||
"Part length must be greater than zero and at most 2000 mm": "אורך החלק חייב להיות גדול מאפס ולא יותר מ־2000 mm",
|
||||
"Parts": "חלקים",
|
||||
"Parts only": "חלקים בלבד",
|
||||
"Pass through": "מעבר דרך",
|
||||
"Path": "מסלול",
|
||||
"Pattern": "מערך",
|
||||
@@ -446,16 +570,21 @@
|
||||
"Place at the origin instead": "מיקום בראשית במקום זאת",
|
||||
"Planar": "מישורי",
|
||||
"Plastic Rules": "כללי פלסטיק",
|
||||
"Play": "הפעלה",
|
||||
"Play the joint's motion in the view": "הפעלת תנועת המפרק בתצוגה",
|
||||
"Playback": "הפעלה",
|
||||
"Plot": "גרף",
|
||||
"Point on line": "נקודה על קו",
|
||||
"Point on plane": "נקודה על מישור",
|
||||
"Point to Point": "מנקודה לנקודה",
|
||||
"Point to Position": "מנקודה למיקום",
|
||||
"Polycarbonate - Clear": "פוליקרבונט - שקוף",
|
||||
"Position": "מיקום",
|
||||
"Position (0 to 1)": "מיקום (0 עד 1)",
|
||||
"Position (G0)": "מיקום (G0)",
|
||||
"Position along the edge, 0 at the start end, 1 at the other": "מיקום לאורך המקצוע: 0 בקצה ההתחלה, 1 בקצה השני",
|
||||
"Preferences › Design turns the offer back on": "ניתן להפעיל את ההצעה מחדש בהעדפות › תכן",
|
||||
"Preferences…": "העדפות…",
|
||||
"Preview": "תצוגה מקדימה",
|
||||
"Preview Row": "תצוגה מקדימה של השורה",
|
||||
"Preview leaves the current design unchanged.": "התצוגה המקדימה אינה משנה את התכן הנוכחי.",
|
||||
@@ -464,12 +593,21 @@
|
||||
"Profiles/Curves": "פרופילים/עקומות",
|
||||
"Quantity": "כמות",
|
||||
"Quantity must be at least 2": "הכמות חייבת להיות 2 לפחות",
|
||||
"RACK AND PINION": "מסרק וגלגל שיניים",
|
||||
"RECENT": "אחרונים",
|
||||
"REFERENCE": "ייחוס",
|
||||
"REFOLD": "קיפול מחדש",
|
||||
"REMOVE": "הסרה",
|
||||
"RENAME": "שינוי שם",
|
||||
"REPLACE FACE": "החלפת פאה",
|
||||
"REST": "משען",
|
||||
"REVERSE NORMAL": "היפוך נורמל",
|
||||
"RIGID GROUP": "קבוצה קשיחה",
|
||||
"RULED SURFACE": "משטח ישרים",
|
||||
"RULES": "כללים",
|
||||
"Radius": "רדיוס",
|
||||
"Radius Fit": "התאמת רדיוס",
|
||||
"Range": "טווח",
|
||||
"Ratio": "יחס",
|
||||
"Read-only list: shortcuts can't be changed here.": "רשימה לקריאה בלבד: לא ניתן לשנות כאן קיצורי דרך.",
|
||||
"Reattach selected face": "הצמדה מחדש לפאה שנבחרה",
|
||||
@@ -477,6 +615,7 @@
|
||||
"Recover unsaved design?": "לשחזר תכן שלא נשמר?",
|
||||
"Rectangular": "מלבני",
|
||||
"Rectangular Tube": "צינור מלבני",
|
||||
"Reduce to Fit": "הקטנה להתאמה",
|
||||
"Reference face": "פאת ייחוס",
|
||||
"Reference plane": "מישור ייחוס",
|
||||
"Relief Depth": "עומק הקלה",
|
||||
@@ -500,6 +639,7 @@
|
||||
"Repair Part": "תיקון חלק",
|
||||
"Replace existing library file": "החלפת קובץ הספרייה הקיים",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "דיווח על באג — בדיקת צילום מסך ופתיחת טיוטה ב־GitHub",
|
||||
"Report an Issue…": "דיווח על בעיה…",
|
||||
"Report copied. Keep this text to submit on GitHub when you are online.": "הדיווח הועתק. יש לשמור את הטקסט כדי לשלוח אותו ב-GitHub כשהחיבור לאינטרנט יהיה זמין.",
|
||||
"Report text copied. Paste it into GitHub and submit your report there.": "טקסט הדיווח הועתק. יש להדביק אותו ב־GitHub ולשלוח את הדיווח שם.",
|
||||
"Reports are public. A GitHub account is required.": "הדיווחים ציבוריים. נדרש חשבון GitHub.",
|
||||
@@ -532,16 +672,32 @@
|
||||
"Ring of tools on the right button": "טבעת כלים בלחצן הימני",
|
||||
"Roll Back Here": "החזרת ציר הזמן עד כאן",
|
||||
"Roll History Marker Here": "העברת סמן ציר הזמן לכאן",
|
||||
"Rolling": "מתגלגל",
|
||||
"Root radius": "רדיוס שורש",
|
||||
"Rotate": "סיבוב",
|
||||
"Round": "עגול",
|
||||
"Round Bar": "מוט עגול",
|
||||
"Round Tube": "צינור עגול",
|
||||
"Rubber - Black": "גומי - שחור",
|
||||
"Rule": "כלל",
|
||||
"Rule name": "שם הכלל",
|
||||
"Rules": "כללים",
|
||||
"Rules run from top to bottom. Matched drives change effective values.": "הכללים פועלים מלמעלה למטה. כללי קביעה שתנאיהם מתקיימים משנים את הערכים בפועל.",
|
||||
"SCREW": "בורג",
|
||||
"SECTION": "חתך",
|
||||
"SHEET METAL RULES": "כללי פח",
|
||||
"SNAP FIT": "חיבור הצמדה",
|
||||
"SPLIT FACE": "פיצול פאה",
|
||||
"STITCH": "תפירה",
|
||||
"STOCK": "חומר גלם",
|
||||
"SURFACE EXTRUDE": "שליפת משטח",
|
||||
"SURFACE LOFT": "לופט משטח",
|
||||
"SURFACE QUALITY": "איכות משטח",
|
||||
"SURFACE REVOLVE": "סיבוב משטח",
|
||||
"SURFACE SWEEP": "סחיפת משטח",
|
||||
"Same way": "באותו כיוון",
|
||||
"Save As JSON…": "שמירה בשם JSON…",
|
||||
"Save As…": "שמירה בשם…",
|
||||
"Save Rule": "שמירת הכלל",
|
||||
"Save View": "שמירת תצוגה",
|
||||
"Save screenshot…": "שמירת צילום המסך…",
|
||||
@@ -550,6 +706,9 @@
|
||||
"Scale factor": "מקדם קנה מידה",
|
||||
"Scene": "סצנה",
|
||||
"Screenshot (optional)": "צילום מסך (אופציונלי)",
|
||||
"Screenshot captured locally. Review it before attaching it on GitHub.": "צילום המסך נשמר מקומית. יש לבדוק אותו לפני צירופו ב-GitHub.",
|
||||
"Scroll left": "גלילה שמאלה",
|
||||
"Scroll right": "גלילה ימינה",
|
||||
"Search": "חיפוש",
|
||||
"Search commands": "חיפוש פקודות",
|
||||
"Search this library": "חיפוש בספרייה זו",
|
||||
@@ -581,6 +740,7 @@
|
||||
"Send your report on GitHub": "שליחת הדוח ב־GitHub",
|
||||
"Shaded": "מוצלל",
|
||||
"Shape": "צורה",
|
||||
"Sheet Metal": "פח",
|
||||
"Sheet Rules": "כללי פח",
|
||||
"Show Agent Cursor": "הצגת סמן הסוכן",
|
||||
"Show ghost": "הצגת צללית",
|
||||
@@ -608,6 +768,7 @@
|
||||
"Speed": "מהירות",
|
||||
"Splitting tool": "כלי פיצול",
|
||||
"Square": "ריבוע",
|
||||
"Stainless Steel": "פלדת אל-חלד",
|
||||
"Stand it up": "העמדה",
|
||||
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "העמדה: רבע סיבוב סביב X, כפיצ'ר הזזה אחד שאפשר לבטל.",
|
||||
"Stand this assembly up?": "להעמיד את המכלול הזה?",
|
||||
@@ -616,6 +777,8 @@
|
||||
"Start bulge": "תפיחה בהתחלה",
|
||||
"Start continuity": "רציפות בהתחלה",
|
||||
"Stationary Side": "צד קבוע",
|
||||
"Steel": "פלדה",
|
||||
"Steel - Brushed": "פלדה - מוברשת",
|
||||
"Step": "צעד",
|
||||
"Steps": "צעדים",
|
||||
"Steps to reproduce (optional)": "שלבים לשחזור (אופציונלי)",
|
||||
@@ -626,12 +789,19 @@
|
||||
"Style": "סגנון",
|
||||
"Summary": "סיכום",
|
||||
"Suppress": "השבתה",
|
||||
"Suppress Features": "השבתת תכונות",
|
||||
"Suppress Selected Folders": "השבתת התיקיות שנבחרו",
|
||||
"Suppressed": "מושבת",
|
||||
"Suppression": "השבתה",
|
||||
"Surface": "משטח",
|
||||
"Surface or face": "משטח או פאה",
|
||||
"Surface or faces": "משטח או פאות",
|
||||
"Surface, or faces to turn over": "משטח, או פאות להיפוך",
|
||||
"Surfaces": "משטחים",
|
||||
"Swap Ends": "החלפת קצוות",
|
||||
"TANGENT PLANE": "מישור משיק",
|
||||
"THICKEN": "עיבוי",
|
||||
"TRANSFORM": "התמרה",
|
||||
"TRIM": "גיזום",
|
||||
"Tangent": "משיק",
|
||||
"Tangent (G1)": "משיק (G1)",
|
||||
@@ -639,9 +809,11 @@
|
||||
"Tangent chain": "שרשרת משיקה",
|
||||
"Tangent to Edge": "משיק למקצוע",
|
||||
"Tap drill diameter": "קוטר קידוח להברגה",
|
||||
"Taper to Fit": "הצרה להתאמה",
|
||||
"Target": "יעד",
|
||||
"Target Body": "גוף יעד",
|
||||
"Target Face (optional)": "פאת יעד (אופציונלי)",
|
||||
"Target edges": "מקצועות יעד",
|
||||
"Tear": "קריעה",
|
||||
"Technical details": "פרטים טכניים",
|
||||
"The dashed sketch is a saved plane guide. Select a new face to repair the attachment.": "הסקיצה המקווקוות מסמנת מישור שמור. יש לבחור פאה חדשה כדי לתקן את ההצמדה.",
|
||||
@@ -649,6 +821,7 @@
|
||||
"The direction must be an axis or a straight sketch curve that lies in the sketch plane": "הכיוון חייב להיות ציר או עקומת סקיצה ישרה הנמצאת במישור הסקיצה",
|
||||
"The mirror line cannot also be mirrored": "אי אפשר לשקף גם את קו השיקוף עצמו",
|
||||
"The mirror line must be a straight curve of this sketch, not an origin axis": "קו השיקוף חייב להיות עקומה ישרה של הסקיצה הזו, ולא אחד מצירי הראשית",
|
||||
"The origin axis is perpendicular to the sketch plane, so it has no line in the plane": "ציר הראשית ניצב למישור הסקיצה, ולכן אין לו קו במישור",
|
||||
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "מופע היצירה השמור חסר. יש לבחור מיקום יצירה תקין לפני הקישור מחדש.",
|
||||
"Then": "אז",
|
||||
"These features will be deleted:": "הפיצ'רים הבאים יימחקו:",
|
||||
@@ -680,16 +853,21 @@
|
||||
"Top Left": "למעלה משמאל",
|
||||
"Top Middle": "למעלה באמצע",
|
||||
"Top Right": "למעלה מימין",
|
||||
"Top level": "רמה עליונה",
|
||||
"Trimming tool (plane, face or curves)": "כלי גיזום (מישור, פאה או עקומות)",
|
||||
"Turn off section analysis before exporting the view.": "יש לכבות את ניתוח החתך לפני ייצוא התצוגה.",
|
||||
"Two Distances": "שני מרחקים",
|
||||
"Type": "סוג",
|
||||
"Type a distance or an angle to move by": "יש להקליד מרחק או זווית להזזה",
|
||||
"UNFOLD": "פתיחת קיפול",
|
||||
"UNSTITCH": "ביטול תפירה",
|
||||
"UNTRIM": "ביטול גיזום",
|
||||
"Unit": "יחידה",
|
||||
"Unsuppress": "ביטול השבתה",
|
||||
"Unsuppress Selected Folders": "ביטול השבתת התיקיות שנבחרו",
|
||||
"Updating the preview": "התצוגה המקדימה מתעדכנת",
|
||||
"Use active author placement": "שימוש במיקום היצירה הפעיל",
|
||||
"Use axis direction only": "השתמש בכיוון הציר בלבד",
|
||||
"Use fixed plane": "שימוש במישור קבוע",
|
||||
"Use saved fixed plane": "שימוש במישור הקבוע השמור",
|
||||
"Use selected face as fixed plane": "שימוש בפאה הנבחרת כמישור קבוע",
|
||||
@@ -699,13 +877,16 @@
|
||||
"VIEWS": "תצוגות",
|
||||
"Value": "ערך",
|
||||
"Variable Radius": "רדיוס משתנה",
|
||||
"Variable radius": "רדיוס משתנה",
|
||||
"Velocity": "מהירות",
|
||||
"Vertices": "קודקודים",
|
||||
"View": "תצוגה",
|
||||
"View navigator": "ניווט תצוגה",
|
||||
"Volume": "נפח",
|
||||
"Wall": "דופן",
|
||||
"Wall Location": "מיקום הדופן",
|
||||
"Wall Thickness": "עובי דופן",
|
||||
"Walnut": "אגוז",
|
||||
"Web thickness": "עובי דופן",
|
||||
"What did you do just before the problem? A short list is enough": "מה עשית רגע לפני הבעיה? רשימה קצרה מספיקה",
|
||||
"What did you expect to see?": "מה ציפית לראות?",
|
||||
@@ -740,6 +921,9 @@
|
||||
"a picked edge's body is gone": "הגוף של המקצוע שנבחר כבר לא קיים",
|
||||
"add a key for a joint first": "יש להוסיף תחילה מפתח למפרק",
|
||||
"ai.connected_toast": "SolidCraft זמין עכשיו ב־{clients}. יש להפעיל אותם מחדש כדי להשתמש בו (הגדרות > עוזר).",
|
||||
"ai.http.allow": "אפשר HTTP לא מאובטח",
|
||||
"ai.http.confirm": "אפשר עבור נקודת קצה זו",
|
||||
"ai.http.warning": "מפתח ה־API והשיחה יישלחו ללא הצפנה. כל מי שיש לו גישה לרשת יכול לקרוא או לשנות אותם. יש להשתמש באפשרות זו רק ברשת מהימנה.",
|
||||
"ai.prefs.allow_control": "לאפשר לסוכני AI להתחבר",
|
||||
"ai.prefs.allow_control_note": "ערוץ מקומי (MCP). שום דבר לא יוצא מהמחשב הזה.\n\nפותח ערוץ בקרה במחשב זה בלבד (127.0.0.1, פורט 27878 או הפנוי הבא). תוכנה שמתחברת צריכה גם להציג אסימון סודי שרק חשבון המשתמש שלך יכול לקרוא, ונוצר מחדש בכל הפעלה. ייכנס לתוקף בהפעלה הבאה של SolidCraft. כבוי: שום דבר אינו מאזין, אלא אם SolidCraft מופעל עם --control.",
|
||||
"ai.prefs.availability": "זמינות",
|
||||
@@ -751,6 +935,8 @@
|
||||
"ai.prefs.follow_camera": "לעקוב אחר המצלמה",
|
||||
"ai.prefs.follow_camera_off": "מעקב המצלמה האוטומטי כבוי.",
|
||||
"ai.prefs.follow_camera_on": "המצלמה פונה אל הסקיצות שהסוכן יוצר.",
|
||||
"ai.prefs.instructions": "הוראות מותאמות אישית",
|
||||
"ai.prefs.instructions_note": "נשלחות לסוכן בתחילת כל צ'אט, אחרי ההוראות של SolidCraft עצמה: דרך העבודה המועדפת עליך וכללים שיש להקפיד עליהם. שינוי חל מהצ'אט הבא (התחברות או צ'אט חדש).",
|
||||
"ai.prefs.intro": "סוכני AI ב־SolidCraft.",
|
||||
"ai.prefs.make_default": "לקבוע כברירת מחדל",
|
||||
"ai.prefs.no_agents": "אין עדיין סוכנים. אפשר להוסיף סוכן או לרענן כדי למצוא סוכנים מותקנים.",
|
||||
@@ -1035,6 +1221,7 @@
|
||||
"cmd.occurrence_move": "הזזת מופע",
|
||||
"cmd.occurrence_properties": "תכונות פיזיקליות של מופע",
|
||||
"cmd.occurrence_reparent": "העברה לרכיב",
|
||||
"cmd.panel.action": "הפעלת פעולה בכותרת הלוח",
|
||||
"cmd.panel.activate": "הפעלת לשונית חלונית",
|
||||
"cmd.panel.close": "סגירת חלונית",
|
||||
"cmd.panel.collapse": "כיווץ מקטע חלונית",
|
||||
@@ -1081,6 +1268,9 @@
|
||||
"cmd.render.compare.divider": "הזז את מחיצת ההשוואה של הרינדור",
|
||||
"cmd.render.result.discard": "השלך תוצאת רינדור",
|
||||
"cmd.render.result.select": "בחר תוצאת רינדור",
|
||||
"cmd.render.surface.import": "ייבוא מפות משטח",
|
||||
"cmd.render.texture.list": "הצגת תמונות משטח מוטמעות",
|
||||
"cmd.render.texture.remove": "הסרת תמונת משטח מוטמעת",
|
||||
"cmd.render_aging_describe": "הזדקנות משטח בשכבות",
|
||||
"cmd.render_backends": "מנועי הדמיה",
|
||||
"cmd.render_batch": "רינדור כל הצילומים",
|
||||
@@ -1219,6 +1409,7 @@
|
||||
"cmd.sketch_move": "הזזה/העתקה של ישויות סקיצה",
|
||||
"cmd.sketch_move3d": "הזזת נקודה תלת־ממדית",
|
||||
"cmd.sketch_move_point": "גרירת נקודת סקיצה",
|
||||
"cmd.sketch_move_points": "גרירת נקודות סקיצה",
|
||||
"cmd.sketch_offset_break": "ניתוק הקשר להיסט",
|
||||
"cmd.sketch_options": "אפשרויות חלונית הסקיצה",
|
||||
"cmd.sketch_paste": "הדבקת ישויות סקיצה",
|
||||
@@ -1324,11 +1515,13 @@
|
||||
"cmd.view_previous_tab": "הכרטיסייה הקודמת",
|
||||
"cmd.view_rename": "שינוי שם תצוגה שמורה",
|
||||
"cmd.view_render_frame": "מדריכי מסגרת רינדור",
|
||||
"cmd.view_ribbon_tabs_only": "רק לשוניות הסרט",
|
||||
"cmd.view_right": "תצוגה ימנית",
|
||||
"cmd.view_save": "תצוגה שמורה חדשה",
|
||||
"cmd.view_shots_strip": "סרגל צילומים",
|
||||
"cmd.view_spin": "סיבוב מבט רציף",
|
||||
"cmd.view_tool": "כלי ניווט",
|
||||
"cmd.view_tool_zoom": "שינוי הזום בגרירה",
|
||||
"cmd.view_top": "תצוגה עליונה",
|
||||
"cmd.view_visual_style": "סגנון תצוגה",
|
||||
"color.blue": "כחול",
|
||||
@@ -1419,6 +1612,9 @@
|
||||
"document.changed_on_disk": "{name} השתנה בדיסק",
|
||||
"document.changed_on_disk_detail": "{name} שונה על ידי תוכנה או מחשב אחרים מאז שנפתח או נשמר לאחרונה. דריסה מחליפה את השינויים האלה בשינויים שלך; שמירת עותק שומרת את שניהם.",
|
||||
"document.copy_name": "עותק של {name}",
|
||||
"document.newer_file": "נשמר בגרסה חדשה יותר של SolidCraft (פורמט {format}, גרסה זו קוראת עד {reads}). אם משהו חסר, יש לעדכן.",
|
||||
"document.newer_file_change": "נשמר בגרסה חדשה יותר של SolidCraft (פורמט {format} עם `{change}`, שגרסה זו אינה מכירה). אם משהו חסר, יש לעדכן.",
|
||||
"document.newer_file_not_read": "לא נקרא: {what}. השמירה משאירה אותו בקובץ.",
|
||||
"document.open_anyway": "פתיחה בכל זאת",
|
||||
"document.open_elsewhere": "{name} כבר פתוח",
|
||||
"document.open_elsewhere_detail": "{name} פתוח ב־SolidCraft {app} אצל {who}, נפתח {when}. אם שניכם תשמרו, אחד מכם יאבד עבודה. יש לפתוח אותו לקריאה בלבד כדי לעיין בו בלי לדרוס אותו.",
|
||||
@@ -1454,8 +1650,11 @@
|
||||
"file.open.portal_fallback.xwayland": "בורר הקבצים שנפתח אינו של שולחן העבודה: תחת XWayland הפורטל דוחה את חלון האב.",
|
||||
"file_load.built": "נבנו עד כה: {built} מתוך {total} פיצ'רים.",
|
||||
"file_load.converting": "המרת גוף מוצק {done} מתוך {total}",
|
||||
"file_load.inserted": "{name} הוכנס",
|
||||
"file_load.inserting": "הוספת גאומטריה",
|
||||
"file_load.link_item": "{name} ({done} מתוך {total})",
|
||||
"file_load.opened": "{name} נפתח",
|
||||
"file_load.opened_in_tab": "{name} נפתח בלשונית חדשה",
|
||||
"file_load.opening": "פותח את התכן",
|
||||
"file_load.preparing_thumbnail": "מכין את התמונה הממוזערת...",
|
||||
"file_load.preparing_view": "מכין את התצוגה...",
|
||||
@@ -1691,7 +1890,7 @@
|
||||
"help.sketch.insert_svg": "קריאת הנתיבים של שרטוט SVG לתוך סקיצה, בקנה המידה, בסיבוב ובמיקום שנקבעו. כך הופכים לוגו או קו מתאר וקטורי לעקומות שניתן לשחל.",
|
||||
"help.sketch.inspect": "פותח דוח על הסקיצה הפתוחה: כמה דרגות חופש נותרו והאם היא מאולצת במלואה. השתמשו בו כדי לגלות מה עדיין זז לפני שמסיימים את הסקיצה.",
|
||||
"help.sketch.intersect": "מוסיף את העקומות או הנקודות שבהן גופים, פאות, קצוות או סקיצות אחרות חותכים את מישור הסקיצה. הן נשארות מקושרות ועוקבות אחר המודל כשהוא משתנה.",
|
||||
"help.sketch.intersection_curve": "יוצר עקומות תלת־ממדיות במקום שבו שני גופים או פאות נפגשים, מקושרות אליהם. מתאים למסלולים או למסילות הנחיה שעוקבים אחר המודל.",
|
||||
"help.sketch.intersection_curve": "יוצר עקומות תלת־ממדיות במקום שבו שני גופים, פאות או עקומות של סקיצות אחרות נפגשים, מקושרות אליהם. מתאים למסלולים או למסילות הנחיה שעוקבים אחר המודל.",
|
||||
"help.sketch.isoparametric_curve": "יוצר עקומה תלת־ממדית לאורך כיוון U או V של פאה דרך נקודה שבוחרים, מקושרת לפאה. מתאים למסלול או לקו פיצול שעוקבים אחר המשטח.",
|
||||
"help.sketch.line": "משרטט ישרים מחוברים נקודה אחר נקודה, כשרשרת פתוחה או כקו מתאר סגור. זה הכלי הבסיסי לרוב הפרופילים.",
|
||||
"help.sketch.line.midpoint": "משרטט ישר מנקודת האמצע שלו אל אחד הקצוות, כשהקצה השני משוקף. מתאים לישרים הממורכזים סביב נקודה, כמו קצה סימטרי.",
|
||||
@@ -1797,6 +1996,7 @@
|
||||
"help.view.navigate": "סיבוב, הזזה או זום של התצוגה בצעדים, כמו רבע סיבוב שמאלה. מתאים להזזה מדויקת של המצלמה במקום גרירה.",
|
||||
"help.view.navigation_bar": "הצגה או הסתרה של סרגל הניווט בתחתית חלון התצוגה. אפשר להסתיר אותו לשטח נוסף, או להציג אותו כדי להגיע אל כלי התצוגה בעכבר.",
|
||||
"help.view.rename": "מתן שם חדש לתצוגה שמורה. התצוגה עצמה נשארת כפי שהיא.",
|
||||
"help.view.ribbon_tabs_only": "מקפל את הסרט לשורת הלשוניות שלו, כך שחלון התצוגה מקבל את המקום שתפסו הלוחות. לחיצה על לשונית מציגה את הלוחות שלה מעל התצוגה; בחירה חוזרת בפקודה משאירה את הסרט כולו מוצג.",
|
||||
"help.view.save": "שמירת מיקום המצלמה הנוכחי כתצוגה בשם בתכן. מתאים לחזרה לאותה זווית בהמשך, למשל לצילומי מסך.",
|
||||
"help.view.spin": "הקפת המודל בתצוגה באופן רציף עד ללחיצה או למקש הבא. מתאים להצגת מודל מכל הצדדים.",
|
||||
"help.view.tool": "נעילת כלי מצלמה, מסלול, הזזה או זום, כך שגרירה בכפתור השמאלי של העכבר מזיזה את התצוגה עד שמכבים אותו. מתאים למשטח מגע או כשאין כפתור עכבר אמצעי.",
|
||||
@@ -2163,6 +2363,7 @@
|
||||
"navbar.value_tip": "{name} · {value}",
|
||||
"navbar.visual_style": "סגנון תצוגה",
|
||||
"navbar.zoom": "זום",
|
||||
"navbar.zoom_hold_tip": "זום (החזקת קיצור המקשים)",
|
||||
"needs two joints": "נדרשים שני מפרקים",
|
||||
"network.allow_once": "התרה פעם אחת",
|
||||
"network.always_allow": "התרה תמיד לשרת הזה",
|
||||
@@ -2316,6 +2517,7 @@
|
||||
"prefs.carousel_previous": "הקודם",
|
||||
"prefs.choose_font": "בחירת קובץ גופן…",
|
||||
"prefs.click": "לחיצה",
|
||||
"prefs.command_dialog_values": "תיבות דו-שיח של פקודות",
|
||||
"prefs.contact_shading": "הצללת מגע רכה",
|
||||
"prefs.contact_shading_note": "הצללה רכה במקומות שבהם חלקים מתקרבים.",
|
||||
"prefs.context_menu": "תפריט הקשר",
|
||||
@@ -2362,6 +2564,8 @@
|
||||
"prefs.edges": "מקצועות והצללה",
|
||||
"prefs.extend": "הרחבה",
|
||||
"prefs.extra_navigation": "Ctrl עם גרירה שמאלית במקום ריק מסובב את המבט; החזקת Space מזיזה אותו.",
|
||||
"prefs.extrude_arrow_face_stops": "חץ השליפה נעצר בפאות",
|
||||
"prefs.extrude_arrow_face_stops_note": "גרירת חץ המרחק של השליפה נעצרת בפאות שטוחות המקבילות לפרופיל, כמו הצד הרחוק של גוש שנחתך, ומדגישה את קו המתאר של הפאה. כבוי: החץ זז בצעדים עגולים בלבד.",
|
||||
"prefs.extrude_view_isometric": "סיבוב התצוגה לאיזומטרית בפתיחת שליפה",
|
||||
"prefs.extrude_view_return": "חזרה לתצוגה הקודמת בסגירת שליפה",
|
||||
"prefs.follows_os": "לפי מערכת ההפעלה",
|
||||
@@ -2411,6 +2615,8 @@
|
||||
"prefs.graphics_page.surfaces_edges": "משטחים וקצוות",
|
||||
"prefs.graphics_page.view_bar": "גם בסרגל התצוגה",
|
||||
"prefs.graphics_presets_note": "הגדרות קבועות מראש קובעות יחד את הבחירות שלמטה.",
|
||||
"prefs.ground_shadow_follows_model": "צל הקרקע עוקב אחר המודל",
|
||||
"prefs.ground_shadow_follows_model_help": "מקם את הצל מתחת לגוף הגלוי הנמוך ביותר. כשהאפשרות כבויה, הצל של מודלים מוגבהים נשאר במישור הקרקע העובר בראשית הצירים.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "הסתרת סמלי אילוצים בהקטנה",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out_note": "סמלי האילוצים נעלמים כשהשרטוט מוקטן מספיק כדי שלא יעמיסו על התצוגה. כבוי: הם נשארים בגודל מסך קבוע בכל זום.",
|
||||
"prefs.history": "ציר הזמן",
|
||||
@@ -2452,6 +2658,8 @@
|
||||
"prefs.layout_page.card": "כרטיס",
|
||||
"prefs.layout_page.column": "עמודה",
|
||||
"prefs.layout_page.hover": "מעבר עכבר",
|
||||
"prefs.layout_page.hover_panel_menus": "מעבר בין תפריטי לוחות בריחוף",
|
||||
"prefs.layout_page.hover_panel_menus_help": "לחצו על שם לוח ברצועת הכלים כדי לפתוח את התפריט שלו. העבירו את הסמן מעל שם של לוח אחר כדי לעבור לתפריט שלו. הזיזו את הסמן אל מחוץ לרצועת הכלים ולתפריט הפתוח כדי לסגור אותו.",
|
||||
"prefs.layout_page.icons_labels": "סמלים + תוויות",
|
||||
"prefs.layout_page.ok_cancel": "אישור וביטול",
|
||||
"prefs.layout_page.on_hover": "בעת מעבר עכבר",
|
||||
@@ -2490,6 +2698,8 @@
|
||||
"prefs.mouse": "עכבר ומשטח מגע",
|
||||
"prefs.mouse_heading": "עכבר",
|
||||
"prefs.mouse_mapping": "מיפוי העכבר",
|
||||
"prefs.move_drag_highlights": "הדגשת מה שמתחת לסמן בזמן שחצי ההזזה גוררים רכיב",
|
||||
"prefs.move_drag_highlights_tip": "כבוי: בזמן שחצי ההזזה גוררים רכיב, דבר אינו מודגש מתחת לסמן, וכך מכלולים גדולים נשארים חלקים. כשמוצג חתך או ניתוח ההדגשה נשארת, וחלק שנגרר לאורך המפרקים שלו לעולם אינו מדגיש.",
|
||||
"prefs.navigation": "ניווט",
|
||||
"prefs.navigation.axes_signed": "צירים",
|
||||
"prefs.navigation.cube_compact": "קובייה",
|
||||
@@ -2518,6 +2728,22 @@
|
||||
"prefs.persistent_ribbon_pressed": "הדגשת פקודות פעילות ברצועת הכלים",
|
||||
"prefs.persistent_ribbon_pressed_help": "הדגשת פקודות ברצועת הכלים כל עוד ההשפעות שלהן פעילות. כבו אפשרות זו כדי לשמור על המראה הניטרלי הקודם ועל עזרת הפקודות.",
|
||||
"prefs.perspective_fov": "שדה ראייה",
|
||||
"prefs.picker.carousel": "קרוסלה",
|
||||
"prefs.picker.change": "שינוי",
|
||||
"prefs.picker.clear": "ניקוי מסננים",
|
||||
"prefs.picker.day": "ערכת נושא ליום",
|
||||
"prefs.picker.empty": "אין ערכת נושא שתואמת למסננים האלה.",
|
||||
"prefs.picker.follow": "לפי המערכת",
|
||||
"prefs.picker.grid": "רשת",
|
||||
"prefs.picker.high": "ניגודיות גבוהה",
|
||||
"prefs.picker.high_note": "{source} · ניגודיות גבוהה",
|
||||
"prefs.picker.hint": "מתחלפת בהתאם למערכת ההפעלה.",
|
||||
"prefs.picker.import": "ייבוא קובץ ערכת נושא…",
|
||||
"prefs.picker.imported": "מיובאות",
|
||||
"prefs.picker.night": "ערכת נושא ללילה",
|
||||
"prefs.picker.one": "ערכת נושא אחת",
|
||||
"prefs.picker.pick": "בחירה מהרשימה למטה",
|
||||
"prefs.picker.standard": "רגילה",
|
||||
"prefs.placement": "מיקום",
|
||||
"prefs.plugins_enabled": "מופעל",
|
||||
"prefs.plugins_installed": "מותקנים",
|
||||
@@ -2554,6 +2780,8 @@
|
||||
"prefs.quality_smoother": "חלק יותר",
|
||||
"prefs.quick_measure": "מדידה מהירה בפינת התצוגה",
|
||||
"prefs.quick_measure_note": "מציג את מידות הבחירה בפינה הימנית התחתונה של התצוגה, מעל ההיסטוריה: אורך מקצוע (או Ø ו-R למקצוע עגול), שטח פאה, מיקום נקודה, המרחק והזווית בין שני פריטים, והסכום של כמה מקצועות או פאות. לחיצה מעתיקה אותו.",
|
||||
"prefs.remember_dialog_values": "זכור ערכים בתיבות דו-שיח של פקודות",
|
||||
"prefs.remember_dialog_values.help": "כבוי כברירת מחדל. כשהוא פועל, לאחר הצלחת פקודה, הערכים והאפשרויות הבטוחים שלה ישמשו שוב בפעם הבאה שתפתח את אותה פקודה, עד לסגירת SolidCraft.",
|
||||
"prefs.render_previous_geometry_budget": "שימוש בתקציב הגאומטריה הקודם לתצוגה מקדימה של רינדור",
|
||||
"prefs.render_previous_geometry_budget_help": "שחזור מגבלת הגאומטריה הקבועה הקודמת (512 MiB במחשב שולחני של 64 סיביות, 128 MiB בפלטפורמות אחרות) במקום התקציב המבוסס על ההתקן.",
|
||||
"prefs.reports_saved": "{n} שמורים",
|
||||
@@ -2562,6 +2790,8 @@
|
||||
"prefs.ribbon_click_ends_tool_note": "לחיצה על לחצן הסרט המודגש של כלי השרטוט שבשימוש מסיימת את הכלי, כך שלחיצות בתצוגה בוחרות שוב. כבוי: הלחיצה מפעילה את הכלי מחדש.",
|
||||
"prefs.ribbon_reveal": "הצגת תוצאות חיפוש בסרט",
|
||||
"prefs.ribbon_reveal_note": "מעבר עם הסמן מעל תוצאה בחיפוש פקודות מראה היכן הפקודה נמצאת בסרט: הלשונית, הכפתור או השורה שלה בתפריט הנפתח של לוח. פקודה שהופעלה מהחיפוש מהבהבת שם, ופקודה פעילה מתפריט נפתח מסמנת את הלוח שלה.",
|
||||
"prefs.ribbon_tabs_only": "הצגת לשוניות הסרט בלבד",
|
||||
"prefs.ribbon_tabs_only_note": "הסרט מתקפל לשורת הלשוניות שלו. לחיצה על לשונית מציגה את הלוחות שלה מעל התצוגה עד שפקודה מתחילה או שהמצביע עוזב אותם. הפקודה ״רק לשוניות הסרט״ בלשונית תצוגה, או קיצור המקשים שלה, מחליפה הגדרה זו.",
|
||||
"prefs.right_click": "לחיצה ימנית",
|
||||
"prefs.saving": "הפעלה ושמירה",
|
||||
"prefs.scale_note": "גם Ctrl + / Ctrl − בחלון. הכפתורים בגודל 28 px ב־100%.",
|
||||
@@ -2633,6 +2863,7 @@
|
||||
"prefs.sketch_exit_view_note": "לאן התצוגה עוברת אחרי סיום סקיצה, ביטול או Esc כפול.",
|
||||
"prefs.sketch_face_activates_part": "שרטוט על פאה של חלק הופך את החלק לפעיל",
|
||||
"prefs.sketch_face_activates_part_note": "שרטוט שמתחיל על פאה של גוף ברכיב אחר מפעיל קודם את הרכיב הזה, כך שהשרטוט שייך לו ונשאר עם החלק בסייר. החלק נשאר פעיל אחר כך. כבוי: השרטוט שייך לרכיב הפעיל.",
|
||||
"prefs.sketch_mirror_auto_line": "שימוש בישר העזר שנבחר מראש כציר שיקוף",
|
||||
"prefs.sketch_open_look_at": "הפניית התצוגה אל הסקיצה בפתיחתה",
|
||||
"prefs.sketch_open_look_at_note": "כבוי: יצירה או עריכה של סקיצה משאירות את התצוגה כפי שהיא. «מבט אל» מפנה את התצוגה אל הסקיצה כשרוצים.",
|
||||
"prefs.sketch_recent_snap": "הצמדה אוטומטית למרכזים ולנקודות אמצע אחרונים",
|
||||
@@ -3914,6 +4145,7 @@
|
||||
"render.stage_panels.extent": "ממדי התחום",
|
||||
"render.stage_panels.fit_save": "התאמה ושמירה",
|
||||
"render.stage_panels.gpu_required": "נדרש מעקב נתיב ב-GPU",
|
||||
"render.stage_panels.grain_angle": "זווית סיבי העץ",
|
||||
"render.stage_panels.keep": "שמור",
|
||||
"render.stage_panels.kept": "נשמר",
|
||||
"render.stage_panels.kept_fields": "מצלמה, דגימה, ממדים, קישורים אופטיים, גאומטריית CAD ותכונות הנדסיות.",
|
||||
@@ -4032,6 +4264,19 @@
|
||||
"render.subsurface_enable": "הילוך אקראי לפיזור פנימי",
|
||||
"render.subsurface_help": "לדיאלקטרי סגור בלבד. פיזור ובליעת RGB הם מקדמים נפרדים לכל mm. המסלולים מתפזרים בתוך הגאומטריה בפועל, ואז נשברים או מוחזרים בגבול. יש לבטל העברת צל ישרה, הסרת רעש משטחית ונפח חיצוני; ולהשתמש בשכבת פרנל אחת. אינו שיידר דיפוזיה, אינו רשת צפיפות ואינו חומר מדוד. תווך צפוף עשוי לדרוש יותר דגימות ועומק מסלול.",
|
||||
"render.subsurface_rate": "קצב התנגשויות הפיזור",
|
||||
"render.surface_import.author": "יוצר",
|
||||
"render.surface_import.busy": "מתבצע ייבוא תמונה…",
|
||||
"render.surface_import.complete": "בחר תמונה והזן את היוצר, המקור והרישיון שלה.",
|
||||
"render.surface_import.embedded": "תמונה מיובאת",
|
||||
"render.surface_import.failed": "ייבוא התמונה לא הושלם.",
|
||||
"render.surface_import.license": "רישיון / בעלות",
|
||||
"render.surface_import.raytrace_required": "תמונות מחייבות מעקב קרניים ב־CPU או ב־GPU. יש לבחור זאת תחת פלט.",
|
||||
"render.surface_import.repeat_u": "חזרת U",
|
||||
"render.surface_import.repeat_v": "חזרת V",
|
||||
"render.surface_import.save": "שמירת תמונה על הגוף",
|
||||
"render.surface_import.saved_cpu": "יש לשמור את המפות לשימוש בהדמיה ב־CPU.",
|
||||
"render.surface_import.source": "מקור",
|
||||
"render.surface_import.title": "ייבוא תמונה",
|
||||
"render.tone_aces_fitted": "ACES מותאם",
|
||||
"render.tone_reinhard": "ריינהרד",
|
||||
"render.transmission_scope": "זכוכית GPU תומכת בתווכים נפרדים מאושרים. תווכים מקוננים ללא הגבלה ומעריך העברה ללא רעש אינם מסופקים.",
|
||||
@@ -4108,6 +4353,8 @@
|
||||
"sample.plate.name": "לוח לדוגמה",
|
||||
"sample.vents.about": "חריצים במערך מלבני: מספר השורות, מספר העמודות והפסיעה הם פרמטרים",
|
||||
"sample.vents.name": "לוח אוורור",
|
||||
"scale.per_axis": "לפי ציר",
|
||||
"scale.uniform": "אחיד",
|
||||
"search.command_keys": "Enter הרצה Shift+Enter הרצה והשארה פתוח Esc סגירה",
|
||||
"search.commands": "פקודות",
|
||||
"search.design": "בתכן זה",
|
||||
@@ -4780,6 +5027,7 @@
|
||||
"ui.click_selects_whole_bodies": "לחיצה בוחרת גופים שלמים",
|
||||
"ui.click_then_press_key": "יש ללחוץ ואז להקיש על המקש החדש",
|
||||
"ui.close": "סגירה",
|
||||
"ui.close_tab": "סגירת הלשונית",
|
||||
"ui.coil": "סליל",
|
||||
"ui.coincident": "מתלכד",
|
||||
"ui.collapse": "כיווץ",
|
||||
@@ -4997,6 +5245,11 @@
|
||||
"ui.factor": "מקדם",
|
||||
"ui.fade": "דעיכה",
|
||||
"ui.fade_length": "אורך דעיכה",
|
||||
"ui.fair_iterations": "מספר איטרציות",
|
||||
"ui.fair_keep_edges": "שמירה על המקצועות",
|
||||
"ui.fair_strength": "עוצמה",
|
||||
"ui.fair_surface_title": "החלקת משטח",
|
||||
"ui.fair_tolerance_default": "0.001 מגודל הגוף",
|
||||
"ui.feature_history": "ציר הזמן",
|
||||
"ui.feature_options": "אפשרויות פיצ'ר",
|
||||
"ui.features": "פיצ'רים",
|
||||
@@ -5182,6 +5435,7 @@
|
||||
"ui.inspect": "בדיקה",
|
||||
"ui.instances": "מופעים",
|
||||
"ui.intersect": "חיתוך משותף",
|
||||
"ui.intersection_curve_second_pick": "עקומת חיתוך: יש ללחוץ על הפאה או העקומה השנייה (לחיצה ימנית להתחלה מחדש)",
|
||||
"ui.iso_4753": "ISO 4753",
|
||||
"ui.isolate": "בידוד",
|
||||
"ui.join": "איחוד",
|
||||
@@ -5210,6 +5464,7 @@
|
||||
"ui.manipulator_free_alt": "גרירה חופשית של הידית גם עם Alt/Option",
|
||||
"ui.manipulator_free_alt_tip": "פעיל: Alt/Option או Ctrl/Cmd על חיצי השליפה / הידית מבטלים הצמדת ערכים. כבוי: Ctrl/Cmd בלבד.",
|
||||
"ui.manufacture": "ייצור",
|
||||
"ui.match_surface_title": "התאמת משטח",
|
||||
"ui.material_review.axes": "צירים מקומיים של החומר; לא הוגדרה אוריינטציה לגוף.",
|
||||
"ui.material_review.claims": "המקורות והרישיונות הם טענות שלא אומתו. ערכים חסרים נשארים לא ידועים.",
|
||||
"ui.material_review.compressive_strength": "חוזק לחיצה",
|
||||
@@ -5232,6 +5487,9 @@
|
||||
"ui.material_review.title": "בדיקת נתוני חומר פיזיקליים",
|
||||
"ui.material_review.unknown": "לא ידוע",
|
||||
"ui.material_review.yield_strength": "חוזק כניעה",
|
||||
"ui.mcp_agents": "סוכנים דרך MCP",
|
||||
"ui.mcp_compact": "פלט מקוצר",
|
||||
"ui.mcp_compact_note": "לסוכנים שמחוברים דרך שרת ה-MCP (solidcraft-cli mcp): inspect_design מסכם סקיצות ותוצאות הכלים אינן מעוצבות, כך שהן צורכות פחות טוקנים. חל על הפעלות סוכן שמתחילות אחרי השינוי; גם --compact או SOLIDCRAFT_MCP_COMPACT=1 מפעילים אותו.",
|
||||
"ui.measure": "מדידה",
|
||||
"ui.mesh_format_3mf": "3MF",
|
||||
"ui.mesh_format_3mf_tagged": "3MF עם תגי פאות",
|
||||
@@ -5273,6 +5531,7 @@
|
||||
"ui.options": "אפשרויות",
|
||||
"ui.orbit": "סיבוב מבט",
|
||||
"ui.orbit_around": "סיבוב מבט סביב",
|
||||
"ui.organize": "ארגון",
|
||||
"ui.orientation": "אוריינטציה",
|
||||
"ui.origin": "ראשית",
|
||||
"ui.palette_construction_mode": "מצב עזר (X)",
|
||||
@@ -5280,6 +5539,65 @@
|
||||
"ui.palette_sketch_options": "אפשרויות סקיצה",
|
||||
"ui.pan": "הזזת מבט",
|
||||
"ui.pan_zoom_orbit": "הזזה, זום, סיבוב מבט",
|
||||
"ui.panel_fixture.apply": "החלה",
|
||||
"ui.panel_fixture.cancel": "ביטול",
|
||||
"ui.panel_fixture.caption": "דוגמת לוח סינתטית",
|
||||
"ui.panel_fixture.draft": "טיוטה שמורה",
|
||||
"ui.panel_fixture.editor": "עורך לדוגמה",
|
||||
"ui.panel_fixture.explanation": "תוכן סינתטי זה שומר את הטיוטה כשהוא מוסתר או מועבר.",
|
||||
"ui.panel_fixture.fill": "דוגמה למילוי הגובה",
|
||||
"ui.panel_fixture.finish": "סיום",
|
||||
"ui.panel_fixture.finished": "הפעולה הסינתטית הסתיימה",
|
||||
"ui.panel_fixture.footer": "פעולות הדוגמה משאירות את התכנון ללא שינוי.",
|
||||
"ui.panel_fixture.guard": "סירוב לביטול כל עוד הפעולה פעילה",
|
||||
"ui.panel_fixture.inline": "דוגמה בשורה אחת",
|
||||
"ui.panel_fixture.live": "הפעולה הסינתטית פעילה",
|
||||
"ui.panel_fixture.log": "יומן לדוגמה",
|
||||
"ui.panel_fixture.pin": "דוגמה מוצמדת",
|
||||
"ui.panel_fixture.title": "{title} · {id}",
|
||||
"ui.panel_fixture.updates": "עדכוני שירות: {count}",
|
||||
"ui.panel_host.attention": "נדרשת תשומת לב",
|
||||
"ui.panel_host.bottom": "למטה",
|
||||
"ui.panel_host.close": "סגירת לוח",
|
||||
"ui.panel_host.collapse": "כיווץ לוח",
|
||||
"ui.panel_host.expand": "הרחבת הלוח",
|
||||
"ui.panel_host.floating": "צף",
|
||||
"ui.panel_host.focus": "מיקוד בלוח",
|
||||
"ui.panel_host.focus_target": "מיקוד ב־{title}",
|
||||
"ui.panel_host.header": "כותרת לוח",
|
||||
"ui.panel_host.join": "צירוף לשוניות",
|
||||
"ui.panel_host.join_target": "צירוף לשוניות עם {title}",
|
||||
"ui.panel_host.left": "שמאל",
|
||||
"ui.panel_host.menu": "פעולות לוח",
|
||||
"ui.panel_host.move": "העברה אל",
|
||||
"ui.panel_host.overflow": "לוחות נוספים",
|
||||
"ui.panel_host.popout": "פתיחה בחלון",
|
||||
"ui.panel_host.resize": "שינוי גודל לוח",
|
||||
"ui.panel_host.right": "ימין",
|
||||
"ui.panel_host.rule.availability": "לוח זה אינו זמין.",
|
||||
"ui.panel_host.rule.bounds": "הלוחות אינם מתאימים למגבלות הגודל שלהם.",
|
||||
"ui.panel_host.rule.capability": "מארח זה אינו תומך בפעולה זו.",
|
||||
"ui.panel_host.rule.collection_limit": "פריסת הלוחות הגיעה למגבלה.",
|
||||
"ui.panel_host.rule.duplicate_id": "מזהה לוח זה כבר בשימוש.",
|
||||
"ui.panel_host.rule.exactly_once": "כל לוח חייב להופיע פעם אחת.",
|
||||
"ui.panel_host.rule.feature_disabled": "פעולה זו מושבתת בלוח זה.",
|
||||
"ui.panel_host.rule.id": "מזהה הלוח אינו תקין.",
|
||||
"ui.panel_host.rule.incompatible_tabs": "לוחות אלה אינם יכולים להשתייך לאותה קבוצת לשוניות.",
|
||||
"ui.panel_host.rule.lifecycle": "הלוח לא הצליח להשלים פעולה זו.",
|
||||
"ui.panel_host.rule.missing_target": "יעד לוח זה אינו זמין עוד.",
|
||||
"ui.panel_host.rule.no_space": "אין מספיק מקום לפריסת הלוחות הזו.",
|
||||
"ui.panel_host.rule.pin_conflict": "לוח אחר כבר מוצמד לקצה זה.",
|
||||
"ui.panel_host.rule.pin_order": "לוח מוצמד חייב להישאר ראשון בקצה שלו.",
|
||||
"ui.panel_host.rule.placement": "לא ניתן להעביר לוח זה למיקום זה.",
|
||||
"ui.panel_host.rule.revision": "הפריסה השתנתה. יש לנסות שוב.",
|
||||
"ui.panel_host.rule.schema": "בקשת הלוח אינה תקינה.",
|
||||
"ui.panel_host.rule.version": "גרסת פריסת לוחות זו אינה נתמכת.",
|
||||
"ui.panel_host.sash": "שינוי גודל לוחות סמוכים",
|
||||
"ui.panel_host.scroll_horizontal": "גלילת עמודות הלוחות",
|
||||
"ui.panel_host.scroll_vertical": "גלילת ערימות הלוחות",
|
||||
"ui.panel_host.surface": "לוח",
|
||||
"ui.panel_host.tab": "לשונית לוח",
|
||||
"ui.panel_host.tabs": "כל הלשוניות",
|
||||
"ui.parallel": "מקביל",
|
||||
"ui.parameters": "פרמטרים",
|
||||
"ui.part_size_needs_a_part": "בחרו חלק סטנדרטי שהוכנס (בורג, אום, דסקית או פין) כדי לשנות את גודלו. כדי להגדיל או להקטין גוף, השתמשו בשינוי קנה מידה.",
|
||||
@@ -5317,6 +5635,7 @@
|
||||
"ui.point_cloud_add_filter": "הוספת מסנן…",
|
||||
"ui.point_cloud_auto_range": "טווח אוטומטי",
|
||||
"ui.point_cloud_color_by": "צביעה לפי",
|
||||
"ui.point_cloud_dots": "נקודות בודדות",
|
||||
"ui.point_cloud_file_colors": "צבעי הקובץ",
|
||||
"ui.point_cloud_filter_by": "סינון לפי",
|
||||
"ui.point_cloud_height_high": "הגובה המרבי",
|
||||
|
||||
@@ -1621,7 +1621,7 @@
|
||||
"help.sketch.insert_svg": "Čita putanje SVG crteža u skicu, skalirane, zakrenute i postavljene kako zadate. Upotrijebite ga za pretvaranje logotipa ili vektorskog obrisa u krivulje koje možete ekstrudirati.",
|
||||
"help.sketch.inspect": "Otvara izvješće o otvorenoj skici: koliko je stupnjeva slobode preostalo i je li potpuno ograničena. Upotrijebite ga kako biste saznali što se još pomiče prije dovršetka skice.",
|
||||
"help.sketch.intersect": "Dodaje krivulje ili točke u kojima tijela, lica, bridovi ili druge skice presijecaju ravninu skice. Ostaju povezane i prate model kad se promijeni.",
|
||||
"help.sketch.intersection_curve": "Izrađuje 3D krivulje na mjestu gdje se dva tijela ili lica susreću, povezane s njima. Upotrijebite ih kao putanje ili vodilice koje prate model.",
|
||||
"help.sketch.intersection_curve": "Izrađuje 3D krivulje na mjestu gdje se dva tijela, lica ili krivulje drugih skica susreću, povezane s njima. Upotrijebite ih kao putanje ili vodilice koje prate model.",
|
||||
"help.sketch.isoparametric_curve": "Izrađuje 3D krivulju u smjeru U ili V lica kroz točku koju odaberete, povezanu s licem. Upotrijebite je kao putanju ili liniju razdvajanja koja prati plohu.",
|
||||
"help.sketch.line": "Crta povezane linije točku po točku, kao otvoreni lanac ili zatvoreni obris. To je osnovni alat za većinu profila.",
|
||||
"help.sketch.line.midpoint": "Crta liniju od njezine središnje točke do jednog kraja, a drugi kraj zrcali. Upotrijebite ga za linije centrirane na točku, primjerice simetričan brid.",
|
||||
|
||||
@@ -1621,7 +1621,7 @@
|
||||
"help.sketch.insert_svg": "Beolvassa egy SVG-rajz útvonalait egy vázlatba, az Ön által megadott léptékkel, elforgatással és helyen. Használja egy logó vagy vektoros körvonal kihúzható görbékké alakítására.",
|
||||
"help.sketch.inspect": "Jelentést nyit a megnyitott vázlatról: hány szabadságfok maradt, és teljesen kényszerezett-e. Használja annak kiderítésére, mi mozdul még, mielőtt befejezi a vázlatot.",
|
||||
"help.sketch.intersect": "Hozzáadja azokat a görbéket vagy pontokat, ahol testek, lapok, élek vagy más vázlatok metszik a vázlatsíkot. Kapcsoltak maradnak, és követik a modellt, ha az megváltozik.",
|
||||
"help.sketch.intersection_curve": "3D görbéket hoz létre ott, ahol két test vagy lap találkozik, hozzájuk kapcsolva. Használja őket útvonalként vagy vezérgörbeként, amely követi a modellt.",
|
||||
"help.sketch.intersection_curve": "3D görbéket hoz létre ott, ahol két test, lap vagy más vázlatok görbéi találkozik, hozzájuk kapcsolva. Használja őket útvonalként vagy vezérgörbeként, amely követi a modellt.",
|
||||
"help.sketch.isoparametric_curve": "3D görbét hoz létre egy lap U vagy V iránya mentén egy kiválasztott ponton át, a laphoz kapcsolva. Használja útvonalként vagy osztóvonalként, amely követi a felületet.",
|
||||
"help.sketch.line": "Összefüggő vonalakat rajzol pontról pontra, nyitott láncként vagy zárt körvonalként. Ez a legtöbb profil alapeszköze.",
|
||||
"help.sketch.line.midpoint": "Vonalat rajzol a felezőpontjától az egyik végéig, a másik véget tükrözve. Használja egy pontra középen álló vonalakhoz, például szimmetrikus élhez.",
|
||||
@@ -3807,6 +3807,8 @@
|
||||
"sample.plate.name": "Mintalemez",
|
||||
"sample.vents.about": "Hornyok téglalap mintában: a sorok, oszlopok és osztás paraméterek",
|
||||
"sample.vents.name": "Szellőzőlemez",
|
||||
"scale.per_axis": "Tengelyenként",
|
||||
"scale.uniform": "Egyenletes",
|
||||
"search.command_keys": "Enter futtatás Shift+Enter futtatás és nyitva tartás Esc bezárás",
|
||||
"search.commands": "Parancsok",
|
||||
"search.design": "Ebben a tervben",
|
||||
|
||||
@@ -1621,7 +1621,7 @@
|
||||
"help.sketch.insert_svg": "Membaca path gambar SVG ke dalam sketsa, diskalakan, diputar, dan ditempatkan sesuai pengaturan Anda. Gunakan untuk mengubah logo atau kontur vektor menjadi kurva yang dapat diekstrusi.",
|
||||
"help.sketch.inspect": "Membuka laporan tentang sketsa yang terbuka: berapa derajat kebebasan yang tersisa dan apakah sketsa terbatasi penuh. Gunakan untuk mengetahui apa yang masih bergerak sebelum Anda menyelesaikan sketsa.",
|
||||
"help.sketch.intersect": "Menambahkan kurva atau titik tempat bodi, muka, tepi, atau sketsa lain memotong bidang sketsa. Semuanya tetap tertaut dan mengikuti model saat model berubah.",
|
||||
"help.sketch.intersection_curve": "Membuat kurva 3D di tempat dua bodi atau muka bertemu, tertaut padanya. Gunakan sebagai jalur atau rel pemandu yang mengikuti model.",
|
||||
"help.sketch.intersection_curve": "Membuat kurva 3D di tempat dua bodi, muka, atau kurva sketsa lain bertemu, tertaut padanya. Gunakan sebagai jalur atau rel pemandu yang mengikuti model.",
|
||||
"help.sketch.isoparametric_curve": "Membuat kurva 3D sepanjang arah U atau V sebuah muka melalui titik yang Anda pilih, tertaut ke muka itu. Gunakan sebagai jalur atau garis pisah yang mengikuti permukaan.",
|
||||
"help.sketch.line": "Menggambar garis-garis tersambung titik demi titik, sebagai rantai terbuka atau kontur tertutup. Ini alat dasar untuk sebagian besar profil.",
|
||||
"help.sketch.line.midpoint": "Menggambar garis dari titik tengahnya ke satu ujung, dengan ujung lainnya dicerminkan. Gunakan untuk garis yang dipusatkan pada sebuah titik, seperti tepi simetris.",
|
||||
@@ -3807,6 +3807,8 @@
|
||||
"sample.plate.name": "Pelat Contoh",
|
||||
"sample.vents.about": "Slot dalam pola persegi panjang: baris, kolom, dan pitch adalah parameter",
|
||||
"sample.vents.name": "Pelat Ventilasi",
|
||||
"scale.per_axis": "Per sumbu",
|
||||
"scale.uniform": "Seragam",
|
||||
"search.command_keys": "Enter jalankan Shift+Enter jalankan dan tetap buka Esc tutup",
|
||||
"search.commands": "Perintah",
|
||||
"search.design": "Di desain ini",
|
||||
|
||||
@@ -1625,7 +1625,7 @@
|
||||
"help.sketch.insert_svg": "Legge i tracciati di un disegno SVG in uno schizzo, scalati, ruotati e posizionati come imposti. Usalo per trasformare un logo o un contorno vettoriale in curve che puoi estrudere.",
|
||||
"help.sketch.inspect": "Apre un rapporto sullo schizzo aperto: quanti gradi di libertà restano e se è completamente vincolato. Usalo per scoprire cosa si muove ancora prima di terminare lo schizzo.",
|
||||
"help.sketch.intersect": "Aggiunge le curve o i punti in cui corpi, facce, spigoli o altri schizzi attraversano il piano dello schizzo. Restano collegati e seguono il modello quando cambia.",
|
||||
"help.sketch.intersection_curve": "Crea curve 3D dove si incontrano due corpi o facce, collegate a essi. Usale come percorsi o guide che seguono il modello.",
|
||||
"help.sketch.intersection_curve": "Crea curve 3D dove si incontrano due corpi, facce o curve di altri schizzi, collegate a essi. Usale come percorsi o guide che seguono il modello.",
|
||||
"help.sketch.isoparametric_curve": "Crea una curva 3D lungo la direzione U o V di una faccia passante per un punto che scegli, collegata alla faccia. Usala come percorso o linea di divisione che segue la superficie.",
|
||||
"help.sketch.line": "Disegna linee collegate punto per punto, come catena aperta o contorno chiuso. È lo strumento di base per la maggior parte dei profili.",
|
||||
"help.sketch.line.midpoint": "Disegna una linea dal suo punto medio fino a un'estremità, con l'altra estremità speculare. Usalo per linee centrate su un punto, come un bordo simmetrico.",
|
||||
@@ -3813,6 +3813,8 @@
|
||||
"sample.plate.name": "Piastra di esempio",
|
||||
"sample.vents.about": "Asole in ripetizione rettangolare: righe, colonne e passo sono parametri",
|
||||
"sample.vents.name": "Piastra di ventilazione",
|
||||
"scale.per_axis": "Per asse",
|
||||
"scale.uniform": "Uniforme",
|
||||
"search.command_keys": "Invio esegui Maiusc+Invio esegui e tieni aperto Esc chiudi",
|
||||
"search.commands": "Comandi",
|
||||
"search.design": "In questo progetto",
|
||||
|
||||
+320
-1
@@ -1,4 +1,9 @@
|
||||
{
|
||||
" Keys": " キーフレーム",
|
||||
" Maximum": " 最大",
|
||||
" Minimum": " 最小",
|
||||
" Of": " 対象",
|
||||
" Value": " 値",
|
||||
"(active rule)": "(使用中のルール)",
|
||||
"0 turns it off": "0で無効化",
|
||||
"1. Open GitHub draft below, then paste the report copied to your clipboard.": "1. 下のボタンでGitHubの下書きを開き、クリップボードにコピーされたレポートを貼り付けます。",
|
||||
@@ -10,7 +15,18 @@
|
||||
"A new configuration, a copy of the active one": "アクティブな構成をコピーして新しい構成を作成",
|
||||
"A placed Single hole has a handle to drag it, and picking its face again moves the hole there instead of taking it out": "配置した単一の穴にドラッグ用のハンドルを付け、面をもう一度選ぶと穴を削除せずにそこへ移動します",
|
||||
"A short title for the problem (optional)": "問題の短いタイトル(省略可)",
|
||||
"ABS - Black": "ABS - ブラック",
|
||||
"ABS - Grey": "ABS - グレー",
|
||||
"ABS - White": "ABS - ホワイト",
|
||||
"ALIGN": "整列",
|
||||
"ARRANGE": "配置",
|
||||
"AS-BUILT JOINT": "現位置ジョイント",
|
||||
"ASSIGN PLASTIC RULE": "プラスチック ルールを割り当て",
|
||||
"AXIS PERPENDICULAR TO FACE": "面に垂直な軸",
|
||||
"AXIS THROUGH CYLINDER": "円柱を通る軸",
|
||||
"AXIS THROUGH EDGE": "エッジを通る軸",
|
||||
"AXIS THROUGH TWO PLANES": "2平面を通る軸",
|
||||
"AXIS THROUGH TWO POINTS": "2点を通る軸",
|
||||
"About SolidCraft": "SolidCraft について",
|
||||
"Acceleration": "加速度",
|
||||
"Access": "アクセス性",
|
||||
@@ -27,6 +43,7 @@
|
||||
"Add bend": "曲げを追加",
|
||||
"Add condition": "条件を追加",
|
||||
"Add or edit rule": "ルールを追加または編集",
|
||||
"Add shortcut": "ショートカットを追加",
|
||||
"Add to New Folder": "新しいフォルダーに追加",
|
||||
"Add visible bodies": "表示中のボディを追加",
|
||||
"Advanced": "詳細",
|
||||
@@ -37,13 +54,17 @@
|
||||
"All components": "すべてのコンポーネント",
|
||||
"All open designs": "すべての開いているデザイン",
|
||||
"Allowed expressions, separated by semicolons": "使用可能な式(セミコロンで区切る)",
|
||||
"Allowed values": "許容値",
|
||||
"Also deletes": "同時に削除される要素:",
|
||||
"Also place them from the origin (fully constrained)": "原点からの位置も付ける(完全拘束)",
|
||||
"Aluminum - Satin": "アルミニウム - サテン",
|
||||
"Anchor": "基準点",
|
||||
"Angle 2": "角度 2",
|
||||
"Angle Display": "角度の表示",
|
||||
"Animate": "アニメーション",
|
||||
"Anodized Black": "黒アルマイト",
|
||||
"Another bend on the far edge of the last flange": "最後のフランジの先端にさらに曲げを追加",
|
||||
"Any": "いずれか",
|
||||
"App/system information and command settings are included. Review private details before sharing.": "アプリ・システム情報とコマンド設定が含まれます。共有前に非公開の情報を確認してください。",
|
||||
"Appearance name": "外観名",
|
||||
"Applied Radius": "適用半径",
|
||||
@@ -58,6 +79,8 @@
|
||||
"Assign Rule": "ルールを割当",
|
||||
"Attachment": "アタッチメント",
|
||||
"BLEND SURFACE": "ブレンドサーフェス",
|
||||
"BOSS": "ボス",
|
||||
"BOUNDARY FILL": "境界フィル",
|
||||
"BOUNDARY SURFACE": "境界サーフェス",
|
||||
"BROWSER": "ブラウザ",
|
||||
"Back to the original order": "元の順序に戻す",
|
||||
@@ -85,13 +108,21 @@
|
||||
"Bottom Right": "右下",
|
||||
"Boundary": "境界",
|
||||
"Bounding box": "境界ボックス",
|
||||
"Brass": "真鍮",
|
||||
"Bridge": "ブリッジ",
|
||||
"Built-in": "組み込み",
|
||||
"By definition": "定義別",
|
||||
"By part number": "部品番号別",
|
||||
"CAM": "カム",
|
||||
"CHECK": "チェック",
|
||||
"COMPONENTS": "コンポーネント",
|
||||
"CONFIGURATIONS": "構成",
|
||||
"CONSTRAIN": "拘束",
|
||||
"CONTACT": "接触",
|
||||
"CONTACT SET": "接触セット",
|
||||
"CONVERT": "変換",
|
||||
"CONVERT MESH": "メッシュを変換",
|
||||
"CONVERT TO SHEET METAL": "板金に変換",
|
||||
"CSV or JSON: updates existing parameters, adds new ones": "CSVまたはJSON:既存のパラメータを更新し、新しいパラメータを追加します",
|
||||
"Capture All Positions": "すべての位置をキャプチャ",
|
||||
"Capture Pos.": "位置を記録",
|
||||
@@ -111,11 +142,15 @@
|
||||
"Choose Variable radius or Spline in Type.": "タイプで Variable radius またはスプラインを選択してください。",
|
||||
"Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.": "新しい平面を選択してください。スケッチジオメトリと寸法は2次元座標系のまま維持されます。",
|
||||
"Choose…": "選択…",
|
||||
"Chord length": "弦長",
|
||||
"Chrome": "クロム",
|
||||
"Circle center": "円の中心",
|
||||
"Circle or sphere": "円または球",
|
||||
"Circular": "円形",
|
||||
"Circumference": "円周",
|
||||
"Clockwise": "時計回り",
|
||||
"Close All Tabs": "すべてのタブを閉じる",
|
||||
"Close Other Tabs": "他のタブを閉じる",
|
||||
"Close draft": "下書きを閉じる",
|
||||
"Collapse": "折りたたむ",
|
||||
"Collisions": "衝突",
|
||||
@@ -129,6 +164,7 @@
|
||||
"Config Table": "構成テーブル",
|
||||
"Configuration": "構成",
|
||||
"Configuration rules": "構成ルール",
|
||||
"Connect AI assistants…": "AI アシスタントを接続…",
|
||||
"Constant radius": "一定半径",
|
||||
"Contact": "接触",
|
||||
"Contact Sets": "接触セット",
|
||||
@@ -137,6 +173,7 @@
|
||||
"Continuity": "連続性",
|
||||
"Control Radius": "制御半径",
|
||||
"Copies the definition only. Assign it from Physical Material when ready.": "定義のみをコピーします。準備ができたら物理マテリアルから割り当ててください。",
|
||||
"Copper": "銅",
|
||||
"Copy Error Message": "エラーメッセージをコピー",
|
||||
"Copy Warning Message": "警告メッセージをコピー",
|
||||
"Copy all licences": "すべてのライセンスをコピー",
|
||||
@@ -150,6 +187,7 @@
|
||||
"Counterbore Ø": "座ぐり Ø",
|
||||
"Countersink angle": "皿穴角度",
|
||||
"Countersink Ø": "皿穴 Ø",
|
||||
"Crash Reports…": "クラッシュ レポート…",
|
||||
"Create Hole": "穴を作成",
|
||||
"Create Issue — review draft": "Issueを作成 — 下書きの確認",
|
||||
"Create Issue…": "Issueを作成…",
|
||||
@@ -162,12 +200,17 @@
|
||||
"Curv. Comb": "曲率コーム",
|
||||
"Curvature (G2)": "曲率 (G2)",
|
||||
"Curvature change (G3)": "曲率変化 (G3)",
|
||||
"Curvature continuous (G2)": "曲率連続 (G2)",
|
||||
"Custom": "カスタム",
|
||||
"Customize Toolbar": "ツールバーのカスタマイズ",
|
||||
"Customize…": "カスタマイズ…",
|
||||
"Cylindrical": "円筒",
|
||||
"Cylindrical face": "円筒面",
|
||||
"DELETE FACE": "面を削除",
|
||||
"DELETE FEATURE": "フィーチャーを削除",
|
||||
"DRIVE JOINTS": "ジョイントを駆動",
|
||||
"Deboss": "デボス",
|
||||
"Default material": "既定の材料",
|
||||
"Delete": "削除",
|
||||
"Delete Folders and Features…": "フォルダーとフィーチャーを削除…",
|
||||
"Delete Selected Features…": "選択したフィーチャーを削除…",
|
||||
@@ -180,20 +223,26 @@
|
||||
"Dependents": "依存先",
|
||||
"Describe the problem you saw": "発生した問題を説明してください",
|
||||
"Describe what happened before continuing.": "続ける前に、何が起きたか記入してください。",
|
||||
"Design Properties…": "デザインのプロパティ…",
|
||||
"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": "新しい図形に寸法を付ける",
|
||||
"Direct interaction": "直接操作",
|
||||
"Direction": "方向",
|
||||
"Discard": "破棄",
|
||||
"Distance": "距離",
|
||||
"Distance 1": "距離 1",
|
||||
"Distance X": "距離 X",
|
||||
"Distance Y": "距離 Y",
|
||||
"Distance and Angle": "距離と角度",
|
||||
"Do not export hidden edges": "出力しない",
|
||||
"Don't ask again": "今後確認しない",
|
||||
"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": "ビュー上で中ボタンをダブルクリックした時の動作: ホーム(全体を収めたホームビュー)、全体表示(向きを維持して全体を収める)、または無効",
|
||||
"Download design": "デザインをダウンロード",
|
||||
"Drag to move the boss across the model. It snaps to sketch points on the face.": "ドラッグしてボスをモデル上で移動します。面上のスケッチ点にスナップします。",
|
||||
"Drag to move the hole; click to remove it": "ドラッグで穴を移動、クリックで削除",
|
||||
"Drag to move the rest across the model. It snaps to sketch points on the face.": "ドラッグしてレストをモデル上で移動します。面上のスケッチ点にスナップします。",
|
||||
@@ -201,7 +250,16 @@
|
||||
"Drag to point the catch in another direction.": "ドラッグしてキャッチの向きを変えます。",
|
||||
"Drag to turn the rest in 5° steps. Hold Alt or Ctrl to turn it freely.": "ドラッグしてレストを 5° 刻みで回転します。Alt または Ctrl を押したままにすると自由に回転します。",
|
||||
"Drive Joint": "ジョイントを駆動",
|
||||
"Drive parameter": "パラメータを駆動",
|
||||
"Drive suppression": "抑制を駆動",
|
||||
"Duration": "時間",
|
||||
"EDIT DIMENSION": "寸法を編集",
|
||||
"EDIT JOINT": "ジョイントを編集",
|
||||
"EDIT MOTION STUDY": "モーション スタディを編集",
|
||||
"EDIT TEXT": "テキストを編集",
|
||||
"EMBOSS": "エンボス",
|
||||
"EXPLODED VIEW": "分解図",
|
||||
"EXTEND": "延長",
|
||||
"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": "エッジ",
|
||||
@@ -215,10 +273,12 @@
|
||||
"End bulge": "終了側の膨らみ",
|
||||
"End continuity": "終了側の連続性",
|
||||
"Equal Distance": "等距離",
|
||||
"Exact": "正確",
|
||||
"Expand": "展開",
|
||||
"Expected result (optional)": "期待した結果(任意)",
|
||||
"Explode": "分解",
|
||||
"Explode Text": "テキストを分解",
|
||||
"Export 3MF…": "3MF をエクスポート…",
|
||||
"Export Animation…": "アニメーションをエクスポート…",
|
||||
"Export Bend Table…": "曲げテーブルを書き出す…",
|
||||
"Export CSV…": "CSVを書き出し…",
|
||||
@@ -226,7 +286,12 @@
|
||||
"Export Current View as DXF…": "現在の表示をDXFでエクスポート…",
|
||||
"Export DXF": "DXF出力",
|
||||
"Export DXF…": "DXFをエクスポート…",
|
||||
"Export Design as JSON…": "デザインを JSON としてエクスポート…",
|
||||
"Export JSON…": "JSONを書き出し…",
|
||||
"Export OBJ…": "OBJ をエクスポート…",
|
||||
"Export STEP…": "STEP をエクスポート…",
|
||||
"Export STL…": "STL をエクスポート…",
|
||||
"Export URDF…": "URDF をエクスポート…",
|
||||
"Export design library...": "デザインライブラリを書き出す...",
|
||||
"Export hidden edges as dashed lines": "点線で出力",
|
||||
"Exported by SolidWorks, which usually saves Y up": "SolidWorks で書き出されたファイルです。通常は Y 軸が上になります",
|
||||
@@ -234,13 +299,19 @@
|
||||
"Extend to meet": "交わるまで延長",
|
||||
"Extents": "範囲",
|
||||
"FILES": "ファイル",
|
||||
"FLANGE": "フランジ",
|
||||
"FLAT": "展開",
|
||||
"FLAT PATTERN": "展開図",
|
||||
"FOLD": "折り曲げ",
|
||||
"FRAME MEMBER": "フレーム部材",
|
||||
"Face": "面",
|
||||
"Face Appearance": "面の外観",
|
||||
"Face attachment lost": "面アタッチメントが失われました",
|
||||
"Face center": "面の中心",
|
||||
"Faces": "面",
|
||||
"Faces / edges / vertices": "面 / エッジ / 頂点",
|
||||
"Faces on Block": "ブロック上の面",
|
||||
"Faces on Ring": "リング上の面",
|
||||
"Faces to split": "分割する面",
|
||||
"Faces/Body": "面/ボディ",
|
||||
"Factor must be a positive number": "係数は正の数にしてください",
|
||||
@@ -265,20 +336,30 @@
|
||||
"Flat size": "展開サイズ",
|
||||
"Flat size X": "展開サイズ X",
|
||||
"Flat size Y": "展開サイズ Y",
|
||||
"Flipped": "反転",
|
||||
"Follow camera (Look At sketches)": "カメラ追従(スケッチを正面視)",
|
||||
"Follow inherited appearance": "継承された外観に従う",
|
||||
"Follow material": "材料に従う",
|
||||
"Follow the camera (Look At sketches)": "カメラ追従(スケッチを正面視)",
|
||||
"Font": "フォント",
|
||||
"Free Move": "自由移動",
|
||||
"From": "開始",
|
||||
"From Bodies": "ボディから",
|
||||
"From Object": "オブジェクトから",
|
||||
"From Snap": "移動元スナップ",
|
||||
"From edges": "開始側エッジ",
|
||||
"Full Length": "全長",
|
||||
"Full thread depth": "ねじ深さ全体",
|
||||
"Further Bends": "追加の曲げ",
|
||||
"G2 corners require a constant radius and Exact fit. G2 corner construction is not supported yet.": "G2 コーナーには一定半径と Exact fit が必要です。G2 コーナーの構築はまだサポートされていません。",
|
||||
"GEAR": "ギア",
|
||||
"GROOVE": "溝",
|
||||
"Gap": "間隔",
|
||||
"Glass - Clear": "ガラス - クリア",
|
||||
"Glass - Smoked": "ガラス - スモーク",
|
||||
"Grid": "グリッド",
|
||||
"Groove": "溝",
|
||||
"HDR file changed since selection; inspect and select it again before saving": "選択後に HDR ファイルが変更されました。保存する前に確認して再選択してください",
|
||||
"HISTORY": "履歴",
|
||||
"Halo under sketch curves": "スケッチ曲線の下に縁取りを表示",
|
||||
"Height": "高さ",
|
||||
@@ -287,6 +368,7 @@
|
||||
"Hidden edges": "隠線",
|
||||
"History layout": "履歴のレイアウト",
|
||||
"History names": "履歴名",
|
||||
"Hold click": "長押し",
|
||||
"Hole (optional)": "穴 (オプション)",
|
||||
"Hole Depth": "穴の深さ",
|
||||
"Hole Diameter": "穴径",
|
||||
@@ -296,6 +378,8 @@
|
||||
"Horiz./Vert.": "水平 / 垂直",
|
||||
"I-Beam": "I形鋼",
|
||||
"IMAGES": "画像",
|
||||
"INSERT PART": "パーツを挿入",
|
||||
"INSERT SVG": "SVG を挿入",
|
||||
"If you are offline or GitHub does not open, use Copy report and keep the text to submit later.": "オフラインの場合やGitHubが開かない場合は、「レポートをコピー」でテキストを保存し、後で送信してください。",
|
||||
"Import material into design": "マテリアルを設計にインポート",
|
||||
"Import…": "読み込み…",
|
||||
@@ -304,6 +388,8 @@
|
||||
"In plane": "平面内",
|
||||
"Included": "挟角",
|
||||
"Inner Sharp": "内側仮想シャープ",
|
||||
"Insert Mesh…": "メッシュを挿入…",
|
||||
"Insert STEP…": "STEP を挿入…",
|
||||
"Inside": "内側",
|
||||
"Instant": "即時",
|
||||
"Interface font": "インターフェースフォント",
|
||||
@@ -312,6 +398,8 @@
|
||||
"Isocurve": "等パラメータ線",
|
||||
"Issue title": "Issueタイトル",
|
||||
"Items": "項目",
|
||||
"JOINT": "ジョイント",
|
||||
"JOINT ORIGIN": "ジョイント原点",
|
||||
"JOINTS": "ジョイント",
|
||||
"Joint 1": "ジョイント 1",
|
||||
"Joint 2": "ジョイント 2",
|
||||
@@ -348,9 +436,15 @@
|
||||
"M5 or M5x0.5": "M5 または M5x0.5",
|
||||
"M5, M5x0.5, 1/4-20 UNC": "M5, M5x0.5, 1/4-20 UNC",
|
||||
"MANAGE": "管理",
|
||||
"MANAGE PLASTIC RULES": "プラスチック ルールを管理",
|
||||
"MATERIALS": "材料",
|
||||
"MIDPLANE": "中間平面",
|
||||
"MODEL": "モデル",
|
||||
"MOTION": "モーション",
|
||||
"MOTION LINK": "モーション リンク",
|
||||
"MOTION STUDY": "モーション スタディ",
|
||||
"MOTION TRACE": "モーション トレース",
|
||||
"MOVE/COPY TEXT": "テキストを移動/コピー",
|
||||
"Margin": "マージン",
|
||||
"Mass": "質量",
|
||||
"Mass Center": "重心",
|
||||
@@ -361,6 +455,7 @@
|
||||
"Max": "最大",
|
||||
"Max Thickness": "最大厚さ",
|
||||
"Maximum": "最大",
|
||||
"Mesh": "メッシュ",
|
||||
"Mesh body": "メッシュボディ",
|
||||
"Mid Radius": "中間半径",
|
||||
"Middle double-click": "中ボタンのダブルクリック",
|
||||
@@ -383,6 +478,7 @@
|
||||
"Move holes by clicking or dragging": "クリックまたはドラッグで移動",
|
||||
"Move later": "後へ移動",
|
||||
"Move up": "上へ移動",
|
||||
"NEW COMPONENT": "新規コンポーネント",
|
||||
"Name": "名前",
|
||||
"Name on import": "インポート名",
|
||||
"Nearest": "最も近い",
|
||||
@@ -400,11 +496,19 @@
|
||||
"Nominal Radius": "公称半径",
|
||||
"Nominal diameter": "呼び径",
|
||||
"None": "なし",
|
||||
"Not": "否定",
|
||||
"Not available yet": "まだ利用できません",
|
||||
"Not saved yet": "まだ保存されていません",
|
||||
"Notch": "ノッチ",
|
||||
"Nylon - Natural": "ナイロン - ナチュラル",
|
||||
"OFFSET FACE": "面をオフセット",
|
||||
"OFFSET SURFACE": "オフセットサーフェス",
|
||||
"OUTPUT": "出力",
|
||||
"Oak": "オーク",
|
||||
"Of": "の値",
|
||||
"Offset X": "オフセット X",
|
||||
"Offset Y": "オフセット Y",
|
||||
"Offset Z": "オフセット Z",
|
||||
"On your computer": "このコンピューター上",
|
||||
"Opacity": "不透明度",
|
||||
"Open GitHub": "GitHubを開く",
|
||||
@@ -427,10 +531,30 @@
|
||||
"Outer radius": "外側コーナー半径",
|
||||
"Outside": "外側",
|
||||
"Overall length": "全長",
|
||||
"PATCH": "パッチ",
|
||||
"PERPENDICULAR PLANE": "垂直平面",
|
||||
"PHYSICAL MATERIAL": "物理マテリアル",
|
||||
"PICK CONTEXT": "コンテキストを選ぶ",
|
||||
"PLANE ALONG PATH": "パスに沿った平面",
|
||||
"PLANE THROUGH THREE POINTS": "3点を通る平面",
|
||||
"PLANE THROUGH TWO EDGES": "2エッジを通る平面",
|
||||
"POINT ALONG PATH": "パスに沿った点",
|
||||
"POINT AT CENTER": "中心の点",
|
||||
"POINT AT EDGE AND PLANE": "エッジと平面の交点",
|
||||
"POINT AT VERTEX": "頂点上の点",
|
||||
"POINT THROUGH THREE PLANES": "3平面の交点",
|
||||
"POINT THROUGH TWO EDGES": "2エッジの交点",
|
||||
"Paint - Blue": "塗装 - ブルー",
|
||||
"Paint - Green": "塗装 - グリーン",
|
||||
"Paint - Orange": "塗装 - オレンジ",
|
||||
"Paint - Purple": "塗装 - パープル",
|
||||
"Paint - Red": "塗装 - レッド",
|
||||
"Paint - Yellow": "塗装 - イエロー",
|
||||
"Param. Graph": "パラメータ図",
|
||||
"Parameter": "パラメータ",
|
||||
"Part length must be greater than zero and at most 2000 mm": "パーツの長さは0より大きく、2000 mm以下にしてください",
|
||||
"Parts": "部品",
|
||||
"Parts only": "部品のみ",
|
||||
"Pass through": "通過点",
|
||||
"Path": "パス",
|
||||
"Pattern": "パターン",
|
||||
@@ -446,16 +570,21 @@
|
||||
"Place at the origin instead": "代わりに原点に配置",
|
||||
"Planar": "平面",
|
||||
"Plastic Rules": "樹脂ルール",
|
||||
"Play": "再生",
|
||||
"Play the joint's motion in the view": "ビューでジョイントの動きを再生",
|
||||
"Playback": "再生",
|
||||
"Plot": "グラフ",
|
||||
"Point on line": "点-線",
|
||||
"Point on plane": "点-平面",
|
||||
"Point to Point": "点から点",
|
||||
"Point to Position": "点から位置",
|
||||
"Polycarbonate - Clear": "ポリカーボネート - クリア",
|
||||
"Position": "位置",
|
||||
"Position (0 to 1)": "位置 (0〜1)",
|
||||
"Position (G0)": "位置 (G0)",
|
||||
"Position along the edge, 0 at the start end, 1 at the other": "エッジ上の位置(始点が0、終点が1)",
|
||||
"Preferences › Design turns the offer back on": "環境設定 › デザイン で再びオンにできます",
|
||||
"Preferences…": "環境設定…",
|
||||
"Preview": "プレビュー",
|
||||
"Preview Row": "行をプレビュー",
|
||||
"Preview leaves the current design unchanged.": "プレビューは現在のデザインを変更しません。",
|
||||
@@ -464,12 +593,21 @@
|
||||
"Profiles/Curves": "プロファイル/曲線",
|
||||
"Quantity": "数量",
|
||||
"Quantity must be at least 2": "数量は2以上にしてください",
|
||||
"RACK AND PINION": "ラック アンド ピニオン",
|
||||
"RECENT": "最近使用した項目",
|
||||
"REFERENCE": "参照",
|
||||
"REFOLD": "折り戻し",
|
||||
"REMOVE": "除去",
|
||||
"RENAME": "名前を変更",
|
||||
"REPLACE FACE": "面を置換",
|
||||
"REST": "レスト",
|
||||
"REVERSE NORMAL": "法線を反転",
|
||||
"RIGID GROUP": "リジッド グループ",
|
||||
"RULED SURFACE": "ルールドサーフェス",
|
||||
"RULES": "ルール",
|
||||
"Radius": "半径",
|
||||
"Radius Fit": "半径フィット",
|
||||
"Range": "範囲",
|
||||
"Ratio": "比率",
|
||||
"Read-only list: shortcuts can't be changed here.": "読み取り専用の一覧です。ここではショートカットを変更できません。",
|
||||
"Reattach selected face": "選択した面を再アタッチ",
|
||||
@@ -477,6 +615,7 @@
|
||||
"Recover unsaved design?": "未保存のデザインを復元しますか?",
|
||||
"Rectangular": "長方形",
|
||||
"Rectangular Tube": "角形鋼管",
|
||||
"Reduce to Fit": "縮小してフィット",
|
||||
"Reference face": "参照面",
|
||||
"Reference plane": "参照平面",
|
||||
"Relief Depth": "リリーフ深さ",
|
||||
@@ -500,6 +639,7 @@
|
||||
"Repair Part": "パーツを修復",
|
||||
"Replace existing library file": "既存のライブラリファイルを置き換える",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "バグを報告 — スクリーンショットを確認してGitHub下書きを作成",
|
||||
"Report an Issue…": "問題を報告…",
|
||||
"Report copied. Keep this text to submit on GitHub when you are online.": "レポートをコピーしました。このテキストを保存し、オンラインになったらGitHubで送信してください。",
|
||||
"Report text copied. Paste it into GitHub and submit your report there.": "報告本文をコピーしました。GitHubに貼り付けて投稿してください。",
|
||||
"Reports are public. A GitHub account is required.": "報告は公開されます。GitHubアカウントが必要です。",
|
||||
@@ -532,16 +672,32 @@
|
||||
"Ring of tools on the right button": "右ボタンのツールリング",
|
||||
"Roll Back Here": "ここまでロールバック",
|
||||
"Roll History Marker Here": "履歴マーカーをここに移動",
|
||||
"Rolling": "ローリング",
|
||||
"Root radius": "付け根半径",
|
||||
"Rotate": "回転",
|
||||
"Round": "丸め",
|
||||
"Round Bar": "丸鋼",
|
||||
"Round Tube": "丸形鋼管",
|
||||
"Rubber - Black": "ゴム - ブラック",
|
||||
"Rule": "ルール",
|
||||
"Rule name": "ルール名",
|
||||
"Rules": "ルール",
|
||||
"Rules run from top to bottom. Matched drives change effective values.": "ルールは上から順に実行されます。条件に一致する駆動ルールが有効値を変更します。",
|
||||
"SCREW": "ねじ",
|
||||
"SECTION": "断面",
|
||||
"SHEET METAL RULES": "板金ルール",
|
||||
"SNAP FIT": "スナップ フィット",
|
||||
"SPLIT FACE": "面を分割",
|
||||
"STITCH": "ステッチ",
|
||||
"STOCK": "ストック",
|
||||
"SURFACE EXTRUDE": "サーフェス押し出し",
|
||||
"SURFACE LOFT": "サーフェスロフト",
|
||||
"SURFACE QUALITY": "サーフェス品質",
|
||||
"SURFACE REVOLVE": "サーフェス回転",
|
||||
"SURFACE SWEEP": "サーフェススイープ",
|
||||
"Same way": "同じ方向",
|
||||
"Save As JSON…": "JSON として保存…",
|
||||
"Save As…": "名前を付けて保存…",
|
||||
"Save Rule": "ルールを保存",
|
||||
"Save View": "ビューを保存",
|
||||
"Save screenshot…": "スクリーンショットを保存…",
|
||||
@@ -550,6 +706,9 @@
|
||||
"Scale factor": "尺度係数",
|
||||
"Scene": "シーン",
|
||||
"Screenshot (optional)": "スクリーンショット(任意)",
|
||||
"Screenshot captured locally. Review it before attaching it on GitHub.": "スクリーンショットをローカルで取得しました。GitHub に添付する前に確認してください。",
|
||||
"Scroll left": "左へスクロール",
|
||||
"Scroll right": "右へスクロール",
|
||||
"Search": "検索",
|
||||
"Search commands": "コマンドを検索",
|
||||
"Search this library": "このライブラリを検索",
|
||||
@@ -581,6 +740,7 @@
|
||||
"Send your report on GitHub": "GitHubでレポートを送信",
|
||||
"Shaded": "シェーディング",
|
||||
"Shape": "形状",
|
||||
"Sheet Metal": "板金",
|
||||
"Sheet Rules": "板金ルール",
|
||||
"Show Agent Cursor": "エージェントカーソルを表示",
|
||||
"Show ghost": "ゴーストを表示",
|
||||
@@ -608,6 +768,7 @@
|
||||
"Speed": "速度",
|
||||
"Splitting tool": "分割ツール",
|
||||
"Square": "正方形",
|
||||
"Stainless Steel": "ステンレス鋼",
|
||||
"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?": "このアセンブリを正立させますか?",
|
||||
@@ -616,6 +777,8 @@
|
||||
"Start bulge": "開始側の膨らみ",
|
||||
"Start continuity": "開始側の連続性",
|
||||
"Stationary Side": "固定側",
|
||||
"Steel": "鋼",
|
||||
"Steel - Brushed": "スチール - ヘアライン",
|
||||
"Step": "ステップ",
|
||||
"Steps": "ステップ数",
|
||||
"Steps to reproduce (optional)": "再現手順(任意)",
|
||||
@@ -626,12 +789,19 @@
|
||||
"Style": "スタイル",
|
||||
"Summary": "概要",
|
||||
"Suppress": "抑制",
|
||||
"Suppress Features": "フィーチャを抑制",
|
||||
"Suppress Selected Folders": "選択したフォルダーを抑制",
|
||||
"Suppressed": "抑制",
|
||||
"Suppression": "抑制",
|
||||
"Surface": "サーフェス",
|
||||
"Surface or face": "サーフェスまたは面",
|
||||
"Surface or faces": "サーフェスまたは面",
|
||||
"Surface, or faces to turn over": "サーフェス、または裏返す面",
|
||||
"Surfaces": "サーフェス",
|
||||
"Swap Ends": "端点を入れ替え",
|
||||
"TANGENT PLANE": "正接平面",
|
||||
"THICKEN": "厚み付け",
|
||||
"TRANSFORM": "変形",
|
||||
"TRIM": "トリム",
|
||||
"Tangent": "正接",
|
||||
"Tangent (G1)": "接線 (G1)",
|
||||
@@ -639,9 +809,11 @@
|
||||
"Tangent chain": "接線チェーン",
|
||||
"Tangent to Edge": "エッジに正接",
|
||||
"Tap drill diameter": "タップ下穴径",
|
||||
"Taper to Fit": "テーパーで合わせる",
|
||||
"Target": "ターゲット",
|
||||
"Target Body": "ターゲットボディ",
|
||||
"Target Face (optional)": "対象面 (オプション)",
|
||||
"Target edges": "ターゲットのエッジ",
|
||||
"Tear": "引き裂き",
|
||||
"Technical details": "技術情報",
|
||||
"The dashed sketch is a saved plane guide. Select a new face to repair the attachment.": "破線のスケッチは保存された平面ガイドです。新しい面を選択してアタッチメントを修復してください。",
|
||||
@@ -649,6 +821,7 @@
|
||||
"The direction must be an axis or a straight sketch curve that lies in the sketch plane": "方向は、軸、またはスケッチ平面上にある直線のスケッチ曲線にしてください",
|
||||
"The mirror line cannot also be mirrored": "ミラー線自体をミラーすることはできません",
|
||||
"The mirror line must be a straight curve of this sketch, not an origin axis": "ミラー線は、原点の軸ではなく、このスケッチの直線にしてください",
|
||||
"The origin axis is perpendicular to the sketch plane, so it has no line in the plane": "原点軸はスケッチ平面に垂直なため、平面内に線がありません",
|
||||
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "保存された作成元のオカレンスが見つかりません。再バインドする前に有効な作成元配置を選択してください。",
|
||||
"Then": "次を実行 (Then)",
|
||||
"These features will be deleted:": "以下のフィーチャーが削除されます:",
|
||||
@@ -680,16 +853,21 @@
|
||||
"Top Left": "左上",
|
||||
"Top Middle": "上中央",
|
||||
"Top Right": "右上",
|
||||
"Top level": "最上位",
|
||||
"Trimming tool (plane, face or curves)": "トリムツール(平面、面、または曲線)",
|
||||
"Turn off section analysis before exporting the view.": "断面解析をオフにしてからエクスポートしてください。",
|
||||
"Two Distances": "2 つの距離",
|
||||
"Type": "タイプ",
|
||||
"Type a distance or an angle to move by": "移動する距離または角度を入力してください",
|
||||
"UNFOLD": "展開",
|
||||
"UNSTITCH": "縫い合わせ解除",
|
||||
"UNTRIM": "トリム解除",
|
||||
"Unit": "単位",
|
||||
"Unsuppress": "抑制を解除",
|
||||
"Unsuppress Selected Folders": "選択したフォルダーの抑制を解除",
|
||||
"Updating the preview": "プレビューを更新中",
|
||||
"Use active author placement": "アクティブな作成元配置を使用",
|
||||
"Use axis direction only": "軸の方向のみを使用",
|
||||
"Use fixed plane": "固定平面を使用",
|
||||
"Use saved fixed plane": "保存された固定平面を使用",
|
||||
"Use selected face as fixed plane": "選択した面を固定平面として使用",
|
||||
@@ -699,13 +877,16 @@
|
||||
"VIEWS": "ビュー",
|
||||
"Value": "値",
|
||||
"Variable Radius": "可変半径",
|
||||
"Variable radius": "可変半径",
|
||||
"Velocity": "速度",
|
||||
"Vertices": "頂点",
|
||||
"View": "表示",
|
||||
"View navigator": "ビューナビゲーター",
|
||||
"Volume": "体積",
|
||||
"Wall": "肉厚",
|
||||
"Wall Location": "壁の位置",
|
||||
"Wall Thickness": "壁の厚さ",
|
||||
"Walnut": "ウォルナット",
|
||||
"Web thickness": "ウェブ厚",
|
||||
"What did you do just before the problem? A short list is enough": "問題の直前に行った操作を簡単に記入してください",
|
||||
"What did you expect to see?": "どのような結果を期待していましたか?",
|
||||
@@ -740,6 +921,9 @@
|
||||
"a picked edge's body is gone": "選択したエッジのボディが存在しなくなりました",
|
||||
"add a key for a joint first": "先にジョイントのキーを追加してください",
|
||||
"ai.connected_toast": "SolidCraft が {clients} で使えるようになりました。使うには再起動してください(設定 > アシスタント)。",
|
||||
"ai.http.allow": "安全でない HTTP を許可",
|
||||
"ai.http.confirm": "このエンドポイントで許可",
|
||||
"ai.http.warning": "API キーと会話は暗号化されずに送信されます。ネットワークにアクセスできる人は、内容を読み取ったり改ざんしたりする可能性があります。信頼できるネットワークでのみ使用してください。",
|
||||
"ai.prefs.allow_control": "AIエージェントの接続を許可",
|
||||
"ai.prefs.allow_control_note": "ローカルチャネル(MCP)です。このコンピューターから外には何も送信しません。\n\nこのコンピューター内だけのコントロールチャンネル(127.0.0.1、ポート 27878 または次に空いているポート)を開きます。接続するプログラムは、起動のたびに作られ、あなたのユーザーアカウントだけが読める秘密のトークンも提示する必要があります。次に SolidCraft を起動したときから有効です。オフ:--control を付けて起動しない限り、何も待ち受けません。",
|
||||
"ai.prefs.availability": "利用設定",
|
||||
@@ -751,6 +935,8 @@
|
||||
"ai.prefs.follow_camera": "カメラを追従",
|
||||
"ai.prefs.follow_camera_off": "カメラの自動追従はオフです。",
|
||||
"ai.prefs.follow_camera_on": "エージェントが作成するスケッチにカメラを向けます。",
|
||||
"ai.prefs.instructions": "カスタム指示",
|
||||
"ai.prefs.instructions_note": "各チャットの開始時に、SolidCraft 自身の指示の後に続けてエージェントへ送られます。作業の進め方や守ってほしいルールを書いてください。変更は次のチャット(接続または新しいチャット)から適用されます。",
|
||||
"ai.prefs.intro": "SolidCraftのAIエージェント。",
|
||||
"ai.prefs.make_default": "既定にする",
|
||||
"ai.prefs.no_agents": "エージェントはまだありません。追加するか、更新してインストール済みのエージェントを検索してください。",
|
||||
@@ -1035,6 +1221,7 @@
|
||||
"cmd.occurrence_move": "オカレンスを移動",
|
||||
"cmd.occurrence_properties": "オカレンスの物理的プロパティ",
|
||||
"cmd.occurrence_reparent": "コンポーネントへ移動",
|
||||
"cmd.panel.action": "パネル操作を実行",
|
||||
"cmd.panel.activate": "パネルのタブをアクティブ化",
|
||||
"cmd.panel.close": "パネルを閉じる",
|
||||
"cmd.panel.collapse": "パネルのセクションを折りたたむ",
|
||||
@@ -1081,6 +1268,9 @@
|
||||
"cmd.render.compare.divider": "レンダー比較の境界を移動",
|
||||
"cmd.render.result.discard": "レンダリング結果を破棄",
|
||||
"cmd.render.result.select": "レンダリング結果を選択",
|
||||
"cmd.render.surface.import": "サーフェスマップをインポート",
|
||||
"cmd.render.texture.list": "埋め込みサーフェス画像の一覧",
|
||||
"cmd.render.texture.remove": "埋め込みサーフェス画像を削除",
|
||||
"cmd.render_aging_describe": "レイヤー式表面エイジング",
|
||||
"cmd.render_backends": "レンダリング バックエンド",
|
||||
"cmd.render_batch": "すべてのショットをレンダリング",
|
||||
@@ -1219,6 +1409,7 @@
|
||||
"cmd.sketch_move": "スケッチ要素を移動/コピー",
|
||||
"cmd.sketch_move3d": "3D 点を移動",
|
||||
"cmd.sketch_move_point": "スケッチ点をドラッグ",
|
||||
"cmd.sketch_move_points": "スケッチ点を移動",
|
||||
"cmd.sketch_offset_break": "オフセット関連付けを解除",
|
||||
"cmd.sketch_options": "スケッチ パレット オプション",
|
||||
"cmd.sketch_paste": "スケッチ要素を貼り付け",
|
||||
@@ -1324,11 +1515,13 @@
|
||||
"cmd.view_previous_tab": "前のタブ",
|
||||
"cmd.view_rename": "名前付きビューの名前を変更",
|
||||
"cmd.view_render_frame": "レンダーフレームガイド",
|
||||
"cmd.view_ribbon_tabs_only": "リボンのタブのみ",
|
||||
"cmd.view_right": "右側面ビュー",
|
||||
"cmd.view_save": "新規名前付きビュー",
|
||||
"cmd.view_shots_strip": "ショットストリップ",
|
||||
"cmd.view_spin": "連続オービット",
|
||||
"cmd.view_tool": "ナビゲーションツール",
|
||||
"cmd.view_tool_zoom": "ドラッグでズーム",
|
||||
"cmd.view_top": "上面ビュー",
|
||||
"cmd.view_visual_style": "表示スタイル",
|
||||
"color.blue": "青",
|
||||
@@ -1419,6 +1612,9 @@
|
||||
"document.changed_on_disk": "{name} がディスク上で変更されました",
|
||||
"document.changed_on_disk_detail": "{name} は、開いたとき(または最後に保存したとき)以降に別のプログラムまたはコンピューターによって変更されました。上書きするとその変更は失われます。コピーを保存すると両方が残ります。",
|
||||
"document.copy_name": "{name} のコピー",
|
||||
"document.newer_file": "新しいバージョンの SolidCraft で保存されています(形式 {format}、このバージョンは {reads} まで読み取れます)。足りないものがあれば更新してください。",
|
||||
"document.newer_file_change": "新しいバージョンの SolidCraft で保存されています(形式 {format}、このバージョンが認識しない `{change}` を含みます)。足りないものがあれば更新してください。",
|
||||
"document.newer_file_not_read": "読み取れませんでした: {what}。保存しても失われません。",
|
||||
"document.open_anyway": "このまま開く",
|
||||
"document.open_elsewhere": "{name} はすでに開かれています",
|
||||
"document.open_elsewhere_detail": "{name} は {who} の SolidCraft {app} で開かれています({when}に開かれました)。両方で保存すると、どちらかの作業が失われます。上書きせずに確認するには読み取り専用で開いてください。",
|
||||
@@ -1454,8 +1650,11 @@
|
||||
"file.open.portal_fallback.xwayland": "開いたファイル選択はデスクトップのものではありません。XWayland ではポータルが親ウィンドウを拒否します。",
|
||||
"file_load.built": "構築済み: 全{total}フィーチャー中{built}件。",
|
||||
"file_load.converting": "ソリッドを変換中: {done}/{total}",
|
||||
"file_load.inserted": "{name} を挿入しました",
|
||||
"file_load.inserting": "ジオメトリを挿入中",
|
||||
"file_load.link_item": "{name} ({done}/{total})",
|
||||
"file_load.opened": "{name} を開きました",
|
||||
"file_load.opened_in_tab": "{name} を新しいタブで開きました",
|
||||
"file_load.opening": "デザインを開いています",
|
||||
"file_load.preparing_thumbnail": "サムネイルを準備中...",
|
||||
"file_load.preparing_view": "ビューを準備中...",
|
||||
@@ -1691,7 +1890,7 @@
|
||||
"help.sketch.insert_svg": "SVG 図面のパスをスケッチに読み込み、指定した位置に拡大縮小・回転して配置します。ロゴやベクター輪郭を、押し出しできる曲線にするときに使います。",
|
||||
"help.sketch.inspect": "開いているスケッチのレポートを開きます。残りの自由度と、完全に拘束されているかどうかを表示します。スケッチを終える前に、まだ動く部分を確認するときに使います。",
|
||||
"help.sketch.intersect": "ボディ、面、エッジ、ほかのスケッチがスケッチ平面と交差する曲線または点を追加します。リンクが保たれ、モデルが変わると追従します。",
|
||||
"help.sketch.intersection_curve": "2つのボディまたは面が接する位置に、それらにリンクした 3D 曲線を作成します。モデルに追従するパスやガイドレールとして使います。",
|
||||
"help.sketch.intersection_curve": "2つのボディ、面、またはほかのスケッチの曲線が接する位置に、それらにリンクした 3D 曲線を作成します。モデルに追従するパスやガイドレールとして使います。",
|
||||
"help.sketch.isoparametric_curve": "選んだ点を通る面の U または V 方向に沿った 3D 曲線を、面にリンクして作成します。表面に追従するパスや分割線として使います。",
|
||||
"help.sketch.line": "点を順に指定して、つながった直線を開いた連なりまたは閉じた外形として描きます。ほとんどの輪郭に使う基本のツールです。",
|
||||
"help.sketch.line.midpoint": "中点から一方の端まで直線を描き、もう一方の端は反対側に対称に作られます。対称なエッジなど、点を中心にした直線に使います。",
|
||||
@@ -1797,6 +1996,7 @@
|
||||
"help.view.navigate": "左に90度回転するなど、ステップごとにビューを回転、移動、ズームします。ドラッグではなく、カメラを正確に動かすときに使います。",
|
||||
"help.view.navigation_bar": "ビューポート下部のナビゲーションバーを表示または非表示にします。広く使いたいときは非表示に、マウスでビューツールを使うときは表示にします。",
|
||||
"help.view.rename": "保存した名前付きビューの名前を変更します。ビュー自体は変わりません。",
|
||||
"help.view.ribbon_tabs_only": "リボンをタブの行だけに折りたたみ、パネルが使っていた場所をビューに充てます。タブをクリックするとそのパネルがビューの上に表示されます。もう一度選ぶとリボン全体を表示したままにします。",
|
||||
"help.view.save": "現在のカメラ位置を名前付きビューとしてデザインに保存します。スクリーンショット用など、後で同じ角度に戻るときに使います。",
|
||||
"help.view.spin": "次のクリックまたはキー操作まで、モデルのまわりでビューを連続して周回させます。モデルをあらゆる方向から見せるときに使います。",
|
||||
"help.view.tool": "オービット、パン、ズームのカメラツールを固定し、オフにするまで左ボタンのドラッグでビューを動かせるようにします。トラックパッドや、中ボタンのないマウスで使います。",
|
||||
@@ -2163,6 +2363,7 @@
|
||||
"navbar.value_tip": "{name} · {value}",
|
||||
"navbar.visual_style": "表示スタイル",
|
||||
"navbar.zoom": "ズーム",
|
||||
"navbar.zoom_hold_tip": "ズーム(ショートカットを長押し)",
|
||||
"needs two joints": "2つのジョイントが必要",
|
||||
"network.allow_once": "今回のみ許可",
|
||||
"network.always_allow": "このサーバーを常に許可",
|
||||
@@ -2316,6 +2517,7 @@
|
||||
"prefs.carousel_previous": "前へ",
|
||||
"prefs.choose_font": "フォントファイルを選択…",
|
||||
"prefs.click": "クリック",
|
||||
"prefs.command_dialog_values": "コマンドダイアログ",
|
||||
"prefs.contact_shading": "柔らかな接触陰影",
|
||||
"prefs.contact_shading_note": "部品が近接する部分に柔らかな陰影を付けます。",
|
||||
"prefs.context_menu": "コンテキストメニュー",
|
||||
@@ -2362,6 +2564,8 @@
|
||||
"prefs.edges": "エッジと陰影",
|
||||
"prefs.extend": "拡張",
|
||||
"prefs.extra_navigation": "何もない場所でCtrl+左ドラッグすると回転し、Spaceを押しながらドラッグすると移動します。",
|
||||
"prefs.extrude_arrow_face_stops": "押し出し矢印を面で止める",
|
||||
"prefs.extrude_arrow_face_stops_note": "押し出しの距離矢印をドラッグすると、プロファイルに平行な平らな面(カットするブロックの反対側など)で止まり、その面の輪郭を表示します。オフ: 矢印はきりのよい刻みでのみ動きます。",
|
||||
"prefs.extrude_view_isometric": "押し出しを開いたときに等角ビューにする",
|
||||
"prefs.extrude_view_return": "押し出しを閉じたときに元のビューに戻す",
|
||||
"prefs.follows_os": "OS の設定に従う",
|
||||
@@ -2411,6 +2615,8 @@
|
||||
"prefs.graphics_page.surfaces_edges": "サーフェスとエッジ",
|
||||
"prefs.graphics_page.view_bar": "ビューバーにも表示",
|
||||
"prefs.graphics_presets_note": "プリセットは以下の設定をまとめて変更します。",
|
||||
"prefs.ground_shadow_follows_model": "地面の影をモデルに追従させる",
|
||||
"prefs.ground_shadow_follows_model_help": "表示中のボディの最下部に影を配置します。オフの場合、持ち上げたモデルの影は原点を通る地面上に残ります。",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "ズームアウト時に拘束グリフを隠す",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out_note": "スケッチを十分にズームアウトすると、拘束グリフは表示されず画面を散らかしません。オフ: ズームに関係なく固定の画面サイズのままです。",
|
||||
"prefs.history": "履歴",
|
||||
@@ -2452,6 +2658,8 @@
|
||||
"prefs.layout_page.card": "カード",
|
||||
"prefs.layout_page.column": "列",
|
||||
"prefs.layout_page.hover": "ホバー時",
|
||||
"prefs.layout_page.hover_panel_menus": "ポイントしてパネルメニューを切り替える",
|
||||
"prefs.layout_page.hover_panel_menus_help": "リボンのパネル名をクリックしてメニューを開きます。別のパネル名にポインターを合わせると切り替わります。リボンと開いているメニューの外に移動すると閉じます。",
|
||||
"prefs.layout_page.icons_labels": "アイコン+ラベル",
|
||||
"prefs.layout_page.ok_cancel": "OK とキャンセル",
|
||||
"prefs.layout_page.on_hover": "ホバー時",
|
||||
@@ -2490,6 +2698,8 @@
|
||||
"prefs.mouse": "マウスとトラックパッド",
|
||||
"prefs.mouse_heading": "マウス",
|
||||
"prefs.mouse_mapping": "マウスの割り当て",
|
||||
"prefs.move_drag_highlights": "移動の矢印でコンポーネントをドラッグ中にポインターの下をハイライト",
|
||||
"prefs.move_drag_highlights_tip": "オフ: 移動の矢印でコンポーネントをドラッグしている間はポインターの下をハイライトしないため、大きなアセンブリでも滑らかに動きます。断面や解析を表示中は通常どおりハイライトし、ジョイントに沿ってドラッグする部品ではハイライトしません。",
|
||||
"prefs.navigation": "ナビゲーション",
|
||||
"prefs.navigation.axes_signed": "軸",
|
||||
"prefs.navigation.cube_compact": "キューブ",
|
||||
@@ -2518,6 +2728,22 @@
|
||||
"prefs.persistent_ribbon_pressed": "有効なリボンコマンドを強調表示",
|
||||
"prefs.persistent_ribbon_pressed_help": "効果が有効な間、リボンのコマンドを強調表示します。無効にすると、従来の中立的な表示とコマンドのヘルプを保持します。",
|
||||
"prefs.perspective_fov": "画角",
|
||||
"prefs.picker.carousel": "カルーセル",
|
||||
"prefs.picker.change": "変更",
|
||||
"prefs.picker.clear": "フィルターをクリア",
|
||||
"prefs.picker.day": "日中のテーマ",
|
||||
"prefs.picker.empty": "このフィルターに一致するテーマはありません。",
|
||||
"prefs.picker.follow": "システムに合わせる",
|
||||
"prefs.picker.grid": "グリッド",
|
||||
"prefs.picker.high": "ハイコントラスト",
|
||||
"prefs.picker.high_note": "{source} · ハイコントラスト",
|
||||
"prefs.picker.hint": "OSの設定に合わせて切り替わります。",
|
||||
"prefs.picker.import": "テーマファイルをインポート…",
|
||||
"prefs.picker.imported": "インポート済み",
|
||||
"prefs.picker.night": "夜間のテーマ",
|
||||
"prefs.picker.one": "単一のテーマ",
|
||||
"prefs.picker.pick": "下から選択",
|
||||
"prefs.picker.standard": "標準",
|
||||
"prefs.placement": "配置",
|
||||
"prefs.plugins_enabled": "有効",
|
||||
"prefs.plugins_installed": "インストール済み",
|
||||
@@ -2554,6 +2780,8 @@
|
||||
"prefs.quality_smoother": "より滑らか",
|
||||
"prefs.quick_measure": "ビューの隅にクイック測定",
|
||||
"prefs.quick_measure_note": "選択したものの寸法をビューの右下、履歴の上に表示します: エッジの長さ(円形のものは Ø と R)、面の面積、点の位置、2 つの項目の距離と角度、複数のエッジや面の合計。クリックでコピーします。",
|
||||
"prefs.remember_dialog_values": "コマンドダイアログの値を記憶",
|
||||
"prefs.remember_dialog_values.help": "既定ではオフです。オンにすると、コマンドが成功した後、SolidCraft を閉じるまで、同じコマンドを次に開いたときに安全な値とオプションを再利用します。",
|
||||
"prefs.render_previous_geometry_budget": "レンダープレビューで以前のジオメトリ予算を使用",
|
||||
"prefs.render_previous_geometry_budget_help": "デバイスに基づく予算の代わりに、以前の固定ジオメトリ上限(64ビットデスクトップでは512 MiB、その他では128 MiB)に戻します。",
|
||||
"prefs.reports_saved": "{n} 件保存済み",
|
||||
@@ -2562,6 +2790,8 @@
|
||||
"prefs.ribbon_click_ends_tool_note": "使用中のスケッチツールの点灯したリボンボタンをクリックするとツールが終了し、ビューのクリックで再び選択できます。オフ: クリックするとツールが最初から開始されます。",
|
||||
"prefs.ribbon_reveal": "検索結果をリボンに表示",
|
||||
"prefs.ribbon_reveal_note": "コマンド検索で結果にポインターを合わせると、そのコマンドがリボンのどこにあるか(タブ、ボタン、またはパネルのドロップダウン内の行)を表示します。検索から開始したコマンドはそこで点滅し、ドロップダウンから実行中のコマンドはそのパネルに印が付きます。",
|
||||
"prefs.ribbon_tabs_only": "リボンのタブだけを表示",
|
||||
"prefs.ribbon_tabs_only_note": "リボンをタブの行だけに折りたたみます。タブをクリックすると、コマンドが始まるかポインターが離れるまで、そのパネルがビューの上に表示されます。表示タブの「リボンのタブのみ」またはそのショートカットで切り替えます。",
|
||||
"prefs.right_click": "右クリック",
|
||||
"prefs.saving": "起動と保存",
|
||||
"prefs.scale_note": "ウィンドウ内では Ctrl + / Ctrl − でも変更できます。100% のボタンは 28 px です。",
|
||||
@@ -2633,6 +2863,7 @@
|
||||
"prefs.sketch_exit_view_note": "「スケッチを終了」、キャンセル、Esc の 2 回押しの後にビューが移る先です。",
|
||||
"prefs.sketch_face_activates_part": "部品の面にスケッチするとその部品をアクティブにする",
|
||||
"prefs.sketch_face_activates_part_note": "別のコンポーネントのボディの面で始めたスケッチは、先にそのコンポーネントをアクティブにし、スケッチはそのコンポーネントに属してブラウザでその部品の下に表示されます。部品はその後もアクティブのままです。オフ:スケッチはアクティブなコンポーネントに属します。",
|
||||
"prefs.sketch_mirror_auto_line": "事前に選択した補助直線をミラー線に使用する",
|
||||
"prefs.sketch_open_look_at": "スケッチを開いたときに正対させる",
|
||||
"prefs.sketch_open_look_at_note": "オフ: スケッチの作成や編集でビューを動かしません。必要なときは「正面視」でスケッチに正対します。",
|
||||
"prefs.sketch_recent_snap": "最近使った中心と中点へ自動スナップ",
|
||||
@@ -3914,6 +4145,7 @@
|
||||
"render.stage_panels.extent": "範囲",
|
||||
"render.stage_panels.fit_save": "フィットして保存",
|
||||
"render.stage_panels.gpu_required": "GPUパストレーシングが必要です",
|
||||
"render.stage_panels.grain_angle": "木目の角度",
|
||||
"render.stage_panels.keep": "保持",
|
||||
"render.stage_panels.kept": "保持",
|
||||
"render.stage_panels.kept_fields": "カメラ、サンプリング、画像寸法、光学設定、CAD形状、工学的特性。",
|
||||
@@ -4032,6 +4264,19 @@
|
||||
"render.subsurface_enable": "内部散乱ランダムウォーク",
|
||||
"render.subsurface_help": "閉じた誘電体専用。散乱とRGB吸収は別々の毎mm係数です。実際の形状内で散乱し、境界で屈折・反射します。直線透過影、表面ノイズ除去、外部ボリュームを無効にし、フレネル層数を1にします。拡散シェーダー、密度グリッド、実測物性ではありません。高密度ではサンプル数と深度が必要です。",
|
||||
"render.subsurface_rate": "散乱衝突率",
|
||||
"render.surface_import.author": "作者",
|
||||
"render.surface_import.busy": "画像をインポート中…",
|
||||
"render.surface_import.complete": "画像を選び、作者、出典、ライセンスを入力してください。",
|
||||
"render.surface_import.embedded": "インポートした画像",
|
||||
"render.surface_import.failed": "画像のインポートが完了しませんでした。",
|
||||
"render.surface_import.license": "ライセンス / 所有権",
|
||||
"render.surface_import.raytrace_required": "画像にはCPUまたはGPUのレイトレーシングが必要です。出力で選択してください。",
|
||||
"render.surface_import.repeat_u": "U方向の繰り返し",
|
||||
"render.surface_import.repeat_v": "V方向の繰り返し",
|
||||
"render.surface_import.save": "ボディに画像を保存",
|
||||
"render.surface_import.saved_cpu": "CPUレンダリングで使うにはマップを保存してください。",
|
||||
"render.surface_import.source": "出典",
|
||||
"render.surface_import.title": "画像をインポート",
|
||||
"render.tone_aces_fitted": "ACES近似",
|
||||
"render.tone_reinhard": "Reinhard",
|
||||
"render.transmission_scope": "GPUガラスは検証された非交差媒体に対応します。無制限の入れ子媒体やノイズのない透過推定器は提供しません。",
|
||||
@@ -4108,6 +4353,8 @@
|
||||
"sample.plate.name": "サンプル プレート",
|
||||
"sample.vents.about": "矩形パターンに並んだスロット: 行数、列数、ピッチはパラメータ",
|
||||
"sample.vents.name": "通気プレート",
|
||||
"scale.per_axis": "軸ごと",
|
||||
"scale.uniform": "均等",
|
||||
"search.command_keys": "Enter 実行 Shift+Enter 実行して開いたままにする Esc 閉じる",
|
||||
"search.commands": "コマンド",
|
||||
"search.design": "このデザイン内",
|
||||
@@ -4780,6 +5027,7 @@
|
||||
"ui.click_selects_whole_bodies": "クリックでボディ全体を選択",
|
||||
"ui.click_then_press_key": "クリックした後、割り当てるキーを押してください",
|
||||
"ui.close": "閉じる",
|
||||
"ui.close_tab": "タブを閉じる",
|
||||
"ui.coil": "コイル",
|
||||
"ui.coincident": "一致",
|
||||
"ui.collapse": "折りたたむ",
|
||||
@@ -4997,6 +5245,11 @@
|
||||
"ui.factor": "倍率",
|
||||
"ui.fade": "切り上げ",
|
||||
"ui.fade_length": "切り上げ長さ",
|
||||
"ui.fair_iterations": "反復回数",
|
||||
"ui.fair_keep_edges": "エッジを保持",
|
||||
"ui.fair_strength": "強度",
|
||||
"ui.fair_surface_title": "サーフェスフェアリング",
|
||||
"ui.fair_tolerance_default": "ボディサイズの 0.001 倍",
|
||||
"ui.feature_history": "フィーチャー履歴",
|
||||
"ui.feature_options": "フィーチャーオプション",
|
||||
"ui.features": "フィーチャー",
|
||||
@@ -5182,6 +5435,7 @@
|
||||
"ui.inspect": "検査",
|
||||
"ui.instances": "インスタンス",
|
||||
"ui.intersect": "交差",
|
||||
"ui.intersection_curve_second_pick": "交差曲線: 2 つ目の面または曲線をクリックします (右クリックでやり直し)",
|
||||
"ui.iso_4753": "ISO 4753",
|
||||
"ui.isolate": "分離表示",
|
||||
"ui.join": "結合",
|
||||
@@ -5210,6 +5464,7 @@
|
||||
"ui.manipulator_free_alt": "マニピュレータのフリードラッグを Alt/Option でも有効",
|
||||
"ui.manipulator_free_alt_tip": "オン: 押し出し/ギズモ矢印で Alt/Option または Ctrl/Cmd が値スナップを無効にします。オフ: Ctrl/Cmd のみ。",
|
||||
"ui.manufacture": "製造",
|
||||
"ui.match_surface_title": "サーフェス一致",
|
||||
"ui.material_review.axes": "材料ローカル軸。ボディの方向は割り当てません。",
|
||||
"ui.material_review.claims": "出典とライセンスの申告は未確認です。欠落値は不明のままです。",
|
||||
"ui.material_review.compressive_strength": "圧縮強度",
|
||||
@@ -5232,6 +5487,9 @@
|
||||
"ui.material_review.title": "物理材料データを確認",
|
||||
"ui.material_review.unknown": "不明",
|
||||
"ui.material_review.yield_strength": "降伏強度",
|
||||
"ui.mcp_agents": "MCP 経由のエージェント",
|
||||
"ui.mcp_compact": "コンパクトな出力",
|
||||
"ui.mcp_compact_note": "MCP サーバー (solidcraft-cli mcp) 経由で接続したエージェント向け: inspect_design はスケッチを要約し、ツールの結果は整形されないため、トークンが少なくて済みます。変更後に開始したエージェントのセッションから適用されます。--compact または SOLIDCRAFT_MCP_COMPACT=1 でも有効になります。",
|
||||
"ui.measure": "計測",
|
||||
"ui.mesh_format_3mf": "3MF",
|
||||
"ui.mesh_format_3mf_tagged": "面タグ付き 3MF",
|
||||
@@ -5273,6 +5531,7 @@
|
||||
"ui.options": "オプション",
|
||||
"ui.orbit": "回転",
|
||||
"ui.orbit_around": "回転中心",
|
||||
"ui.organize": "整理",
|
||||
"ui.orientation": "向き",
|
||||
"ui.origin": "原点",
|
||||
"ui.palette_construction_mode": "補助線モード (X)",
|
||||
@@ -5280,6 +5539,65 @@
|
||||
"ui.palette_sketch_options": "スケッチオプション",
|
||||
"ui.pan": "パン",
|
||||
"ui.pan_zoom_orbit": "パン、ズーム、オービット",
|
||||
"ui.panel_fixture.apply": "適用",
|
||||
"ui.panel_fixture.cancel": "キャンセル",
|
||||
"ui.panel_fixture.caption": "合成パネル標本",
|
||||
"ui.panel_fixture.draft": "保持された下書き",
|
||||
"ui.panel_fixture.editor": "標本エディター",
|
||||
"ui.panel_fixture.explanation": "この合成コンテンツは非表示や移動後も下書きを保持します。",
|
||||
"ui.panel_fixture.fill": "高さを埋める標本",
|
||||
"ui.panel_fixture.finish": "完了",
|
||||
"ui.panel_fixture.finished": "合成トランザクション終了",
|
||||
"ui.panel_fixture.footer": "標本の操作は設計を変更しません。",
|
||||
"ui.panel_fixture.guard": "トランザクション中のキャンセルを拒否",
|
||||
"ui.panel_fixture.inline": "一行の標本",
|
||||
"ui.panel_fixture.live": "合成トランザクション実行中",
|
||||
"ui.panel_fixture.log": "標本ログ",
|
||||
"ui.panel_fixture.pin": "固定標本",
|
||||
"ui.panel_fixture.title": "{title} · {id}",
|
||||
"ui.panel_fixture.updates": "サービス更新: {count}",
|
||||
"ui.panel_host.attention": "確認が必要",
|
||||
"ui.panel_host.bottom": "下",
|
||||
"ui.panel_host.close": "パネルを閉じる",
|
||||
"ui.panel_host.collapse": "パネルを折りたたむ",
|
||||
"ui.panel_host.expand": "パネルを展開",
|
||||
"ui.panel_host.floating": "フローティング",
|
||||
"ui.panel_host.focus": "パネルにフォーカス",
|
||||
"ui.panel_host.focus_target": "{title}にフォーカス",
|
||||
"ui.panel_host.header": "パネルヘッダー",
|
||||
"ui.panel_host.join": "タブを結合",
|
||||
"ui.panel_host.join_target": "{title}とタブを結合",
|
||||
"ui.panel_host.left": "左",
|
||||
"ui.panel_host.menu": "パネル操作",
|
||||
"ui.panel_host.move": "移動先",
|
||||
"ui.panel_host.overflow": "その他のパネル",
|
||||
"ui.panel_host.popout": "ウィンドウで開く",
|
||||
"ui.panel_host.resize": "パネルのサイズ変更",
|
||||
"ui.panel_host.right": "右",
|
||||
"ui.panel_host.rule.availability": "このパネルは使用できません。",
|
||||
"ui.panel_host.rule.bounds": "パネルがサイズ制限内に収まりません。",
|
||||
"ui.panel_host.rule.capability": "このホストはその操作に対応していません。",
|
||||
"ui.panel_host.rule.collection_limit": "パネルレイアウトが上限に達しました。",
|
||||
"ui.panel_host.rule.duplicate_id": "このパネルIDは既に使用されています。",
|
||||
"ui.panel_host.rule.exactly_once": "各パネルは1回表示する必要があります。",
|
||||
"ui.panel_host.rule.feature_disabled": "このパネルではこの操作は無効です。",
|
||||
"ui.panel_host.rule.id": "パネルIDが無効です。",
|
||||
"ui.panel_host.rule.incompatible_tabs": "これらのパネルはタブグループを共有できません。",
|
||||
"ui.panel_host.rule.lifecycle": "パネルはこの操作を完了できませんでした。",
|
||||
"ui.panel_host.rule.missing_target": "このパネルの対象は使用できなくなりました。",
|
||||
"ui.panel_host.rule.no_space": "このパネル配置に必要な空間がありません。",
|
||||
"ui.panel_host.rule.pin_conflict": "この端には別のパネルが既に固定されています。",
|
||||
"ui.panel_host.rule.pin_order": "固定パネルは端の先頭に置く必要があります。",
|
||||
"ui.panel_host.rule.placement": "このパネルはその位置に移動できません。",
|
||||
"ui.panel_host.rule.revision": "レイアウトが変更されました。操作をやり直してください。",
|
||||
"ui.panel_host.rule.schema": "パネルのリクエストが無効です。",
|
||||
"ui.panel_host.rule.version": "このパネルレイアウトのバージョンには対応していません。",
|
||||
"ui.panel_host.sash": "隣接パネルのサイズ変更",
|
||||
"ui.panel_host.scroll_horizontal": "パネル列をスクロール",
|
||||
"ui.panel_host.scroll_vertical": "パネルの積み重ねをスクロール",
|
||||
"ui.panel_host.surface": "パネル",
|
||||
"ui.panel_host.tab": "パネルタブ",
|
||||
"ui.panel_host.tabs": "すべてのタブ",
|
||||
"ui.parallel": "平行",
|
||||
"ui.parameters": "パラメータ",
|
||||
"ui.part_size_needs_a_part": "サイズを変更する挿入済みの標準パーツ(ねじ、ナット、座金、ピン)を選択してください。ボディを拡大・縮小するには尺度を使います。",
|
||||
@@ -5317,6 +5635,7 @@
|
||||
"ui.point_cloud_add_filter": "フィルターを追加…",
|
||||
"ui.point_cloud_auto_range": "範囲を自動設定",
|
||||
"ui.point_cloud_color_by": "色の付け方",
|
||||
"ui.point_cloud_dots": "単独の点",
|
||||
"ui.point_cloud_file_colors": "ファイルの色",
|
||||
"ui.point_cloud_filter_by": "フィルター",
|
||||
"ui.point_cloud_height_high": "最高の高さ",
|
||||
|
||||
+874
-46
File diff suppressed because it is too large
Load Diff
@@ -1621,7 +1621,7 @@
|
||||
"help.sketch.insert_svg": "Membaca laluan lukisan SVG ke dalam lakaran, diskalakan, diputar dan diletakkan mengikut tetapan anda. Gunakan untuk menukar logo atau garis luar vektor kepada lengkung yang boleh disemperit.",
|
||||
"help.sketch.inspect": "Membuka laporan tentang lakaran yang terbuka: berapa banyak darjah kebebasan yang tinggal dan sama ada ia terkekang sepenuhnya. Gunakan untuk mengetahui apa yang masih bergerak sebelum anda menamatkan lakaran.",
|
||||
"help.sketch.intersect": "Menambah lengkung atau titik tempat jasad, muka, tepi atau lakaran lain melintasi satah lakaran. Ia kekal terpaut dan mengikut model apabila model berubah.",
|
||||
"help.sketch.intersection_curve": "Mencipta lengkung 3D tempat dua jasad atau muka bertemu, terpaut kepadanya. Gunakan sebagai laluan atau rel pemandu yang mengikut model.",
|
||||
"help.sketch.intersection_curve": "Mencipta lengkung 3D tempat dua jasad, muka atau lengkung lakaran lain bertemu, terpaut kepadanya. Gunakan sebagai laluan atau rel pemandu yang mengikut model.",
|
||||
"help.sketch.isoparametric_curve": "Mencipta lengkung 3D di sepanjang arah U atau V muka melalui titik yang anda pilih, terpaut kepada muka itu. Gunakan sebagai laluan atau garis belahan yang mengikut permukaan.",
|
||||
"help.sketch.line": "Melukis garis bersambung titik demi titik, sebagai rantaian terbuka atau garis luar tertutup. Ia ialah alat asas bagi kebanyakan profil.",
|
||||
"help.sketch.line.midpoint": "Melukis garis daripada titik tengahnya ke satu hujung, dengan hujung satu lagi dicerminkan. Gunakan untuk garis yang berpusat pada satu titik, seperti tepi simetri.",
|
||||
@@ -3807,6 +3807,8 @@
|
||||
"sample.plate.name": "Plat Contoh",
|
||||
"sample.vents.about": "Corak slot segi empat tepat: baris, lajur dan pic sebagai parameter",
|
||||
"sample.vents.name": "Plat Pengudaraan",
|
||||
"scale.per_axis": "Mengikut paksi",
|
||||
"scale.uniform": "Seragam",
|
||||
"search.command_keys": "Enter jalankan Shift+Enter jalankan dan kekalkan terbuka Esc tutup",
|
||||
"search.commands": "Arahan",
|
||||
"search.design": "Dalam reka bentuk ini",
|
||||
|
||||
@@ -1621,7 +1621,7 @@
|
||||
"help.sketch.insert_svg": "Leser banene fra en SVG-tegning inn i en skisse, skalert, dreid og plassert slik du angir. Bruk det til å gjøre en logo eller vektorkontur om til kurver du kan ekstrudere.",
|
||||
"help.sketch.inspect": "Åpner en rapport om den åpne skissen: hvor mange frihetsgrader som gjenstår og om den er fullstendig bundet. Bruk det til å finne ut hva som fortsatt beveger seg før du fullfører skissen.",
|
||||
"help.sketch.intersect": "Legger til kurvene eller punktene der kropper, flater, kanter eller andre skisser krysser skisseplanet. De forblir koblet og følger modellen når den endres.",
|
||||
"help.sketch.intersection_curve": "Oppretter 3D-kurver der to kropper eller flater møtes, koblet til dem. Bruk dem som baner eller føringskurver som følger modellen.",
|
||||
"help.sketch.intersection_curve": "Oppretter 3D-kurver der to kropper, flater eller kurver fra andre skisser møtes, koblet til dem. Bruk dem som baner eller føringskurver som følger modellen.",
|
||||
"help.sketch.isoparametric_curve": "Oppretter en 3D-kurve langs U- eller V-retningen til en flate gjennom et punkt du velger, koblet til flaten. Bruk den som en bane eller delelinje som følger flaten.",
|
||||
"help.sketch.line": "Tegner sammenhengende linjer punkt for punkt, som en åpen kjede eller en lukket kontur. Det er det grunnleggende verktøyet for de fleste profiler.",
|
||||
"help.sketch.line.midpoint": "Tegner en linje fra midtpunktet og ut til den ene enden, med den andre enden speilet. Bruk det til linjer sentrert på et punkt, for eksempel en symmetrisk kant.",
|
||||
@@ -3807,6 +3807,8 @@
|
||||
"sample.plate.name": "Eksempelplate",
|
||||
"sample.vents.about": "Spor i et rektangulært mønster: rader, kolonner og stigning er parametere",
|
||||
"sample.vents.name": "Ventilasjonsplate",
|
||||
"scale.per_axis": "Per akse",
|
||||
"scale.uniform": "Jevn",
|
||||
"search.command_keys": "Enter kjør Shift+Enter kjør og behold åpen Esc lukk",
|
||||
"search.commands": "Kommandoer",
|
||||
"search.design": "I dette designet",
|
||||
|
||||
@@ -1625,7 +1625,7 @@
|
||||
"help.sketch.insert_svg": "Leest de paden van een SVG-tekening in een schets, geschaald, gedraaid en geplaatst waar je instelt. Gebruik dit om een logo of vectorcontour om te zetten in curven die je kunt extruderen.",
|
||||
"help.sketch.inspect": "Opent een rapport over de geopende schets: hoeveel vrijheidsgraden er nog over zijn en of hij volledig beperkt is. Gebruik dit om te zien wat nog beweegt voordat je de schets voltooit.",
|
||||
"help.sketch.intersect": "Voegt de curven of punten toe waar lichamen, vlakken, randen of andere schetsen het schetsvlak kruisen. Ze blijven gekoppeld en volgen het model wanneer dat verandert.",
|
||||
"help.sketch.intersection_curve": "Maakt 3D-curven waar twee lichamen of vlakken samenkomen, eraan gekoppeld. Gebruik ze als paden of geleiders die het model volgen.",
|
||||
"help.sketch.intersection_curve": "Maakt 3D-curven waar twee lichamen, vlakken of curven van andere schetsen samenkomen, eraan gekoppeld. Gebruik ze als paden of geleiders die het model volgen.",
|
||||
"help.sketch.isoparametric_curve": "Maakt een 3D-curve langs de U- of V-richting van een vlak door een punt dat je kiest, gekoppeld aan het vlak. Gebruik dit als pad of splitslijn die het oppervlak volgt.",
|
||||
"help.sketch.line": "Tekent verbonden lijnen punt voor punt, als open keten of gesloten contour. Het is het basisgereedschap voor de meeste profielen.",
|
||||
"help.sketch.line.midpoint": "Tekent een lijn vanuit zijn middelpunt naar één uiteinde, waarbij het andere uiteinde wordt gespiegeld. Gebruik dit voor lijnen die op een punt zijn gecentreerd, zoals een symmetrische rand.",
|
||||
@@ -3818,6 +3818,8 @@
|
||||
"sample.plate.name": "Voorbeeldplaat",
|
||||
"sample.vents.about": "Sleuven in een rechthoekig patroon: rijen, kolommen en spoed zijn parameters",
|
||||
"sample.vents.name": "Ventilatieplaat",
|
||||
"scale.per_axis": "Per as",
|
||||
"scale.uniform": "Uniform",
|
||||
"search.command_keys": "Enter uitvoeren Shift+Enter uitvoeren en open houden Esc sluiten",
|
||||
"search.commands": "Opdrachten",
|
||||
"search.design": "In dit ontwerp",
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"en": {
|
||||
"ai.prefs.instructions": "Custom instructions",
|
||||
"ai.prefs.instructions_note": "Sent to the agent at the start of every chat, after SolidCraft's own instructions: how you like to work, rules to keep to. A change applies from the next chat (Connect or New chat)."
|
||||
},
|
||||
"ar": {
|
||||
"ai.prefs.instructions": "تعليمات مخصصة",
|
||||
"ai.prefs.instructions_note": "تُرسَل إلى الوكيل في بداية كل محادثة، بعد تعليمات SolidCraft الخاصة: طريقتك المفضلة في العمل والقواعد التي يجب الالتزام بها. يسري أي تغيير من المحادثة التالية (اتصال أو محادثة جديدة)."
|
||||
},
|
||||
"he": {
|
||||
"ai.prefs.instructions": "הוראות מותאמות אישית",
|
||||
"ai.prefs.instructions_note": "נשלחות לסוכן בתחילת כל צ'אט, אחרי ההוראות של SolidCraft עצמה: דרך העבודה המועדפת עליך וכללים שיש להקפיד עליהם. שינוי חל מהצ'אט הבא (התחברות או צ'אט חדש)."
|
||||
},
|
||||
"ja": {
|
||||
"ai.prefs.instructions": "カスタム指示",
|
||||
"ai.prefs.instructions_note": "各チャットの開始時に、SolidCraft 自身の指示の後に続けてエージェントへ送られます。作業の進め方や守ってほしいルールを書いてください。変更は次のチャット(接続または新しいチャット)から適用されます。"
|
||||
},
|
||||
"vi": {
|
||||
"ai.prefs.instructions": "Hướng dẫn tùy chỉnh",
|
||||
"ai.prefs.instructions_note": "Được gửi cho agent khi bắt đầu mỗi cuộc trò chuyện, sau hướng dẫn riêng của SolidCraft: cách bạn muốn làm việc, các quy tắc cần tuân theo. Thay đổi có hiệu lực từ cuộc trò chuyện tiếp theo (Kết nối hoặc Cuộc trò chuyện mới)."
|
||||
},
|
||||
"zh": {
|
||||
"ai.prefs.instructions": "自定义指令",
|
||||
"ai.prefs.instructions_note": "在每次对话开始时发送给代理,位于 SolidCraft 自身的指令之后:你喜欢的工作方式、需要遵守的规则。更改从下一次对话(连接或新对话)起生效。"
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"ar": {
|
||||
"prefs.layout_page.hover_panel_menus": "تبديل قوائم اللوحات عند مرور المؤشر",
|
||||
"prefs.layout_page.hover_panel_menus_help": "انقر على اسم لوحة في الشريط لفتح قائمتها. مرّر المؤشر فوق اسم لوحة أخرى للتبديل. حرّك المؤشر خارج الشريط والقائمة المفتوحة لإغلاقها."
|
||||
},
|
||||
"de": {
|
||||
"prefs.layout_page.hover_panel_menus": "Bereichsmenüs beim Darüberfahren wechseln",
|
||||
"prefs.layout_page.hover_panel_menus_help": "Klicken Sie auf einen Bereichsnamen, um das Menü zu öffnen. Bewegen Sie den Zeiger auf einen anderen Bereichsnamen, um das Menü zu wechseln. Außerhalb des Menübands und des geöffneten Menüs schließt es sich."
|
||||
},
|
||||
"en": {
|
||||
"prefs.layout_page.hover_panel_menus": "Switch panel menus on hover",
|
||||
"prefs.layout_page.hover_panel_menus_help": "Click a ribbon panel name to open its menu. Hover another panel name to switch. Move outside the ribbon and open menu to close it."
|
||||
},
|
||||
"he": {
|
||||
"prefs.layout_page.hover_panel_menus": "מעבר בין תפריטי לוחות בריחוף",
|
||||
"prefs.layout_page.hover_panel_menus_help": "לחצו על שם לוח ברצועת הכלים כדי לפתוח את התפריט שלו. העבירו את הסמן מעל שם של לוח אחר כדי לעבור לתפריט שלו. הזיזו את הסמן אל מחוץ לרצועת הכלים ולתפריט הפתוח כדי לסגור אותו."
|
||||
},
|
||||
"ja": {
|
||||
"prefs.layout_page.hover_panel_menus": "ポイントしてパネルメニューを切り替える",
|
||||
"prefs.layout_page.hover_panel_menus_help": "リボンのパネル名をクリックしてメニューを開きます。別のパネル名にポインターを合わせると切り替わります。リボンと開いているメニューの外に移動すると閉じます。"
|
||||
},
|
||||
"ko": {
|
||||
"prefs.layout_page.hover_panel_menus": "포인터를 올려 패널 메뉴 전환",
|
||||
"prefs.layout_page.hover_panel_menus_help": "리본의 패널 이름을 클릭하여 메뉴를 엽니다. 다른 패널 이름에 포인터를 올리면 메뉴가 전환됩니다. 리본과 열린 메뉴 밖으로 포인터를 이동하면 메뉴가 닫힙니다."
|
||||
},
|
||||
"vi": {
|
||||
"prefs.layout_page.hover_panel_menus": "Chuyển menu nhóm khi di chuột",
|
||||
"prefs.layout_page.hover_panel_menus_help": "Nhấp vào tên nhóm trên dải công cụ để mở menu. Di chuột lên tên nhóm khác để chuyển menu. Di chuyển con trỏ ra ngoài dải công cụ và menu đang mở để đóng menu."
|
||||
},
|
||||
"zh": {
|
||||
"prefs.layout_page.hover_panel_menus": "悬停切换面板菜单",
|
||||
"prefs.layout_page.hover_panel_menus_help": "点击功能区的面板名称打开菜单。将指针悬停在另一面板名称上即可切换菜单。将指针移到功能区和已打开菜单之外即可关闭菜单。"
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"en": {
|
||||
"The origin axis is perpendicular to the sketch plane, so it has no line in the plane": "The origin axis is perpendicular to the sketch plane, so it has no line in the plane"
|
||||
},
|
||||
"ar": {
|
||||
"The origin axis is perpendicular to the sketch plane, so it has no line in the plane": "محور الأصل عمودي على مستوى الرسم، لذا ليس له خط في المستوى"
|
||||
},
|
||||
"de": {
|
||||
"The origin axis is perpendicular to the sketch plane, so it has no line in the plane": "Die Ursprungsachse steht senkrecht auf der Skizzenebene und hat daher keine Linie in ihr"
|
||||
},
|
||||
"he": {
|
||||
"The origin axis is perpendicular to the sketch plane, so it has no line in the plane": "ציר הראשית ניצב למישור הסקיצה, ולכן אין לו קו במישור"
|
||||
},
|
||||
"ja": {
|
||||
"The origin axis is perpendicular to the sketch plane, so it has no line in the plane": "原点軸はスケッチ平面に垂直なため、平面内に線がありません"
|
||||
},
|
||||
"ru": {
|
||||
"The origin axis is perpendicular to the sketch plane, so it has no line in the plane": "Ось начала координат перпендикулярна плоскости эскиза, поэтому линии в этой плоскости нет"
|
||||
},
|
||||
"vi": {
|
||||
"The origin axis is perpendicular to the sketch plane, so it has no line in the plane": "Trục gốc vuông góc với mặt phẳng phác thảo nên không có đường thẳng trong mặt phẳng này"
|
||||
},
|
||||
"zh": {
|
||||
"The origin axis is perpendicular to the sketch plane, so it has no line in the plane": "原点轴垂直于草图平面,因此在该平面内没有直线"
|
||||
}
|
||||
}
|
||||
@@ -1408,6 +1408,8 @@
|
||||
"sample.plate.name": "Placa de exemplo",
|
||||
"sample.vents.about": "Rasgos em padrão retangular: linhas, colunas e passo são parâmetros",
|
||||
"sample.vents.name": "Placa de ventilação",
|
||||
"scale.per_axis": "Por eixo",
|
||||
"scale.uniform": "Uniforme",
|
||||
"search.command_keys": "Enter executar Shift+Enter executar e manter aberto Esc fechar",
|
||||
"search.commands": "Comandos",
|
||||
"search.design": "Neste projeto",
|
||||
|
||||
@@ -1621,7 +1621,7 @@
|
||||
"help.sketch.insert_svg": "Citește traseele unui desen SVG într-o schiță, scalate, rotite și plasate așa cum stabiliți. Folosiți-o pentru a transforma un logo sau un contur vectorial în curbe pe care le puteți extruda.",
|
||||
"help.sketch.inspect": "Deschide un raport despre schița deschisă: câte grade de libertate au rămas și dacă este complet constrânsă. Folosiți-l pentru a afla ce se mai mișcă înainte de a finaliza schița.",
|
||||
"help.sketch.intersect": "Adaugă curbele sau punctele în care corpuri, fețe, muchii sau alte schițe taie planul schiței. Rămân legate și urmează modelul când acesta se schimbă.",
|
||||
"help.sketch.intersection_curve": "Creează curbe 3D acolo unde se întâlnesc două corpuri sau fețe, legate de ele. Folosiți-le ca trasee sau ghidaje care urmează modelul.",
|
||||
"help.sketch.intersection_curve": "Creează curbe 3D acolo unde se întâlnesc două corpuri, fețe sau curbe ale altor schițe, legate de ele. Folosiți-le ca trasee sau ghidaje care urmează modelul.",
|
||||
"help.sketch.isoparametric_curve": "Creează o curbă 3D pe direcția U sau V a unei fețe, printr-un punct ales de dumneavoastră, legată de față. Folosiți-o ca traseu sau linie de separare care urmează suprafața.",
|
||||
"help.sketch.line": "Desenează linii conectate punct cu punct, ca lanț deschis sau contur închis. Este instrumentul de bază pentru majoritatea profilurilor.",
|
||||
"help.sketch.line.midpoint": "Desenează o linie de la mijlocul ei spre un capăt, celălalt capăt fiind în oglindă. Folosiți-o pentru linii centrate pe un punct, de exemplu o muchie simetrică.",
|
||||
@@ -3807,6 +3807,8 @@
|
||||
"sample.plate.name": "Placă exemplu",
|
||||
"sample.vents.about": "Canale într-o matrice dreptunghiulară: rândurile, coloanele și pasul sunt parametri",
|
||||
"sample.vents.name": "Placă de ventilație",
|
||||
"scale.per_axis": "Pe axe",
|
||||
"scale.uniform": "Uniform",
|
||||
"search.command_keys": "Enter execută Shift+Enter execută și păstrează deschis Esc închide",
|
||||
"search.commands": "Comenzi",
|
||||
"search.design": "În acest proiect",
|
||||
|
||||
@@ -647,6 +647,7 @@
|
||||
"The direction must be an axis or a straight sketch curve that lies in the sketch plane": "Направление должно быть осью или прямолинейной кривой эскиза, лежащей в плоскости эскиза",
|
||||
"The mirror line cannot also be mirrored": "Линию зеркала нельзя одновременно отражать",
|
||||
"The mirror line must be a straight curve of this sketch, not an origin axis": "Линия зеркала должна быть прямолинейной кривой этого эскиза, а не осью начала координат",
|
||||
"The origin axis is perpendicular to the sketch plane, so it has no line in the plane": "Ось начала координат перпендикулярна плоскости эскиза, поэтому линии в этой плоскости нет",
|
||||
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "Сохранённое исходное вхождение отсутствует. Выберите корректное исходное положение перед перепривязкой.",
|
||||
"Then": "Затем",
|
||||
"These features will be deleted:": "Эти операции будут удалены:",
|
||||
@@ -1610,7 +1611,7 @@
|
||||
"help.sketch.insert_svg": "Читает контуры рисунка SVG в эскиз, масштабируя, поворачивая и размещая их так, как вы зададите. Используйте, чтобы превратить логотип или векторный контур в кривые, которые можно выдавить.",
|
||||
"help.sketch.inspect": "Открывает отчёт об открытом эскизе: сколько осталось степеней свободы и определён ли он полностью. Используйте, чтобы выяснить, что ещё двигается, прежде чем завершить эскиз.",
|
||||
"help.sketch.intersect": "Добавляет кривые или точки, где тела, грани, рёбра или другие эскизы пересекают плоскость эскиза. Они остаются связанными и следуют за моделью при её изменении.",
|
||||
"help.sketch.intersection_curve": "Создаёт 3D-кривые там, где встречаются два тела или две грани, связанные с ними. Используйте как траектории или направляющие, следующие за моделью.",
|
||||
"help.sketch.intersection_curve": "Создаёт 3D-кривые там, где встречаются два тела, две грани или кривые других эскизов, связанные с ними. Используйте как траектории или направляющие, следующие за моделью.",
|
||||
"help.sketch.isoparametric_curve": "Создаёт 3D-кривую вдоль направления U или V грани через выбранную точку, связанную с гранью. Используйте как траекторию или линию разделения, следующую за поверхностью.",
|
||||
"help.sketch.line": "Рисует соединённые линии точка за точкой, как открытую цепочку или замкнутый контур. Это основной инструмент для большинства профилей.",
|
||||
"help.sketch.line.midpoint": "Рисует линию от её середины до одного конца, второй конец отражается зеркально. Используйте для линий с центром в точке, например симметричного ребра.",
|
||||
@@ -3750,6 +3751,8 @@
|
||||
"sample.plate.name": "Образец пластины",
|
||||
"sample.vents.about": "Пазы в прямоугольном массиве: число рядов, столбцов и шаг заданы параметрами",
|
||||
"sample.vents.name": "Вентиляционная пластина",
|
||||
"scale.per_axis": "По осям",
|
||||
"scale.uniform": "Равномерно",
|
||||
"search.command_keys": "Enter выполнить Shift+Enter выполнить и оставить открытым Esc закрыть",
|
||||
"search.commands": "Команды",
|
||||
"search.design": "В этом дизайне",
|
||||
|
||||
@@ -1621,7 +1621,7 @@
|
||||
"help.sketch.insert_svg": "Учитава путање SVG цртежа у скицу, скалиране, окренуте и постављене како зададете. Користите га да логотип или векторски обрис претворите у криве које можете екструдирати.",
|
||||
"help.sketch.inspect": "Отвара извештај о отвореној скици: колико је степени слободе остало и да ли је потпуно ограничена. Користите га да сазнате шта се још помера пре него што завршите скицу.",
|
||||
"help.sketch.intersect": "Додаје криве или тачке у којима тела, лица, ивице или друге скице секу раван скице. Оне остају повезане и прате модел када се он промени.",
|
||||
"help.sketch.intersection_curve": "Прави 3D криве тамо где се два тела или лица састају, повезане са њима. Користите их као путање или водилице које прате модел.",
|
||||
"help.sketch.intersection_curve": "Прави 3D криве тамо где се два тела, лица или криве других скица састају, повезане са њима. Користите их као путање или водилице које прате модел.",
|
||||
"help.sketch.isoparametric_curve": "Прави 3D криву дуж U или V смера лица кроз тачку коју изаберете, повезану са лицем. Користите је као путању или линију поделе која прати површ.",
|
||||
"help.sketch.line": "Црта повезане линије тачку по тачку, као отворени ланац или затворени обрис. То је основни алат за већину профила.",
|
||||
"help.sketch.line.midpoint": "Црта линију од њене средишње тачке до једног краја, а други крај се пресликава. Користите је за линије центриране на тачки, на пример за симетричну ивицу.",
|
||||
|
||||
@@ -1622,7 +1622,7 @@
|
||||
"help.sketch.insert_svg": "Läser in banorna från en SVG-ritning i en skiss, skalade, vridna och placerade där du anger. Använd det för att göra en logotyp eller en vektorkontur till kurvor som du kan extrudera.",
|
||||
"help.sketch.inspect": "Öppnar en rapport om den öppna skissen: hur många frihetsgrader som återstår och om den är fullt definierad. Använd det för att ta reda på vad som fortfarande rör sig innan du avslutar skissen.",
|
||||
"help.sketch.intersect": "Lägger till de kurvor eller punkter där kroppar, ytor, kanter eller andra skisser korsar skissplanet. De förblir länkade och följer modellen när den ändras.",
|
||||
"help.sketch.intersection_curve": "Skapar 3D-kurvor där två kroppar eller ytor möts, länkade till dem. Använd dem som banor eller styrkurvor som följer modellen.",
|
||||
"help.sketch.intersection_curve": "Skapar 3D-kurvor där två kroppar, ytor eller kurvor från andra skisser möts, länkade till dem. Använd dem som banor eller styrkurvor som följer modellen.",
|
||||
"help.sketch.isoparametric_curve": "Skapar en 3D-kurva längs en ytas U- eller V-riktning genom en punkt som du väljer, länkad till ytan. Använd den som en bana eller delningslinje som följer ytan.",
|
||||
"help.sketch.line": "Ritar sammanhängande linjer punkt för punkt, som en öppen kedja eller en sluten kontur. Det är det grundläggande verktyget för de flesta profiler.",
|
||||
"help.sketch.line.midpoint": "Ritar en linje från dess mittpunkt ut till ena änden, med den andra änden speglad. Använd det för linjer centrerade på en punkt, till exempel en symmetrisk kant.",
|
||||
@@ -3808,6 +3808,8 @@
|
||||
"sample.plate.name": "Exempelplatta",
|
||||
"sample.vents.about": "Spår i ett rektangulärt mönster: rader, kolumner och delning är parametrar",
|
||||
"sample.vents.name": "Ventilationsplatta",
|
||||
"scale.per_axis": "Per axel",
|
||||
"scale.uniform": "Likformig",
|
||||
"search.command_keys": "Enter kör Shift+Enter kör och behåll öppen Esc stäng",
|
||||
"search.commands": "Kommandon",
|
||||
"search.design": "I denna konstruktion",
|
||||
|
||||
@@ -1621,7 +1621,7 @@
|
||||
"help.sketch.insert_svg": "Bir SVG çiziminin yollarını, belirlediğiniz ölçekte, açıda ve konumda bir eskize okur. Bir logoyu veya vektör dış hattını ekstrüzyon yapabileceğiniz eğrilere dönüştürmek için kullanın.",
|
||||
"help.sketch.inspect": "Açık eskiz için bir rapor açar: kaç serbestlik derecesi kaldığını ve tam kısıtlı olup olmadığını. Eskizi bitirmeden önce nelerin hâlâ hareket ettiğini öğrenmek için kullanın.",
|
||||
"help.sketch.intersect": "Gövdelerin, yüzlerin, kenarların veya diğer eskizlerin eskiz düzlemini kestiği eğrileri ya da noktaları ekler. Bağlı kalır ve model değiştiğinde onu izler.",
|
||||
"help.sketch.intersection_curve": "İki gövdenin veya yüzün buluştuğu yerde, onlara bağlı 3B eğriler oluşturur. Bunları modeli izleyen yollar veya kılavuz raylar olarak kullanın.",
|
||||
"help.sketch.intersection_curve": "İki gövdenin, yüzün veya diğer eskizlerin eğrilerinin buluştuğu yerde, onlara bağlı 3B eğriler oluşturur. Bunları modeli izleyen yollar veya kılavuz raylar olarak kullanın.",
|
||||
"help.sketch.isoparametric_curve": "Bir yüzün U veya V yönü boyunca, seçtiğiniz bir noktadan geçen ve yüze bağlı bir 3B eğri oluşturur. Yüzeyi izleyen bir yol veya bölme çizgisi olarak kullanın.",
|
||||
"help.sketch.line": "Bağlantılı çizgileri nokta nokta, açık bir zincir veya kapalı bir dış hat olarak çizer. Çoğu profil için temel araçtır.",
|
||||
"help.sketch.line.midpoint": "Bir çizgiyi orta noktasından bir uca doğru çizer; diğer uç yansıtılır. Bir nokta etrafında ortalanmış çizgiler, örneğin simetrik bir kenar için kullanın.",
|
||||
@@ -3807,6 +3807,8 @@
|
||||
"sample.plate.name": "Örnek plaka",
|
||||
"sample.vents.about": "Dikdörtgen desende yuvalar: satırlar, sütunlar ve aralık parametredir",
|
||||
"sample.vents.name": "Havalandırma plakası",
|
||||
"scale.per_axis": "Eksen başına",
|
||||
"scale.uniform": "Tekdüze",
|
||||
"search.command_keys": "Enter çalıştır Shift+Enter çalıştır ve açık tut Esc kapat",
|
||||
"search.commands": "Komutlar",
|
||||
"search.design": "Bu tasarımda",
|
||||
|
||||
@@ -1681,7 +1681,7 @@
|
||||
"help.sketch.insert_svg": "Зчитує контури малюнка SVG в ескіз, масштабовані, повернуті й розміщені так, як ви задасте. Використовуйте, щоб перетворити логотип або векторний контур на криві, які можна видавити.",
|
||||
"help.sketch.inspect": "Відкриває звіт про відкритий ескіз: скільки ступенів вільності залишилося і чи він повністю обмежений. Використовуйте його, щоб дізнатися, що ще рухається, перш ніж завершити ескіз.",
|
||||
"help.sketch.intersect": "Додає криві чи точки, де тіла, грані, ребра або інші ескізи перетинають площину ескізу. Вони залишаються пов'язаними й слідують за моделлю, коли вона змінюється.",
|
||||
"help.sketch.intersection_curve": "Створює 3D-криві там, де сходяться два тіла чи грані, пов'язані з ними. Використовуйте їх як траєкторії або напрямні, що слідують за моделлю.",
|
||||
"help.sketch.intersection_curve": "Створює 3D-криві там, де сходяться два тіла, грані чи криві інших ескізів, пов'язані з ними. Використовуйте їх як траєкторії або напрямні, що слідують за моделлю.",
|
||||
"help.sketch.isoparametric_curve": "Створює 3D-криву, пов'язану з гранню, вздовж напрямку U або V грані через вибрану вами точку. Використовуйте як траєкторію або лінію розділення, що слідує за поверхнею.",
|
||||
"help.sketch.line": "Малює з'єднані прямі точка за точкою — як розімкнений ланцюг або замкнений контур. Це основний інструмент для більшості профілів.",
|
||||
"help.sketch.line.midpoint": "Малює пряму від її середини до одного кінця, а другий кінець відображається дзеркально. Використовуйте для прямих, центрованих на точці, наприклад симетричного краю.",
|
||||
@@ -4074,6 +4074,8 @@
|
||||
"sample.plate.name": "Зразкова пластина",
|
||||
"sample.vents.about": "Пази в прямокутному масиві: рядки, стовпці та крок є параметрами",
|
||||
"sample.vents.name": "Вентиляційна пластина",
|
||||
"scale.per_axis": "По осях",
|
||||
"scale.uniform": "Рівномірно",
|
||||
"search.command_keys": "Enter виконати Shift+Enter виконати й залишити відкритим Esc закрити",
|
||||
"search.commands": "Команди",
|
||||
"search.design": "У цьому проєкті",
|
||||
|
||||
+320
-1
@@ -1,4 +1,9 @@
|
||||
{
|
||||
" Keys": " Khung hình khóa",
|
||||
" Maximum": " Tối đa",
|
||||
" Minimum": " Tối thiểu",
|
||||
" Of": " Giá trị của",
|
||||
" Value": " Giá trị",
|
||||
"(active rule)": "(quy tắc đang dùng)",
|
||||
"0 turns it off": "0: tắt tính năng này",
|
||||
"1. Open GitHub draft below, then paste the report copied to your clipboard.": "1. Mở bản nháp GitHub bên dưới, sau đó dán báo cáo đã sao chép vào bộ nhớ tạm.",
|
||||
@@ -10,7 +15,18 @@
|
||||
"A new configuration, a copy of the active one": "Cấu hình mới, sao chép từ cấu hình hiện tại",
|
||||
"A placed Single hole has a handle to drag it, and picking its face again moves the hole there instead of taking it out": "Lỗ đơn đã đặt có tay nắm để kéo rê; chọn lại mặt chứa sẽ di chuyển lỗ đến vị trí đó thay vì xóa đi",
|
||||
"A short title for the problem (optional)": "Tiêu đề ngắn gọn cho sự cố (tùy chọn)",
|
||||
"ABS - Black": "ABS - Đen",
|
||||
"ABS - Grey": "ABS - Xám",
|
||||
"ABS - White": "ABS - Trắng",
|
||||
"ALIGN": "CĂN CHỈNH THÂN KHỐI",
|
||||
"ARRANGE": "SẮP XẾP",
|
||||
"AS-BUILT JOINT": "KHỚP NỐI THEO VỊ TRÍ HIỆN TRẠNG",
|
||||
"ASSIGN PLASTIC RULE": "GÁN QUY TẮC NHỰA KỸ THUẬT",
|
||||
"AXIS PERPENDICULAR TO FACE": "TRỤC VUÔNG GÓC VỚI BỀ MẶT",
|
||||
"AXIS THROUGH CYLINDER": "TRỤC QUA HÌNH TRỤ",
|
||||
"AXIS THROUGH EDGE": "TRỤC ĐI QUA CẠNH",
|
||||
"AXIS THROUGH TWO PLANES": "TRỤC GIAO GIỮA HAI MẶT PHẲNG",
|
||||
"AXIS THROUGH TWO POINTS": "TRỤC ĐI QUA HAI ĐIỂM",
|
||||
"About SolidCraft": "Giới thiệu SolidCraft",
|
||||
"Acceleration": "Gia tốc",
|
||||
"Access": "Truy cập",
|
||||
@@ -27,6 +43,7 @@
|
||||
"Add bend": "Thêm góc uốn",
|
||||
"Add condition": "Thêm điều kiện",
|
||||
"Add or edit rule": "Thêm hoặc sửa quy tắc",
|
||||
"Add shortcut": "Thêm phím tắt",
|
||||
"Add to New Folder": "Thêm vào thư mục mới",
|
||||
"Add visible bodies": "Thêm các thân khối đang hiện",
|
||||
"Advanced": "Nâng cao",
|
||||
@@ -37,13 +54,17 @@
|
||||
"All components": "Tất cả cụm chi tiết",
|
||||
"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",
|
||||
"Allowed values": "Giá trị cho phép",
|
||||
"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)",
|
||||
"Aluminum - Satin": "Nhôm - Satin",
|
||||
"Anchor": "Điểm neo",
|
||||
"Angle 2": "Góc 2",
|
||||
"Angle Display": "Hiển thị góc",
|
||||
"Animate": "Mô phỏng chuyển động",
|
||||
"Anodized Black": "Anode đen",
|
||||
"Another bend on the far edge of the last flange": "Thêm một góc uốn ở cạnh xa của gờ viền cuối cùng",
|
||||
"Any": "Bất kỳ",
|
||||
"App/system information and command settings are included. Review private details before sharing.": "Báo cáo bao gồm thông tin ứng dụng/hệ thống và các cài đặt lệnh. Vui lòng kiểm tra lại thông tin cá nhân trước khi chia sẻ.",
|
||||
"Appearance name": "Tên vật liệu bề mặt",
|
||||
"Applied Radius": "Bán kính áp dụng",
|
||||
@@ -58,6 +79,8 @@
|
||||
"Assign Rule": "Gán quy tắc",
|
||||
"Attachment": "Mối liên kết",
|
||||
"BLEND SURFACE": "MẶT NỐI MƯỢT",
|
||||
"BOSS": "VẤU BẮT ỐC VỎ NHỰA",
|
||||
"BOUNDARY FILL": "ĐIỀN ĐẦY BIÊN DẠNG KHỐI",
|
||||
"BOUNDARY SURFACE": "BỀ MẶT BIÊN DẠNG",
|
||||
"BROWSER": "CÂY ĐỐI TƯỢNG",
|
||||
"Back to the original order": "Trở về thứ tự ban đầu",
|
||||
@@ -85,13 +108,21 @@
|
||||
"Bottom Right": "Dưới cùng bên phải",
|
||||
"Boundary": "Biên dạng bao",
|
||||
"Bounding box": "Hộp bao ngoài",
|
||||
"Brass": "Đồng thau",
|
||||
"Bridge": "Cầu nối",
|
||||
"Built-in": "Tích hợp sẵn",
|
||||
"By definition": "Theo định nghĩa",
|
||||
"By part number": "Theo số hiệu chi tiết",
|
||||
"CAM": "KHỚP CAM",
|
||||
"CHECK": "KIỂM TRA",
|
||||
"COMPONENTS": "CHI TIẾT",
|
||||
"CONFIGURATIONS": "CẤU HÌNH",
|
||||
"CONSTRAIN": "RÀNG BUỘC",
|
||||
"CONTACT": "TIẾP XÚC",
|
||||
"CONTACT SET": "BỘ TIẾP XÚC",
|
||||
"CONVERT": "CHUYỂN ĐỔI",
|
||||
"CONVERT MESH": "CHUYỂN ĐỔI LƯỚI",
|
||||
"CONVERT TO SHEET METAL": "CHUYỂN THÀNH KIM LOẠI TẤM",
|
||||
"CSV or JSON: updates existing parameters, adds new ones": "CSV hoặc JSON: cập nhật tham số hiện có, thêm tham số mới",
|
||||
"Capture All Positions": "Chụp lại tất cả vị trí",
|
||||
"Capture Pos.": "Ghi vị trí",
|
||||
@@ -111,11 +142,15 @@
|
||||
"Choose Variable radius or Spline in Type.": "Vui lòng chọn 'Bán kính thay đổi' hoặc 'Đường cong Spline' trong mục Kiểu.",
|
||||
"Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.": "Chọn một mặt phẳng mới. Hình học và kích thước phác thảo được giữ nguyên trong hệ tọa độ 2D.",
|
||||
"Choose…": "Chọn…",
|
||||
"Chord length": "Chiều dài dây cung",
|
||||
"Chrome": "Crôm",
|
||||
"Circle center": "Tâm đường tròn",
|
||||
"Circle or sphere": "Đường tròn hoặc khối cầu",
|
||||
"Circular": "Tròn",
|
||||
"Circumference": "Chu vi",
|
||||
"Clockwise": "Theo chiều kim đồng hồ",
|
||||
"Close All Tabs": "Đóng tất cả các tab",
|
||||
"Close Other Tabs": "Đóng các tab khác",
|
||||
"Close draft": "Đóng bản nháp",
|
||||
"Collapse": "Thu gọn",
|
||||
"Collisions": "Va chạm",
|
||||
@@ -129,6 +164,7 @@
|
||||
"Config Table": "Bảng cấu hình",
|
||||
"Configuration": "Cấu hình",
|
||||
"Configuration rules": "Quy tắc cấu hình",
|
||||
"Connect AI assistants…": "Kết nối trợ lý AI…",
|
||||
"Constant radius": "Bán kính không đổi",
|
||||
"Contact": "Tiếp xúc",
|
||||
"Contact Sets": "Tập hợp tiếp xúc",
|
||||
@@ -137,6 +173,7 @@
|
||||
"Continuity": "Độ liên tục",
|
||||
"Control Radius": "Bán kính kiểm soát",
|
||||
"Copies the definition only. Assign it from Physical Material when ready.": "Chỉ sao chép định nghĩa. Hãy gán từ Vật liệu vật lý khi sẵn sàng.",
|
||||
"Copper": "Đồng",
|
||||
"Copy Error Message": "Sao chép thông báo lỗi",
|
||||
"Copy Warning Message": "Sao chép thông báo cảnh báo",
|
||||
"Copy all licences": "Sao chép tất cả giấy phép",
|
||||
@@ -150,6 +187,7 @@
|
||||
"Counterbore Ø": "Lỗ khoét bậc Ø",
|
||||
"Countersink angle": "Góc vát miệng lỗ (Countersink)",
|
||||
"Countersink Ø": "Lỗ khoét côn Ø",
|
||||
"Crash Reports…": "Báo cáo sự cố…",
|
||||
"Create Hole": "Tạo lỗ",
|
||||
"Create Issue — review draft": "Tạo Issue — xem lại bản nháp",
|
||||
"Create Issue…": "Tạo Issue…",
|
||||
@@ -162,12 +200,17 @@
|
||||
"Curv. Comb": "Lược độ cong",
|
||||
"Curvature (G2)": "Độ cong (G2)",
|
||||
"Curvature change (G3)": "Thay đổi độ cong (G3)",
|
||||
"Curvature continuous (G2)": "Liên tục độ cong (G2)",
|
||||
"Custom": "Tùy chỉnh",
|
||||
"Customize Toolbar": "Tùy biến thanh công cụ",
|
||||
"Customize…": "Tùy chỉnh…",
|
||||
"Cylindrical": "Trụ",
|
||||
"Cylindrical face": "Mặt trụ",
|
||||
"DELETE FACE": "XÓA MẶT",
|
||||
"DELETE FEATURE": "XÓA ĐẶC TRƯNG",
|
||||
"DRIVE JOINTS": "DẪN ĐỘNG CÁC KHỚP NỐI",
|
||||
"Deboss": "Dập chìm (Deboss)",
|
||||
"Default material": "Vật liệu mặc định",
|
||||
"Delete": "Xóa",
|
||||
"Delete Folders and Features…": "Xóa thư mục cùng các tính năng…",
|
||||
"Delete Selected Features…": "Xóa các tính năng đã chọn…",
|
||||
@@ -180,20 +223,26 @@
|
||||
"Dependents": "Phần phụ thuộc",
|
||||
"Describe the problem you saw": "Mô tả sự cố bạn đã gặp",
|
||||
"Describe what happened before continuing.": "Mô tả điều đã xảy ra trước khi tiếp tục.",
|
||||
"Design Properties…": "Thuộc tính thiết kế…",
|
||||
"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",
|
||||
"Direct interaction": "Tương tác trực tiếp",
|
||||
"Direction": "Hướng",
|
||||
"Discard": "Hủy bỏ thay đổi",
|
||||
"Distance": "Khoảng cách",
|
||||
"Distance 1": "Khoảng cách 1",
|
||||
"Distance X": "Khoảng cách X",
|
||||
"Distance Y": "Khoảng cách Y",
|
||||
"Distance and Angle": "Khoảng cách và góc",
|
||||
"Do not export hidden edges": "Không xuất nét khuất",
|
||||
"Don't ask again": "Không hỏi lại nữa",
|
||||
"Double-click to rename": "Nhấp đúp để đổi tên",
|
||||
"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": "Nhấp đúp chuột giữa trong khung nhìn: Chuẩn (Home - góc nhìn chuẩn, bao trọn mô hình), Vừa vặn (Fit - bao trọn mô hình, giữ nguyên hướng nhìn) hoặc Không làm gì",
|
||||
"Download design": "Tải xuống thiết kế",
|
||||
"Drag to move the boss across the model. It snaps to sketch points on the face.": "Kéo để di chuyển vấu lồi trên mô hình. Vấu sẽ bắt dính vào các điểm phác thảo trên bề mặt.",
|
||||
"Drag to move the hole; click to remove it": "Kéo để di chuyển lỗ; bấm để xóa",
|
||||
"Drag to move the rest across the model. It snaps to sketch points on the face.": "Kéo để di chuyển gờ đỡ trên mô hình. Gờ sẽ bắt dính vào các điểm phác thảo trên bề mặt.",
|
||||
@@ -201,7 +250,16 @@
|
||||
"Drag to point the catch in another direction.": "Kéo để hướng lẫy cài sang hướng khác.",
|
||||
"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",
|
||||
"Drive parameter": "Điều khiển tham số",
|
||||
"Drive suppression": "Điều khiển bỏ kích hoạt",
|
||||
"Duration": "Thời lượng",
|
||||
"EDIT DIMENSION": "SỬA KÍCH THƯỚC",
|
||||
"EDIT JOINT": "CHỈNH SỬA KHỚP NỐI",
|
||||
"EDIT MOTION STUDY": "SỬA NGHIÊN CỨU CHUYỂN ĐỘNG",
|
||||
"EDIT TEXT": "CHỈNH SỬA VĂN BẢN",
|
||||
"EMBOSS": "DẬP NỔI (EMBOSS)",
|
||||
"EXPLODED VIEW": "TẠO CHẾ ĐỘ XEM PHÂN RÃ",
|
||||
"EXTEND": "KÉO DÀI",
|
||||
"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",
|
||||
@@ -215,10 +273,12 @@
|
||||
"End bulge": "Độ phình cuối",
|
||||
"End continuity": "Độ liên tục cuối",
|
||||
"Equal Distance": "Khoảng cách bằng nhau",
|
||||
"Exact": "Chính xác",
|
||||
"Expand": "Mở rộng",
|
||||
"Expected result (optional)": "Kết quả mong muốn (tùy chọn)",
|
||||
"Explode": "Phân rã",
|
||||
"Explode Text": "Phân rã văn bản",
|
||||
"Export 3MF…": "Xuất 3MF…",
|
||||
"Export Animation…": "Xuất hoạt cảnh…",
|
||||
"Export Bend Table…": "Xuất bảng uốn gập…",
|
||||
"Export CSV…": "Xuất CSV…",
|
||||
@@ -226,7 +286,12 @@
|
||||
"Export Current View as DXF…": "Xuất góc nhìn hiện tại dạng DXF…",
|
||||
"Export DXF": "Xuất DXF",
|
||||
"Export DXF…": "Xuất DXF…",
|
||||
"Export Design as JSON…": "Xuất thiết kế dạng JSON…",
|
||||
"Export JSON…": "Xuất JSON…",
|
||||
"Export OBJ…": "Xuất OBJ…",
|
||||
"Export STEP…": "Xuất STEP…",
|
||||
"Export STL…": "Xuất STL…",
|
||||
"Export URDF…": "Xuất URDF…",
|
||||
"Export design library...": "Xuất thư viện thiết kế...",
|
||||
"Export hidden edges as dashed lines": "Xuất nét khuất dưới dạng đường nét đứt",
|
||||
"Exported by SolidWorks, which usually saves Y up": "Được xuất từ SolidWorks, phần mềm thường lưu với trục Y hướng lên",
|
||||
@@ -234,13 +299,19 @@
|
||||
"Extend to meet": "Kéo dài đến giao nhau",
|
||||
"Extents": "Phạm vi kéo dài",
|
||||
"FILES": "TỆP",
|
||||
"FLANGE": "CHẤN GẬP MÉP TÔN",
|
||||
"FLAT": "PHẲNG",
|
||||
"FLAT PATTERN": "BẢN KHAI TRIỂN PHẲNG",
|
||||
"FOLD": "GẬP TẤM KIM LOẠI",
|
||||
"FRAME MEMBER": "PHẦN TỬ KHUNG KẾT CẤU",
|
||||
"Face": "Bề mặt",
|
||||
"Face Appearance": "Diện mạo bề mặt",
|
||||
"Face attachment lost": "Mất liên kết bề mặt",
|
||||
"Face center": "Tâm mặt",
|
||||
"Faces": "Các bề mặt",
|
||||
"Faces / edges / vertices": "Bề mặt / Cạnh / Đỉnh",
|
||||
"Faces on Block": "Các mặt trên khối hộp",
|
||||
"Faces on Ring": "Các mặt trên vành",
|
||||
"Faces to split": "Các mặt cần phân tách",
|
||||
"Faces/Body": "Bề mặt/Thân",
|
||||
"Factor must be a positive number": "Hệ số phải là một số dương",
|
||||
@@ -265,20 +336,30 @@
|
||||
"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",
|
||||
"Flipped": "Đã lật",
|
||||
"Follow camera (Look At sketches)": "Đi theo camera (Nhìn vuông góc phác thảo)",
|
||||
"Follow inherited appearance": "Theo diện mạo kế thừa",
|
||||
"Follow material": "Theo vật liệu",
|
||||
"Follow the camera (Look At sketches)": "Đi theo camera (Nhìn vuông góc phác thảo)",
|
||||
"Font": "Phông chữ",
|
||||
"Free Move": "Di chuyển tự do",
|
||||
"From": "Điểm bắt đầu",
|
||||
"From Bodies": "Từ thân khối",
|
||||
"From Object": "Từ đối tượng",
|
||||
"From Snap": "Từ điểm bắt",
|
||||
"From edges": "Từ các cạnh",
|
||||
"Full Length": "Toàn bộ chiều dài",
|
||||
"Full thread depth": "Toàn bộ chiều sâu ren",
|
||||
"Further Bends": "Các góc uốn tiếp theo",
|
||||
"G2 corners require a constant radius and Exact fit. G2 corner construction is not supported yet.": "Góc lượn G2 yêu cầu bán kính không đổi và kiểu khớp chính xác. Hiện chưa hỗ trợ tạo góc lượn G2.",
|
||||
"GEAR": "KHỚP BÁNH RĂNG",
|
||||
"GROOVE": "RÃNH",
|
||||
"Gap": "Khe hở",
|
||||
"Glass - Clear": "Kính - Trong suốt",
|
||||
"Glass - Smoked": "Kính - Màu khói",
|
||||
"Grid": "Lưới tọa độ",
|
||||
"Groove": "Rãnh",
|
||||
"HDR file changed since selection; inspect and select it again before saving": "Tệp HDR đã thay đổi sau khi chọn; hãy kiểm tra và chọn lại trước khi lưu",
|
||||
"HISTORY": "LỊCH SỬ",
|
||||
"Halo under sketch curves": "Vầng sáng dưới đường cong phác thảo",
|
||||
"Height": "Chiều cao",
|
||||
@@ -287,6 +368,7 @@
|
||||
"Hidden edges": "Nét khuất",
|
||||
"History layout": "Bố cục lịch sử",
|
||||
"History names": "Tên trong lịch sử",
|
||||
"Hold click": "Nhấn giữ",
|
||||
"Hole (optional)": "Lỗ (tùy chọn)",
|
||||
"Hole Depth": "Độ sâu lỗ",
|
||||
"Hole Diameter": "Đường kính lỗ",
|
||||
@@ -296,6 +378,8 @@
|
||||
"Horiz./Vert.": "Ngang/Dọc",
|
||||
"I-Beam": "Thép hình chữ I",
|
||||
"IMAGES": "HÌNH ẢNH",
|
||||
"INSERT PART": "CHÈN CHI TIẾT TIÊU CHUẨN",
|
||||
"INSERT SVG": "CHÈN TỆP SVG",
|
||||
"If you are offline or GitHub does not open, use Copy report and keep the text to submit later.": "Nếu bạn đang ngoại tuyến hoặc không mở được GitHub, hãy dùng “Sao chép báo cáo” và giữ lại nội dung để gửi sau.",
|
||||
"Import material into design": "Nhập vật liệu vào thiết kế",
|
||||
"Import…": "Nhập…",
|
||||
@@ -304,6 +388,8 @@
|
||||
"In plane": "Trong mặt phẳng",
|
||||
"Included": "Góc kẹp",
|
||||
"Inner Sharp": "Cạnh sắc bên trong",
|
||||
"Insert Mesh…": "Chèn lưới…",
|
||||
"Insert STEP…": "Chèn STEP…",
|
||||
"Inside": "Bên trong",
|
||||
"Instant": "Tức thì",
|
||||
"Interface font": "Phông chữ giao diện",
|
||||
@@ -312,6 +398,8 @@
|
||||
"Isocurve": "Đường đẳng tham số",
|
||||
"Issue title": "Tiêu đề vấn đề",
|
||||
"Items": "Các mục",
|
||||
"JOINT": "KHỚP NỐI CƠ KHÍ",
|
||||
"JOINT ORIGIN": "GỐC TỌA ĐỘ KHỚP NỐI",
|
||||
"JOINTS": "KHỚP NỐI",
|
||||
"Joint 1": "Khớp 1",
|
||||
"Joint 2": "Khớp 2",
|
||||
@@ -348,9 +436,15 @@
|
||||
"M5 or M5x0.5": "M5 hoặc M5x0.5",
|
||||
"M5, M5x0.5, 1/4-20 UNC": "M5, M5x0.5, 1/4-20 UNC",
|
||||
"MANAGE": "QUẢN LÝ",
|
||||
"MANAGE PLASTIC RULES": "QUẢN LÝ QUY TẮC NHỰA KỸ THUẬT",
|
||||
"MATERIALS": "VẬT LIỆU",
|
||||
"MIDPLANE": "MẶT PHẲNG Ở GIỮA (MIDPLANE)",
|
||||
"MODEL": "MÔ HÌNH",
|
||||
"MOTION": "CHUYỂN ĐỘNG",
|
||||
"MOTION LINK": "LIÊN KẾT TRUYỀN ĐỘNG",
|
||||
"MOTION STUDY": "NGHIÊN CỨU CHUYỂN ĐỘNG",
|
||||
"MOTION TRACE": "QUỸ ĐẠO CHUYỂN ĐỘNG",
|
||||
"MOVE/COPY TEXT": "DI CHUYỂN/SAO CHÉP VĂN BẢN",
|
||||
"Margin": "Lề biên",
|
||||
"Mass": "Khối lượng",
|
||||
"Mass Center": "Tâm khối lượng",
|
||||
@@ -361,6 +455,7 @@
|
||||
"Max": "Tối đa",
|
||||
"Max Thickness": "Độ dày tối đa",
|
||||
"Maximum": "Tối đa",
|
||||
"Mesh": "Lưới",
|
||||
"Mesh body": "Khối lưới (Mesh)",
|
||||
"Mid Radius": "Bán kính giữa",
|
||||
"Middle double-click": "Nhấp đúp chuột giữa",
|
||||
@@ -383,6 +478,7 @@
|
||||
"Move holes by clicking or dragging": "Di chuyển bằng nhấp hoặc kéo",
|
||||
"Move later": "Di chuyển xuống sau",
|
||||
"Move up": "Chuyển lên",
|
||||
"NEW COMPONENT": "TẠO MỚI CỤM CHI TIẾT",
|
||||
"Name": "Tên",
|
||||
"Name on import": "Tên khi nhập",
|
||||
"Nearest": "Gần nhất",
|
||||
@@ -400,11 +496,19 @@
|
||||
"Nominal Radius": "Bán kính danh định",
|
||||
"Nominal diameter": "Đường kính danh nghĩa",
|
||||
"None": "Không có",
|
||||
"Not": "Không",
|
||||
"Not available yet": "Chưa khả dụng",
|
||||
"Not saved yet": "Chưa được lưu",
|
||||
"Notch": "Rãnh khía chữ V",
|
||||
"Nylon - Natural": "Nylon - Màu tự nhiên",
|
||||
"OFFSET FACE": "KHOẢNG DỜI (OFFSET) BỀ MẶT",
|
||||
"OFFSET SURFACE": "TẠO MẶT SONG SONG CÁCH ĐỀU (OFFSET SURFACE)",
|
||||
"OUTPUT": "ĐẦU RA",
|
||||
"Oak": "Gỗ sồi",
|
||||
"Of": "Giá trị của",
|
||||
"Offset X": "Độ lệch X",
|
||||
"Offset Y": "Độ lệch Y",
|
||||
"Offset Z": "Độ lệch Z",
|
||||
"On your computer": "Trên máy tính của bạn",
|
||||
"Opacity": "Độ mờ đục",
|
||||
"Open GitHub": "Mở GitHub",
|
||||
@@ -427,10 +531,30 @@
|
||||
"Outer radius": "Bán kính ngoài",
|
||||
"Outside": "Bên ngoài",
|
||||
"Overall length": "Chiều dài tổng thể",
|
||||
"PATCH": "VÁ MẶT CONG (PATCH)",
|
||||
"PERPENDICULAR PLANE": "VUÔNG GÓC MẶT PHẲNG",
|
||||
"PHYSICAL MATERIAL": "GÁN VẬT LIỆU VẬT LÝ",
|
||||
"PICK CONTEXT": "CHỌN NGỮ CẢNH",
|
||||
"PLANE ALONG PATH": "MẶT PHẲNG DỌC THEO ĐƯỜNG DẪN",
|
||||
"PLANE THROUGH THREE POINTS": "MẶT PHẲNG ĐI QUA BA ĐIỂM",
|
||||
"PLANE THROUGH TWO EDGES": "MẶT PHẲNG ĐI QUA HAI CẠNH",
|
||||
"POINT ALONG PATH": "ĐIỂM DỌC THEO ĐƯỜNG DẪN",
|
||||
"POINT AT CENTER": "ĐIỂM TẠI TÂM",
|
||||
"POINT AT EDGE AND PLANE": "ĐIỂM GIAO GIỮA CẠNH VÀ MẶT PHẲNG",
|
||||
"POINT AT VERTEX": "ĐIỂM TẠI ĐỈNH",
|
||||
"POINT THROUGH THREE PLANES": "ĐIỂM GIAO GIỮA BA MẶT PHẲNG",
|
||||
"POINT THROUGH TWO EDGES": "ĐIỂM GIAO GIỮA HAI CẠNH",
|
||||
"Paint - Blue": "Sơn - Xanh dương",
|
||||
"Paint - Green": "Sơn - Xanh lá",
|
||||
"Paint - Orange": "Sơn - Cam",
|
||||
"Paint - Purple": "Sơn - Tím",
|
||||
"Paint - Red": "Sơn - Đỏ",
|
||||
"Paint - Yellow": "Sơn - Vàng",
|
||||
"Param. Graph": "Đồ thị tham số",
|
||||
"Parameter": "Tham số",
|
||||
"Part length must be greater than zero and at most 2000 mm": "Chiều dài chi tiết phải lớn hơn 0 và tối đa 2000 mm",
|
||||
"Parts": "Chi tiết",
|
||||
"Parts only": "Chỉ chi tiết",
|
||||
"Pass through": "Đi xuyên qua",
|
||||
"Path": "Đường dẫn",
|
||||
"Pattern": "Mảng",
|
||||
@@ -446,16 +570,21 @@
|
||||
"Place at the origin instead": "Đặt tại gốc tọa độ",
|
||||
"Planar": "Phẳng",
|
||||
"Plastic Rules": "Quy tắc nhựa",
|
||||
"Play": "Phát",
|
||||
"Play the joint's motion in the view": "Mô phỏng chuyển động khớp nối trong vùng nhìn",
|
||||
"Playback": "Phát lại",
|
||||
"Plot": "Vẽ đồ thị",
|
||||
"Point on line": "Điểm trên đường thẳng",
|
||||
"Point on plane": "Điểm trên mặt phẳng",
|
||||
"Point to Point": "Từ điểm đến điểm",
|
||||
"Point to Position": "Từ điểm đến vị trí",
|
||||
"Polycarbonate - Clear": "Polycarbonate - Trong suốt",
|
||||
"Position": "Vị trí",
|
||||
"Position (0 to 1)": "Vị trí (0 đến 1)",
|
||||
"Position (G0)": "Vị trí liên tục (G0)",
|
||||
"Position along the edge, 0 at the start end, 1 at the other": "Vị trí dọc theo cạnh, 0 tại điểm đầu, 1 tại điểm cuối",
|
||||
"Preferences › Design turns the offer back on": "Tùy chọn cài đặt › Thiết kế sẽ bật lại lời nhắc này",
|
||||
"Preferences…": "Tùy chọn…",
|
||||
"Preview": "Xem trước",
|
||||
"Preview Row": "Xem trước hàng",
|
||||
"Preview leaves the current design unchanged.": "Xem trước giữ nguyên thiết kế hiện tại không đổi.",
|
||||
@@ -464,12 +593,21 @@
|
||||
"Profiles/Curves": "Biên dạng / Đường cong",
|
||||
"Quantity": "Số lượng",
|
||||
"Quantity must be at least 2": "Số lượng phải từ 2 trở lên",
|
||||
"RACK AND PINION": "KHỚP THANH RĂNG - BÁNH RĂNG",
|
||||
"RECENT": "GẦN ĐÂY",
|
||||
"REFERENCE": "THAM CHIẾU",
|
||||
"REFOLD": "GẬP LẠI TẤM KIM LOẠI",
|
||||
"REMOVE": "XÓA THÂN KHỐI",
|
||||
"RENAME": "ĐỔI TÊN TÍNH NĂNG",
|
||||
"REPLACE FACE": "THAY THẾ BỀ MẶT",
|
||||
"REST": "VẤU ĐỠ NHỰA (REST)",
|
||||
"REVERSE NORMAL": "ĐẢO CHIỀU PHÁP TUYẾN MẶT",
|
||||
"RIGID GROUP": "NHÓM NGÀM CỨNG (RIGID GROUP)",
|
||||
"RULED SURFACE": "MẶT KẺ (RULED SURFACE)",
|
||||
"RULES": "QUY TẮC",
|
||||
"Radius": "Bán kính",
|
||||
"Radius Fit": "Bán kính vừa vặn",
|
||||
"Range": "Phạm vi",
|
||||
"Ratio": "Tỷ lệ",
|
||||
"Read-only list: shortcuts can't be changed here.": "Danh sách chỉ đọc: không thể thay đổi phím tắt ở đây.",
|
||||
"Reattach selected face": "Gắn lại bề mặt đã chọn",
|
||||
@@ -477,6 +615,7 @@
|
||||
"Recover unsaved design?": "Khôi phục thiết kế chưa lưu?",
|
||||
"Rectangular": "Hình chữ nhật",
|
||||
"Rectangular Tube": "Thép hộp chữ nhật",
|
||||
"Reduce to Fit": "Thu nhỏ cho vừa",
|
||||
"Reference face": "Bề mặt tham chiếu",
|
||||
"Reference plane": "Mặt phẳng tham chiếu",
|
||||
"Relief Depth": "Độ sâu rãnh thoát",
|
||||
@@ -500,6 +639,7 @@
|
||||
"Repair Part": "Sửa chi tiết",
|
||||
"Replace existing library file": "Thay thế tệp thư viện hiện có",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "Báo cáo sự cố — xem lại ảnh chụp màn hình và mở bản nháp trên GitHub",
|
||||
"Report an Issue…": "Báo cáo sự cố…",
|
||||
"Report copied. Keep this text to submit on GitHub when you are online.": "Đã sao chép báo cáo. Hãy giữ lại nội dung này để gửi lên GitHub khi có kết nối mạng.",
|
||||
"Report text copied. Paste it into GitHub and submit your report there.": "Đã sao chép nội dung báo cáo. Hãy dán vào GitHub và gửi báo cáo của bạn tại đó.",
|
||||
"Reports are public. A GitHub account is required.": "Các báo cáo mang tính công khai. Bạn cần có tài khoản GitHub.",
|
||||
@@ -532,16 +672,32 @@
|
||||
"Ring of tools on the right button": "Vòng tròn công cụ khi giữ chuột phải",
|
||||
"Roll Back Here": "Lùi lịch sử về đây",
|
||||
"Roll History Marker Here": "Cuộn mốc lịch sử tới đây",
|
||||
"Rolling": "Lăn",
|
||||
"Root radius": "Bán kính góc lượn chân ren",
|
||||
"Rotate": "Xoay",
|
||||
"Round": "Bo tròn",
|
||||
"Round Bar": "Thép tròn trơn",
|
||||
"Round Tube": "Thép ống tròn",
|
||||
"Rubber - Black": "Cao su - Đen",
|
||||
"Rule": "Quy tắc",
|
||||
"Rule name": "Tên quy tắc",
|
||||
"Rules": "Quy tắc",
|
||||
"Rules run from top to bottom. Matched drives change effective values.": "Các quy tắc chạy từ trên xuống dưới. Các thông số điều khiển khớp sẽ thay đổi giá trị hiệu lực.",
|
||||
"SCREW": "KHỚP TRỤC VÍT",
|
||||
"SECTION": "MẶT CẮT",
|
||||
"SHEET METAL RULES": "QUY TẮC KIM LOẠI TẤM",
|
||||
"SNAP FIT": "KHỚP GÀI BẤM (SNAP FIT)",
|
||||
"SPLIT FACE": "CHIA TÁCH BỀ MẶT",
|
||||
"STITCH": "KHÂU MẶT CONG (STITCH)",
|
||||
"STOCK": "PHÔI",
|
||||
"SURFACE EXTRUDE": "ĐÙN MẶT CONG (SURFACE EXTRUDE)",
|
||||
"SURFACE LOFT": "VUỐT NỐI MẶT CONG (SURFACE LOFT)",
|
||||
"SURFACE QUALITY": "CHẤT LƯỢNG BỀ MẶT",
|
||||
"SURFACE REVOLVE": "XOAY TRÒN TẠO MẶT CONG (SURFACE REVOLVE)",
|
||||
"SURFACE SWEEP": "QUÉT TẠO MẶT CONG (SURFACE SWEEP)",
|
||||
"Same way": "Cùng hướng",
|
||||
"Save As JSON…": "Lưu dạng JSON…",
|
||||
"Save As…": "Lưu thành…",
|
||||
"Save Rule": "Lưu quy tắc",
|
||||
"Save View": "Lưu góc nhìn",
|
||||
"Save screenshot…": "Lưu ảnh chụp màn hình…",
|
||||
@@ -550,6 +706,9 @@
|
||||
"Scale factor": "Hệ số tỷ lệ",
|
||||
"Scene": "Cảnh",
|
||||
"Screenshot (optional)": "Ảnh chụp màn hình (tùy chọn)",
|
||||
"Screenshot captured locally. Review it before attaching it on GitHub.": "Ảnh chụp màn hình đã được lưu cục bộ. Hãy xem lại trước khi đính kèm trên GitHub.",
|
||||
"Scroll left": "Cuộn sang trái",
|
||||
"Scroll right": "Cuộn sang phải",
|
||||
"Search": "Tìm kiếm",
|
||||
"Search commands": "Tìm kiếm lệnh",
|
||||
"Search this library": "Tìm trong thư viện này",
|
||||
@@ -581,6 +740,7 @@
|
||||
"Send your report on GitHub": "Gửi báo cáo của bạn trên GitHub",
|
||||
"Shaded": "Tô bóng",
|
||||
"Shape": "Hình dạng",
|
||||
"Sheet Metal": "Tôn tấm",
|
||||
"Sheet Rules": "Quy tắc tấm kim loại",
|
||||
"Show Agent Cursor": "Hiển thị con trỏ của AI Agent",
|
||||
"Show ghost": "Hiển thị bóng mờ",
|
||||
@@ -608,6 +768,7 @@
|
||||
"Speed": "Tốc độ",
|
||||
"Splitting tool": "Công cụ cắt chia",
|
||||
"Square": "Hình vuông",
|
||||
"Stainless Steel": "Thép không gỉ",
|
||||
"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?",
|
||||
@@ -616,6 +777,8 @@
|
||||
"Start bulge": "Độ phình đầu",
|
||||
"Start continuity": "Độ liên tục đầu",
|
||||
"Stationary Side": "Phía cố định",
|
||||
"Steel": "Thép",
|
||||
"Steel - Brushed": "Thép - Xước",
|
||||
"Step": "Bước",
|
||||
"Steps": "Các bước",
|
||||
"Steps to reproduce (optional)": "Các bước tái hiện (tùy chọn)",
|
||||
@@ -626,12 +789,19 @@
|
||||
"Style": "Kiểu hiển thị",
|
||||
"Summary": "Tóm tắt",
|
||||
"Suppress": "Bỏ kích hoạt (Suppress)",
|
||||
"Suppress Features": "Bỏ kích hoạt đặc trưng",
|
||||
"Suppress Selected Folders": "Bỏ kích hoạt các thư mục đã chọn",
|
||||
"Suppressed": "Bị tạm nén",
|
||||
"Suppression": "Bỏ kích hoạt",
|
||||
"Surface": "Bề mặt",
|
||||
"Surface or face": "Bề mặt hoặc mặt",
|
||||
"Surface or faces": "Bề mặt hoặc các mặt",
|
||||
"Surface, or faces to turn over": "Mặt cong, hoặc các mặt cần lật hướng",
|
||||
"Surfaces": "Các bề mặt",
|
||||
"Swap Ends": "Hoán đổi hai đầu",
|
||||
"TANGENT PLANE": "TIẾP TUYẾN MẶT PHẲNG",
|
||||
"THICKEN": "TẠO ĐỘ DÀY CHO MẶT CONG",
|
||||
"TRANSFORM": "BIẾN ĐỔI",
|
||||
"TRIM": "CẮT XÉN",
|
||||
"Tangent": "Tiếp tuyến",
|
||||
"Tangent (G1)": "Tiếp tuyến (G1)",
|
||||
@@ -639,9 +809,11 @@
|
||||
"Tangent chain": "Chuỗi tiếp tuyến",
|
||||
"Tangent to Edge": "Tiếp tuyến với cạnh",
|
||||
"Tap drill diameter": "Đường kính mũi khoan taro",
|
||||
"Taper to Fit": "Vát cho vừa",
|
||||
"Target": "Mục tiêu",
|
||||
"Target Body": "Khối mục tiêu",
|
||||
"Target Face (optional)": "Mặt đích (tùy chọn)",
|
||||
"Target edges": "Các cạnh đích",
|
||||
"Tear": "Xé rách mép chấn",
|
||||
"Technical details": "Chi tiết kỹ thuật",
|
||||
"The dashed sketch is a saved plane guide. Select a new face to repair the attachment.": "Phác thảo nét đứt là đường dẫn mặt phẳng đã lưu. Hãy chọn một bề mặt mới để sửa lại liên kết.",
|
||||
@@ -649,6 +821,7 @@
|
||||
"The direction must be an axis or a straight sketch curve that lies in the sketch plane": "Hướng phải là một trục hoặc một đường cong phác thảo thẳng nằm trong mặt phẳng phác thảo",
|
||||
"The mirror line cannot also be mirrored": "Đường đối xứng không thể đồng thời được lấy đối xứng",
|
||||
"The mirror line must be a straight curve of this sketch, not an origin axis": "Đường đối xứng phải là một đường cong thẳng của phác thảo này, không phải trục gốc",
|
||||
"The origin axis is perpendicular to the sketch plane, so it has no line in the plane": "Trục gốc vuông góc với mặt phẳng phác thảo nên không có đường thẳng trong mặt phẳng này",
|
||||
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "Bản thể tác giả đã lưu bị thiếu. Hãy chọn một vị trí tác giả hợp lệ trước khi gán lại.",
|
||||
"Then": "Thì",
|
||||
"These features will be deleted:": "Các tính năng sau sẽ bị xóa:",
|
||||
@@ -680,16 +853,21 @@
|
||||
"Top Left": "Trên cùng bên trái",
|
||||
"Top Middle": "Trên cùng ở giữa",
|
||||
"Top Right": "Trên cùng bên phải",
|
||||
"Top level": "Cấp cao nhất",
|
||||
"Trimming tool (plane, face or curves)": "Công cụ cắt (mặt phẳng, bề mặt hoặc đường cong)",
|
||||
"Turn off section analysis before exporting the view.": "Tắt phân tích mặt cắt trước khi xuất khung nhìn.",
|
||||
"Two Distances": "Hai khoảng cách",
|
||||
"Type": "Kiểu",
|
||||
"Type a distance or an angle to move by": "Nhập khoảng cách hoặc góc để di chuyển",
|
||||
"UNFOLD": "TRẢI PHẲNG TẠM THỜI",
|
||||
"UNSTITCH": "HỦY KHÂU MẶT CONG (UNSTITCH)",
|
||||
"UNTRIM": "HỦY CẮT TỈA (UNTRIM)",
|
||||
"Unit": "Đơn vị",
|
||||
"Unsuppress": "Kích hoạt lại (Unsuppress)",
|
||||
"Unsuppress Selected Folders": "Kích hoạt lại các thư mục đã chọn",
|
||||
"Updating the preview": "Đang cập nhật hình xem trước",
|
||||
"Use active author placement": "Sử dụng vị trí tác giả đang kích hoạt",
|
||||
"Use axis direction only": "Chỉ dùng hướng trục",
|
||||
"Use fixed plane": "Sử dụng mặt phẳng cố định",
|
||||
"Use saved fixed plane": "Sử dụng mặt phẳng cố định đã lưu",
|
||||
"Use selected face as fixed plane": "Sử dụng bề mặt đã chọn làm mặt phẳng cố định",
|
||||
@@ -699,13 +877,16 @@
|
||||
"VIEWS": "GÓC NHÌN",
|
||||
"Value": "Giá trị",
|
||||
"Variable Radius": "Bán kính thay đổi",
|
||||
"Variable radius": "Bán kính thay đổi",
|
||||
"Velocity": "Vận tốc",
|
||||
"Vertices": "Các đỉnh",
|
||||
"View": "Xem",
|
||||
"View navigator": "Khối điều hướng góc nhìn",
|
||||
"Volume": "Thể tích",
|
||||
"Wall": "Thành vách",
|
||||
"Wall Location": "Vị trí thành vách",
|
||||
"Wall Thickness": "Độ dày thành vách",
|
||||
"Walnut": "Gỗ óc chó",
|
||||
"Web thickness": "Độ dày bụng",
|
||||
"What did you do just before the problem? A short list is enough": "Bạn đã làm gì ngay trước khi sự cố xảy ra? Chỉ cần liệt kê ngắn gọn",
|
||||
"What did you expect to see?": "Bạn mong muốn nhìn thấy kết quả gì?",
|
||||
@@ -740,6 +921,9 @@
|
||||
"a picked edge's body is gone": "thân của một cạnh đã chọn không còn nữa",
|
||||
"add a key for a joint first": "hãy thêm một khóa cho khớp trước",
|
||||
"ai.connected_toast": "SolidCraft hiện đã sẵn sàng trong {clients}. Khởi động lại ứng dụng để sử dụng (Cài đặt > Trợ lý ảo).",
|
||||
"ai.http.allow": "Cho phép HTTP không bảo mật",
|
||||
"ai.http.confirm": "Cho phép với điểm cuối này",
|
||||
"ai.http.warning": "Khóa API và cuộc trò chuyện sẽ được gửi mà không mã hóa. Bất kỳ ai có quyền truy cập mạng đều có thể đọc hoặc thay đổi chúng. Chỉ sử dụng trên mạng mà bạn tin cậy.",
|
||||
"ai.prefs.allow_control": "Cho phép tác nhân AI kết nối",
|
||||
"ai.prefs.allow_control_note": "Kênh cục bộ (MCP). Không có gì rời khỏi máy tính này.\n\nMở kênh điều khiển chỉ trên máy tính này (127.0.0.1, cổng 27878 hoặc cổng trống tiếp theo). Chương trình kết nối phải xuất trình mã token bảo mật chỉ tài khoản người dùng của bạn đọc được, được tạo mới mỗi lần khởi động. Có hiệu lực vào lần khởi động tiếp theo của SolidCraft. Khi tắt: không lắng nghe trừ khi bạn bật.",
|
||||
"ai.prefs.availability": "Khả dụng",
|
||||
@@ -751,6 +935,8 @@
|
||||
"ai.prefs.follow_camera": "Theo camera",
|
||||
"ai.prefs.follow_camera_off": "Tự động xoay camera theo phác thảo đang tắt.",
|
||||
"ai.prefs.follow_camera_on": "Camera sẽ tự động xoay vuông góc với phác thảo do agent tạo.",
|
||||
"ai.prefs.instructions": "Hướng dẫn tùy chỉnh",
|
||||
"ai.prefs.instructions_note": "Được gửi cho agent khi bắt đầu mỗi cuộc trò chuyện, sau hướng dẫn riêng của SolidCraft: cách bạn muốn làm việc, các quy tắc cần tuân theo. Thay đổi có hiệu lực từ cuộc trò chuyện tiếp theo (Kết nối hoặc Cuộc trò chuyện mới).",
|
||||
"ai.prefs.intro": "Tác nhân AI trong SolidCraft.",
|
||||
"ai.prefs.make_default": "Đặt làm mặc định",
|
||||
"ai.prefs.no_agents": "Chưa có agent nào. Thêm một agent hoặc làm mới để tìm các agent đã cài đặt.",
|
||||
@@ -1035,6 +1221,7 @@
|
||||
"cmd.occurrence_move": "Di chuyển đối tượng",
|
||||
"cmd.occurrence_properties": "Thuộc tính cơ lý của bản thể (Occurrence)",
|
||||
"cmd.occurrence_reparent": "Chuyển sang Cụm chi tiết",
|
||||
"cmd.panel.action": "Chạy thao tác trên tiêu đề bảng",
|
||||
"cmd.panel.activate": "Kích hoạt thẻ bảng",
|
||||
"cmd.panel.close": "Đóng bảng",
|
||||
"cmd.panel.collapse": "Thu gọn vùng bảng",
|
||||
@@ -1081,6 +1268,9 @@
|
||||
"cmd.render.compare.divider": "Di chuyển vạch so sánh kết xuất",
|
||||
"cmd.render.result.discard": "Bỏ kết quả kết xuất",
|
||||
"cmd.render.result.select": "Chọn kết quả kết xuất",
|
||||
"cmd.render.surface.import": "Nhập bản đồ bề mặt",
|
||||
"cmd.render.texture.list": "Liệt kê ảnh bề mặt được nhúng",
|
||||
"cmd.render.texture.remove": "Xóa ảnh bề mặt được nhúng",
|
||||
"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",
|
||||
@@ -1219,6 +1409,7 @@
|
||||
"cmd.sketch_move": "Di chuyển/Sao chép thực thể phác thảo",
|
||||
"cmd.sketch_move3d": "Di chuyển điểm 3D",
|
||||
"cmd.sketch_move_point": "Kéo điểm phác thảo",
|
||||
"cmd.sketch_move_points": "Kéo các điểm phác thảo",
|
||||
"cmd.sketch_offset_break": "Hủy liên kết khoảng dời (Offset)",
|
||||
"cmd.sketch_options": "Tùy chọn bảng phác thảo",
|
||||
"cmd.sketch_paste": "Dán các thực thể phác thảo",
|
||||
@@ -1324,11 +1515,13 @@
|
||||
"cmd.view_previous_tab": "Thẻ trước",
|
||||
"cmd.view_rename": "Đổi tên góc nhìn đã lưu",
|
||||
"cmd.view_render_frame": "Đường dẫn khung kết xuất",
|
||||
"cmd.view_ribbon_tabs_only": "Chỉ các thẻ ribbon",
|
||||
"cmd.view_right": "Góc nhìn bên phải",
|
||||
"cmd.view_save": "Tạo góc nhìn đã lưu mới",
|
||||
"cmd.view_shots_strip": "Dải cảnh chụp",
|
||||
"cmd.view_spin": "Xoay quỹ đạo liên tục",
|
||||
"cmd.view_tool": "Công cụ điều hướng",
|
||||
"cmd.view_tool_zoom": "Kéo để thu phóng",
|
||||
"cmd.view_top": "Góc nhìn từ trên",
|
||||
"cmd.view_visual_style": "Kiểu hiển thị trực quan",
|
||||
"color.blue": "Xanh dương",
|
||||
@@ -1419,6 +1612,9 @@
|
||||
"document.changed_on_disk": "{name} đã thay đổi trên đĩa",
|
||||
"document.changed_on_disk_detail": "{name} đã bị thay đổi bởi chương trình hoặc máy tính khác kể từ khi bạn mở hoặc lưu lần cuối. Ghi đè sẽ thay thế các thay đổi đó bằng thay đổi của bạn; Lưu bản sao sẽ giữ lại cả hai.",
|
||||
"document.copy_name": "Bản sao {name}",
|
||||
"document.newer_file": "Được lưu bằng phiên bản SolidCraft mới hơn (định dạng {format}, phiên bản này đọc được đến {reads}). Nếu thiếu gì, hãy cập nhật.",
|
||||
"document.newer_file_change": "Được lưu bằng phiên bản SolidCraft mới hơn (định dạng {format} với `{change}`, phiên bản này không biết). Nếu thiếu gì, hãy cập nhật.",
|
||||
"document.newer_file_not_read": "Không đọc được: {what}. Khi lưu, nội dung này vẫn được giữ lại.",
|
||||
"document.open_anyway": "Vẫn mở",
|
||||
"document.open_elsewhere": "{name} hiện đang được mở",
|
||||
"document.open_elsewhere_detail": "{name} đang được mở trong SolidCraft {app} trên {who}, mở lúc {when}. Nếu cả hai cùng lưu, một người sẽ mất phần công việc. Mở ở chế độ chỉ đọc để xem mà không ghi đè lên tệp.",
|
||||
@@ -1454,8 +1650,11 @@
|
||||
"file.open.portal_fallback.xwayland": "Bộ chọn tệp đã mở không phải của riêng desktop: dưới XWayland, portal từ chối cửa sổ cha.",
|
||||
"file_load.built": "Đã dựng được: {built} trên {total} tính năng.",
|
||||
"file_load.converting": "Đang chuyển đổi khối đặc {done} trên {total}",
|
||||
"file_load.inserted": "Đã chèn {name}",
|
||||
"file_load.inserting": "Đang chèn hình học",
|
||||
"file_load.link_item": "{name} ({done} trên {total})",
|
||||
"file_load.opened": "Đã mở {name}",
|
||||
"file_load.opened_in_tab": "Đã mở {name} trong thẻ mới",
|
||||
"file_load.opening": "Đang mở thiết kế",
|
||||
"file_load.preparing_thumbnail": "Đang chuẩn bị ảnh thu nhỏ...",
|
||||
"file_load.preparing_view": "Đang chuẩn bị góc nhìn...",
|
||||
@@ -1691,7 +1890,7 @@
|
||||
"help.sketch.insert_svg": "Đọc các đường dẫn của bản vẽ SVG vào phác thảo, theo tỉ lệ, góc xoay và vị trí bạn đặt. Dùng để biến logo hoặc đường bao véc tơ thành đường cong có thể đùn.",
|
||||
"help.sketch.inspect": "Mở báo cáo về bản phác thảo đang mở: còn bao nhiêu bậc tự do và đã ràng buộc hoàn toàn chưa. Dùng nó để biết phần nào vẫn còn di chuyển được trước khi hoàn tất bản phác thảo.",
|
||||
"help.sketch.intersect": "Thêm các đường cong hoặc điểm nơi khối, mặt, cạnh hoặc phác thảo khác cắt qua mặt phẳng phác thảo. Chúng vẫn được liên kết và thay đổi theo mô hình.",
|
||||
"help.sketch.intersection_curve": "Tạo đường cong 3D tại nơi hai khối hoặc hai mặt gặp nhau, liên kết với chúng. Dùng làm đường dẫn hoặc ray dẫn hướng thay đổi theo mô hình.",
|
||||
"help.sketch.intersection_curve": "Tạo đường cong 3D tại nơi hai khối, hai mặt hoặc đường cong của các phác thảo khác gặp nhau, liên kết với chúng. Dùng làm đường dẫn hoặc ray dẫn hướng thay đổi theo mô hình.",
|
||||
"help.sketch.isoparametric_curve": "Tạo đường cong 3D theo hướng U hoặc V của một mặt qua điểm bạn chọn, liên kết với mặt đó. Dùng làm đường dẫn hoặc đường tách thay đổi theo bề mặt.",
|
||||
"help.sketch.line": "Vẽ các đường thẳng nối liền từng điểm một, thành chuỗi hở hoặc đường bao khép kín. Đây là công cụ cơ bản cho hầu hết biên dạng.",
|
||||
"help.sketch.line.midpoint": "Vẽ đường thẳng từ trung điểm ra một đầu, đầu kia đối xứng qua trung điểm. Dùng cho đường thẳng có tâm tại một điểm, như cạnh đối xứng.",
|
||||
@@ -1797,6 +1996,7 @@
|
||||
"help.view.navigate": "Xoay, dịch hoặc thu phóng khung nhìn theo từng bước, chẳng hạn xoay một phần tư vòng sang trái. Dùng để điều khiển camera chính xác thay vì kéo chuột.",
|
||||
"help.view.navigation_bar": "Hiện hoặc ẩn thanh điều hướng ở cuối khung nhìn. Ẩn đi để có thêm chỗ, hiện ra để dùng các công cụ xem bằng chuột.",
|
||||
"help.view.rename": "Đặt tên mới cho một góc nhìn đã lưu. Bản thân góc nhìn không đổi.",
|
||||
"help.view.ribbon_tabs_only": "Thu gọn ribbon chỉ còn hàng thẻ, để khung nhìn có thêm chỗ mà các bảng từng chiếm. Bấm một thẻ để hiện các bảng của nó đè lên khung nhìn; chọn lại lệnh này để luôn hiện toàn bộ ribbon.",
|
||||
"help.view.save": "Lưu vị trí camera hiện tại thành một góc nhìn có tên trong thiết kế. Dùng để quay lại đúng góc đó sau này, chẳng hạn khi chụp ảnh màn hình.",
|
||||
"help.view.spin": "Quay khung nhìn quanh mô hình liên tục cho đến cú nhấp hoặc phím tiếp theo. Dùng để trình bày mô hình từ mọi phía.",
|
||||
"help.view.tool": "Giữ cố định một công cụ camera (xoay quanh, dịch hoặc thu phóng) để kéo bằng nút chuột trái sẽ di chuyển khung nhìn cho đến khi bạn tắt nó. Dùng trên bàn di chuột hoặc khi không có nút chuột giữa.",
|
||||
@@ -2163,6 +2363,7 @@
|
||||
"navbar.value_tip": "{name} · {value}",
|
||||
"navbar.visual_style": "Kiểu hiển thị trực quan",
|
||||
"navbar.zoom": "Thu phóng",
|
||||
"navbar.zoom_hold_tip": "Thu phóng (giữ phím tắt)",
|
||||
"needs two joints": "cần hai khớp nối",
|
||||
"network.allow_once": "Cho phép một lần",
|
||||
"network.always_allow": "Luôn cho phép máy chủ này",
|
||||
@@ -2316,6 +2517,7 @@
|
||||
"prefs.carousel_previous": "Trước",
|
||||
"prefs.choose_font": "Chọn tệp phông chữ…",
|
||||
"prefs.click": "Nhấp chuột",
|
||||
"prefs.command_dialog_values": "Hộp thoại lệnh",
|
||||
"prefs.contact_shading": "Đổ bóng tiếp xúc mềm",
|
||||
"prefs.contact_shading_note": "Tạo bóng đổ mềm tại những vị trí các chi tiết tiếp giáp gần nhau.",
|
||||
"prefs.context_menu": "Menu ngữ cảnh",
|
||||
@@ -2362,6 +2564,8 @@
|
||||
"prefs.edges": "Cạnh và đổ bóng",
|
||||
"prefs.extend": "Kéo dài",
|
||||
"prefs.extra_navigation": "Ctrl + kéo chuột trái trên vùng trống để xoay; giữ phím Cách (Space) để trượt góc nhìn.",
|
||||
"prefs.extrude_arrow_face_stops": "Mũi tên đùn dừng tại các mặt",
|
||||
"prefs.extrude_arrow_face_stops_note": "Khi kéo mũi tên khoảng cách của Đùn, nó dừng tại các mặt phẳng song song với biên dạng, chẳng hạn mặt bên kia của khối đang cắt, và tô viền mặt đó. Tắt: mũi tên chỉ di chuyển theo các bước tròn.",
|
||||
"prefs.extrude_view_isometric": "Xoay sang góc nhìn trục đo khi mở Đùn khối",
|
||||
"prefs.extrude_view_return": "Trở lại góc nhìn trước khi đóng Đùn khối",
|
||||
"prefs.follows_os": "Theo hệ điều hành",
|
||||
@@ -2411,6 +2615,8 @@
|
||||
"prefs.graphics_page.surfaces_edges": "Bề mặt và cạnh",
|
||||
"prefs.graphics_page.view_bar": "Cũng có trên thanh khung nhìn",
|
||||
"prefs.graphics_presets_note": "Các cấu hình định sẵn sẽ thiết lập đồng thời các tùy chọn bên dưới.",
|
||||
"prefs.ground_shadow_follows_model": "Bóng trên mặt đất di chuyển theo mô hình",
|
||||
"prefs.ground_shadow_follows_model_help": "Đặt bóng bên dưới khối hiển thị thấp nhất. Khi tắt, bóng của mô hình được nâng lên vẫn nằm trên mặt phẳng đất đi qua gốc tọa độ.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Ẩn biểu tượng ràng buộc khi thu nhỏ",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out_note": "Biểu tượng ràng buộc biến mất khi bản phác được thu nhỏ đủ xa để không làm rối khung nhìn. Tắt: chúng giữ kích thước màn hình cố định ở mọi mức thu phóng.",
|
||||
"prefs.history": "Lịch sử",
|
||||
@@ -2452,6 +2658,8 @@
|
||||
"prefs.layout_page.card": "Thẻ",
|
||||
"prefs.layout_page.column": "Cột",
|
||||
"prefs.layout_page.hover": "Rê chuột",
|
||||
"prefs.layout_page.hover_panel_menus": "Chuyển menu nhóm khi di chuột",
|
||||
"prefs.layout_page.hover_panel_menus_help": "Nhấp vào tên nhóm trên dải công cụ để mở menu. Di chuột lên tên nhóm khác để chuyển menu. Di chuyển con trỏ ra ngoài dải công cụ và menu đang mở để đóng menu.",
|
||||
"prefs.layout_page.icons_labels": "Biểu tượng + nhãn",
|
||||
"prefs.layout_page.ok_cancel": "Đồng ý và Hủy bỏ",
|
||||
"prefs.layout_page.on_hover": "Khi rê chuột",
|
||||
@@ -2490,6 +2698,8 @@
|
||||
"prefs.mouse": "Chuột và bàn di cảm ứng",
|
||||
"prefs.mouse_heading": "Chuột",
|
||||
"prefs.mouse_mapping": "Gán chức năng chuột",
|
||||
"prefs.move_drag_highlights": "Tô sáng thứ nằm dưới con trỏ khi các mũi tên của Di chuyển kéo một thành phần",
|
||||
"prefs.move_drag_highlights_tip": "Tắt: khi các mũi tên của Di chuyển kéo một thành phần, không có gì dưới con trỏ được tô sáng, nhờ đó các cụm lắp ráp lớn vẫn mượt. Khi đang hiện mặt cắt hoặc phân tích thì vẫn tô sáng như thường, và chi tiết được kéo dọc theo khớp nối không bao giờ tô sáng.",
|
||||
"prefs.navigation": "Điều hướng góc nhìn",
|
||||
"prefs.navigation.axes_signed": "Trục",
|
||||
"prefs.navigation.cube_compact": "Khối lập phương",
|
||||
@@ -2518,6 +2728,22 @@
|
||||
"prefs.persistent_ribbon_pressed": "Tô sáng lệnh đang bật trên dải băng",
|
||||
"prefs.persistent_ribbon_pressed_help": "Tô sáng các lệnh trên dải băng khi hiệu ứng của chúng đang bật. Tắt tùy chọn này để giữ giao diện trung tính và trợ giúp lệnh như trước.",
|
||||
"prefs.perspective_fov": "Trường nhìn",
|
||||
"prefs.picker.carousel": "Băng chuyền",
|
||||
"prefs.picker.change": "Thay đổi",
|
||||
"prefs.picker.clear": "Xóa bộ lọc",
|
||||
"prefs.picker.day": "Giao diện ban ngày",
|
||||
"prefs.picker.empty": "Không có giao diện nào khớp với các bộ lọc này.",
|
||||
"prefs.picker.follow": "Theo hệ thống",
|
||||
"prefs.picker.grid": "Lưới",
|
||||
"prefs.picker.high": "Tương phản cao",
|
||||
"prefs.picker.high_note": "{source} · Tương phản cao",
|
||||
"prefs.picker.hint": "Tự động chuyển theo hệ điều hành.",
|
||||
"prefs.picker.import": "Nhập tệp giao diện…",
|
||||
"prefs.picker.imported": "Đã nhập",
|
||||
"prefs.picker.night": "Giao diện ban đêm",
|
||||
"prefs.picker.one": "Một giao diện",
|
||||
"prefs.picker.pick": "Chọn bên dưới",
|
||||
"prefs.picker.standard": "Tiêu chuẩn",
|
||||
"prefs.placement": "Vị trí đặt",
|
||||
"prefs.plugins_enabled": "Đã bật",
|
||||
"prefs.plugins_installed": "Đã cài đặt",
|
||||
@@ -2554,6 +2780,8 @@
|
||||
"prefs.quality_smoother": "mượt mà hơn",
|
||||
"prefs.quick_measure": "Đo nhanh ở góc khung nhìn",
|
||||
"prefs.quick_measure_note": "Hiển thị kích thước của đối tượng được chọn ở góc dưới bên phải khung nhìn, phía trên Lịch sử: chiều dài của cạnh (hoặc Ø và R với cạnh tròn), diện tích của mặt, vị trí của điểm, khoảng cách và góc giữa hai đối tượng, tổng của nhiều cạnh hoặc mặt. Nhấp để sao chép.",
|
||||
"prefs.remember_dialog_values": "Ghi nhớ giá trị hộp thoại lệnh",
|
||||
"prefs.remember_dialog_values.help": "Tắt theo mặc định. Khi bật, sau khi lệnh thành công, các giá trị và tùy chọn an toàn của lệnh sẽ được dùng lại vào lần tiếp theo bạn mở cùng lệnh đó, cho đến khi SolidCraft đóng.",
|
||||
"prefs.render_previous_geometry_budget": "Dùng ngân sách hình học trước đây cho bản xem trước kết xuất",
|
||||
"prefs.render_previous_geometry_budget_help": "Khôi phục giới hạn hình học cố định trước đây (512 MiB trên máy tính 64 bit, 128 MiB trên các nền tảng khác) thay cho ngân sách dựa trên thiết bị.",
|
||||
"prefs.reports_saved": "Đã lưu {n}",
|
||||
@@ -2562,6 +2790,8 @@
|
||||
"prefs.ribbon_click_ends_tool_note": "Nhấp vào nút ruy-băng đang sáng của công cụ phác thảo đang dùng sẽ kết thúc công cụ, để các cú nhấp trong khung nhìn lại chọn đối tượng. Tắt: cú nhấp khởi động lại công cụ.",
|
||||
"prefs.ribbon_reveal": "Hiện kết quả tìm kiếm trên ribbon",
|
||||
"prefs.ribbon_reveal_note": "Khi rê chuột lên một kết quả trong Tìm lệnh, ribbon cho biết lệnh nằm ở đâu: tab, nút hoặc dòng của nó trong menu thả xuống của một bảng. Lệnh bắt đầu từ tìm kiếm sẽ nhấp nháy ở đó, và lệnh đang chạy từ menu thả xuống sẽ đánh dấu bảng của nó.",
|
||||
"prefs.ribbon_tabs_only": "Chỉ hiện các thẻ của ribbon",
|
||||
"prefs.ribbon_tabs_only_note": "Ribbon thu gọn chỉ còn hàng thẻ. Bấm một thẻ sẽ hiện các bảng của nó đè lên khung nhìn cho đến khi một lệnh bắt đầu hoặc con trỏ rời khỏi chúng. Lệnh Chỉ các thẻ ribbon trong thẻ Xem, hoặc phím tắt của nó, bật tắt tùy chọn này.",
|
||||
"prefs.right_click": "Nhấp chuột phải",
|
||||
"prefs.saving": "Khởi động và lưu",
|
||||
"prefs.scale_note": "Cũng có thể dùng Ctrl + / Ctrl − trong cửa sổ. Các nút có kích thước 28 px ở mức 100%.",
|
||||
@@ -2633,6 +2863,7 @@
|
||||
"prefs.sketch_exit_view_note": "Góc nhìn sẽ chuyển đến đâu sau khi bấm Hoàn tất phác thảo, Hủy bỏ hoặc bấm phím Esc hai lần.",
|
||||
"prefs.sketch_face_activates_part": "Phác thảo trên mặt của một chi tiết sẽ kích hoạt chi tiết đó",
|
||||
"prefs.sketch_face_activates_part_note": "Bản phác thảo bắt đầu trên mặt của một khối thuộc thành phần khác sẽ kích hoạt thành phần đó trước, nên bản phác thảo thuộc về nó và nằm cùng chi tiết đó trong trình duyệt. Chi tiết vẫn được kích hoạt sau đó. Tắt: bản phác thảo thuộc thành phần đang hoạt động.",
|
||||
"prefs.sketch_mirror_auto_line": "Dùng đường dựng hình đã chọn làm đường đối xứng",
|
||||
"prefs.sketch_open_look_at": "Xoay góc nhìn trực diện phác thảo khi mở",
|
||||
"prefs.sketch_open_look_at_note": "Tắt: tạo hoặc chỉnh sửa phác thảo sẽ giữ nguyên góc nhìn. Dùng Nhìn trực diện để nhìn thẳng vào phác thảo khi cần.",
|
||||
"prefs.sketch_recent_snap": "Tự động bắt điểm vào tâm và trung điểm gần đây",
|
||||
@@ -3914,6 +4145,7 @@
|
||||
"render.stage_panels.extent": "Phạm vi",
|
||||
"render.stage_panels.fit_save": "Khớp và lưu",
|
||||
"render.stage_panels.gpu_required": "Cần dò đường đi của tia bằng GPU",
|
||||
"render.stage_panels.grain_angle": "Góc vân gỗ",
|
||||
"render.stage_panels.keep": "Giữ lại",
|
||||
"render.stage_panels.kept": "Được giữ lại",
|
||||
"render.stage_panels.kept_fields": "Camera, lấy mẫu, kích thước, liên kết quang học, hình học CAD và đặc tính kỹ thuật.",
|
||||
@@ -4032,6 +4264,19 @@
|
||||
"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ạ",
|
||||
"render.surface_import.author": "Tác giả",
|
||||
"render.surface_import.busy": "Đang nhập ảnh…",
|
||||
"render.surface_import.complete": "Chọn ảnh rồi nhập tác giả, nguồn và giấy phép.",
|
||||
"render.surface_import.embedded": "Ảnh đã nhập",
|
||||
"render.surface_import.failed": "Quá trình nhập ảnh chưa hoàn tất.",
|
||||
"render.surface_import.license": "Giấy phép / quyền sở hữu",
|
||||
"render.surface_import.raytrace_required": "Ảnh cần dò tia trên CPU hoặc GPU. Hãy chọn trong phần Đầu ra.",
|
||||
"render.surface_import.repeat_u": "Chu kỳ U",
|
||||
"render.surface_import.repeat_v": "Chu kỳ V",
|
||||
"render.surface_import.save": "Lưu ảnh trên khối",
|
||||
"render.surface_import.saved_cpu": "Lưu bản đồ để dùng khi kết xuất bằng CPU.",
|
||||
"render.surface_import.source": "Nguồn",
|
||||
"render.surface_import.title": "Nhập ảnh",
|
||||
"render.tone_aces_fitted": "ACES khớp",
|
||||
"render.tone_reinhard": "Reinhard",
|
||||
"render.transmission_scope": "Thủy tinh GPU hỗ trợ các môi trường rời rạc đã xác thực. Không cung cấp môi trường lồng nhau không giới hạn hay ước tính truyền dẫn không nhiễu.",
|
||||
@@ -4108,6 +4353,8 @@
|
||||
"sample.plate.name": "Tấm mẫu",
|
||||
"sample.vents.about": "Các rãnh then trong mảng chữ nhật: số hàng, số cột và bước rãnh là các tham số",
|
||||
"sample.vents.name": "Tấm khe thông gió",
|
||||
"scale.per_axis": "Theo từng trục",
|
||||
"scale.uniform": "Đồng đều",
|
||||
"search.command_keys": "Enter chạy Shift+Enter chạy và giữ mở Esc đóng",
|
||||
"search.commands": "Lệnh",
|
||||
"search.design": "Trong thiết kế này",
|
||||
@@ -4780,6 +5027,7 @@
|
||||
"ui.click_selects_whole_bodies": "Nhấp chuột chọn toàn bộ thân khối",
|
||||
"ui.click_then_press_key": "Nhấp chuột, sau đó nhấn phím mới",
|
||||
"ui.close": "Đóng",
|
||||
"ui.close_tab": "Đóng thẻ",
|
||||
"ui.coil": "Lò xo xoắn",
|
||||
"ui.coincident": "Trùng điểm",
|
||||
"ui.collapse": "Thu gọn",
|
||||
@@ -4997,6 +5245,11 @@
|
||||
"ui.factor": "Hệ số",
|
||||
"ui.fade": "Thoát ren",
|
||||
"ui.fade_length": "Chiều dài thoát ren",
|
||||
"ui.fair_iterations": "Số lần lặp",
|
||||
"ui.fair_keep_edges": "Giữ các cạnh",
|
||||
"ui.fair_strength": "Cường độ",
|
||||
"ui.fair_surface_title": "LÀM MƯỢT BỀ MẶT",
|
||||
"ui.fair_tolerance_default": "0,001 lần kích thước thân khối",
|
||||
"ui.feature_history": "Lịch sử tính năng",
|
||||
"ui.feature_options": "Tùy chọn tính năng",
|
||||
"ui.features": "Các tính năng",
|
||||
@@ -5182,6 +5435,7 @@
|
||||
"ui.inspect": "Kiểm tra",
|
||||
"ui.instances": "Số bản",
|
||||
"ui.intersect": "Giao nhau",
|
||||
"ui.intersection_curve_second_pick": "Đường cong giao tuyến: nhấp vào bề mặt hoặc đường cong thứ hai (nhấp chuột phải để bắt đầu lại)",
|
||||
"ui.iso_4753": "ISO 4753",
|
||||
"ui.isolate": "Cô lập đối tượng",
|
||||
"ui.join": "Nối liền",
|
||||
@@ -5210,6 +5464,7 @@
|
||||
"ui.manipulator_free_alt": "Kéo tự do bộ thao tác cũng bằng Alt/Option",
|
||||
"ui.manipulator_free_alt_tip": "Bật: Alt/Option hoặc Ctrl/Cmd trên các mũi tên đùn / gizmo sẽ tắt bước nhảy giá trị. Tắt: chỉ Ctrl/Cmd.",
|
||||
"ui.manufacture": "Gia công chế tạo",
|
||||
"ui.match_surface_title": "KHỚP BỀ MẶT",
|
||||
"ui.material_review.axes": "Các trục cục bộ của vật liệu; chưa gán hướng cho thân khối.",
|
||||
"ui.material_review.claims": "Nguồn gốc và giấy phép là thông tin chưa được kiểm chứng. Các giá trị thiếu vẫn ở dạng chưa biết.",
|
||||
"ui.material_review.compressive_strength": "Độ bền nén",
|
||||
@@ -5232,6 +5487,9 @@
|
||||
"ui.material_review.title": "Xem xét dữ liệu thuộc tính vật liệu",
|
||||
"ui.material_review.unknown": "Chưa biết",
|
||||
"ui.material_review.yield_strength": "Giới hạn chảy",
|
||||
"ui.mcp_agents": "Tác tử qua MCP",
|
||||
"ui.mcp_compact": "Đầu ra gọn",
|
||||
"ui.mcp_compact_note": "Dành cho các tác tử kết nối qua máy chủ MCP (solidcraft-cli mcp): inspect_design tóm tắt các bản phác thảo và kết quả công cụ không được định dạng đẹp, nên tốn ít token hơn. Áp dụng cho các phiên tác tử bắt đầu sau khi thay đổi; --compact hoặc SOLIDCRAFT_MCP_COMPACT=1 cũng bật nó.",
|
||||
"ui.measure": "Đo lường",
|
||||
"ui.mesh_format_3mf": "3MF",
|
||||
"ui.mesh_format_3mf_tagged": "3MF kèm thẻ bề mặt",
|
||||
@@ -5273,6 +5531,7 @@
|
||||
"ui.options": "Tùy chọn",
|
||||
"ui.orbit": "Xoay quanh tâm (Orbit)",
|
||||
"ui.orbit_around": "Xoay quanh",
|
||||
"ui.organize": "Sắp xếp",
|
||||
"ui.orientation": "Định hướng",
|
||||
"ui.origin": "Gốc tọa độ",
|
||||
"ui.palette_construction_mode": "Chế độ đường dựng (X)",
|
||||
@@ -5280,6 +5539,65 @@
|
||||
"ui.palette_sketch_options": "Tùy chọn phác thảo",
|
||||
"ui.pan": "Dời góc nhìn (Pan)",
|
||||
"ui.pan_zoom_orbit": "Dời góc nhìn, thu phóng, xoay quanh tâm",
|
||||
"ui.panel_fixture.apply": "Áp dụng",
|
||||
"ui.panel_fixture.cancel": "Hủy bỏ",
|
||||
"ui.panel_fixture.caption": "Mẫu bảng tổng hợp",
|
||||
"ui.panel_fixture.draft": "Bản nháp được giữ lại",
|
||||
"ui.panel_fixture.editor": "Trình sửa mẫu",
|
||||
"ui.panel_fixture.explanation": "Nội dung tổng hợp này giữ bản nháp khi bị ẩn hoặc di chuyển.",
|
||||
"ui.panel_fixture.fill": "Mẫu lấp đầy chiều cao",
|
||||
"ui.panel_fixture.finish": "Hoàn tất",
|
||||
"ui.panel_fixture.finished": "Giao dịch tổng hợp đã kết thúc",
|
||||
"ui.panel_fixture.footer": "Các thao tác mẫu giữ nguyên thiết kế.",
|
||||
"ui.panel_fixture.guard": "Từ chối hủy khi giao dịch đang hoạt động",
|
||||
"ui.panel_fixture.inline": "Mẫu một hàng",
|
||||
"ui.panel_fixture.live": "Giao dịch tổng hợp đang hoạt động",
|
||||
"ui.panel_fixture.log": "Nhật ký mẫu",
|
||||
"ui.panel_fixture.pin": "Mẫu đã ghim",
|
||||
"ui.panel_fixture.title": "{title} · {id}",
|
||||
"ui.panel_fixture.updates": "Số lần cập nhật dịch vụ: {count}",
|
||||
"ui.panel_host.attention": "Cần chú ý",
|
||||
"ui.panel_host.bottom": "Dưới",
|
||||
"ui.panel_host.close": "Đóng bảng",
|
||||
"ui.panel_host.collapse": "Thu gọn bảng",
|
||||
"ui.panel_host.expand": "Mở rộng bảng",
|
||||
"ui.panel_host.floating": "Nổi",
|
||||
"ui.panel_host.focus": "Đặt tiêu điểm vào bảng",
|
||||
"ui.panel_host.focus_target": "Đặt tiêu điểm vào {title}",
|
||||
"ui.panel_host.header": "Tiêu đề bảng",
|
||||
"ui.panel_host.join": "Ghép thẻ",
|
||||
"ui.panel_host.join_target": "Ghép thẻ với {title}",
|
||||
"ui.panel_host.left": "Trái",
|
||||
"ui.panel_host.menu": "Thao tác bảng",
|
||||
"ui.panel_host.move": "Chuyển đến",
|
||||
"ui.panel_host.overflow": "Thêm bảng",
|
||||
"ui.panel_host.popout": "Mở trong cửa sổ",
|
||||
"ui.panel_host.resize": "Đổi kích thước bảng",
|
||||
"ui.panel_host.right": "Phải",
|
||||
"ui.panel_host.rule.availability": "Bảng này không khả dụng.",
|
||||
"ui.panel_host.rule.bounds": "Các bảng không vừa trong giới hạn kích thước.",
|
||||
"ui.panel_host.rule.capability": "Máy chủ này không hỗ trợ thao tác đó.",
|
||||
"ui.panel_host.rule.collection_limit": "Bố cục bảng đã đạt giới hạn.",
|
||||
"ui.panel_host.rule.duplicate_id": "Định danh bảng này đã được sử dụng.",
|
||||
"ui.panel_host.rule.exactly_once": "Mỗi bảng phải xuất hiện một lần.",
|
||||
"ui.panel_host.rule.feature_disabled": "Thao tác này bị tắt cho bảng này.",
|
||||
"ui.panel_host.rule.id": "Định danh bảng không hợp lệ.",
|
||||
"ui.panel_host.rule.incompatible_tabs": "Các bảng này không thể dùng chung nhóm thẻ.",
|
||||
"ui.panel_host.rule.lifecycle": "Bảng không thể hoàn thành thao tác này.",
|
||||
"ui.panel_host.rule.missing_target": "Đích bảng này không còn khả dụng.",
|
||||
"ui.panel_host.rule.no_space": "Không đủ chỗ cho bố cục bảng này.",
|
||||
"ui.panel_host.rule.pin_conflict": "Một bảng khác đã được ghim vào cạnh này.",
|
||||
"ui.panel_host.rule.pin_order": "Bảng đã ghim phải luôn đứng đầu cạnh của nó.",
|
||||
"ui.panel_host.rule.placement": "Không thể chuyển bảng này đến vị trí đó.",
|
||||
"ui.panel_host.rule.revision": "Bố cục đã thay đổi. Hãy thử lại thao tác.",
|
||||
"ui.panel_host.rule.schema": "Yêu cầu bảng không hợp lệ.",
|
||||
"ui.panel_host.rule.version": "Phiên bản bố cục bảng này không được hỗ trợ.",
|
||||
"ui.panel_host.sash": "Đổi kích thước các bảng liền kề",
|
||||
"ui.panel_host.scroll_horizontal": "Cuộn các cột bảng",
|
||||
"ui.panel_host.scroll_vertical": "Cuộn các chồng bảng",
|
||||
"ui.panel_host.surface": "Bảng",
|
||||
"ui.panel_host.tab": "Thẻ bảng",
|
||||
"ui.panel_host.tabs": "Tất cả thẻ",
|
||||
"ui.parallel": "Song song",
|
||||
"ui.parameters": "Bảng tham số",
|
||||
"ui.part_size_needs_a_part": "Hãy chọn một chi tiết tiêu chuẩn đã chèn (vít, đai ốc, vòng đệm hoặc chốt) để đổi kích thước. Để phóng to hoặc thu nhỏ một thân khối, hãy dùng Thay đổi tỷ lệ.",
|
||||
@@ -5317,6 +5635,7 @@
|
||||
"ui.point_cloud_add_filter": "Thêm bộ lọc…",
|
||||
"ui.point_cloud_auto_range": "Phạm vi tự động",
|
||||
"ui.point_cloud_color_by": "Tô màu theo",
|
||||
"ui.point_cloud_dots": "Điểm đơn lẻ",
|
||||
"ui.point_cloud_file_colors": "Màu trong tệp",
|
||||
"ui.point_cloud_filter_by": "Lọc theo",
|
||||
"ui.point_cloud_height_high": "Cao nhất",
|
||||
|
||||
@@ -1624,7 +1624,7 @@
|
||||
"help.sketch.insert_svg": "將 SVG 圖形的路徑讀入草圖,並依您的設定縮放、旋轉和放置。用於把標誌或向量輪廓轉成可擠出的曲線。",
|
||||
"help.sketch.inspect": "開啟目前草圖的報告:還剩多少個自由度,以及是否已完全拘束。用於在完成草圖之前找出哪些部分仍會移動。",
|
||||
"help.sketch.intersect": "加入實體、面、邊或其他草圖與草圖平面相交的曲線或點。它們保持連結,並在模型變更時跟隨。",
|
||||
"help.sketch.intersection_curve": "在兩個實體或面相接處建立 3D 曲線,並與它們連結。用作跟隨模型的路徑或導軌。",
|
||||
"help.sketch.intersection_curve": "在兩個實體、面或其他草圖的曲線相接處建立 3D 曲線,並與它們連結。用作跟隨模型的路徑或導軌。",
|
||||
"help.sketch.isoparametric_curve": "通過您選取的點,沿面的 U 或 V 方向建立 3D 曲線,並與該面連結。用作跟隨曲面的路徑或分割線。",
|
||||
"help.sketch.line": "逐點畫出相連的直線,可為開放的鏈或封閉的輪廓。這是大多數輪廓的基本工具。",
|
||||
"help.sketch.line.midpoint": "從中點向一端畫直線,另一端對稱映射。用於以一點為中心的直線,例如對稱的邊。",
|
||||
@@ -3813,6 +3813,8 @@
|
||||
"sample.plate.name": "範例板",
|
||||
"sample.vents.about": "依矩形陣列排列的槽:列數、欄數和間距均為參數",
|
||||
"sample.vents.name": "通風板",
|
||||
"scale.per_axis": "依軸",
|
||||
"scale.uniform": "等比",
|
||||
"search.command_keys": "Enter 執行 Shift+Enter 執行並保持開啟 Esc 關閉",
|
||||
"search.commands": "命令",
|
||||
"search.design": "此設計中",
|
||||
@@ -4443,6 +4445,7 @@
|
||||
"ui.click_selects_whole_bodies": "點擊選擇整個實體",
|
||||
"ui.click_then_press_key": "點擊,然後按新按鍵",
|
||||
"ui.close": "關閉",
|
||||
"ui.close_tab": "關閉分頁",
|
||||
"ui.coil": "線圈",
|
||||
"ui.coincident": "重合",
|
||||
"ui.collapse": "摺疊",
|
||||
|
||||
+320
-1
@@ -1,4 +1,9 @@
|
||||
{
|
||||
" Keys": " 关键帧",
|
||||
" Maximum": " 最大值",
|
||||
" Minimum": " 最小值",
|
||||
" Of": " 对象",
|
||||
" Value": " 值",
|
||||
"(active rule)": "(当前规则)",
|
||||
"0 turns it off": "0 表示关闭",
|
||||
"1. Open GitHub draft below, then paste the report copied to your clipboard.": "1. 点击下方按钮打开 GitHub 草稿,然后粘贴已复制到剪贴板的报告。",
|
||||
@@ -10,7 +15,18 @@
|
||||
"A new configuration, a copy of the active one": "新建配置,为当前配置的副本",
|
||||
"A placed Single hole has a handle to drag it, and picking its face again moves the hole there instead of taking it out": "已放置的单个孔带有可拖动的控点,再次选择该面会将孔移到该处而不是删除它",
|
||||
"A short title for the problem (optional)": "问题的简短标题(可选)",
|
||||
"ABS - Black": "ABS - 黑色",
|
||||
"ABS - Grey": "ABS - 灰色",
|
||||
"ABS - White": "ABS - 白色",
|
||||
"ALIGN": "对齐",
|
||||
"ARRANGE": "排列",
|
||||
"AS-BUILT JOINT": "现状接头",
|
||||
"ASSIGN PLASTIC RULE": "指定塑料规则",
|
||||
"AXIS PERPENDICULAR TO FACE": "垂直于面的轴",
|
||||
"AXIS THROUGH CYLINDER": "通过圆柱的轴",
|
||||
"AXIS THROUGH EDGE": "过边的轴",
|
||||
"AXIS THROUGH TWO PLANES": "过两个平面的轴",
|
||||
"AXIS THROUGH TWO POINTS": "过两点的轴",
|
||||
"About SolidCraft": "关于 SolidCraft",
|
||||
"Acceleration": "加速度",
|
||||
"Access": "可达性",
|
||||
@@ -27,6 +43,7 @@
|
||||
"Add bend": "添加折弯",
|
||||
"Add condition": "添加条件",
|
||||
"Add or edit rule": "添加或编辑规则",
|
||||
"Add shortcut": "添加快捷键",
|
||||
"Add to New Folder": "添加到新文件夹",
|
||||
"Add visible bodies": "添加可见实体",
|
||||
"Advanced": "高级",
|
||||
@@ -37,13 +54,17 @@
|
||||
"All components": "所有零部件",
|
||||
"All open designs": "所有打开的设计",
|
||||
"Allowed expressions, separated by semicolons": "允许的表达式,以分号分隔",
|
||||
"Allowed values": "允许的值",
|
||||
"Also deletes": "同时删除",
|
||||
"Also place them from the origin (fully constrained)": "同时从原点定位(完全约束)",
|
||||
"Aluminum - Satin": "铝 - 缎面",
|
||||
"Anchor": "基准点",
|
||||
"Angle 2": "角度 2",
|
||||
"Angle Display": "角度显示",
|
||||
"Animate": "动画",
|
||||
"Anodized Black": "黑色阳极氧化",
|
||||
"Another bend on the far edge of the last flange": "在最后一个法兰的远端边上再加一道折弯",
|
||||
"Any": "任意",
|
||||
"App/system information and command settings are included. Review private details before sharing.": "报告包含应用和系统信息以及命令设置。共享前请检查私人信息。",
|
||||
"Appearance name": "外观名称",
|
||||
"Applied Radius": "应用半径",
|
||||
@@ -58,6 +79,8 @@
|
||||
"Assign Rule": "分配规则",
|
||||
"Attachment": "附着",
|
||||
"BLEND SURFACE": "混合曲面",
|
||||
"BOSS": "凸台",
|
||||
"BOUNDARY FILL": "边界填充",
|
||||
"BOUNDARY SURFACE": "边界曲面",
|
||||
"BROWSER": "浏览器",
|
||||
"Back to the original order": "恢复原始顺序",
|
||||
@@ -85,13 +108,21 @@
|
||||
"Bottom Right": "右下",
|
||||
"Boundary": "边界",
|
||||
"Bounding box": "边界框",
|
||||
"Brass": "黄铜",
|
||||
"Bridge": "桥接",
|
||||
"Built-in": "内置",
|
||||
"By definition": "按定义",
|
||||
"By part number": "按零件号",
|
||||
"CAM": "凸轮",
|
||||
"CHECK": "检查",
|
||||
"COMPONENTS": "组件",
|
||||
"CONFIGURATIONS": "配置",
|
||||
"CONSTRAIN": "约束",
|
||||
"CONTACT": "接触",
|
||||
"CONTACT SET": "接触集",
|
||||
"CONVERT": "转换",
|
||||
"CONVERT MESH": "转换网格",
|
||||
"CONVERT TO SHEET METAL": "转换为钣金",
|
||||
"CSV or JSON: updates existing parameters, adds new ones": "CSV 或 JSON:更新现有参数、添加新参数",
|
||||
"Capture All Positions": "捕获所有位置",
|
||||
"Capture Pos.": "捕获位置",
|
||||
@@ -111,11 +142,15 @@
|
||||
"Choose Variable radius or Spline in Type.": "请在“类型”中选择 Variable radius 或样条曲线。",
|
||||
"Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.": "选择一个新平面。草图几何和尺寸在其二维坐标中保持不变。",
|
||||
"Choose…": "选择…",
|
||||
"Chord length": "弦长",
|
||||
"Chrome": "铬",
|
||||
"Circle center": "圆心",
|
||||
"Circle or sphere": "圆或球",
|
||||
"Circular": "圆周",
|
||||
"Circumference": "周长",
|
||||
"Clockwise": "顺时针",
|
||||
"Close All Tabs": "关闭所有标签页",
|
||||
"Close Other Tabs": "关闭其他标签页",
|
||||
"Close draft": "关闭草稿",
|
||||
"Collapse": "折叠",
|
||||
"Collisions": "碰撞",
|
||||
@@ -129,6 +164,7 @@
|
||||
"Config Table": "配置表",
|
||||
"Configuration": "配置",
|
||||
"Configuration rules": "配置规则",
|
||||
"Connect AI assistants…": "连接 AI 助手…",
|
||||
"Constant radius": "等半径",
|
||||
"Contact": "接触",
|
||||
"Contact Sets": "接触集",
|
||||
@@ -137,6 +173,7 @@
|
||||
"Continuity": "连续性",
|
||||
"Control Radius": "控制半径",
|
||||
"Copies the definition only. Assign it from Physical Material when ready.": "仅复制定义。准备好后可在物理材质中指定。",
|
||||
"Copper": "铜",
|
||||
"Copy Error Message": "复制错误消息",
|
||||
"Copy Warning Message": "复制警告消息",
|
||||
"Copy all licences": "复制所有许可证",
|
||||
@@ -150,6 +187,7 @@
|
||||
"Counterbore Ø": "柱形沉孔 Ø",
|
||||
"Countersink angle": "沉头角度",
|
||||
"Countersink Ø": "锥形沉孔 Ø",
|
||||
"Crash Reports…": "崩溃报告…",
|
||||
"Create Hole": "创建孔",
|
||||
"Create Issue — review draft": "创建 Issue——审阅草稿",
|
||||
"Create Issue…": "创建 Issue…",
|
||||
@@ -162,12 +200,17 @@
|
||||
"Curv. Comb": "曲率梳",
|
||||
"Curvature (G2)": "曲率 (G2)",
|
||||
"Curvature change (G3)": "曲率变化 (G3)",
|
||||
"Curvature continuous (G2)": "曲率连续 (G2)",
|
||||
"Custom": "自定义",
|
||||
"Customize Toolbar": "自定义工具栏",
|
||||
"Customize…": "自定义…",
|
||||
"Cylindrical": "圆柱",
|
||||
"Cylindrical face": "圆柱面",
|
||||
"DELETE FACE": "删除面",
|
||||
"DELETE FEATURE": "删除特征",
|
||||
"DRIVE JOINTS": "驱动接头",
|
||||
"Deboss": "凹印",
|
||||
"Default material": "默认材质",
|
||||
"Delete": "删除",
|
||||
"Delete Folders and Features…": "删除文件夹及其特征…",
|
||||
"Delete Selected Features…": "删除所选特征…",
|
||||
@@ -180,20 +223,26 @@
|
||||
"Dependents": "依赖项",
|
||||
"Describe the problem you saw": "请描述您遇到的问题",
|
||||
"Describe what happened before continuing.": "继续之前,请描述发生了什么。",
|
||||
"Design Properties…": "设计属性…",
|
||||
"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": "为新图形标注尺寸",
|
||||
"Direct interaction": "直接交互",
|
||||
"Direction": "方向",
|
||||
"Discard": "放弃",
|
||||
"Distance": "距离",
|
||||
"Distance 1": "距离 1",
|
||||
"Distance X": "距离 X",
|
||||
"Distance Y": "距离 Y",
|
||||
"Distance and Angle": "距离和角度",
|
||||
"Do not export hidden edges": "不导出隐藏线",
|
||||
"Don't ask again": "不再询问",
|
||||
"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": "在视图中双击中键:主视图(主视图,框住所显示的内容)、适合窗口(框住所显示的内容,保持方向)或无动作",
|
||||
"Download design": "下载设计",
|
||||
"Drag to move the boss across the model. It snaps to sketch points on the face.": "拖动以在模型上移动凸台。它会吸附到面上的草图点。",
|
||||
"Drag to move the hole; click to remove it": "拖动以移动孔,单击以删除",
|
||||
"Drag to move the rest across the model. It snaps to sketch points on the face.": "拖动以在模型上移动垫块。它会吸附到面上的草图点。",
|
||||
@@ -201,7 +250,16 @@
|
||||
"Drag to point the catch in another direction.": "拖动以将卡钩指向另一个方向。",
|
||||
"Drag to turn the rest in 5° steps. Hold Alt or Ctrl to turn it freely.": "拖动以 5° 为步长旋转垫块。按住 Alt 或 Ctrl 可自由旋转。",
|
||||
"Drive Joint": "驱动接头",
|
||||
"Drive parameter": "驱动参数",
|
||||
"Drive suppression": "驱动抑制",
|
||||
"Duration": "时长",
|
||||
"EDIT DIMENSION": "编辑尺寸",
|
||||
"EDIT JOINT": "编辑接头",
|
||||
"EDIT MOTION STUDY": "编辑运动研究",
|
||||
"EDIT TEXT": "编辑文字",
|
||||
"EMBOSS": "压印",
|
||||
"EXPLODED VIEW": "爆炸图",
|
||||
"EXTEND": "延伸",
|
||||
"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": "边",
|
||||
@@ -215,10 +273,12 @@
|
||||
"End bulge": "终止鼓度",
|
||||
"End continuity": "终止连续性",
|
||||
"Equal Distance": "等距离",
|
||||
"Exact": "精确",
|
||||
"Expand": "展开",
|
||||
"Expected result (optional)": "预期结果(可选)",
|
||||
"Explode": "爆炸",
|
||||
"Explode Text": "分解文字",
|
||||
"Export 3MF…": "导出 3MF…",
|
||||
"Export Animation…": "导出动画…",
|
||||
"Export Bend Table…": "导出折弯表…",
|
||||
"Export CSV…": "导出 CSV…",
|
||||
@@ -226,7 +286,12 @@
|
||||
"Export Current View as DXF…": "将当前视图导出为DXF…",
|
||||
"Export DXF": "导出DXF",
|
||||
"Export DXF…": "导出DXF…",
|
||||
"Export Design as JSON…": "将设计导出为 JSON…",
|
||||
"Export JSON…": "导出 JSON…",
|
||||
"Export OBJ…": "导出 OBJ…",
|
||||
"Export STEP…": "导出 STEP…",
|
||||
"Export STL…": "导出 STL…",
|
||||
"Export URDF…": "导出 URDF…",
|
||||
"Export design library...": "导出设计库...",
|
||||
"Export hidden edges as dashed lines": "以虚线导出隐藏线",
|
||||
"Exported by SolidWorks, which usually saves Y up": "由 SolidWorks 导出,通常以 Y 轴向上保存",
|
||||
@@ -234,13 +299,19 @@
|
||||
"Extend to meet": "延伸至相交",
|
||||
"Extents": "范围",
|
||||
"FILES": "文件",
|
||||
"FLANGE": "法兰",
|
||||
"FLAT": "展开",
|
||||
"FLAT PATTERN": "展开图",
|
||||
"FOLD": "折叠",
|
||||
"FRAME MEMBER": "框架构件",
|
||||
"Face": "面",
|
||||
"Face Appearance": "面外观",
|
||||
"Face attachment lost": "面附着丢失",
|
||||
"Face center": "面中心",
|
||||
"Faces": "面",
|
||||
"Faces / edges / vertices": "面 / 边 / 顶点",
|
||||
"Faces on Block": "块上的面",
|
||||
"Faces on Ring": "环上的面",
|
||||
"Faces to split": "要分割的面",
|
||||
"Faces/Body": "面/实体",
|
||||
"Factor must be a positive number": "系数必须是正数",
|
||||
@@ -265,20 +336,30 @@
|
||||
"Flat size": "平板尺寸",
|
||||
"Flat size X": "平板尺寸 X",
|
||||
"Flat size Y": "平板尺寸 Y",
|
||||
"Flipped": "已翻转",
|
||||
"Follow camera (Look At sketches)": "跟随相机(注视草图)",
|
||||
"Follow inherited appearance": "跟随继承的外观",
|
||||
"Follow material": "跟随材质",
|
||||
"Follow the camera (Look At sketches)": "跟随相机(注视草图)",
|
||||
"Font": "字体",
|
||||
"Free Move": "自由移动",
|
||||
"From": "起点",
|
||||
"From Bodies": "从实体创建",
|
||||
"From Object": "从对象",
|
||||
"From Snap": "起始捕捉",
|
||||
"From edges": "起始边",
|
||||
"Full Length": "全长",
|
||||
"Full thread depth": "完整螺纹深度",
|
||||
"Further Bends": "更多折弯",
|
||||
"G2 corners require a constant radius and Exact fit. G2 corner construction is not supported yet.": "G2 拐角需要恒定半径和 Exact fit。尚不支持构建 G2 拐角。",
|
||||
"GEAR": "齿轮",
|
||||
"GROOVE": "凹槽",
|
||||
"Gap": "间隙",
|
||||
"Glass - Clear": "玻璃 - 透明",
|
||||
"Glass - Smoked": "玻璃 - 烟色",
|
||||
"Grid": "网格",
|
||||
"Groove": "凹槽",
|
||||
"HDR file changed since selection; inspect and select it again before saving": "选择后 HDR 文件已更改;保存前请检查并重新选择",
|
||||
"HISTORY": "历史",
|
||||
"Halo under sketch curves": "在草图曲线下方显示光晕",
|
||||
"Height": "高度",
|
||||
@@ -287,6 +368,7 @@
|
||||
"Hidden edges": "隐藏线",
|
||||
"History layout": "历史布局",
|
||||
"History names": "历史名称",
|
||||
"Hold click": "按住单击",
|
||||
"Hole (optional)": "孔(可选)",
|
||||
"Hole Depth": "孔深",
|
||||
"Hole Diameter": "孔径",
|
||||
@@ -296,6 +378,8 @@
|
||||
"Horiz./Vert.": "水平 / 垂直",
|
||||
"I-Beam": "工字钢",
|
||||
"IMAGES": "图像",
|
||||
"INSERT PART": "插入零件",
|
||||
"INSERT SVG": "插入 SVG",
|
||||
"If you are offline or GitHub does not open, use Copy report and keep the text to submit later.": "如果处于离线状态或 GitHub 无法打开,请使用“复制报告”并保留文本以便稍后提交。",
|
||||
"Import material into design": "将材质导入设计",
|
||||
"Import…": "导入…",
|
||||
@@ -304,6 +388,8 @@
|
||||
"In plane": "在平面内",
|
||||
"Included": "夹角",
|
||||
"Inner Sharp": "内侧虚拟尖点",
|
||||
"Insert Mesh…": "插入网格…",
|
||||
"Insert STEP…": "插入 STEP…",
|
||||
"Inside": "内侧",
|
||||
"Instant": "即时",
|
||||
"Interface font": "界面字体",
|
||||
@@ -312,6 +398,8 @@
|
||||
"Isocurve": "等参线",
|
||||
"Issue title": "Issue 标题",
|
||||
"Items": "项目",
|
||||
"JOINT": "接头",
|
||||
"JOINT ORIGIN": "接头原点",
|
||||
"JOINTS": "关节",
|
||||
"Joint 1": "接头 1",
|
||||
"Joint 2": "接头 2",
|
||||
@@ -348,9 +436,15 @@
|
||||
"M5 or M5x0.5": "M5 或 M5x0.5",
|
||||
"M5, M5x0.5, 1/4-20 UNC": "M5, M5x0.5, 1/4-20 UNC",
|
||||
"MANAGE": "管理",
|
||||
"MANAGE PLASTIC RULES": "管理塑料规则",
|
||||
"MATERIALS": "材料",
|
||||
"MIDPLANE": "中平面",
|
||||
"MODEL": "模型",
|
||||
"MOTION": "运动",
|
||||
"MOTION LINK": "运动关联",
|
||||
"MOTION STUDY": "运动研究",
|
||||
"MOTION TRACE": "运动轨迹",
|
||||
"MOVE/COPY TEXT": "移动/复制文字",
|
||||
"Margin": "余量",
|
||||
"Mass": "质量",
|
||||
"Mass Center": "质心",
|
||||
@@ -361,6 +455,7 @@
|
||||
"Max": "最大",
|
||||
"Max Thickness": "最大厚度",
|
||||
"Maximum": "最大",
|
||||
"Mesh": "网格",
|
||||
"Mesh body": "网格实体",
|
||||
"Mid Radius": "中间半径",
|
||||
"Middle double-click": "中键双击",
|
||||
@@ -383,6 +478,7 @@
|
||||
"Move holes by clicking or dragging": "通过单击或拖动移动",
|
||||
"Move later": "向后移动",
|
||||
"Move up": "上移",
|
||||
"NEW COMPONENT": "新建组件",
|
||||
"Name": "名称",
|
||||
"Name on import": "导入名称",
|
||||
"Nearest": "最近",
|
||||
@@ -400,11 +496,19 @@
|
||||
"Nominal Radius": "公称半径",
|
||||
"Nominal diameter": "公称直径",
|
||||
"None": "无",
|
||||
"Not": "非",
|
||||
"Not available yet": "暂不可用",
|
||||
"Not saved yet": "尚未保存",
|
||||
"Notch": "缺口",
|
||||
"Nylon - Natural": "尼龙 - 本色",
|
||||
"OFFSET FACE": "偏移面",
|
||||
"OFFSET SURFACE": "偏移曲面",
|
||||
"OUTPUT": "输出",
|
||||
"Oak": "橡木",
|
||||
"Of": "的值",
|
||||
"Offset X": "偏移 X",
|
||||
"Offset Y": "偏移 Y",
|
||||
"Offset Z": "偏移 Z",
|
||||
"On your computer": "在此计算机上",
|
||||
"Opacity": "不透明度",
|
||||
"Open GitHub": "打开 GitHub",
|
||||
@@ -427,10 +531,30 @@
|
||||
"Outer radius": "外圆角半径",
|
||||
"Outside": "外侧",
|
||||
"Overall length": "总长",
|
||||
"PATCH": "补面",
|
||||
"PERPENDICULAR PLANE": "垂直平面",
|
||||
"PHYSICAL MATERIAL": "物理材质",
|
||||
"PICK CONTEXT": "选择上下文",
|
||||
"PLANE ALONG PATH": "沿路径的平面",
|
||||
"PLANE THROUGH THREE POINTS": "过三点的平面",
|
||||
"PLANE THROUGH TWO EDGES": "过两条边的平面",
|
||||
"POINT ALONG PATH": "沿路径的点",
|
||||
"POINT AT CENTER": "中心点",
|
||||
"POINT AT EDGE AND PLANE": "边与平面交点",
|
||||
"POINT AT VERTEX": "顶点处的点",
|
||||
"POINT THROUGH THREE PLANES": "过三个平面的点",
|
||||
"POINT THROUGH TWO EDGES": "过两条边的点",
|
||||
"Paint - Blue": "油漆 - 蓝色",
|
||||
"Paint - Green": "油漆 - 绿色",
|
||||
"Paint - Orange": "油漆 - 橙色",
|
||||
"Paint - Purple": "油漆 - 紫色",
|
||||
"Paint - Red": "油漆 - 红色",
|
||||
"Paint - Yellow": "油漆 - 黄色",
|
||||
"Param. Graph": "参数图",
|
||||
"Parameter": "参数",
|
||||
"Part length must be greater than zero and at most 2000 mm": "零件长度必须大于零且不超过 2000 mm",
|
||||
"Parts": "零件",
|
||||
"Parts only": "仅零件",
|
||||
"Pass through": "通过点",
|
||||
"Path": "路径",
|
||||
"Pattern": "阵列",
|
||||
@@ -446,16 +570,21 @@
|
||||
"Place at the origin instead": "改为放置在原点",
|
||||
"Planar": "平面",
|
||||
"Plastic Rules": "塑料规则",
|
||||
"Play": "播放",
|
||||
"Play the joint's motion in the view": "在视图中播放接头运动",
|
||||
"Playback": "播放",
|
||||
"Plot": "图表",
|
||||
"Point on line": "点在直线上",
|
||||
"Point on plane": "点在平面上",
|
||||
"Point to Point": "点到点",
|
||||
"Point to Position": "点到位置",
|
||||
"Polycarbonate - Clear": "聚碳酸酯 - 透明",
|
||||
"Position": "位置",
|
||||
"Position (0 to 1)": "位置(0 到 1)",
|
||||
"Position (G0)": "位置 (G0)",
|
||||
"Position along the edge, 0 at the start end, 1 at the other": "沿边的位置,起点端为 0,另一端为 1",
|
||||
"Preferences › Design turns the offer back on": "可在 首选项 › 设计 中重新开启此提示",
|
||||
"Preferences…": "首选项…",
|
||||
"Preview": "预览",
|
||||
"Preview Row": "预览行",
|
||||
"Preview leaves the current design unchanged.": "预览不会更改当前设计。",
|
||||
@@ -464,12 +593,21 @@
|
||||
"Profiles/Curves": "轮廓/曲线",
|
||||
"Quantity": "数量",
|
||||
"Quantity must be at least 2": "数量至少为 2",
|
||||
"RACK AND PINION": "齿轮齿条",
|
||||
"RECENT": "最近使用",
|
||||
"REFERENCE": "参考",
|
||||
"REFOLD": "重新折弯",
|
||||
"REMOVE": "移除",
|
||||
"RENAME": "重命名",
|
||||
"REPLACE FACE": "替换面",
|
||||
"REST": "垫块",
|
||||
"REVERSE NORMAL": "反转法向",
|
||||
"RIGID GROUP": "刚性组",
|
||||
"RULED SURFACE": "直纹曲面",
|
||||
"RULES": "规则",
|
||||
"Radius": "半径",
|
||||
"Radius Fit": "半径拟合",
|
||||
"Range": "范围",
|
||||
"Ratio": "比率",
|
||||
"Read-only list: shortcuts can't be changed here.": "只读列表:此处无法更改快捷键。",
|
||||
"Reattach selected face": "重新附着所选面",
|
||||
@@ -477,6 +615,7 @@
|
||||
"Recover unsaved design?": "恢复未保存的设计?",
|
||||
"Rectangular": "矩形",
|
||||
"Rectangular Tube": "矩形管",
|
||||
"Reduce to Fit": "缩小以适合",
|
||||
"Reference face": "参考面",
|
||||
"Reference plane": "参考平面",
|
||||
"Relief Depth": "止裂槽深度",
|
||||
@@ -500,6 +639,7 @@
|
||||
"Repair Part": "修复零件",
|
||||
"Replace existing library file": "替换现有库文件",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "报告缺陷 — 复核截图并开启 GitHub 草稿",
|
||||
"Report an Issue…": "报告问题…",
|
||||
"Report copied. Keep this text to submit on GitHub when you are online.": "报告已复制。请保留此文本,联网后在 GitHub 上提交。",
|
||||
"Report text copied. Paste it into GitHub and submit your report there.": "报告文本已复制。请在GitHub中粘贴并提交。",
|
||||
"Reports are public. A GitHub account is required.": "报告将公开发布。您需要GitHub账户。",
|
||||
@@ -532,16 +672,32 @@
|
||||
"Ring of tools on the right button": "右键工具环",
|
||||
"Roll Back Here": "回滚到此处",
|
||||
"Roll History Marker Here": "将历史标记滚动到此处",
|
||||
"Rolling": "滚动",
|
||||
"Root radius": "根部圆角半径",
|
||||
"Rotate": "旋转",
|
||||
"Round": "圆角",
|
||||
"Round Bar": "圆钢",
|
||||
"Round Tube": "圆管",
|
||||
"Rubber - Black": "橡胶 - 黑色",
|
||||
"Rule": "规则",
|
||||
"Rule name": "规则名称",
|
||||
"Rules": "规则",
|
||||
"Rules run from top to bottom. Matched drives change effective values.": "规则从上到下依次运行。满足条件的驱动规则更改有效值。",
|
||||
"SCREW": "螺旋",
|
||||
"SECTION": "剖面",
|
||||
"SHEET METAL RULES": "钣金规则",
|
||||
"SNAP FIT": "卡扣",
|
||||
"SPLIT FACE": "拆分面",
|
||||
"STITCH": "缝合",
|
||||
"STOCK": "毛坯",
|
||||
"SURFACE EXTRUDE": "曲面拉伸",
|
||||
"SURFACE LOFT": "曲面放样",
|
||||
"SURFACE QUALITY": "曲面质量",
|
||||
"SURFACE REVOLVE": "曲面旋转",
|
||||
"SURFACE SWEEP": "曲面扫掠",
|
||||
"Same way": "同方向",
|
||||
"Save As JSON…": "另存为 JSON…",
|
||||
"Save As…": "另存为…",
|
||||
"Save Rule": "保存规则",
|
||||
"Save View": "保存视图",
|
||||
"Save screenshot…": "保存屏幕截图…",
|
||||
@@ -550,6 +706,9 @@
|
||||
"Scale factor": "比例因子",
|
||||
"Scene": "场景",
|
||||
"Screenshot (optional)": "屏幕截图(可选)",
|
||||
"Screenshot captured locally. Review it before attaching it on GitHub.": "已在本地截取屏幕截图。在 GitHub 上附加前请先检查。",
|
||||
"Scroll left": "向左滚动",
|
||||
"Scroll right": "向右滚动",
|
||||
"Search": "搜索",
|
||||
"Search commands": "搜索命令",
|
||||
"Search this library": "搜索此库",
|
||||
@@ -581,6 +740,7 @@
|
||||
"Send your report on GitHub": "在 GitHub 上发送报告",
|
||||
"Shaded": "着色",
|
||||
"Shape": "形状",
|
||||
"Sheet Metal": "钣金",
|
||||
"Sheet Rules": "钣金规则",
|
||||
"Show Agent Cursor": "显示代理光标",
|
||||
"Show ghost": "显示虚影",
|
||||
@@ -608,6 +768,7 @@
|
||||
"Speed": "速度",
|
||||
"Splitting tool": "分割工具",
|
||||
"Square": "正方形",
|
||||
"Stainless Steel": "不锈钢",
|
||||
"Stand it up": "将其立起",
|
||||
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "将其立起:绕 X 轴旋转四分之一周,作为一个可撤销的移动特征。",
|
||||
"Stand this assembly up?": "立起此装配?",
|
||||
@@ -616,6 +777,8 @@
|
||||
"Start bulge": "起始鼓度",
|
||||
"Start continuity": "起始连续性",
|
||||
"Stationary Side": "固定侧",
|
||||
"Steel": "钢",
|
||||
"Steel - Brushed": "钢 - 拉丝",
|
||||
"Step": "步骤",
|
||||
"Steps": "步数",
|
||||
"Steps to reproduce (optional)": "重现步骤(可选)",
|
||||
@@ -626,12 +789,19 @@
|
||||
"Style": "样式",
|
||||
"Summary": "摘要",
|
||||
"Suppress": "抑制",
|
||||
"Suppress Features": "抑制特征",
|
||||
"Suppress Selected Folders": "抑制所选文件夹",
|
||||
"Suppressed": "已抑制",
|
||||
"Suppression": "抑制",
|
||||
"Surface": "曲面",
|
||||
"Surface or face": "曲面或面",
|
||||
"Surface or faces": "曲面或面",
|
||||
"Surface, or faces to turn over": "曲面,或要翻转的面",
|
||||
"Surfaces": "曲面",
|
||||
"Swap Ends": "交换端点",
|
||||
"TANGENT PLANE": "相切平面",
|
||||
"THICKEN": "加厚",
|
||||
"TRANSFORM": "变换",
|
||||
"TRIM": "修剪",
|
||||
"Tangent": "相切",
|
||||
"Tangent (G1)": "相切 (G1)",
|
||||
@@ -639,9 +809,11 @@
|
||||
"Tangent chain": "相切链",
|
||||
"Tangent to Edge": "与边相切",
|
||||
"Tap drill diameter": "攻丝底孔直径",
|
||||
"Taper to Fit": "锥化以适配",
|
||||
"Target": "目标",
|
||||
"Target Body": "目标实体",
|
||||
"Target Face (optional)": "目标面(可选)",
|
||||
"Target edges": "目标边",
|
||||
"Tear": "撕裂",
|
||||
"Technical details": "技术详情",
|
||||
"The dashed sketch is a saved plane guide. Select a new face to repair the attachment.": "虚线草图是已保存的平面引导。选择一个新面以修复附着。",
|
||||
@@ -649,6 +821,7 @@
|
||||
"The direction must be an axis or a straight sketch curve that lies in the sketch plane": "方向必须是轴,或位于草图平面内的直线草图曲线",
|
||||
"The mirror line cannot also be mirrored": "镜像线本身不能再被镜像",
|
||||
"The mirror line must be a straight curve of this sketch, not an origin axis": "镜像线必须是此草图中的直线曲线,不能是原点轴",
|
||||
"The origin axis is perpendicular to the sketch plane, so it has no line in the plane": "原点轴垂直于草图平面,因此在该平面内没有直线",
|
||||
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "保存的作者实例缺失。重新绑定前请选择有效的作者放置。",
|
||||
"Then": "操作",
|
||||
"These features will be deleted:": "以下特征将被删除:",
|
||||
@@ -680,16 +853,21 @@
|
||||
"Top Left": "左上",
|
||||
"Top Middle": "上中",
|
||||
"Top Right": "右上",
|
||||
"Top level": "顶层",
|
||||
"Trimming tool (plane, face or curves)": "修剪工具(平面、面或曲线)",
|
||||
"Turn off section analysis before exporting the view.": "请关闭截面分析后再导出视图。",
|
||||
"Two Distances": "两个距离",
|
||||
"Type": "类型",
|
||||
"Type a distance or an angle to move by": "请输入要移动的距离或角度",
|
||||
"UNFOLD": "展开",
|
||||
"UNSTITCH": "取消缝合",
|
||||
"UNTRIM": "取消修剪",
|
||||
"Unit": "单位",
|
||||
"Unsuppress": "取消抑制",
|
||||
"Unsuppress Selected Folders": "取消抑制所选文件夹",
|
||||
"Updating the preview": "正在更新预览",
|
||||
"Use active author placement": "使用活动作者放置",
|
||||
"Use axis direction only": "仅使用轴方向",
|
||||
"Use fixed plane": "使用固定平面",
|
||||
"Use saved fixed plane": "使用已保存的固定平面",
|
||||
"Use selected face as fixed plane": "使用所选面作为固定平面",
|
||||
@@ -699,13 +877,16 @@
|
||||
"VIEWS": "视图",
|
||||
"Value": "值",
|
||||
"Variable Radius": "可变半径",
|
||||
"Variable radius": "可变半径",
|
||||
"Velocity": "速度",
|
||||
"Vertices": "顶点",
|
||||
"View": "视图",
|
||||
"View navigator": "视图导航器",
|
||||
"Volume": "体积",
|
||||
"Wall": "壁厚",
|
||||
"Wall Location": "壁位置",
|
||||
"Wall Thickness": "壁厚度",
|
||||
"Walnut": "胡桃木",
|
||||
"Web thickness": "腹板厚度",
|
||||
"What did you do just before the problem? A short list is enough": "问题发生前您进行了哪些操作?简要列出即可",
|
||||
"What did you expect to see?": "您期望看到什么结果?",
|
||||
@@ -740,6 +921,9 @@
|
||||
"a picked edge's body is gone": "所选边所属的实体已不存在",
|
||||
"add a key for a joint first": "请先为接头添加关键帧",
|
||||
"ai.connected_toast": "SolidCraft 现已可在 {clients} 中使用。请重启它们(设置 > 助手)。",
|
||||
"ai.http.allow": "允许不安全的 HTTP",
|
||||
"ai.http.confirm": "允许此端点",
|
||||
"ai.http.warning": "API 密钥和对话将以未加密的方式发送。任何能够访问该网络的人都可能读取或篡改它们。请仅在可信网络上使用。",
|
||||
"ai.prefs.allow_control": "允许AI代理连接",
|
||||
"ai.prefs.allow_control_note": "本地通道(MCP)。任何内容都不会离开此计算机。\n\n仅在本机开放控制通道(127.0.0.1,端口 27878 或下一个空闲端口)。连接的程序还必须出示一个每次启动时新生成、只有你的用户账户能读取的秘密令牌。下次启动 SolidCraft 时生效。关闭:除非用 --control 启动,否则不监听任何端口。",
|
||||
"ai.prefs.availability": "可用性",
|
||||
@@ -751,6 +935,8 @@
|
||||
"ai.prefs.follow_camera": "跟随相机",
|
||||
"ai.prefs.follow_camera_off": "相机自动跟随已关闭。",
|
||||
"ai.prefs.follow_camera_on": "相机将转向代理创建的草图。",
|
||||
"ai.prefs.instructions": "自定义指令",
|
||||
"ai.prefs.instructions_note": "在每次对话开始时发送给代理,位于 SolidCraft 自身的指令之后:你喜欢的工作方式、需要遵守的规则。更改从下一次对话(连接或新对话)起生效。",
|
||||
"ai.prefs.intro": "SolidCraft中的AI代理。",
|
||||
"ai.prefs.make_default": "设为默认",
|
||||
"ai.prefs.no_agents": "尚无代理。添加代理或刷新以查找已安装的代理。",
|
||||
@@ -1035,6 +1221,7 @@
|
||||
"cmd.occurrence_move": "移动实例",
|
||||
"cmd.occurrence_properties": "实例物理属性",
|
||||
"cmd.occurrence_reparent": "移动到组件",
|
||||
"cmd.panel.action": "执行面板操作",
|
||||
"cmd.panel.activate": "激活面板选项卡",
|
||||
"cmd.panel.close": "关闭面板",
|
||||
"cmd.panel.collapse": "折叠面板分区",
|
||||
@@ -1081,6 +1268,9 @@
|
||||
"cmd.render.compare.divider": "移动渲染比较分隔线",
|
||||
"cmd.render.result.discard": "丢弃渲染结果",
|
||||
"cmd.render.result.select": "选择渲染结果",
|
||||
"cmd.render.surface.import": "导入表面贴图",
|
||||
"cmd.render.texture.list": "列出嵌入的表面图像",
|
||||
"cmd.render.texture.remove": "移除嵌入的表面图像",
|
||||
"cmd.render_aging_describe": "分层表面老化",
|
||||
"cmd.render_backends": "渲染后端",
|
||||
"cmd.render_batch": "渲染所有镜头",
|
||||
@@ -1219,6 +1409,7 @@
|
||||
"cmd.sketch_move": "移动/复制草图图元",
|
||||
"cmd.sketch_move3d": "移动三维点",
|
||||
"cmd.sketch_move_point": "拖动草图点",
|
||||
"cmd.sketch_move_points": "移动草图点",
|
||||
"cmd.sketch_offset_break": "断开偏移关联",
|
||||
"cmd.sketch_options": "草图选项板选项",
|
||||
"cmd.sketch_paste": "粘贴草图图元",
|
||||
@@ -1324,11 +1515,13 @@
|
||||
"cmd.view_previous_tab": "上一个标签页",
|
||||
"cmd.view_rename": "重命名命名视图",
|
||||
"cmd.view_render_frame": "渲染构图辅助线",
|
||||
"cmd.view_ribbon_tabs_only": "仅功能区选项卡",
|
||||
"cmd.view_right": "右视图",
|
||||
"cmd.view_save": "新建命名视图",
|
||||
"cmd.view_shots_strip": "镜头条",
|
||||
"cmd.view_spin": "连续环绕",
|
||||
"cmd.view_tool": "导航工具",
|
||||
"cmd.view_tool_zoom": "拖动缩放",
|
||||
"cmd.view_top": "俯视图",
|
||||
"cmd.view_visual_style": "视觉样式",
|
||||
"color.blue": "蓝色",
|
||||
@@ -1419,6 +1612,9 @@
|
||||
"document.changed_on_disk": "{name} 已在磁盘上更改",
|
||||
"document.changed_on_disk_detail": "自您打开或上次保存 {name} 以来,它已被其他程序或计算机更改。覆盖会用您的更改替换这些更改;保存副本则两者都保留。",
|
||||
"document.copy_name": "{name} 副本",
|
||||
"document.newer_file": "由较新版本的 SolidCraft 保存(格式 {format},此版本最高可读取 {reads})。如有缺失,请更新。",
|
||||
"document.newer_file_change": "由较新版本的 SolidCraft 保存(格式 {format},包含此版本未知的 `{change}`)。如有缺失,请更新。",
|
||||
"document.newer_file_not_read": "未读取:{what}。保存时会保留它。",
|
||||
"document.open_anyway": "仍然打开",
|
||||
"document.open_elsewhere": "{name} 已被打开",
|
||||
"document.open_elsewhere_detail": "{name} 已在 {who} 上的 SolidCraft {app} 中打开({when}打开)。如果双方都保存,其中一方的工作会丢失。以只读方式打开即可查看而不会覆盖它。",
|
||||
@@ -1454,8 +1650,11 @@
|
||||
"file.open.portal_fallback.xwayland": "打开的文件选择器并非桌面自带:在 XWayland 下,门户会拒绝父窗口。",
|
||||
"file_load.built": "已构建:{built}/{total} 个特征。",
|
||||
"file_load.converting": "正在转换实体 {done}/{total}",
|
||||
"file_load.inserted": "已插入 {name}",
|
||||
"file_load.inserting": "正在插入几何体",
|
||||
"file_load.link_item": "{name} ({done}/{total})",
|
||||
"file_load.opened": "已打开 {name}",
|
||||
"file_load.opened_in_tab": "已在新标签页中打开 {name}",
|
||||
"file_load.opening": "正在打开设计",
|
||||
"file_load.preparing_thumbnail": "正在准备缩略图...",
|
||||
"file_load.preparing_view": "正在准备视图...",
|
||||
@@ -1691,7 +1890,7 @@
|
||||
"help.sketch.insert_svg": "将 SVG 图形中的路径读入草图,并按设定进行缩放、旋转和放置。用于把标志或矢量轮廓变成可拉伸的曲线。",
|
||||
"help.sketch.inspect": "打开当前草图的报告:显示剩余的自由度以及是否已完全约束。在完成草图之前,用它找出仍然可以移动的部分。",
|
||||
"help.sketch.intersect": "添加实体、面、边或其他草图与草图平面相交处的曲线或点。它们保持关联,并随模型变化。",
|
||||
"help.sketch.intersection_curve": "在两个实体或面相交处创建与它们关联的三维曲线。用作跟随模型的路径或导轨。",
|
||||
"help.sketch.intersection_curve": "在两个实体、面或其他草图的曲线相交处创建与它们关联的三维曲线。用作跟随模型的路径或导轨。",
|
||||
"help.sketch.isoparametric_curve": "通过你选取的点,沿面的 U 或 V 方向创建与该面关联的三维曲线。用作跟随曲面的路径或分割线。",
|
||||
"help.sketch.line": "逐点绘制相连的直线,可以是开放链或封闭轮廓。这是绘制大多数轮廓的基本工具。",
|
||||
"help.sketch.line.midpoint": "从中点向一端绘制直线,另一端随之对称生成。用于以某点为中心的直线,例如对称的边。",
|
||||
@@ -1797,6 +1996,7 @@
|
||||
"help.view.navigate": "按步骤旋转、平移或缩放视图,例如向左转四分之一圈。用于精确移动相机,而不是靠拖动。",
|
||||
"help.view.navigation_bar": "显示或隐藏视口底部的导航栏。隐藏可腾出空间,显示便于用鼠标使用视图工具。",
|
||||
"help.view.rename": "为已保存的命名视图改名。视图本身保持不变。",
|
||||
"help.view.ribbon_tabs_only": "将功能区折叠为选项卡行,让视图得到面板原先占用的空间。单击选项卡可在视图上方显示其面板;再次选择此命令则始终显示完整的功能区。",
|
||||
"help.view.save": "将当前相机位置作为命名视图保存到设计中。用于以后回到同一角度,例如截图。",
|
||||
"help.view.spin": "让视图持续绕模型环绕,直到下一次点击或按键。用于从各个方向展示模型。",
|
||||
"help.view.tool": "锁定一个相机工具(环绕、平移或缩放),之后用鼠标左键拖动即可移动视图,直到关闭。用于触控板或没有鼠标中键时。",
|
||||
@@ -2163,6 +2363,7 @@
|
||||
"navbar.value_tip": "{name} · {value}",
|
||||
"navbar.visual_style": "视觉样式",
|
||||
"navbar.zoom": "缩放",
|
||||
"navbar.zoom_hold_tip": "缩放(按住快捷键)",
|
||||
"needs two joints": "需要两个接头",
|
||||
"network.allow_once": "仅允许一次",
|
||||
"network.always_allow": "始终允许此服务器",
|
||||
@@ -2316,6 +2517,7 @@
|
||||
"prefs.carousel_previous": "上一个",
|
||||
"prefs.choose_font": "选择字体文件…",
|
||||
"prefs.click": "单击",
|
||||
"prefs.command_dialog_values": "命令对话框",
|
||||
"prefs.contact_shading": "柔和接触阴影",
|
||||
"prefs.contact_shading_note": "在零件相近处添加柔和阴影。",
|
||||
"prefs.context_menu": "上下文菜单",
|
||||
@@ -2362,6 +2564,8 @@
|
||||
"prefs.edges": "边缘与着色",
|
||||
"prefs.extend": "扩展",
|
||||
"prefs.extra_navigation": "在空白处按住Ctrl并用鼠标左键拖动可旋转;按住空格键可平移。",
|
||||
"prefs.extrude_arrow_face_stops": "拉伸箭头停在面上",
|
||||
"prefs.extrude_arrow_face_stops_note": "拖动拉伸的距离箭头时,它会停在与轮廓平行的平面上(例如正在切除的块的另一侧),并勾勒出该面。关闭:箭头只按整数步长移动。",
|
||||
"prefs.extrude_view_isometric": "打开拉伸时转到等轴测视图",
|
||||
"prefs.extrude_view_return": "关闭拉伸时回到之前的视图",
|
||||
"prefs.follows_os": "跟随操作系统",
|
||||
@@ -2411,6 +2615,8 @@
|
||||
"prefs.graphics_page.surfaces_edges": "曲面与边",
|
||||
"prefs.graphics_page.view_bar": "也可在视图栏中设置",
|
||||
"prefs.graphics_presets_note": "预设会同时设置以下选项。",
|
||||
"prefs.ground_shadow_follows_model": "地面阴影跟随模型",
|
||||
"prefs.ground_shadow_follows_model_help": "将阴影放在可见实体的最低点下方。关闭时,抬高模型的阴影仍位于经过原点的地面上。",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "缩小时隐藏约束符号",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out_note": "草图缩小到一定程度时隐藏约束符号,以免遮挡草图。关闭:任意缩放比例下符号仍保持固定屏幕大小。",
|
||||
"prefs.history": "历史",
|
||||
@@ -2452,6 +2658,8 @@
|
||||
"prefs.layout_page.card": "卡片",
|
||||
"prefs.layout_page.column": "列",
|
||||
"prefs.layout_page.hover": "悬停",
|
||||
"prefs.layout_page.hover_panel_menus": "悬停切换面板菜单",
|
||||
"prefs.layout_page.hover_panel_menus_help": "点击功能区的面板名称打开菜单。将指针悬停在另一面板名称上即可切换菜单。将指针移到功能区和已打开菜单之外即可关闭菜单。",
|
||||
"prefs.layout_page.icons_labels": "图标+标签",
|
||||
"prefs.layout_page.ok_cancel": "确定和取消",
|
||||
"prefs.layout_page.on_hover": "悬停时",
|
||||
@@ -2490,6 +2698,8 @@
|
||||
"prefs.mouse": "鼠标与触控板",
|
||||
"prefs.mouse_heading": "鼠标",
|
||||
"prefs.mouse_mapping": "鼠标映射",
|
||||
"prefs.move_drag_highlights": "用移动的箭头拖动组件时高亮指针下的对象",
|
||||
"prefs.move_drag_highlights_tip": "关闭:用移动的箭头拖动组件时,不高亮指针下的对象,大型装配体也能保持流畅。显示剖切或分析时仍照常高亮;沿关节拖动零件时从不高亮。",
|
||||
"prefs.navigation": "导航",
|
||||
"prefs.navigation.axes_signed": "坐标轴",
|
||||
"prefs.navigation.cube_compact": "立方体",
|
||||
@@ -2518,6 +2728,22 @@
|
||||
"prefs.persistent_ribbon_pressed": "突出显示已启用的功能区命令",
|
||||
"prefs.persistent_ribbon_pressed_help": "在效果启用时突出显示相应的功能区命令。关闭此选项可保留原来的中性外观和命令帮助。",
|
||||
"prefs.perspective_fov": "视场角",
|
||||
"prefs.picker.carousel": "轮播",
|
||||
"prefs.picker.change": "更改",
|
||||
"prefs.picker.clear": "清除筛选条件",
|
||||
"prefs.picker.day": "日间主题",
|
||||
"prefs.picker.empty": "没有符合这些筛选条件的主题。",
|
||||
"prefs.picker.follow": "跟随系统",
|
||||
"prefs.picker.grid": "网格",
|
||||
"prefs.picker.high": "高对比度",
|
||||
"prefs.picker.high_note": "{source} · 高对比度",
|
||||
"prefs.picker.hint": "随操作系统设置自动切换。",
|
||||
"prefs.picker.import": "导入主题文件…",
|
||||
"prefs.picker.imported": "已导入",
|
||||
"prefs.picker.night": "夜间主题",
|
||||
"prefs.picker.one": "单一主题",
|
||||
"prefs.picker.pick": "从下方选择",
|
||||
"prefs.picker.standard": "标准",
|
||||
"prefs.placement": "放置",
|
||||
"prefs.plugins_enabled": "已启用",
|
||||
"prefs.plugins_installed": "已安装",
|
||||
@@ -2554,6 +2780,8 @@
|
||||
"prefs.quality_smoother": "更平滑",
|
||||
"prefs.quick_measure": "在视图角落显示快速测量",
|
||||
"prefs.quick_measure_note": "在视图右下角、历史记录上方显示所选对象的尺寸:边的长度(圆形边显示 Ø 和 R)、面的面积、点的位置、两个对象之间的距离和角度、多条边或多个面的总和。点击即可复制。",
|
||||
"prefs.remember_dialog_values": "记住命令对话框的值",
|
||||
"prefs.remember_dialog_values.help": "默认关闭。开启后,命令成功执行后,在关闭 SolidCraft 前,下次打开同一命令时会重用其安全的值和选项。",
|
||||
"prefs.render_previous_geometry_budget": "使用先前的渲染预览几何内存预算",
|
||||
"prefs.render_previous_geometry_budget_help": "恢复先前的固定几何内存上限(64位桌面为512 MiB,其他平台为128 MiB),替代基于设备的预算。",
|
||||
"prefs.reports_saved": "已保存 {n} 份",
|
||||
@@ -2562,6 +2790,8 @@
|
||||
"prefs.ribbon_click_ends_tool_note": "单击正在使用的草图工具的高亮功能区按钮会结束该工具,在视图中单击即可再次选择。关闭:单击会重新启动该工具。",
|
||||
"prefs.ribbon_reveal": "在功能区中显示搜索结果",
|
||||
"prefs.ribbon_reveal_note": "将指针悬停在“搜索命令”的结果上时,会显示该命令在功能区中的位置:其选项卡、按钮或面板下拉菜单中的行。从搜索启动的命令会在那里闪烁,从下拉菜单运行的命令会标记其面板。",
|
||||
"prefs.ribbon_tabs_only": "仅显示功能区的选项卡",
|
||||
"prefs.ribbon_tabs_only_note": "功能区折叠为选项卡行。单击选项卡会在视图上方显示其面板,直到命令开始或指针离开面板。“视图”选项卡中的“仅功能区选项卡”或其快捷键可切换此项。",
|
||||
"prefs.right_click": "右键单击",
|
||||
"prefs.saving": "启动和保存",
|
||||
"prefs.scale_note": "也可在窗口中使用 Ctrl + / Ctrl −。100% 时按钮高度为 28 像素。",
|
||||
@@ -2633,6 +2863,7 @@
|
||||
"prefs.sketch_exit_view_note": "“完成草图”、取消或连按两次 Esc 后视图的去向。",
|
||||
"prefs.sketch_face_activates_part": "在零件的面上绘制草图时激活该零件",
|
||||
"prefs.sketch_face_activates_part_note": "在另一个组件的实体面上开始的草图会先激活该组件,因此草图属于它,并在浏览器中留在该零件下。之后该零件保持激活。关闭时:草图属于当前激活的组件。",
|
||||
"prefs.sketch_mirror_auto_line": "将预选的构造直线用作镜像线",
|
||||
"prefs.sketch_open_look_at": "打开草图时转向正对草图",
|
||||
"prefs.sketch_open_look_at_note": "关闭:创建或编辑草图时视图保持不变。需要时用“朝向”正对草图。",
|
||||
"prefs.sketch_recent_snap": "自动吸附到最近的圆心和中点",
|
||||
@@ -3914,6 +4145,7 @@
|
||||
"render.stage_panels.extent": "范围",
|
||||
"render.stage_panels.fit_save": "适配并保存",
|
||||
"render.stage_panels.gpu_required": "需要 GPU 路径追踪",
|
||||
"render.stage_panels.grain_angle": "木纹角度",
|
||||
"render.stage_panels.keep": "保留",
|
||||
"render.stage_panels.kept": "保留",
|
||||
"render.stage_panels.kept_fields": "相机、采样、图像尺寸、光学绑定、CAD 几何体和工程属性。",
|
||||
@@ -4032,6 +4264,19 @@
|
||||
"render.subsurface_enable": "内部散射随机游走",
|
||||
"render.subsurface_help": "仅适用于封闭介质。散射和RGB吸收使用独立的每毫米系数。路径在实际几何内部散射,并在边界折射或反射。关闭直线透射阴影、表面降噪和外部体积;使用一个菲涅耳分层。这不是扩散着色器、密度网格或实测材料。高密度可能需要更多样本和路径深度。",
|
||||
"render.subsurface_rate": "散射碰撞率",
|
||||
"render.surface_import.author": "作者",
|
||||
"render.surface_import.busy": "正在导入图像…",
|
||||
"render.surface_import.complete": "请选择图像并填写作者、来源和许可。",
|
||||
"render.surface_import.embedded": "导入的图像",
|
||||
"render.surface_import.failed": "图像导入未完成。",
|
||||
"render.surface_import.license": "许可 / 所有权",
|
||||
"render.surface_import.raytrace_required": "图像需要 CPU 或 GPU 光线追踪。请在输出中选择。",
|
||||
"render.surface_import.repeat_u": "U 方向重复",
|
||||
"render.surface_import.repeat_v": "V 方向重复",
|
||||
"render.surface_import.save": "将图像保存到实体",
|
||||
"render.surface_import.saved_cpu": "保存贴图以用于 CPU 渲染。",
|
||||
"render.surface_import.source": "来源",
|
||||
"render.surface_import.title": "导入图像",
|
||||
"render.tone_aces_fitted": "ACES 拟合",
|
||||
"render.tone_reinhard": "Reinhard",
|
||||
"render.transmission_scope": "GPU 玻璃支持经过验证且互不相交的介质;不支持任意嵌套介质,也不提供无噪声透射估计器。",
|
||||
@@ -4108,6 +4353,8 @@
|
||||
"sample.plate.name": "示例板",
|
||||
"sample.vents.about": "按矩形阵列排列的槽:行数、列数和间距均为参数",
|
||||
"sample.vents.name": "通风板",
|
||||
"scale.per_axis": "按轴",
|
||||
"scale.uniform": "等比",
|
||||
"search.command_keys": "Enter 运行 Shift+Enter 运行并保持打开 Esc 关闭",
|
||||
"search.commands": "命令",
|
||||
"search.design": "此设计中",
|
||||
@@ -4780,6 +5027,7 @@
|
||||
"ui.click_selects_whole_bodies": "单击选择整个实体",
|
||||
"ui.click_then_press_key": "单击,然后按新按键",
|
||||
"ui.close": "关闭",
|
||||
"ui.close_tab": "关闭标签页",
|
||||
"ui.coil": "螺旋",
|
||||
"ui.coincident": "重合",
|
||||
"ui.collapse": "折叠",
|
||||
@@ -4997,6 +5245,11 @@
|
||||
"ui.factor": "系数",
|
||||
"ui.fade": "收尾",
|
||||
"ui.fade_length": "收尾长度",
|
||||
"ui.fair_iterations": "迭代次数",
|
||||
"ui.fair_keep_edges": "保持边",
|
||||
"ui.fair_strength": "强度",
|
||||
"ui.fair_surface_title": "曲面光顺",
|
||||
"ui.fair_tolerance_default": "实体尺寸的 0.001 倍",
|
||||
"ui.feature_history": "特征历史",
|
||||
"ui.feature_options": "特征选项",
|
||||
"ui.features": "特征",
|
||||
@@ -5182,6 +5435,7 @@
|
||||
"ui.inspect": "检查",
|
||||
"ui.instances": "实例数",
|
||||
"ui.intersect": "相交",
|
||||
"ui.intersection_curve_second_pick": "相交曲线:单击第二个面或曲线(右键单击重新开始)",
|
||||
"ui.iso_4753": "ISO 4753",
|
||||
"ui.isolate": "隔离",
|
||||
"ui.join": "连接",
|
||||
@@ -5210,6 +5464,7 @@
|
||||
"ui.manipulator_free_alt": "操纵器自由拖动也适用于 Alt/Option",
|
||||
"ui.manipulator_free_alt_tip": "开:在拉伸/小控件箭头上按 Alt/Option 或 Ctrl/Cmd 可关闭数值捕捉。关:仅 Ctrl/Cmd。",
|
||||
"ui.manufacture": "制造",
|
||||
"ui.match_surface_title": "曲面匹配",
|
||||
"ui.material_review.axes": "材料局部坐标轴;不指定实体方向。",
|
||||
"ui.material_review.claims": "来源和许可证声明未经核实。缺失值保持未知。",
|
||||
"ui.material_review.compressive_strength": "抗压强度",
|
||||
@@ -5232,6 +5487,9 @@
|
||||
"ui.material_review.title": "检查物理材料数据",
|
||||
"ui.material_review.unknown": "未知",
|
||||
"ui.material_review.yield_strength": "屈服强度",
|
||||
"ui.mcp_agents": "通过 MCP 连接的代理",
|
||||
"ui.mcp_compact": "紧凑输出",
|
||||
"ui.mcp_compact_note": "适用于通过 MCP 服务器 (solidcraft-cli mcp) 连接的代理:inspect_design 汇总草图,工具结果不做美化排版,从而占用更少的 token。更改后新开始的代理会话生效;--compact 或 SOLIDCRAFT_MCP_COMPACT=1 也会开启它。",
|
||||
"ui.measure": "测量",
|
||||
"ui.mesh_format_3mf": "3MF",
|
||||
"ui.mesh_format_3mf_tagged": "带面标签的 3MF",
|
||||
@@ -5273,6 +5531,7 @@
|
||||
"ui.options": "选项",
|
||||
"ui.orbit": "环绕",
|
||||
"ui.orbit_around": "环绕",
|
||||
"ui.organize": "整理",
|
||||
"ui.orientation": "定向",
|
||||
"ui.origin": "原点",
|
||||
"ui.palette_construction_mode": "构造模式 (X)",
|
||||
@@ -5280,6 +5539,65 @@
|
||||
"ui.palette_sketch_options": "草图选项",
|
||||
"ui.pan": "平移",
|
||||
"ui.pan_zoom_orbit": "平移、缩放、环绕",
|
||||
"ui.panel_fixture.apply": "应用",
|
||||
"ui.panel_fixture.cancel": "取消",
|
||||
"ui.panel_fixture.caption": "合成面板示例",
|
||||
"ui.panel_fixture.draft": "保留的草稿",
|
||||
"ui.panel_fixture.editor": "示例编辑器",
|
||||
"ui.panel_fixture.explanation": "此合成内容在隐藏或移动时保留草稿。",
|
||||
"ui.panel_fixture.fill": "填充高度示例",
|
||||
"ui.panel_fixture.finish": "完成",
|
||||
"ui.panel_fixture.finished": "合成事务已结束",
|
||||
"ui.panel_fixture.footer": "示例操作不会更改设计。",
|
||||
"ui.panel_fixture.guard": "事务活动期间拒绝取消",
|
||||
"ui.panel_fixture.inline": "单行示例",
|
||||
"ui.panel_fixture.live": "合成事务活动中",
|
||||
"ui.panel_fixture.log": "示例日志",
|
||||
"ui.panel_fixture.pin": "固定示例",
|
||||
"ui.panel_fixture.title": "{title} · {id}",
|
||||
"ui.panel_fixture.updates": "服务更新:{count}",
|
||||
"ui.panel_host.attention": "需要注意",
|
||||
"ui.panel_host.bottom": "底部",
|
||||
"ui.panel_host.close": "关闭面板",
|
||||
"ui.panel_host.collapse": "折叠面板",
|
||||
"ui.panel_host.expand": "展开面板",
|
||||
"ui.panel_host.floating": "浮动",
|
||||
"ui.panel_host.focus": "聚焦面板",
|
||||
"ui.panel_host.focus_target": "聚焦{title}",
|
||||
"ui.panel_host.header": "面板标题栏",
|
||||
"ui.panel_host.join": "合并标签页",
|
||||
"ui.panel_host.join_target": "与{title}合并标签页",
|
||||
"ui.panel_host.left": "左侧",
|
||||
"ui.panel_host.menu": "面板操作",
|
||||
"ui.panel_host.move": "移动到",
|
||||
"ui.panel_host.overflow": "更多面板",
|
||||
"ui.panel_host.popout": "在窗口中打开",
|
||||
"ui.panel_host.resize": "调整面板大小",
|
||||
"ui.panel_host.right": "右侧",
|
||||
"ui.panel_host.rule.availability": "此面板不可用。",
|
||||
"ui.panel_host.rule.bounds": "面板不符合尺寸限制。",
|
||||
"ui.panel_host.rule.capability": "此宿主不支持该操作。",
|
||||
"ui.panel_host.rule.collection_limit": "面板布局已达到上限。",
|
||||
"ui.panel_host.rule.duplicate_id": "此面板标识已被使用。",
|
||||
"ui.panel_host.rule.exactly_once": "每个面板必须出现一次。",
|
||||
"ui.panel_host.rule.feature_disabled": "此面板已禁用该操作。",
|
||||
"ui.panel_host.rule.id": "面板标识无效。",
|
||||
"ui.panel_host.rule.incompatible_tabs": "这些面板无法共享标签组。",
|
||||
"ui.panel_host.rule.lifecycle": "面板无法完成此操作。",
|
||||
"ui.panel_host.rule.missing_target": "此面板目标已不可用。",
|
||||
"ui.panel_host.rule.no_space": "没有足够的空间容纳此面板布局。",
|
||||
"ui.panel_host.rule.pin_conflict": "此边缘已固定另一个面板。",
|
||||
"ui.panel_host.rule.pin_order": "固定面板必须保持在其边缘的首位。",
|
||||
"ui.panel_host.rule.placement": "此面板无法移至该位置。",
|
||||
"ui.panel_host.rule.revision": "布局已更改。请重试该操作。",
|
||||
"ui.panel_host.rule.schema": "面板请求无效。",
|
||||
"ui.panel_host.rule.version": "不支持此面板布局版本。",
|
||||
"ui.panel_host.sash": "调整相邻面板大小",
|
||||
"ui.panel_host.scroll_horizontal": "滚动面板列",
|
||||
"ui.panel_host.scroll_vertical": "滚动面板堆栈",
|
||||
"ui.panel_host.surface": "面板",
|
||||
"ui.panel_host.tab": "面板标签页",
|
||||
"ui.panel_host.tabs": "所有标签页",
|
||||
"ui.parallel": "平行",
|
||||
"ui.parameters": "参数",
|
||||
"ui.part_size_needs_a_part": "请选择已插入的标准零件(螺钉、螺母、垫圈或销)以更改其尺寸。要放大或缩小实体,请使用缩放。",
|
||||
@@ -5317,6 +5635,7 @@
|
||||
"ui.point_cloud_add_filter": "添加筛选…",
|
||||
"ui.point_cloud_auto_range": "自动范围",
|
||||
"ui.point_cloud_color_by": "着色方式",
|
||||
"ui.point_cloud_dots": "独立点",
|
||||
"ui.point_cloud_file_colors": "文件颜色",
|
||||
"ui.point_cloud_filter_by": "筛选依据",
|
||||
"ui.point_cloud_height_high": "最高高度",
|
||||
|
||||
+192
-31
@@ -16,6 +16,9 @@ struct U {
|
||||
ana_c: array<vec4<f32>, 8>,
|
||||
ana_f: vec4<f32>, // x = 1: stripe the negative draft and blocked faces
|
||||
depth: vec4<f32>, // x = 1: reversed depth (1 near, 0 far); y, z: the faces' push (`FACE_BIAS`)
|
||||
// Where the drawn geometry is moved in the world: identity, or a dragged component's move
|
||||
// from where its scene was built (`MovingLayer`)
|
||||
model: mat4x4<f32>,
|
||||
tint: vec4<f32>, // xyz added to a whole hovered body's colour before lighting
|
||||
sel: vec4<f32>, // xyz a whole selected body's colour before lighting
|
||||
// the section hatch colours by part (`solidcraft_render::hatch`), the first the cap's own
|
||||
@@ -25,6 +28,16 @@ struct U {
|
||||
sky: vec4<f32>, // xyz = world up; w = 1: shade in linear light with a sky fill (#7974)
|
||||
};
|
||||
|
||||
/// A scene's point in the world (see `U.model`).
|
||||
fn placed(p: vec3<f32>) -> vec3<f32> {
|
||||
return (u.model * vec4<f32>(p, 1.0)).xyz;
|
||||
}
|
||||
|
||||
/// A scene's direction in the world (see `placed`).
|
||||
fn turned(n: vec3<f32>) -> vec3<f32> {
|
||||
return (u.model * vec4<f32>(n, 0.0)).xyz;
|
||||
}
|
||||
|
||||
fn reversed() -> bool {
|
||||
return u.depth.x > 0.5;
|
||||
}
|
||||
@@ -83,10 +96,11 @@ struct TOut {
|
||||
};
|
||||
|
||||
@vertex
|
||||
fn vs_tri(@location(0) p: vec3<f32>, @location(1) n: vec3<f32>, @location(2) c: u32) -> TOut {
|
||||
fn vs_tri(@location(0) local: vec3<f32>, @location(1) n: vec3<f32>, @location(2) c: u32) -> TOut {
|
||||
var o: TOut;
|
||||
let p = placed(local);
|
||||
o.pos = u.vp * vec4<f32>(p, 1.0);
|
||||
o.n = n;
|
||||
o.n = turned(n);
|
||||
let e = palette_entry(c);
|
||||
o.c = unpack_color(e.x);
|
||||
o.h = e.y;
|
||||
@@ -95,7 +109,8 @@ fn vs_tri(@location(0) p: vec3<f32>, @location(1) n: vec3<f32>, @location(2) c:
|
||||
}
|
||||
|
||||
/// Shared triangles (in their part's own frame) drawn at one placement: the rows of its rigid
|
||||
/// move (`r0`..`r2`, a point's coordinates are their dot products with (p, 1)) and its colour.
|
||||
/// move (`r0`..`r2`, a point's coordinates are their dot products with (p, 1)) and its colour,
|
||||
/// then moved like the rest of its scene (`placed`: a dragged component's layer).
|
||||
/// The vertex colour (location 2) is not read.
|
||||
@vertex
|
||||
fn vs_tri_inst(
|
||||
@@ -107,10 +122,11 @@ fn vs_tri_inst(
|
||||
@location(6) c: u32,
|
||||
) -> TOut {
|
||||
let q = vec4<f32>(p, 1.0);
|
||||
let wp = vec3<f32>(dot(r0, q), dot(r1, q), dot(r2, q));
|
||||
let wp = placed(vec3<f32>(dot(r0, q), dot(r1, q), dot(r2, q)));
|
||||
let pn = vec3<f32>(dot(r0.xyz, n), dot(r1.xyz, n), dot(r2.xyz, n));
|
||||
var o: TOut;
|
||||
o.pos = u.vp * vec4<f32>(wp, 1.0);
|
||||
o.n = vec3<f32>(dot(r0.xyz, n), dot(r1.xyz, n), dot(r2.xyz, n));
|
||||
o.n = turned(pn);
|
||||
let e = palette_entry(c);
|
||||
o.c = unpack_color(e.x);
|
||||
o.h = e.y;
|
||||
@@ -140,7 +156,7 @@ fn vs_tri_inst_flat(
|
||||
@location(6) c: u32,
|
||||
) -> FOut {
|
||||
let q = vec4<f32>(p, 1.0);
|
||||
let wp = vec3<f32>(dot(r0, q), dot(r1, q), dot(r2, q));
|
||||
let wp = placed(vec3<f32>(dot(r0, q), dot(r1, q), dot(r2, q)));
|
||||
var o: FOut;
|
||||
o.pos = u.vp * vec4<f32>(wp, 1.0);
|
||||
o.rp = wp - u.eye.xyz;
|
||||
@@ -785,7 +801,9 @@ fn lifted_z(p: vec3<f32>, c: vec4<f32>, px: f32, half: vec2<f32>) -> f32 {
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vs_line(@builtin(vertex_index) vi: u32, @location(0) a: vec3<f32>, @location(1) b: vec3<f32>, @location(2) c: u32, @location(3) wbits: u32) -> LOut {
|
||||
fn vs_line(@builtin(vertex_index) vi: u32, @location(0) local_a: vec3<f32>, @location(1) local_b: vec3<f32>, @location(2) c: u32, @location(3) wbits: u32) -> LOut {
|
||||
let a = placed(local_a);
|
||||
let b = placed(local_b);
|
||||
var pa = a;
|
||||
var pb = b;
|
||||
var ca = u.vp * vec4<f32>(a, 1.0);
|
||||
@@ -832,7 +850,7 @@ fn vs_line(@builtin(vertex_index) vi: u32, @location(0) a: vec3<f32>, @location(
|
||||
var z = base.z;
|
||||
if (lifted && base.w > 1e-6) {
|
||||
let p = select(pa, pb, t > 0.5);
|
||||
let g = grazing(p, octahedral(wbits & 4095u), octahedral((wbits >> 12u) & 4095u));
|
||||
let g = grazing(p, turned(octahedral(wbits & 4095u)), turned(octahedral((wbits >> 12u) & 4095u)));
|
||||
z = lifted_z(p, base, wd * 0.5 * g + 1.0, half);
|
||||
}
|
||||
var o: LOut;
|
||||
@@ -883,10 +901,10 @@ fn fs_line_all(i: LOut) -> @location(0) vec4<f32> {
|
||||
return out_color(i.c);
|
||||
}
|
||||
|
||||
// Point clouds (`view/clouds.rs`): 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 cloud's style colours them by their own colour, a ramp or a palette,
|
||||
// and shows only those whose filtered values lie in its ranges or sets.
|
||||
// Point clouds and their cells (`view/clouds.rs`): dots, segments as beads (or lines when they
|
||||
// have no size) and triangles, from one record per element. The cloud's style colours them by
|
||||
// their own colour, a ramp or a palette, and shows only those whose filtered values lie in its
|
||||
// ranges or sets; values vary across an element, so a range cuts it where its value crosses an end.
|
||||
|
||||
struct Cloud {
|
||||
style: vec4<f32>, // x: colour (0 own, 1 ramp, 2 palette), y: dot size (px), z, w: ramp range
|
||||
@@ -895,43 +913,88 @@ struct Cloud {
|
||||
@group(1) @binding(3) var<uniform> cloud: Cloud;
|
||||
|
||||
struct CIn {
|
||||
@location(0) p: vec3<f32>,
|
||||
@location(1) v: f32,
|
||||
@location(2) f: vec4<f32>,
|
||||
@location(3) rgb: vec4<f32>,
|
||||
@location(0) a: vec3<f32>,
|
||||
@location(1) b: vec3<f32>,
|
||||
@location(2) c: vec3<f32>,
|
||||
@location(8) before: vec3<f32>,
|
||||
@location(9) after: vec3<f32>,
|
||||
@location(3) v: vec3<f32>,
|
||||
@location(4) f0: vec3<f32>,
|
||||
@location(5) f1: vec3<f32>,
|
||||
@location(10) f2: vec3<f32>,
|
||||
@location(11) f3: vec3<f32>,
|
||||
@location(6) size: vec2<f32>,
|
||||
@location(7) rgb: vec4<f32>,
|
||||
};
|
||||
|
||||
struct COut {
|
||||
@builtin(position) pos: vec4<f32>,
|
||||
@location(0) wp: vec3<f32>,
|
||||
@location(1) v: f32,
|
||||
@location(2) f: vec4<f32>,
|
||||
@location(3) rgb: vec3<f32>,
|
||||
@location(4) at: vec2<f32>, // the place on its disc
|
||||
@location(5) @interpolate(flat) has: u32,
|
||||
@location(1) n: vec3<f32>, // zero: not lit
|
||||
@location(2) v: f32,
|
||||
@location(3) f: vec4<f32>,
|
||||
@location(4) rgb: vec3<f32>,
|
||||
@location(5) at: vec2<f32>, // a dot's place on its disc
|
||||
@location(6) @interpolate(flat) has: u32,
|
||||
};
|
||||
|
||||
fn cloud_out(i: CIn, w: vec3<f32>, wp: vec3<f32>, pos: vec4<f32>, n: vec3<f32>) -> COut {
|
||||
var o: COut;
|
||||
o.pos = pos;
|
||||
o.wp = wp;
|
||||
o.n = n;
|
||||
o.v = dot(w, i.v);
|
||||
o.f = vec4<f32>(dot(w, i.f0), dot(w, i.f1), dot(w, i.f2), dot(w, i.f3));
|
||||
o.rgb = i.rgb.rgb;
|
||||
o.at = vec2<f32>(0.0);
|
||||
o.has = u32(round(i.rgb.a * 255.0));
|
||||
return o;
|
||||
}
|
||||
|
||||
fn behind(c: vec4<f32>) -> bool {
|
||||
return near_z(c) < 0.0;
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vs_cloud_dot(@builtin(vertex_index) vi: u32, i: CIn) -> COut {
|
||||
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(cloud.style.y, 1.0);
|
||||
let centre = u.vp * vec4<f32>(i.p, 1.0);
|
||||
var o: COut;
|
||||
o.pos = vec4<f32>(centre.xy + at * size / u.screen.xy * 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.wp = i.p;
|
||||
o.v = i.v;
|
||||
o.f = i.f;
|
||||
o.rgb = i.rgb.rgb;
|
||||
o.has = u32(round(i.rgb.a * 255.0));
|
||||
let centre = u.vp * vec4<f32>(i.a, 1.0);
|
||||
var pos = vec4<f32>(centre.xy + at * size / u.screen.xy * centre.w, centre.z, centre.w);
|
||||
if (behind(centre)) { pos = vec4<f32>(0.0, 0.0, 0.0, -1.0); }
|
||||
var o = cloud_out(i, vec3<f32>(1.0, 0.0, 0.0), i.a, pos, vec3<f32>(0.0));
|
||||
// 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;
|
||||
}
|
||||
|
||||
/// The least half-width of a segment on screen, in pixels.
|
||||
const MIN_HALF_PX: f32 = 0.75;
|
||||
|
||||
/// The frame of a bead along `d`: along it, across it level, and up, its height axis.
|
||||
fn bead_frame(d: vec3<f32>) -> mat3x3<f32> {
|
||||
var t = vec3<f32>(1.0, 0.0, 0.0);
|
||||
if (length(d) > 1e-9) { t = d / length(d); }
|
||||
var side = cross(vec3<f32>(0.0, 0.0, 1.0), t);
|
||||
if (length(side) < 1e-6) { side = vec3<f32>(1.0, 0.0, 0.0); }
|
||||
side = normalize(side);
|
||||
return mat3x3<f32>(t, side, cross(t, side));
|
||||
}
|
||||
|
||||
/// Corner `j` (0…9) of a bead's cross-section, a stadium: its place across and up, and its
|
||||
/// normal. Corners 0…4 go up the right half-disc, 5…9 down the left one, so 4–5 is the flat top
|
||||
/// and 9–0 the flat bottom.
|
||||
fn stadium(j: u32, size: vec2<f32>) -> vec4<f32> {
|
||||
let k = j % 10u;
|
||||
let right = k < 5u;
|
||||
let a = -1.5707963 + 0.7853982 * f32(k % 5u) + select(3.1415927, 0.0, right);
|
||||
let r = 0.5 * size.y;
|
||||
let flat_half = 0.5 * max(size.x - size.y, 0.0);
|
||||
return vec4<f32>(select(-flat_half, flat_half, right) + r * cos(a), r * sin(a), cos(a), sin(a));
|
||||
}
|
||||
|
||||
/// Whether filter `f` keeps the value `v`.
|
||||
fn cloud_keeps(f: vec4<f32>, v: f32) -> bool {
|
||||
if (f.x < 0.5) { return true; }
|
||||
@@ -940,6 +1003,101 @@ fn cloud_keeps(f: vec4<f32>, v: f32) -> bool {
|
||||
return k >= 0 && k < 32 && ((bitcast<u32>(f.w) >> u32(k)) & 1u) != 0u;
|
||||
}
|
||||
|
||||
/// Whether filter `f` leaves out a whole element with values `v` at its corners: all on one
|
||||
/// side of a range, or one value at every corner, not kept. A set of values with corners that
|
||||
/// differ may keep part of the element: its fragments decide.
|
||||
fn cloud_drops(f: vec4<f32>, v: vec3<f32>) -> bool {
|
||||
if (f.x > 1.5) { return all(v == vec3<f32>(v.x)) && !cloud_keeps(f, v.x); }
|
||||
return f.x > 0.5 && (all(v < vec3<f32>(f.y)) || all(v > vec3<f32>(f.z)));
|
||||
}
|
||||
|
||||
/// Whether a filter leaves out the whole element.
|
||||
fn filtered_out(i: CIn) -> bool {
|
||||
let has = u32(round(i.rgb.a * 255.0));
|
||||
let values = array<vec3<f32>, 4>(i.f0, i.f1, i.f2, i.f3);
|
||||
for (var k = 0u; k < 4u; k++) {
|
||||
if (((has >> k) & 1u) != 0u && cloud_drops(cloud.filters[k], values[k])) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// The part in front of the near plane of the segment between clip-space `a` and `b`, as
|
||||
/// fractions of it from `a`; empty (first above second) when all of it is behind. Clip-space
|
||||
/// coordinates are affine along a line, so the crossing is exact.
|
||||
fn in_front(a: vec4<f32>, b: vec4<f32>) -> vec2<f32> {
|
||||
let za = near_z(a);
|
||||
let zb = near_z(b);
|
||||
if (za < 0.0 && zb < 0.0) { return vec2<f32>(1.0, 0.0); }
|
||||
let t = za / (za - zb);
|
||||
return vec2<f32>(select(0.0, t, za < 0.0), select(1.0, t, zb < 0.0));
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vs_cloud_segment(@builtin(vertex_index) vi: u32, i: CIn) -> COut {
|
||||
// Corner `vi % 10` of the cross-section at the start (`vi < 10`) or the end; the index
|
||||
// buffer joins them into the bead's sides.
|
||||
let pa = u.vp * vec4<f32>(i.a, 1.0);
|
||||
let pb = u.vp * vec4<f32>(i.b, 1.0);
|
||||
let part = in_front(pa, pb);
|
||||
if (filtered_out(i) || part.x > part.y) {
|
||||
return cloud_out(i, vec3<f32>(1.0, 0.0, 0.0), i.a, vec4<f32>(0.0, 0.0, 0.0, -1.0), vec3<f32>(0.0));
|
||||
}
|
||||
// Where the near plane cuts it, the bead ends there, square; its numbers are exact at `e`.
|
||||
let at_end = vi >= 10u;
|
||||
let e = select(part.x, part.y, at_end);
|
||||
let cut = e != select(0.0, 1.0, at_end);
|
||||
let f = bead_frame(i.b - i.a);
|
||||
let c = stadium(vi % 10u, i.size);
|
||||
let axis = mix(i.a, i.b, e);
|
||||
// Where it meets the next or the previous bead, the cross-section lies in the plane halfway
|
||||
// between their directions, stretched across to keep its width: both beads end on the same
|
||||
// section. A turn sharper than 120° ends square instead.
|
||||
let other = select(i.a - i.before, i.after - i.b, at_end);
|
||||
var across_dir = f[1];
|
||||
var stretch = 1.0;
|
||||
if (!cut && length(other) > 1e-9) {
|
||||
let g = bead_frame(other);
|
||||
let mid = f[1] + g[1];
|
||||
if (length(mid) > 1e-6 && dot(normalize(mid), f[1]) > 0.5) {
|
||||
across_dir = normalize(mid);
|
||||
stretch = 1.0 / dot(across_dir, f[1]);
|
||||
}
|
||||
}
|
||||
let wp = axis + across_dir * (c.x * stretch) + f[2] * c.y;
|
||||
let ca = u.vp * vec4<f32>(axis, 1.0);
|
||||
let cw = u.vp * vec4<f32>(wp, 1.0);
|
||||
let half = u.screen.xy * 0.5;
|
||||
var pos = cw;
|
||||
// At least a pixel and a half across: a bead seen from afar, or a line without size. A corner
|
||||
// behind the eye has no place on the screen and needs no widening: the rasteriser clips it.
|
||||
if (cw.w > 0.0) {
|
||||
let c0 = mix(pa, pb, part.x);
|
||||
let c1 = mix(pa, pb, part.y);
|
||||
let along = (c1.xy / c1.w - c0.xy / c0.w) * half;
|
||||
var across = vec2<f32>(-along.y, along.x);
|
||||
if (length(across) < 1e-6) { across = vec2<f32>(1.0, 0.0); }
|
||||
across = normalize(across);
|
||||
var px = (cw.xy / cw.w - ca.xy / ca.w) * half;
|
||||
if (length(px) < MIN_HALF_PX * abs(c.z)) {
|
||||
px = across * c.z * MIN_HALF_PX;
|
||||
pos = vec4<f32>((ca.xy / ca.w + px / half) * cw.w, cw.z, cw.w);
|
||||
}
|
||||
}
|
||||
let n = select(f[1] * c.z + f[2] * c.w, vec3<f32>(0.0), i.size.y <= 0.0);
|
||||
return cloud_out(i, vec3<f32>(1.0 - e, e, 0.0), wp, pos, n);
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vs_cloud_triangle(@builtin(vertex_index) vi: u32, i: CIn) -> COut {
|
||||
// Not `w[vi] = 1.0`: the Windows D3D12 shader compiler cannot write through a dynamic vector index.
|
||||
let w = vec3<f32>(f32(vi == 0u), f32(vi == 1u), f32(vi == 2u));
|
||||
let wp = i.a * w.x + i.b * w.y + i.c * w.z;
|
||||
var n = cross(i.b - i.a, i.c - i.a);
|
||||
if (length(n) > 0.0) { n = normalize(n); }
|
||||
// The true clip-space corner, even behind the eye: the rasteriser clips a triangle exactly.
|
||||
return cloud_out(i, w, wp, u.vp * vec4<f32>(wp, 1.0), n);
|
||||
}
|
||||
|
||||
fn cloud_ramp(t: f32) -> vec3<f32> {
|
||||
var stops = array<vec3<f32>, 5>(
|
||||
vec3<f32>(40.0, 70.0, 200.0), vec3<f32>(30.0, 170.0, 230.0), vec3<f32>(60.0, 200.0, 90.0),
|
||||
@@ -974,5 +1132,8 @@ fn fs_cloud(o: COut) -> @location(0) vec4<f32> {
|
||||
} else if ((o.has & 16u) != 0u && mode > 0.5) {
|
||||
c = cloud_ramp((o.v - cloud.style.z) / max(cloud.style.w - cloud.style.z, 1e-12));
|
||||
}
|
||||
if (length(o.n) > 0.5) {
|
||||
c = c * (0.45 + 0.55 * abs(dot(normalize(o.n), normalize(u.back.xyz))));
|
||||
}
|
||||
return out_color(vec4<f32>(c, 1.0));
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
| assets/locales/pending/ribbon-panel-hover.json | SolidCraft contributors | Original ribbon panel menu preference labels and translations | MIT OR Apache-2.0 |
|
||||
@@ -210,13 +210,16 @@ fn map_plane_ref(m: &Mat, p: &mut PlaneRef) {
|
||||
map_plane_ref(m, base);
|
||||
map_geo_ref(m, point);
|
||||
}
|
||||
PlaneRef::AtAngle { base, axis_origin, axis_dir, line, .. } => {
|
||||
PlaneRef::AtAngle { base, axis_origin, axis_dir, line, pivot, .. } => {
|
||||
map_plane_ref(m, base);
|
||||
*axis_origin = apply_point(m, *axis_origin);
|
||||
*axis_dir = apply_vector(m, *axis_dir);
|
||||
if let Some(g) = line {
|
||||
map_geo_ref(m, g);
|
||||
}
|
||||
if let Some(g) = pivot {
|
||||
map_geo_ref(m, g);
|
||||
}
|
||||
}
|
||||
// Named origin planes and construction planes are the component's own.
|
||||
PlaneRef::Origin { .. } | PlaneRef::Construction { .. } => {}
|
||||
@@ -353,25 +356,7 @@ impl Document {
|
||||
/// Every placement of each component: (component, occurrence path ids, world transform).
|
||||
/// A branch stops past [`MAX_PLACEMENT_DEPTH`] levels, the walk past [`MAX_PLACEMENTS`] placements.
|
||||
pub fn placements(&self) -> Vec<(u64, Vec<u64>, Mat)> {
|
||||
let mut out = vec![(0, Vec::new(), IDENTITY)];
|
||||
let mut stack: Vec<(u64, Vec<u64>, Mat)> = vec![(0, Vec::new(), IDENTITY)];
|
||||
while let Some((comp, path, m)) = stack.pop() {
|
||||
if out.len() > MAX_PLACEMENTS {
|
||||
break;
|
||||
}
|
||||
// Only this branch stops past the depth limit; the others are still walked.
|
||||
if path.len() > MAX_PLACEMENT_DEPTH {
|
||||
continue;
|
||||
}
|
||||
for o in self.occurrences.iter().filter(|o| o.parent == comp) {
|
||||
let mut p = path.clone();
|
||||
p.push(o.id);
|
||||
let w = mat_mul(&m, &o.transform);
|
||||
out.push((o.component, p.clone(), w));
|
||||
stack.push((o.component, p, w));
|
||||
}
|
||||
}
|
||||
out
|
||||
crate::occurrence_tree::OccurrenceTree::new(self).placements_with(|o| o.transform)
|
||||
}
|
||||
|
||||
/// Every body as the world shows it: its world name ("Body1", or "Body1 (Occ:2)" for a
|
||||
@@ -461,13 +446,14 @@ impl Document {
|
||||
if self.component_within(parent, component) {
|
||||
return true;
|
||||
}
|
||||
let tree = crate::occurrence_tree::OccurrenceTree::new(self);
|
||||
let mut stack = vec![component];
|
||||
let mut seen = std::collections::BTreeSet::new();
|
||||
while let Some(c) = stack.pop() {
|
||||
if !seen.insert(c) || seen.len() > MAX_PLACEMENTS {
|
||||
continue;
|
||||
}
|
||||
for o in self.occurrences.iter().filter(|o| o.parent == c) {
|
||||
for o in tree.children(c) {
|
||||
if o.component == parent {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -119,11 +119,14 @@ fn remap_plane(p: &mut PlaneRef, ids: &BTreeMap<u64, u64>, bodies: &BTreeMap<Str
|
||||
remap_plane(base, ids, bodies, depth + 1)?;
|
||||
remap_geo(point, ids, bodies, depth + 1)?;
|
||||
}
|
||||
PlaneRef::AtAngle { base, line, .. } => {
|
||||
PlaneRef::AtAngle { base, line, pivot, .. } => {
|
||||
remap_plane(base, ids, bodies, depth + 1)?;
|
||||
if let Some(g) = line {
|
||||
remap_geo(g, ids, bodies, depth + 1)?;
|
||||
}
|
||||
if let Some(g) = pivot {
|
||||
remap_geo(g, ids, bodies, depth + 1)?;
|
||||
}
|
||||
}
|
||||
PlaneRef::Face { name, anchor, .. } => {
|
||||
if let Some(name) = name {
|
||||
@@ -323,11 +326,14 @@ fn checked_plane(p: &PlaneRef, depth: usize) -> crate::Result<()> {
|
||||
}
|
||||
match p {
|
||||
PlaneRef::Offset { base, .. } | PlaneRef::Flipped { base, .. } => checked_plane(base, depth + 1)?,
|
||||
PlaneRef::AtAngle { base, line, .. } => {
|
||||
PlaneRef::AtAngle { base, line, pivot, .. } => {
|
||||
checked_plane(base, depth + 1)?;
|
||||
if let Some(g) = line {
|
||||
checked_geo(g, depth + 1)?;
|
||||
}
|
||||
if let Some(g) = pivot {
|
||||
checked_geo(g, depth + 1)?;
|
||||
}
|
||||
}
|
||||
PlaneRef::Midplane { a, b } | PlaneRef::TwoEdges { a, b } | PlaneRef::AxisPoint { axis: a, point: b } => {
|
||||
checked_geo(a, depth + 1)?;
|
||||
|
||||
@@ -185,7 +185,7 @@ fn shell_planar_format13_keeps_v12_documents_and_refuses_future() {
|
||||
}
|
||||
assert_eq!(legacy, current, "the migration preserves every prior field");
|
||||
legacy["format"] = json!(crate::format::format_string(crate::format::FORMAT + 1));
|
||||
assert!(crate::format::read(&legacy.to_string()).is_err());
|
||||
assert!(crate::format::read(&legacy.to_string()).unwrap().newer_file.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -923,9 +923,10 @@ impl PlaneRef {
|
||||
refs.push(point.as_mut());
|
||||
refs
|
||||
}
|
||||
PlaneRef::AtAngle { base, line, .. } => {
|
||||
PlaneRef::AtAngle { base, line, pivot, .. } => {
|
||||
let mut v = base.refs_mut();
|
||||
v.extend(line.as_deref_mut());
|
||||
v.extend(pivot.as_deref_mut());
|
||||
v
|
||||
}
|
||||
PlaneRef::Offset { base, .. } | PlaneRef::Flipped { base, .. } => base.refs_mut(),
|
||||
|
||||
@@ -61,6 +61,9 @@ pub enum PlaneRef {
|
||||
/// Another plane rotated about a line (in world coordinates) by an angle expression. With
|
||||
/// `line`, an edge or round face of the model ([`crate::construct::model_axis`]), the line is
|
||||
/// found there on each evaluation and `axis_origin`/`axis_dir` are where it was when picked.
|
||||
/// With `direction_only`, only the axis direction is used: it is projected onto the base
|
||||
/// (nothing of that projection is kept as geometry) and the plane turns about the line
|
||||
/// through `pivot`, or through the base origin when `pivot` is absent.
|
||||
AtAngle {
|
||||
base: Box<PlaneRef>,
|
||||
axis_origin: Vec3,
|
||||
@@ -68,6 +71,12 @@ pub enum PlaneRef {
|
||||
angle: Expr,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
line: Option<Box<GeoRef>>,
|
||||
/// Use only the axis direction (projected onto the base), not the axis as a full line.
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
direction_only: bool,
|
||||
/// Pivot for `direction_only`; absent means the base plane's origin.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pivot: Option<Box<GeoRef>>,
|
||||
},
|
||||
/// A construction plane feature (by id).
|
||||
Construction { feature: FeatureId },
|
||||
@@ -321,6 +330,12 @@ pub struct Extent {
|
||||
/// Through all bodies in the direction(s) instead of `distance`.
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
pub through_all: bool,
|
||||
/// Two Sides as older builds read it: `distance2` goes the other way from wherever the
|
||||
/// first side went, so a negative entered distance sends it back along the normal. Saved
|
||||
/// designs from before entered distances kept their sides carry it, so they rebuild as
|
||||
/// they did whatever their expressions evaluate to; new and edited extrudes leave it off.
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
pub second_follows_first: bool,
|
||||
/// Taper angle (expression; positive grows the profile).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[walk(angle = "Taper angle")]
|
||||
@@ -665,6 +680,12 @@ impl AlignFrame {
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, solidcraft_derive::Walk)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum FeatureKind {
|
||||
/// A feature a newer SolidCraft saved that this build can't read ([`crate::newer_file`]): it
|
||||
/// holds its place on the timeline, reports an error when evaluated, and goes back into the
|
||||
/// file as it came.
|
||||
NewerFileFeature {
|
||||
raw: crate::newer_file::RawFeature,
|
||||
},
|
||||
/// A recorded timeline ownership boundary. Geometry is converted from its
|
||||
/// current local frame into the destination component frame exactly once.
|
||||
BodyTransfer {
|
||||
@@ -1487,7 +1508,7 @@ pub enum FeatureKind {
|
||||
},
|
||||
/// Plastic lip (raised band) or groove (recess) along the rim face picked at `face`.
|
||||
Lip {
|
||||
face: Position,
|
||||
face: FacePick,
|
||||
#[walk(length = "Width")]
|
||||
width: Expr,
|
||||
#[walk(length = "Height")]
|
||||
@@ -2608,6 +2629,7 @@ impl FeatureKind {
|
||||
/// Short type name (timeline tooltip, `inspect`).
|
||||
pub fn type_name(&self) -> &'static str {
|
||||
match self {
|
||||
FeatureKind::NewerFileFeature { .. } => "NewerFileFeature",
|
||||
FeatureKind::BodyTransfer { .. } => "BodyTransferFeature",
|
||||
FeatureKind::Sketch { .. } => "Sketch",
|
||||
FeatureKind::Extrude { .. } => "ExtrudeFeature",
|
||||
@@ -2693,6 +2715,7 @@ impl FeatureKind {
|
||||
/// Design-file variant key (e.g. `"extrude"`, `"box"`, `"sheet_flange"`).
|
||||
pub fn kind_key(&self) -> &'static str {
|
||||
match self {
|
||||
FeatureKind::NewerFileFeature { .. } => "newer_file_feature",
|
||||
FeatureKind::BodyTransfer { .. } => "body_transfer",
|
||||
FeatureKind::Sketch { .. } => "sketch",
|
||||
FeatureKind::Extrude { .. } => "extrude",
|
||||
@@ -2855,6 +2878,7 @@ impl FeatureKind {
|
||||
/// Default name prefix (`Extrude` → `Extrude1`).
|
||||
pub fn base_name(&self) -> &'static str {
|
||||
match self {
|
||||
FeatureKind::NewerFileFeature { .. } => "Unknown",
|
||||
FeatureKind::BodyTransfer { .. } => "BodyTransfer",
|
||||
FeatureKind::Sketch { .. } => "Sketch",
|
||||
FeatureKind::Extrude { .. } => "Extrude",
|
||||
@@ -3127,6 +3151,10 @@ pub fn is_counted_body_name(name: &str) -> bool {
|
||||
/// The document.
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Document {
|
||||
/// Set when a newer SolidCraft saved the file this came from: what to tell the user, and what
|
||||
/// this build couldn't read and keeps for the next save ([`crate::newer_file`]).
|
||||
#[serde(skip)]
|
||||
pub newer_file: Option<std::sync::Arc<crate::newer_file::NewerFile>>,
|
||||
/// Named image intent and immutable embedded HDR sources. Active jobs and device state are not saved.
|
||||
#[serde(default, skip_serializing_if = "crate::render_settings::RenderSettings::is_empty")]
|
||||
pub render: std::sync::Arc<crate::render_settings::RenderSettings>,
|
||||
@@ -3488,6 +3516,7 @@ impl Document {
|
||||
|
||||
pub fn new(name: &str) -> Self {
|
||||
Document {
|
||||
newer_file: None,
|
||||
render: Default::default(),
|
||||
format: default_format(),
|
||||
format_pending: crate::format::pending_names(),
|
||||
@@ -3909,7 +3938,12 @@ impl Document {
|
||||
|
||||
/// Read a design file's JSON (any format this build knows; older ones are upgraded).
|
||||
pub fn from_json(s: &str) -> Result<Document> {
|
||||
crate::render_resources::check_json_budget(s)?;
|
||||
// A newer build's resources may not read here; the section stays out then (`newer_file`).
|
||||
if let Err(why) = crate::render_resources::check_json_budget(s).and_then(|()| crate::render_texture_resources::check_json_budget(s))
|
||||
&& !crate::format::says_newer(s)
|
||||
{
|
||||
return Err(why);
|
||||
}
|
||||
let mut d = crate::format::read(s)?;
|
||||
if d.features.len() > MAX_FEATURES || d.params.len() > MAX_PARAMS {
|
||||
return Err(DocError::Invalid("document too large".into()));
|
||||
@@ -3921,6 +3955,11 @@ impl Document {
|
||||
Ok(d)
|
||||
}
|
||||
pub fn to_json(&self) -> String {
|
||||
if self.newer_file.is_some() {
|
||||
let mut value = serde_json::to_value(self).unwrap_or_default();
|
||||
crate::newer_file::restore(self, &mut value);
|
||||
return serde_json::to_string_pretty(&value).unwrap_or_default();
|
||||
}
|
||||
serde_json::to_string_pretty(self).unwrap_or_default()
|
||||
}
|
||||
|
||||
@@ -4404,7 +4443,7 @@ impl Document {
|
||||
let b = self.resolve_plane_in(vals, base, depth + 1, st)?;
|
||||
Ok(b.offset(Self::eval_in(vals, distance, Kind::Length)?))
|
||||
}
|
||||
PlaneRef::AtAngle { base, axis_origin, axis_dir, angle, line } => {
|
||||
PlaneRef::AtAngle { base, axis_origin, axis_dir, angle, line, direction_only, pivot } => {
|
||||
let b = self.resolve_plane_in(vals, base, depth + 1, st)?;
|
||||
let a = Self::eval_in(vals, angle, Kind::Angle)?;
|
||||
let (axis_origin, axis_dir) = match line {
|
||||
@@ -4414,7 +4453,26 @@ impl Document {
|
||||
}
|
||||
None => (*axis_origin, *axis_dir),
|
||||
};
|
||||
let d = axis_dir.normalized().ok_or_else(|| DocError::Invalid("rotation axis".into()))?;
|
||||
let (axis_origin, d) = if *direction_only {
|
||||
let n = b.normal();
|
||||
let in_plane = axis_dir - n * n.dot(axis_dir);
|
||||
let d = in_plane
|
||||
.normalized()
|
||||
.ok_or_else(|| DocError::Invalid("the axis is square to the plane, so it has no direction in the plane".into()))?;
|
||||
let pivot = match pivot {
|
||||
Some(g) => {
|
||||
let st = st.ok_or_else(|| DocError::Invalid("this plane is built from the model: evaluate first".into()))?;
|
||||
crate::construct::point(&crate::construct::Ctx { doc: self, vals, st, depth }, g)?
|
||||
}
|
||||
None => b.origin,
|
||||
};
|
||||
// Keep the pivot in the base so a slightly off pick still turns about an in-plane line.
|
||||
let pivot = pivot - n * n.dot(pivot - b.origin);
|
||||
(pivot, d)
|
||||
} else {
|
||||
let d = axis_dir.normalized().ok_or_else(|| DocError::Invalid("rotation axis".into()))?;
|
||||
(axis_origin, d)
|
||||
};
|
||||
let rot = |v: Vec3| v * a.cos() + d.cross(v) * a.sin() + d * (d.dot(v) * (1.0 - a.cos()));
|
||||
let o = axis_origin + rot(b.origin - axis_origin);
|
||||
Plane::new(o, rot(b.x), rot(b.y)).ok_or_else(|| DocError::Invalid("degenerate plane".into()))
|
||||
|
||||
+125
-3
@@ -84,6 +84,65 @@ pub struct Drawing {
|
||||
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>>,
|
||||
/// Set when a newer SolidCraft saved the file this came from.
|
||||
#[serde(skip)]
|
||||
pub newer_file: Option<Arc<NewerDrawing>>,
|
||||
}
|
||||
|
||||
/// The fields a drawing's record has: anything else in a file is a newer build's.
|
||||
const RECORD_FIELDS: [&str; 8] = ["format", "format_pending", "name", "sources", "properties", "units", "language", "sheets"];
|
||||
|
||||
/// Move the elements of the list `key` that don't read as `T` out of a newer build's drawing.
|
||||
fn take_unread<T: serde::de::DeserializeOwned>(top: &mut serde_json::Map<String, Value>, key: &str, one: &str, file: &mut NewerDrawing) {
|
||||
let Some(Value::Array(list)) = top.get_mut(key) else { return };
|
||||
let mut readable = Vec::with_capacity(list.len());
|
||||
for element in std::mem::take(list) {
|
||||
match DRAWING.check_numbers(&element, "properties").and_then(|()| serde_json::from_value::<T>(element.clone()).map_err(damaged)) {
|
||||
Ok(_) => readable.push(element),
|
||||
Err(why) => {
|
||||
let name = element.get("name").or(element.get("path")).and_then(Value::as_str).unwrap_or("unnamed");
|
||||
file.not_understood.push(format!("{one} `{name}` ({why})"));
|
||||
file.elements.push((key.to_string(), element));
|
||||
}
|
||||
}
|
||||
}
|
||||
*list = readable;
|
||||
}
|
||||
|
||||
/// What a drawing file says and holds that this build doesn't read ([`Drawing::from_newer`]).
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct NewerDrawing {
|
||||
/// The number the file says.
|
||||
pub format: u32,
|
||||
/// The number this build reads up to.
|
||||
pub reads_up_to: u32,
|
||||
/// The changes the file names that this build doesn't have.
|
||||
pub unknown_changes: Vec<String>,
|
||||
/// What this build couldn't read, by name.
|
||||
pub not_understood: Vec<String>,
|
||||
fields: Vec<(String, Value)>,
|
||||
elements: Vec<(String, Value)>,
|
||||
}
|
||||
|
||||
impl NewerDrawing {
|
||||
/// What to tell the user.
|
||||
pub fn notice(&self) -> String {
|
||||
format!(
|
||||
"Saved by a newer SolidCraft (format {}, this version reads up to {}). If something's missing, update.",
|
||||
self.format, self.reads_up_to
|
||||
)
|
||||
}
|
||||
|
||||
fn restore(&self, value: &mut Value) {
|
||||
for (key, field) in &self.fields {
|
||||
value[key] = field.clone();
|
||||
}
|
||||
for (key, element) in &self.elements {
|
||||
if let Some(list) = value.get_mut(key).and_then(Value::as_array_mut) {
|
||||
list.push(element.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A design the drawing draws.
|
||||
@@ -175,6 +234,7 @@ impl Drawing {
|
||||
units: Units::default(),
|
||||
language: language.into(),
|
||||
sheets: vec![Arc::new(first)],
|
||||
newer_file: None,
|
||||
};
|
||||
drawing.validate()?;
|
||||
Ok(drawing)
|
||||
@@ -188,11 +248,65 @@ impl Drawing {
|
||||
}
|
||||
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)?;
|
||||
if let Some(newer) = DRAWING.newer(version, applied.as_deref()) {
|
||||
return Self::from_newer(v, version, applied, newer);
|
||||
}
|
||||
let d = Self::decode(v, version, applied.as_deref())?;
|
||||
Ok(Drawing { format: DRAWING.format_string(DRAWING.current()), format_pending: DRAWING.pending_names(), ..d })
|
||||
}
|
||||
|
||||
fn decode(mut v: Value, version: u32, applied: Option<&[String]>) -> Result<Drawing> {
|
||||
DRAWING.check_numbers(&v, "properties")?;
|
||||
DRAWING.upgrade(&mut v, version, applied.as_deref())?;
|
||||
DRAWING.upgrade(&mut v, version, applied)?;
|
||||
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 })
|
||||
Ok(d)
|
||||
}
|
||||
|
||||
/// A drawing a newer build saved: it opens with what this build can read of it. Everything in a
|
||||
/// drawing refuses a field it doesn't know, so what it can't read shows as a top-level field,
|
||||
/// a source or a sheet that doesn't read, which stays out and is named in
|
||||
/// [`NewerDrawing::not_understood`]; [`Drawing::to_json`] writes it all back, with the file's
|
||||
/// format. It takes at least one sheet this build can read to open.
|
||||
fn from_newer(mut v: Value, version: u32, applied: Option<Vec<String>>, newer: crate::format::Newer) -> Result<Drawing> {
|
||||
let list = applied.as_deref();
|
||||
let can_read = |v: Value| Self::decode(v, version, list).map_err(|e| e.to_string());
|
||||
let mut file = NewerDrawing {
|
||||
format: newer.format,
|
||||
reads_up_to: newer.reads_up_to,
|
||||
unknown_changes: newer.unknown_changes.clone(),
|
||||
..NewerDrawing::default()
|
||||
};
|
||||
let mut read = can_read(v.clone());
|
||||
if read.is_err() {
|
||||
let top = v.as_object_mut().ok_or_else(|| damaged("expected a JSON object"))?;
|
||||
let keys: Vec<String> = top.keys().cloned().collect();
|
||||
for key in keys {
|
||||
if !RECORD_FIELDS.contains(&key.as_str())
|
||||
&& let Some(value) = top.remove(&key)
|
||||
{
|
||||
file.not_understood.push(format!("field `{key}`"));
|
||||
file.fields.push((key, value));
|
||||
}
|
||||
}
|
||||
take_unread::<Sheet>(top, "sheets", "sheet", &mut file);
|
||||
take_unread::<Source>(top, "sources", "source", &mut file);
|
||||
read = can_read(v);
|
||||
}
|
||||
let d = read.map_err(|why| {
|
||||
DocError::Invalid(format!(
|
||||
"this drawing was saved by a newer SolidCraft (format {}, this version reads up to {}) and this version can't read it: {why}. Update SolidCraft to open it",
|
||||
newer.format, newer.reads_up_to
|
||||
))
|
||||
})?;
|
||||
let (format, format_pending) = if newer.format > newer.reads_up_to {
|
||||
(DRAWING.format_string(newer.format), applied.unwrap_or_default())
|
||||
} else {
|
||||
let mut pending = DRAWING.pending_names();
|
||||
pending.extend(newer.unknown_changes.iter().cloned());
|
||||
(DRAWING.format_string(DRAWING.current()), pending)
|
||||
};
|
||||
Ok(Drawing { format, format_pending, newer_file: Some(Arc::new(file)), ..d })
|
||||
}
|
||||
|
||||
/// The drawing's JSON: the same bytes for the same drawing. JSON longer than a read takes
|
||||
@@ -200,7 +314,15 @@ impl Drawing {
|
||||
/// 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}")))?;
|
||||
let unsaved = |e: serde_json::Error| DocError::Invalid(format!("drawing not saved: {e}"));
|
||||
let text = match &self.newer_file {
|
||||
None => serde_json::to_string_pretty(self).map_err(unsaved)?,
|
||||
Some(file) => {
|
||||
let mut value = serde_json::to_value(self).map_err(unsaved)?;
|
||||
file.restore(&mut value);
|
||||
serde_json::to_string_pretty(&value).map_err(unsaved)?
|
||||
}
|
||||
};
|
||||
require(text.len() <= MAX_DRAWING_BYTES, || format!("drawing too large to save (at most {} MiB)", MAX_DRAWING_BYTES >> 20))?;
|
||||
Ok(text)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ whole format bump: never edit the `DRAWING` family in `format.rs`, another chang
|
||||
|
||||
- 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).
|
||||
the last release does with a drawing that has it (it warns about the newer format number, opens what it can read and keeps the rest).
|
||||
- If older drawings must be rewritten as they open, add the step to the same file:
|
||||
|
||||
```rust
|
||||
|
||||
@@ -325,28 +325,28 @@ fn damaged_files_are_refused_and_never_patched() {
|
||||
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.
|
||||
// A newer build's file isn't held to this build's rules: a field it doesn't know, or a sheet with
|
||||
// a number out of range, stays out. With no sheet left to show, it says why.
|
||||
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"
|
||||
)
|
||||
assert_eq!(why, "read");
|
||||
let why = refused(&d, |v| {
|
||||
v["format"] = json!(DRAWING.format_string(newer));
|
||||
v["sheets"] = json!([{"id": "future", "a_future_field": true}]);
|
||||
});
|
||||
assert!(
|
||||
why.starts_with(&format!(
|
||||
"this drawing was saved by a newer SolidCraft (format {newer}, this version reads up to {current}) and this version can't read it: "
|
||||
)),
|
||||
"{why}"
|
||||
);
|
||||
// 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}");
|
||||
}
|
||||
assert_eq!(why, "read");
|
||||
// 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);
|
||||
@@ -551,3 +551,32 @@ fn mutate(v: &mut Value, pick: &mut usize, lie: &Value, how: u64) -> bool {
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// A drawing a newer build saved opens, with what it can read, and saving keeps the rest.
|
||||
#[test]
|
||||
fn a_newer_builds_drawing_opens_and_keeps_what_it_cant_read() {
|
||||
let d = plate();
|
||||
let mut file: Value = file(&d);
|
||||
assert!(file.as_object().unwrap().keys().all(|key| RECORD_FIELDS.contains(&key.as_str())), "a new field goes in RECORD_FIELDS");
|
||||
let newer = DRAWING.format_string(DRAWING.current() + 1);
|
||||
file["format"] = json!(newer);
|
||||
// With nothing it can't read: the same drawing.
|
||||
let opened = Drawing::from_json(&file.to_string()).unwrap();
|
||||
assert_eq!(opened.sheets, d.sheets);
|
||||
assert!(opened.newer_file.as_ref().unwrap().notice().starts_with("Saved by a newer SolidCraft"));
|
||||
assert_eq!(serde_json::from_str::<Value>(&opened.to_json().unwrap()).unwrap()["format"], json!(newer));
|
||||
// A field and a second sheet it can't read stay out, named, and go back.
|
||||
file["views"] = json!([{"id": "a"}]);
|
||||
let mut future_sheet = file["sheets"][0].clone();
|
||||
future_sheet["name"] = json!("Future");
|
||||
future_sheet["future_layers"] = json!([1]);
|
||||
file["sheets"].as_array_mut().unwrap().push(future_sheet);
|
||||
let opened = Drawing::from_json(&file.to_string()).unwrap();
|
||||
assert_eq!(opened.sheets, d.sheets);
|
||||
let said = &opened.newer_file.as_ref().unwrap().not_understood;
|
||||
assert!(said.iter().any(|what| what.contains("views")) && said.iter().any(|what| what.contains("Future")), "{said:?}");
|
||||
let again: Value = serde_json::from_str(&opened.to_json().unwrap()).unwrap();
|
||||
assert_eq!(again["views"], file["views"]);
|
||||
assert_eq!(again["sheets"].as_array().unwrap().len(), d.sheets.len() + 1);
|
||||
assert_eq!(again["format"], json!(newer));
|
||||
}
|
||||
|
||||
+212
-18
@@ -254,6 +254,35 @@ impl ModelBody {
|
||||
self.mesh.clone()
|
||||
}
|
||||
|
||||
/// Identity of the display cells of the solid this body shows: its own, or for a rigidly
|
||||
/// placed copy its definition's (shared by every placement of it).
|
||||
fn solid_id(&self) -> usize {
|
||||
self.mesh_placement.as_deref().map_or(Arc::as_ptr(&self.mesh) as usize, placed_mesh::PlacedMesh::definition_id)
|
||||
}
|
||||
|
||||
/// How this body is drawn moved from `before`: [`crate::IDENTITY`] when it is `before` where
|
||||
/// it stood (its meshes and plans the same), the rigid motion between them when it is the same
|
||||
/// solid placed elsewhere (a dragged component's body: `before`'s meshes and plans drawn moved
|
||||
/// show it; a definition shown where it is defined is its own placement), `None` when it is
|
||||
/// another body or its geometry differs.
|
||||
pub fn display_moved_from(&self, before: &ModelBody) -> Option<crate::Mat> {
|
||||
if self.name != before.name || self.solid_id() != before.solid_id() {
|
||||
return None;
|
||||
}
|
||||
let (now, was) = (self.mesh_placement.as_deref(), before.mesh_placement.as_deref());
|
||||
if let (Some(now), Some(was)) = (now, was)
|
||||
&& now.tolerance.to_bits() != was.tolerance.to_bits()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let at = |p: Option<&placed_mesh::PlacedMesh>| p.map_or(crate::IDENTITY, |p| *p.transform());
|
||||
let (now, was) = (at(now), at(was));
|
||||
if now == was {
|
||||
return Some(crate::IDENTITY);
|
||||
}
|
||||
Some(crate::mat_mul(&now, &crate::mat_inverse(&was)?))
|
||||
}
|
||||
|
||||
/// [`ModelBody::mesh`] as a shared mesh and the move that places it: for a rigid placement
|
||||
/// of a definition, the definition's own mesh (the same one for every placement of it) and
|
||||
/// the placement; for any other body its own mesh, unmoved. The mesh moved by the matrix is
|
||||
@@ -438,6 +467,21 @@ fn removed_note(gone: &[Vec3]) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
/// Current feature evaluation validation mode (`EachStep`, `EachFeature`, or `Off`).
|
||||
static EVAL_VALIDATION: std::cell::Cell<kernel::Validation> = const { std::cell::Cell::new(kernel::Validation::EachFeature) };
|
||||
}
|
||||
|
||||
/// Restores `EVAL_VALIDATION` to its previous value when dropped.
|
||||
struct EvalValidationGuard(kernel::Validation);
|
||||
|
||||
impl Drop for EvalValidationGuard {
|
||||
/// Restores the saved validation mode on scope exit.
|
||||
fn drop(&mut self) {
|
||||
EVAL_VALIDATION.set(self.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// A blend's refusal as too large (`why`) with the largest `size` (radius, distance) that fits,
|
||||
/// as `search` finds it by building candidates ([`kernel::fillet_max`]): the refusal as it is
|
||||
/// when none fits or the search itself fails (a candidate failing for another reason says
|
||||
@@ -447,6 +491,9 @@ fn with_largest_fit(
|
||||
size: &str,
|
||||
search: impl FnOnce() -> std::result::Result<Option<(f64, kernel::Body)>, kernel::KernelError>,
|
||||
) -> kernel::KernelError {
|
||||
if EVAL_VALIDATION.get() == kernel::Validation::Off {
|
||||
return kernel::KernelError::TooLarge(why);
|
||||
}
|
||||
// On solidcore the refusal names a size already, less a margin (34.99 where 35 builds), and
|
||||
// its edge: the edge stays, the size is the largest the search builds, solidcore's where the
|
||||
// search finds none.
|
||||
@@ -784,7 +831,7 @@ impl ModelState {
|
||||
let mut h = std::collections::hash_map::DefaultHasher::new();
|
||||
serde_json::to_string(&s.plane).unwrap_or_default().hash(&mut h);
|
||||
let mut sketch = s.sketch.clone();
|
||||
quantize(&mut sketch);
|
||||
fingerprint_sketch(&mut sketch);
|
||||
serde_json::to_string(&sketch).unwrap_or_default().hash(&mut h);
|
||||
s.report.ok().hash(&mut h);
|
||||
self.sketch_keys.insert(feature, h.finish());
|
||||
@@ -1253,6 +1300,13 @@ fn quantize(sk: &mut solidcraft_sketch::Sketch) {
|
||||
}
|
||||
}
|
||||
|
||||
/// The stored solve as a cache key: on the [`QUANTUM`] grid, without the editor state
|
||||
/// (see [`solidcraft_sketch::Sketch::clear_id_counters`]).
|
||||
fn fingerprint_sketch(sk: &mut solidcraft_sketch::Sketch) {
|
||||
quantize(sk);
|
||||
sk.clear_id_counters();
|
||||
}
|
||||
|
||||
/// A feature as fingerprinted: its definition, a sketch's stored solve quantized, an imported
|
||||
/// file's text or mesh payload by its content digest.
|
||||
/// Hash [`feature_json`] of `f` into `h`. Every evaluation fingerprints every feature, and in a
|
||||
@@ -1319,7 +1373,7 @@ fn feature_json(f: &Feature, steps: &ImportDigests) -> String {
|
||||
if let FeatureKind::Sketch { .. } = &f.kind {
|
||||
let mut g = f.clone();
|
||||
if let FeatureKind::Sketch { sketch, .. } = &mut g.kind {
|
||||
quantize(sketch);
|
||||
fingerprint_sketch(sketch);
|
||||
}
|
||||
return serde_json::to_string(&g).unwrap_or_default();
|
||||
}
|
||||
@@ -1570,6 +1624,9 @@ fn cache_key(f: &Feature, chain_fp: u64, local: u64, ctx: u64, state: &ModelStat
|
||||
#[derive(Default)]
|
||||
struct PlacementReads {
|
||||
all: bool,
|
||||
/// A body named that `st` lacks: a further placement's (`Block (Part:2)`), found through
|
||||
/// its occurrence (#9957), or gone.
|
||||
unlisted: bool,
|
||||
components: std::collections::BTreeSet<u64>,
|
||||
paths: Vec<Vec<u64>>,
|
||||
bodies: std::collections::BTreeSet<String>,
|
||||
@@ -1608,14 +1665,17 @@ impl PlacementReads {
|
||||
}
|
||||
}
|
||||
|
||||
/// The component of body `name` in `st`; every component when the body is not there (a
|
||||
/// projection then takes the body nearest its point, wherever that is).
|
||||
/// The component of body `name` in `st`; every component and every placement when the body
|
||||
/// is not there (a further placement's body, or the body nearest its point, wherever that is).
|
||||
fn body(&mut self, doc: &Document, st: Option<&ModelState>, name: &str) {
|
||||
match st.and_then(|st| st.body(name)) {
|
||||
Some(b) => {
|
||||
self.components.insert(doc.model_body_component(b));
|
||||
}
|
||||
None => self.all = true,
|
||||
None => {
|
||||
self.all = true;
|
||||
self.unlisted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1851,6 +1911,9 @@ fn placement_digest(doc: &Document, f: &Feature, st: Option<&ModelState>) -> u64
|
||||
}
|
||||
}
|
||||
hash_placed_bodies(doc, st, &reads.bodies, &inverse, &mut h);
|
||||
if reads.unlisted {
|
||||
placements_hash(doc).hash(&mut h);
|
||||
}
|
||||
// Legacy and component-wide reads still need the conservative offset dependency. Body
|
||||
// reads above already include just their own offsets, in the frame that evaluates them.
|
||||
if reads.all || !reads.components.is_empty() || !reads.paths.is_empty() {
|
||||
@@ -2305,6 +2368,7 @@ impl Model {
|
||||
let r = match frame_error.or(bad_param) {
|
||||
Some(e) => Err(kernel::KernelError::Failed(e)),
|
||||
None => kernel::guard(&f.name, || {
|
||||
let _val_guard = EvalValidationGuard(EVAL_VALIDATION.replace(validation));
|
||||
let (r, deferred) =
|
||||
kernel::validating(validation, || eval_feature(doc, &vals, f, &mut next, &mut warning, &mut import_report, &mut repair));
|
||||
r.map_err(|e| match e {
|
||||
@@ -5700,13 +5764,13 @@ fn feature_tools_with_constructor(
|
||||
FeatureKind::Torus { center, major, minor, .. } => {
|
||||
Ok(unnamed(vec![kernel::torus(point(vals, center)?, val(vals, major, Kind::Length)?, val(vals, minor, Kind::Length)?)?]))
|
||||
}
|
||||
FeatureKind::Cone { base, axis, radius, top_radius, height, .. } => Ok(unnamed(vec![more::cone(
|
||||
FeatureKind::Cone { base, axis, radius, top_radius, height, .. } => Ok(vec![more::cone(
|
||||
point(vals, base)?,
|
||||
axis.0,
|
||||
val(vals, radius, Kind::Length)?,
|
||||
val(vals, top_radius, Kind::Length)?,
|
||||
val(vals, height, Kind::Length)?,
|
||||
)?])),
|
||||
)?]),
|
||||
FeatureKind::Hole { .. } => Ok(hole_feature_tools(vals, st, f)?.into_iter().map(|(tool, _)| tool).collect()),
|
||||
_ => Err(DocError::Invalid(format!("{} cannot be patterned or mirrored yet", f.name))),
|
||||
}
|
||||
@@ -6038,15 +6102,28 @@ fn from_profile_on_closed_path(segs: Vec<solidcore_record::Seg2>, path: &Plane,
|
||||
/// Where an extrude of distance `d` starts and ends along the sketch normal, before any start
|
||||
/// offset: one side, the other, both (symmetric), or a second distance on the other side.
|
||||
///
|
||||
/// A second distance (`distance2`) goes the other way from the first side, wherever that went:
|
||||
/// against the normal for `direction: negative` (#2700), and back along it when a negative
|
||||
/// distance or a To Object below the sketch sends the first side down.
|
||||
/// A second distance (`distance2`) goes the other way from the first side: against the normal,
|
||||
/// or along it for `direction: negative` (#2700). Entered distances keep those sides whatever
|
||||
/// their signs; a To Object below the sketch or a Through All that reaches down sends the first
|
||||
/// side down and the second back along the normal.
|
||||
pub(crate) fn extent_sides(vals: &BTreeMap<String, Value>, extent: &crate::Extent, d: f64, direction: crate::Direction) -> Result<(f64, f64)> {
|
||||
Ok(match (&extent.distance2, direction) {
|
||||
(Some(d2), _) => {
|
||||
let first = if direction == crate::Direction::Negative { -d } else { d };
|
||||
let negative = direction == crate::Direction::Negative;
|
||||
let first = if negative { -d } else { d };
|
||||
let d2 = val(vals, d2, Kind::Length)?;
|
||||
(if first < 0.0 { d2 } else { -d2 }, first)
|
||||
// An entered distance keeps its side whatever its sign: Direction fixes the sides,
|
||||
// and a sign only turns its own side over. A reach computed from the model (To
|
||||
// Object, Through All) has the sign of where the object is, so the other side
|
||||
// follows it.
|
||||
let second = if extent.second_follows_first || extent.to.is_some() || extent.through_all {
|
||||
if first < 0.0 { d2 } else { -d2 }
|
||||
} else if negative {
|
||||
d2
|
||||
} else {
|
||||
-d2
|
||||
};
|
||||
(second, first)
|
||||
}
|
||||
(None, crate::Direction::Positive) => (0.0, d),
|
||||
(None, crate::Direction::Negative) => (-d, 0.0),
|
||||
@@ -6726,6 +6803,24 @@ fn replay(
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// A moved copy is copied again as a body: each instance is the copy the Move made,
|
||||
// placed by the pattern (#5931).
|
||||
if let FeatureKind::Move { copy: true, .. } = &src.kind {
|
||||
let made: Vec<ModelBody> = st.bodies.iter().filter(|b| b.feature == src.id).cloned().collect();
|
||||
if made.is_empty() {
|
||||
return Err(DocError::Invalid(format!("{} made no copy to repeat", src.name)));
|
||||
}
|
||||
let mut output = 0;
|
||||
for (k, m) in mats.iter().enumerate() {
|
||||
for mb in &made {
|
||||
let moved = moved_copy(mb, kernel::transform_matrix(&mb.body, *m)?, first + k + 1);
|
||||
let name = copy_body_name(st, f, output, &mb.name);
|
||||
st.bodies.push(ModelBody::traced_new(name, moved, f.id));
|
||||
output += 1;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let (op, targets) = feature_op(src);
|
||||
// A primitive's or a hole's targets are its participants (#1108): the copies change
|
||||
// those bodies only, like the feature itself (#2755).
|
||||
@@ -7088,12 +7183,14 @@ pub(crate) fn sheet_face_plane_in_frame(doc: &Document, st: &ModelState, comp: u
|
||||
PlaneRef::Offset { base, distance } => {
|
||||
PlaneRef::Offset { base: Box::new(sheet_face_plane_in_frame(doc, st, comp, base)?), distance: distance.clone() }
|
||||
}
|
||||
PlaneRef::AtAngle { base, axis_origin, axis_dir, angle, line } => PlaneRef::AtAngle {
|
||||
PlaneRef::AtAngle { base, axis_origin, axis_dir, angle, line, direction_only, pivot } => PlaneRef::AtAngle {
|
||||
base: Box::new(sheet_face_plane_in_frame(doc, st, comp, base)?),
|
||||
axis_origin: *axis_origin,
|
||||
axis_dir: *axis_dir,
|
||||
angle: angle.clone(),
|
||||
line: line.clone(),
|
||||
direction_only: *direction_only,
|
||||
pivot: pivot.clone(),
|
||||
},
|
||||
PlaneRef::Flipped { base, about_y } => {
|
||||
PlaneRef::Flipped { base: Box::new(sheet_face_plane_in_frame(doc, st, comp, base)?), about_y: *about_y }
|
||||
@@ -7194,6 +7291,7 @@ fn eval_feature(
|
||||
// Its tools meet bodies in other placements where those are shown (#3383).
|
||||
let _frames = ToolFrames::install(doc, f, st);
|
||||
match &f.kind {
|
||||
FeatureKind::NewerFileFeature { raw } => Err(DocError::Invalid(raw.why())),
|
||||
FeatureKind::BodyTransfer { bodies, from, to, transform } => {
|
||||
crate::body_transfer::evaluate(doc, st, &crate::plain(bodies), *from, *to, *transform)
|
||||
}
|
||||
@@ -7232,7 +7330,7 @@ fn eval_feature(
|
||||
| FeatureKind::Rib { .. }
|
||||
| FeatureKind::ReplaceFace { .. }
|
||||
| FeatureKind::Align { .. }
|
||||
| FeatureKind::Remove { .. } => more::eval(doc, vals, f, st),
|
||||
| FeatureKind::Remove { .. } => more::eval(doc, vals, f, st, warning),
|
||||
FeatureKind::Sketch { plane, sketch } => {
|
||||
let anchor = crate::face_anchor::plane_face_anchor(plane);
|
||||
let fst = match anchor {
|
||||
@@ -7243,6 +7341,14 @@ fn eval_feature(
|
||||
None if !plane_reads_geometry(plane) && sketch.links.is_empty() => std::borrow::Cow::Borrowed(st),
|
||||
None => state_in_frame(doc, st, f.component)?,
|
||||
};
|
||||
// A further placement a link reads is placed in its frame too (#9957).
|
||||
let named: Vec<&str> = sketch.links.iter().flat_map(|l| l.source.bodies()).collect();
|
||||
let fst = match anchor.map(|anchor| anchor.author_frame(doc, f.component)) {
|
||||
_ if named.is_empty() => fst,
|
||||
Some(Ok(frame)) => with_placed_copies(doc, st, fst, &frame, &named),
|
||||
Some(Err(_)) => fst,
|
||||
None => with_placed_copies(doc, st, fst, &doc.component_transform(f.component), &named),
|
||||
};
|
||||
let (plane, face_warning) = match (anchor, plane) {
|
||||
(Some(_), plane) => doc.resolve_attached_plane_noted(vals, plane, f.component, st, &fst)?,
|
||||
// A flat face of a sheet: the panel's exact plane, from its flat region and fold.
|
||||
@@ -7884,8 +7990,8 @@ fn eval_feature(
|
||||
FeatureKind::Cone { base, axis, radius, top_radius, height, operation } => {
|
||||
let (radius, top_radius, height) =
|
||||
(val(vals, radius, Kind::Length)?, val(vals, top_radius, Kind::Length)?, val(vals, height, Kind::Length)?);
|
||||
let b = more::cone(point(vals, base)?, axis.0, radius, top_radius, height)?;
|
||||
apply_op(doc, st, f, vec![b], *operation, &[])
|
||||
let tool = more::cone(point(vals, base)?, axis.0, radius, top_radius, height)?;
|
||||
apply_traced(doc, st, f, vec![tool], *operation, &[])
|
||||
}
|
||||
FeatureKind::Combine { target, tools, operation, keep_tools } => {
|
||||
if tools.iter().any(|t| t == target) {
|
||||
@@ -8234,7 +8340,7 @@ fn move_constructions_with(doc: &Document, st: &mut ModelState, moved: &[crate::
|
||||
fn plane_is_fixed(plane: &PlaneRef) -> bool {
|
||||
match plane {
|
||||
PlaneRef::Origin { .. } | PlaneRef::Custom { .. } | PlaneRef::Placed { .. } => true,
|
||||
PlaneRef::AtAngle { line: Some(_), .. } => false,
|
||||
PlaneRef::AtAngle { line: Some(_), .. } | PlaneRef::AtAngle { pivot: Some(_), .. } => false,
|
||||
PlaneRef::Offset { base, .. } | PlaneRef::AtAngle { base, .. } | PlaneRef::Flipped { base, .. } => plane_is_fixed(base),
|
||||
_ => false,
|
||||
}
|
||||
@@ -8245,7 +8351,7 @@ fn plane_is_fixed(plane: &PlaneRef) -> bool {
|
||||
fn plane_reads_geometry(plane: &PlaneRef) -> bool {
|
||||
match plane {
|
||||
PlaneRef::Origin { .. } | PlaneRef::Custom { .. } | PlaneRef::Placed { .. } | PlaneRef::Construction { .. } => false,
|
||||
PlaneRef::AtAngle { line: Some(_), .. } => true,
|
||||
PlaneRef::AtAngle { line: Some(_), .. } | PlaneRef::AtAngle { pivot: Some(_), .. } => true,
|
||||
PlaneRef::Offset { base, .. } | PlaneRef::AtAngle { base, .. } | PlaneRef::Flipped { base, .. } => plane_reads_geometry(base),
|
||||
PlaneRef::ThroughPoint { .. }
|
||||
| PlaneRef::Midplane { .. }
|
||||
@@ -8480,6 +8586,51 @@ pub fn state_in_frame_at<'a>(doc: &Document, st: &'a ModelState, comp: u64, fram
|
||||
Ok(Cow::Owned(out))
|
||||
}
|
||||
|
||||
/// `framed` (the model `st` in the frame that `frame` places in the world) with the further
|
||||
/// placements `names` names (`Block (Part:2)`), where they are in the world. A component's model
|
||||
/// holds its first placement only, so a sketch projecting a copy's face found no body of that
|
||||
/// name and took the first placement's (#9957). Only the named ones are placed: placing every
|
||||
/// body of an assembly for each sketch made sketch edits as slow as the design is big.
|
||||
pub fn with_placed_copies<'a>(
|
||||
doc: &Document,
|
||||
st: &ModelState,
|
||||
framed: std::borrow::Cow<'a, ModelState>,
|
||||
frame: &Mat,
|
||||
names: &[&str],
|
||||
) -> std::borrow::Cow<'a, ModelState> {
|
||||
let missing: Vec<&str> = names.iter().copied().filter(|n| !n.is_empty() && framed.body(n).is_none()).collect();
|
||||
if missing.is_empty() || doc.occurrences.is_empty() {
|
||||
return framed;
|
||||
}
|
||||
let Some(inv) = crate::mat_inverse(frame) else { return framed };
|
||||
let mut by_component: std::collections::HashMap<u64, Vec<&ModelBody>> = std::collections::HashMap::new();
|
||||
for (b, owner) in st.bodies.iter().zip(doc.model_body_components(st)) {
|
||||
by_component.entry(owner).or_default().push(b);
|
||||
}
|
||||
let world = crate::WorldNames::new(doc, st);
|
||||
let mut placed: Vec<ModelBody> = Vec::new();
|
||||
for (comp, path, m) in doc.placements() {
|
||||
for b in by_component.get(&comp).into_iter().flatten() {
|
||||
let name = world.name(&b.name, comp, &path);
|
||||
if !missing.contains(&name.as_str()) || placed.iter().any(|p| p.name == name) {
|
||||
continue;
|
||||
}
|
||||
let bm = doc.model_body_offset(b).map(|o| crate::mat_mul(&m, o)).unwrap_or(m);
|
||||
if let Ok(mut copy) = st.body_in_frame(b, &crate::mat_mul(&inv, &bm)) {
|
||||
copy.name = name;
|
||||
copy.source_name = Some(b.definition_name().to_string());
|
||||
placed.push(copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
if placed.is_empty() {
|
||||
return framed;
|
||||
}
|
||||
let mut out = framed.into_owned();
|
||||
out.bodies.extend(placed);
|
||||
std::borrow::Cow::Owned(out)
|
||||
}
|
||||
|
||||
/// The model placed in the world: each component's bodies, sketches, threads and construction
|
||||
/// geometry through every occurrence of it (bodies of the first placement keep their names;
|
||||
/// further instances get the occurrence path in brackets). Without components this is the model
|
||||
@@ -8907,6 +9058,49 @@ mod world_placement_cache_tests {
|
||||
assert!(Arc::ptr_eq(&original_mesh, &again.body("Block").unwrap().mesh()));
|
||||
assert!(Arc::ptr_eq(&names, &crate::naming::face_names(again.body(&second_name).unwrap())));
|
||||
}
|
||||
|
||||
/// A dragged component's bodies are drawn from the meshes and plans made where the drag found
|
||||
/// them, moved: each is the same solid moved rigidly, the others stand where they were, and a
|
||||
/// body whose geometry changed is no such move.
|
||||
#[test]
|
||||
fn a_moved_placement_is_drawn_moved_from_where_it_stood() {
|
||||
// A placement moved, and the part moved off where it is defined.
|
||||
for from_definition in [false, true] {
|
||||
let mut doc = Document::new("placements");
|
||||
let component = doc.add_component(Some("Part"), 0).unwrap();
|
||||
doc.body_components.insert("Block".into(), component);
|
||||
let start = if from_definition { crate::IDENTITY } else { crate::rigid(Vec3::new(10., 20., 0.), Vec3::ZERO, Vec3::Z, 0.4) };
|
||||
let second = doc.add_occurrence(component, 0, start).unwrap();
|
||||
let mut state = ModelState::default();
|
||||
state.bodies.push(ModelBody::new("Block".into(), kernel::box_solid(Vec3::ZERO, Vec3::new(2., 3., 4.)).unwrap(), 1));
|
||||
let before = world_state(&doc, &state).unwrap();
|
||||
let delta = crate::rigid(Vec3::new(4., -7., 1.5), Vec3::new(1., 1., 0.), Vec3::new(0., 1., 1.), 0.6);
|
||||
let moved = doc.occurrences.iter_mut().find(|o| o.id == second).unwrap();
|
||||
moved.transform = crate::mat_mul(&delta, &moved.transform);
|
||||
let after = world_state(&doc, &state).unwrap();
|
||||
assert_eq!(after.bodies.len(), 2);
|
||||
let mut moves = 0;
|
||||
for now in &after.bodies {
|
||||
let was = before.body(&now.name).unwrap();
|
||||
let d = now.display_moved_from(was).unwrap();
|
||||
if d == crate::IDENTITY {
|
||||
assert!(Arc::ptr_eq(&now.mesh(), &was.mesh()), "{}: unchanged, the same mesh", now.name);
|
||||
continue;
|
||||
}
|
||||
moves += 1;
|
||||
assert!(d.iter().flatten().zip(delta.iter().flatten()).all(|(a, b)| (a - b).abs() < 1e-9), "{}: {d:?}", now.name);
|
||||
// `was`'s mesh drawn moved is `now`'s.
|
||||
for (p, q) in was.mesh().positions.iter().zip(&now.mesh().positions) {
|
||||
assert!(crate::apply_point(&d, *p).dist(*q) < 1e-9);
|
||||
}
|
||||
}
|
||||
assert_eq!(moves, 1, "from its definition: {from_definition}");
|
||||
// Another body, or other geometry: no move draws it.
|
||||
assert!(after.bodies[0].display_moved_from(&before.bodies[1]).is_none());
|
||||
let other = ModelBody::new(before.bodies[0].name.clone(), kernel::box_solid(Vec3::ZERO, Vec3::new(1., 1., 1.)).unwrap(), 1);
|
||||
assert!(other.display_moved_from(&before.bodies[0]).is_none());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1759,3 +1759,30 @@ fn mesh_metadata_and_order_remain_part_of_the_fingerprint() {
|
||||
same_as_fresh(&doc, &model);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_added_and_deleted_sketch_entity_keeps_its_fingerprint() {
|
||||
let mut doc = Document::new("sketch fingerprint");
|
||||
let id = doc.add_feature(line_sketch(PlaneRef::Origin { name: "XY".into() }, Vec2::new(10.0, 0.0)), None).unwrap();
|
||||
let mut model = Model::new();
|
||||
model.evaluate(&doc);
|
||||
let key = model.state().sketch_keys.get(&id).copied();
|
||||
let json = super::feature_json(doc.feature(id).unwrap(), &super::ImportDigests::default());
|
||||
|
||||
// Make and delete an id: the counters advance, but the geometry is the same.
|
||||
{
|
||||
let sk = doc.sketch_mut(id).unwrap();
|
||||
let a = sk.add_point(Vec2::new(2.0, 2.0), None).unwrap();
|
||||
let b = sk.add_point(Vec2::new(3.0, 2.0), None).unwrap();
|
||||
let line = sk.add_line_pts(a, b, None).unwrap();
|
||||
sk.remove_curves(&[line]);
|
||||
sk.remove_points(&[b]);
|
||||
sk.remove_points(&[a]);
|
||||
}
|
||||
|
||||
let json_after = super::feature_json(doc.feature(id).unwrap(), &super::ImportDigests::default());
|
||||
assert_eq!(json, json_after, "deleted ids are not geometry");
|
||||
model.evaluate(&doc);
|
||||
assert_eq!(model.state().sketch_keys.get(&id).copied(), key);
|
||||
assert_eq!((model.last_recomputed, model.last_restored), (0, 0));
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::sheet;
|
||||
use solidcore_record::{Loop2, Seg2};
|
||||
use solidcraft_geom::{Aabb3, Ring};
|
||||
|
||||
pub(super) fn eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature, st: &mut ModelState) -> Result<()> {
|
||||
pub(super) fn eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature, st: &mut ModelState, warning: &mut Option<String>) -> Result<()> {
|
||||
match &f.kind {
|
||||
FeatureKind::Emboss { sketch, profiles, depth, deboss, targets, face } => {
|
||||
let ss = st.sketch(sketch.0).ok_or_else(|| DocError::Unknown(format!("sketch {sketch}")))?;
|
||||
@@ -59,7 +59,20 @@ pub(super) fn eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature,
|
||||
if st.bodies.is_empty() {
|
||||
return Err(DocError::Invalid("there is no body to emboss".into()));
|
||||
}
|
||||
apply_op(doc, st, f, tools, if *deboss { Operation::Cut } else { Operation::Join }, &crate::plain(targets))
|
||||
// #10940: an emboss or deboss whose tool lies clear of the body (a sketch on the far
|
||||
// side of the material, a depth that stops short) used to report success and change
|
||||
// nothing. Say so: the body is compared before and after, as the command's reply does.
|
||||
let before: Vec<Body> = st.bodies.iter().map(|b| b.body.clone()).collect();
|
||||
apply_op(doc, st, f, tools, if *deboss { Operation::Cut } else { Operation::Join }, &crate::plain(targets))?;
|
||||
let unchanged = st.bodies.len() == before.len() && st.bodies.iter().zip(&before).all(|(b, old)| old.same(&b.body));
|
||||
if unchanged {
|
||||
let what = if *deboss { "deboss" } else { "emboss" };
|
||||
append_warning(
|
||||
warning,
|
||||
format!("the {what} changed nothing; check where its profile lies, which side of the face the sketch is on, and the depth"),
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
FeatureKind::Rib { .. } => {
|
||||
let tools = rib_tools(vals, st, f, None)?;
|
||||
@@ -1428,7 +1441,8 @@ const MAX_CONE_SIZE: f64 = 1e7;
|
||||
/// of `top_radius` `height` along `axis` (a negative height goes the other way). A top radius of
|
||||
/// 0 (or below a micrometre) makes a full cone ending in an apex. Built, as the torus is, by
|
||||
/// revolving its half section, a trapezoid (a triangle for a full cone) with an edge on the axis.
|
||||
pub(super) fn cone(base: Vec3, axis: Vec3, radius: f64, top_radius: f64, height: f64) -> Result<Body> {
|
||||
/// Its faces are named `bottom`, `side` and `top` ([`crate::lineage::cone_roles`]).
|
||||
pub(super) fn cone(base: Vec3, axis: Vec3, radius: f64, top_radius: f64, height: f64) -> Result<Traced> {
|
||||
let in_range = |v: f64| v.is_finite() && v.abs() <= MAX_CONE_SIZE;
|
||||
if !(in_range(base.x) && in_range(base.y) && in_range(base.z)) {
|
||||
return Err(DocError::Invalid("the cone's base is out of range".into()));
|
||||
@@ -1462,14 +1476,16 @@ pub(super) fn cone(base: Vec3, axis: Vec3, radius: f64, top_radius: f64, height:
|
||||
(Some(b), true) => b,
|
||||
_ => return Err(DocError::Invalid("the cone could not be built".into())),
|
||||
};
|
||||
// Read off the revolve's own history; moving the body keeps its faces' order.
|
||||
let roles = crate::lineage::cone_roles(&body);
|
||||
if (dir - Vec3::Z).len() < 1e-12 && base.len() == 0.0 {
|
||||
return Ok(body);
|
||||
return Ok(Traced::made(body, roles));
|
||||
}
|
||||
// A rotation taking +Z onto the axis (x and y square to it, right-handed), then the base.
|
||||
let x = Plane::from_normal(base, dir).ok_or_else(|| DocError::Invalid("the cone's axis has no direction".into()))?.x;
|
||||
let y = dir.cross(x);
|
||||
let m = [[x.x, x.y, x.z, 0.0], [y.x, y.y, y.z, 0.0], [dir.x, dir.y, dir.z, 0.0], [base.x, base.y, base.z, 1.0]];
|
||||
Ok(kernel::transform_matrix(&body, m)?)
|
||||
Ok(Traced::made(kernel::transform_matrix(&body, m)?, roles))
|
||||
}
|
||||
|
||||
/// A primitive tool's exact geometry, for [`sheet_primitive_cut`].
|
||||
|
||||
@@ -63,13 +63,16 @@ pub(super) fn eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature,
|
||||
let t = val(vals, thickness, Kind::Length)?;
|
||||
let mut tools = Vec::new();
|
||||
for n in bodies {
|
||||
let b = st.body(n).ok_or_else(|| DocError::Unknown(format!("body `{n}`")))?;
|
||||
tools.push(kernel::thicken(&b.body, t, *symmetric)?);
|
||||
let b = st.body(n).cloned().ok_or_else(|| DocError::Unknown(format!("body `{n}`")))?;
|
||||
// Kept face, offset and edge walls. Carry that history so a sketch can attach
|
||||
// to the solid's faces and follow the thickness (#7641).
|
||||
let thickened = kernel::thicken(&b.body, t, *symmetric)?;
|
||||
tools.push(Traced::step(thickened, &[&b.traced()]));
|
||||
}
|
||||
if tools.is_empty() {
|
||||
return Err(DocError::Invalid("pick the surfaces to thicken".into()));
|
||||
}
|
||||
apply_op(doc, st, f, tools, *operation, &crate::plain(targets))
|
||||
apply_traced(doc, st, f, tools, *operation, &crate::plain(targets))
|
||||
}
|
||||
FeatureKind::SurfaceTrim { body, plane, tool, keep, keep_more, curves, with, keep_with, stitch, extend } => {
|
||||
let i = st.bodies.iter().position(|b| &b.name == body).ok_or_else(|| DocError::Unknown(format!("body `{body}`")))?;
|
||||
|
||||
@@ -67,6 +67,7 @@ impl FeatureKind {
|
||||
| FeatureKind::ReplaceFace { body, .. }
|
||||
| FeatureKind::SurfaceOffset { body, .. }
|
||||
| FeatureKind::Boss { body, .. }
|
||||
| FeatureKind::Lip { body, .. }
|
||||
| FeatureKind::SnapFit { body, .. }
|
||||
| FeatureKind::Rest { body, .. } => body.as_deref(),
|
||||
FeatureKind::SheetConvert { body, .. } | FeatureKind::SurfaceRuled { body, .. } => Some(body.as_ref()),
|
||||
|
||||
@@ -29,6 +29,7 @@ pub(super) fn fixtures() -> Vec<(&'static str, Value)> {
|
||||
let hole =
|
||||
|h: Value| json!({"type": "hole", "position": o, "direction": [0, 0, 1], "diameter": "hole.diameter", "depth": "hole.depth", "hole": h});
|
||||
vec![
|
||||
("newer_file_feature", json!({"type": "newer_file_feature", "raw": {"type": "gyroid_lattice", "cell": 2}})),
|
||||
(
|
||||
"body_transfer",
|
||||
json!({"type": "body_transfer", "bodies": ["B"], "from": 0, "to": 1, "transform": [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]}),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
newer_file_feature
|
||||
body_transfer
|
||||
sketch
|
||||
inputs:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
newer_file_feature
|
||||
body_transfer
|
||||
sketch
|
||||
moves: plane.axis_dir[0], plane.axis_dir[1], plane.axis_origin[0], plane.axis_origin[1], plane.axis_origin[2]
|
||||
@@ -150,6 +151,7 @@ boss
|
||||
face points: [0, 0, 0]
|
||||
lip
|
||||
moves: face[0], face[1], face[2]
|
||||
face points: [0, 0, 0]
|
||||
snap_fit
|
||||
moves: direction[0], direction[2], hook[0], hook[1], position[0], position[1], position[2]
|
||||
face points: [0, 0, 0]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
newer_file_feature
|
||||
body_transfer
|
||||
remap_bodies: bodies[0]
|
||||
sketch
|
||||
|
||||
+140
-117
@@ -2,8 +2,9 @@
|
||||
//!
|
||||
//! A design file starts with `"format": "solidcraft/N"`. A file of an older format is read and
|
||||
//! upgraded step by step (N → N+1 → … → current): JSON-level steps first, for changes in the
|
||||
//! file's shape, then document-level steps. A file of a newer format than this build knows is
|
||||
//! refused with a message saying so, never guessed at.
|
||||
//! file's shape, then document-level steps. A file a newer build saved opens too, with a warning:
|
||||
//! what this build reads is read, what it doesn't is reported by name and kept for the next save
|
||||
//! (`crate::newer_file`).
|
||||
//!
|
||||
//! History:
|
||||
//! - 1: every design up to 2026-10-08 (format string since M0). Fields added over time all
|
||||
@@ -264,7 +265,7 @@ const FORMATS: Formats<'static> = Formats { hand: &JSON_STEPS, folded: FOLDED, p
|
||||
|
||||
/// 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
|
||||
/// reading a file's format, telling 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.
|
||||
@@ -317,8 +318,8 @@ impl Family<'_> {
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// in it is read: a file that isn't this family's is refused here. One a newer build saved is
|
||||
/// not: [`Family::newer`] says so.
|
||||
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})"));
|
||||
@@ -329,10 +330,19 @@ impl Family<'_> {
|
||||
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())?;
|
||||
if version == 0 {
|
||||
return Err(not_ours("format 0".into()));
|
||||
}
|
||||
Ok((version, applied))
|
||||
}
|
||||
|
||||
/// What says a file of format `version` with the changes `applied` came from a newer build:
|
||||
/// a number past this build's, or a change this build doesn't have anywhere in its history.
|
||||
/// `None` for a file this build fully knows.
|
||||
pub fn newer(&self, version: u32, applied: Option<&[String]>) -> Option<Newer> {
|
||||
self.formats.newer(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<()> {
|
||||
@@ -350,6 +360,17 @@ impl Family<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// How a file shows a newer build saved it ([`Family::newer`]).
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Newer {
|
||||
/// The number the file says.
|
||||
pub format: u32,
|
||||
/// The number this build reads up to.
|
||||
pub reads_up_to: u32,
|
||||
/// The changes the file names that this build doesn't have.
|
||||
pub unknown_changes: Vec<String>,
|
||||
}
|
||||
|
||||
/// The names of the pending changes this build writes into a design (`format_pending`).
|
||||
pub fn pending_names() -> Vec<String> {
|
||||
DESIGN.pending_names()
|
||||
@@ -394,35 +415,25 @@ 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"));
|
||||
/// [`Family::newer`]. 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 newer(&self, version: u32, applied: Option<&[String]>) -> Option<Newer> {
|
||||
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(()),
|
||||
}
|
||||
let listed = applied.filter(|_| version > current || self.parts(version).is_some()).unwrap_or_default();
|
||||
let unknown_changes: Vec<String> =
|
||||
listed.iter().filter(|name| !self.knows_part_name(self.canonical_applied_name(version, name))).cloned().collect();
|
||||
(version > current || !unknown_changes.is_empty()).then_some(Newer { format: version, reads_up_to: current, unknown_changes })
|
||||
}
|
||||
|
||||
/// Bring a `noun` 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.
|
||||
/// were pending; the others' steps run first, and already-applied changes never run again at
|
||||
/// their later number. A file past this build's number has no steps to run.
|
||||
fn upgrade(&self, noun: &str, v: &mut Value, version: u32, applied: Option<&[String]>) -> Result<()> {
|
||||
self.check(noun, version, applied)?;
|
||||
if version == 0 {
|
||||
return Err(DocError::Invalid(format!("not a SolidCraft {noun} (format 0)")));
|
||||
}
|
||||
// A list beside a format that isn't made of changes is ignored, like any field an upgrade
|
||||
// drops.
|
||||
let applied = applied.filter(|_| self.parts(version).is_some());
|
||||
@@ -677,18 +688,53 @@ fn v2_to_v3(v: &mut Value) -> Result<()> {
|
||||
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)?;
|
||||
check_design_numbers(&v)?;
|
||||
crate::config::validate_rules_json(&v)?;
|
||||
crate::config::validate_styles_json(&v)?;
|
||||
crate::face_anchor::validate_face_anchors_json(&v)?;
|
||||
validate_versioned_geometry(&v)?;
|
||||
validate_emboss_targets(&v)?;
|
||||
validate_freeform_offsets(&v)?;
|
||||
validate_sketch_sizes(&v)?;
|
||||
crate::format_offsets::validate_raw(&v)?;
|
||||
let legacy_wires = validate_wires(&v, version)?;
|
||||
match DESIGN.newer(version, applied.as_deref()) {
|
||||
Some(newer) => crate::newer_file::read(v, version, applied, newer),
|
||||
None => decode(v, version, applied.as_deref()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether a design's text says a newer build saved it, read before anything else in it is.
|
||||
pub fn says_newer(text: &str) -> bool {
|
||||
#[derive(serde::Deserialize)]
|
||||
struct Says {
|
||||
format: Option<String>,
|
||||
format_pending: Option<Vec<String>>,
|
||||
}
|
||||
serde_json::from_str::<Says>(text)
|
||||
.ok()
|
||||
.and_then(|says| Some((DESIGN.version_of(&says.format?)?, says.format_pending)))
|
||||
.is_some_and(|(version, applied)| DESIGN.newer(version, applied.as_deref()).is_some())
|
||||
}
|
||||
|
||||
/// The checks of a design's JSON that need nothing outside the part of it they are given.
|
||||
pub(crate) fn check_raw(v: &Value, version: u32) -> Result<Vec<(usize, usize)>> {
|
||||
check_design_numbers(v)?;
|
||||
crate::config::validate_rules_json(v)?;
|
||||
crate::config::validate_styles_json(v)?;
|
||||
crate::face_anchor::validate_face_anchors_json(v)?;
|
||||
validate_versioned_geometry(v)?;
|
||||
validate_emboss_targets(v)?;
|
||||
validate_freeform_offsets(v)?;
|
||||
validate_sketch_sizes(v)?;
|
||||
crate::format_offsets::validate_raw(v)?;
|
||||
validate_wires(v, version)
|
||||
}
|
||||
|
||||
/// [`check_raw`], the upgrade and the typed record alone: what a piece of a design must pass by
|
||||
/// itself ([`crate::newer_file`]), with nothing else in the file for it to refer to.
|
||||
pub(crate) fn check_piece(mut v: Value, version: u32, applied: Option<&[String]>) -> Result<()> {
|
||||
check_raw(&v, version)?;
|
||||
DESIGN.upgrade(&mut v, version, applied)?;
|
||||
let d: Document = serde_json::from_value(v).map_err(|e| DocError::Invalid(format!("design file: {e}")))?;
|
||||
check_typed_numbers(&d)
|
||||
}
|
||||
|
||||
/// A design's JSON, its format taken out, as the document it holds.
|
||||
pub(crate) fn decode(mut v: Value, version: u32, applied: Option<&[String]>) -> Result<Document> {
|
||||
let legacy_wires = check_raw(&v, version)?;
|
||||
validate_body_transfers(&v)?;
|
||||
DESIGN.upgrade(&mut v, version, applied.as_deref())?;
|
||||
DESIGN.upgrade(&mut v, version, applied)?;
|
||||
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();
|
||||
@@ -1327,9 +1373,9 @@ mod tests {
|
||||
let mut v = serde_json::json!({});
|
||||
formats.upgrade("design", &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();
|
||||
assert!(err.to_string().contains("newer SolidCraft"), "{err}");
|
||||
// A change this build doesn't know is a newer build's: said, and kept.
|
||||
let said = formats.newer(next, Some(&["c-later".into()])).unwrap();
|
||||
assert_eq!(said, Newer { format: next, reads_up_to: next, unknown_changes: vec!["c-later".into()] });
|
||||
// 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();
|
||||
@@ -1372,15 +1418,12 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn personal_card_alias_without_support_refuses_before_mutation() {
|
||||
fn personal_card_alias_without_support_is_a_newer_builds() {
|
||||
let hand: [JsonStep; 27] = [|_| Ok(()); 27];
|
||||
let first = [traced("a-first")];
|
||||
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();
|
||||
assert!(error.to_string().contains("physical-material-cards"), "{error}");
|
||||
assert_eq!(file, original);
|
||||
let said = formats.newer(29, Some(&["physical-material-cards".into()])).unwrap();
|
||||
assert_eq!(said.unknown_changes, ["physical-material-cards"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1391,11 +1434,8 @@ mod tests {
|
||||
let cards = [traced("material-engineering-cards")];
|
||||
let formats = Formats { hand: &hand, folded: &[(28, &first), (29, &second), (30, &cards)], pending: &[] };
|
||||
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();
|
||||
assert!(error.to_string().contains("physical-material-cards"), "{error}");
|
||||
assert_eq!(file, original);
|
||||
let said = formats.newer(version, Some(&["physical-material-cards".into()])).unwrap();
|
||||
assert_eq!(said.unknown_changes, ["physical-material-cards"]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1408,11 +1448,7 @@ mod tests {
|
||||
for names in
|
||||
[["physical-material-cards".into(), "unknown-card-codec".into()], ["unknown-card-codec".into(), "physical-material-cards".into()]]
|
||||
{
|
||||
let mut file = serde_json::json!({"trace": ["b"]});
|
||||
let original = file.clone();
|
||||
let error = formats.upgrade("design", &mut file, 29, Some(&names)).unwrap_err();
|
||||
assert!(error.to_string().contains("unknown-card-codec"), "{error}");
|
||||
assert_eq!(file, original);
|
||||
assert_eq!(formats.newer(29, Some(&names)).unwrap().unknown_changes, ["unknown-card-codec"]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1446,18 +1482,14 @@ mod tests {
|
||||
}
|
||||
|
||||
/// An experimental producer can use a number that a release later folds without its
|
||||
/// change. Its unknown concept must still be refused before any migration runs.
|
||||
/// change. Its unknown concept is still a newer build's.
|
||||
#[test]
|
||||
fn unknown_pending_concepts_are_refused_after_folding() {
|
||||
fn unknown_pending_concepts_are_said_after_folding() {
|
||||
let parts = [traced("a-first")];
|
||||
let later = [traced("b-second")];
|
||||
for pending in [&[][..], &later[..]] {
|
||||
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();
|
||||
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");
|
||||
assert_eq!(formats.newer(2, Some(&["experimental-card".into()])).unwrap().unknown_changes, ["experimental-card"]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1479,23 +1511,20 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// A design naming a pending change this build doesn't have is a newer build's: what it can't
|
||||
/// read of it doesn't read as damage.
|
||||
#[test]
|
||||
fn a_newer_builds_pending_change_is_refused_before_the_design_is_checked() {
|
||||
fn a_newer_builds_pending_change_opens_and_its_fields_are_not_damage() {
|
||||
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();
|
||||
let opened = read(&file.to_string()).unwrap();
|
||||
// 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"
|
||||
)
|
||||
);
|
||||
let said = opened.newer_file.as_ref().unwrap();
|
||||
assert_eq!(said.unknown_changes, ["not-a-change-anyone-made"]);
|
||||
assert!(said.notice().contains(&format!("format {FORMAT} with `not-a-change-anyone-made`")), "{}", said.notice());
|
||||
assert!(matches!(opened.features[0].kind, crate::FeatureKind::NewerFileFeature { .. }));
|
||||
}
|
||||
|
||||
/// Drawings follow the same rules from their own first format: folded formats follow on from 1
|
||||
@@ -1575,17 +1604,17 @@ mod tests {
|
||||
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));
|
||||
// A newer number isn't refused: it is said.
|
||||
let says = |family: &Family<'_>, mut file: Value| {
|
||||
let (version, applied) = family.take_version(&mut file).unwrap();
|
||||
family.newer(version, applied.as_deref())
|
||||
};
|
||||
assert_eq!(says(&DESIGN, tagged(&DESIGN, FORMAT + 1)), Some(Newer { format: FORMAT + 1, reads_up_to: FORMAT, unknown_changes: vec![] }));
|
||||
assert_eq!(read(&tagged(&DESIGN, FORMAT + 1).to_string()).unwrap().newer_file.unwrap().format, FORMAT + 1);
|
||||
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));
|
||||
assert_eq!(says(&DRAWING, tagged(&DRAWING, n)), Some(Newer { format: n, reads_up_to: current, unknown_changes: vec![] }));
|
||||
}
|
||||
for (family, noun) in [(&DESIGN, "design"), (&DRAWING, "drawing")] {
|
||||
assert_eq!(refusal(family, tagged(family, 0)), format!("not a SolidCraft {noun} (format 0)"));
|
||||
@@ -1607,12 +1636,7 @@ mod tests {
|
||||
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_eq!(says(&design, file), Some(Newer { format: next, reads_up_to: next, unknown_changes: vec!["c-later".into()] }));
|
||||
assert!(matches!(DESIGN.take_version(&mut json!({})), Ok((1, None))));
|
||||
assert_eq!(refusal(&DRAWING, json!({})), "not a SolidCraft drawing (no format)");
|
||||
}
|
||||
@@ -1639,19 +1663,13 @@ mod tests {
|
||||
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}");
|
||||
// Of a newer build: said, and what can run runs.
|
||||
let list = ["a-first".to_string(), "c-later".to_string()];
|
||||
assert_eq!(drawing.newer(2, Some(&list)).unwrap().unknown_changes, ["c-later"]);
|
||||
assert_eq!(open(json!({"format": "solidcraft-drawing/2", "format_pending": list})).unwrap(), Vec::<String>::new());
|
||||
assert_eq!(drawing.newer(3, None).unwrap(), Newer { format: 3, reads_up_to: 2, unknown_changes: vec![] });
|
||||
assert_eq!(open(json!({"format": "solidcraft-drawing/3"})).unwrap(), Vec::<String>::new());
|
||||
// An upgrade called without `take_version` still says format 0 isn't one.
|
||||
assert_eq!(drawing.upgrade(&mut json!({}), 0, None).unwrap_err().to_string(), "not a SolidCraft drawing (format 0)");
|
||||
}
|
||||
|
||||
@@ -1666,7 +1684,8 @@ mod tests {
|
||||
assert!(read(&file.to_string()).is_err());
|
||||
}
|
||||
file["format_pending"] = serde_json::json!(["not-a-change-anyone-made"]);
|
||||
assert!(read(&file.to_string()).is_err(), "unknown pending concepts are refused even after folding");
|
||||
let newer = read(&file.to_string()).unwrap();
|
||||
assert_eq!(newer.newer_file.as_ref().unwrap().unknown_changes, ["not-a-change-anyone-made"], "even after folding");
|
||||
file["format_pending"] = Value::from(pending_names());
|
||||
let opened = read(&file.to_string()).unwrap();
|
||||
assert_eq!(opened.format_pending, pending_names());
|
||||
@@ -1895,6 +1914,7 @@ mod tests {
|
||||
distance2: None,
|
||||
start_offset: None,
|
||||
through_all: false,
|
||||
second_follows_first: false,
|
||||
taper: Some("0 deg".into()),
|
||||
corner: Default::default(),
|
||||
to: Some(crate::FacePick(solidcore_record::Vec3::new(0.0, 0.0, 10.0))),
|
||||
@@ -2138,8 +2158,9 @@ mod tests {
|
||||
value["features"] = serde_json::json!([{"type":"sketch", "sketch":{"freeform_offsets":records}}]);
|
||||
let error = read(&value.to_string()).unwrap_err().to_string();
|
||||
assert!(error.contains("free-form offset metadata"), "{error}");
|
||||
// A newer build's file isn't held to this: the feature waits for that build.
|
||||
value["format"] = Value::from(format_string(FORMAT + 1));
|
||||
assert!(read(&value.to_string()).unwrap_err().to_string().contains("newer SolidCraft"));
|
||||
assert!(matches!(read(&value.to_string()).unwrap().features[0].kind, crate::FeatureKind::NewerFileFeature { .. }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2163,8 +2184,7 @@ mod tests {
|
||||
let error = read(&value.to_string()).unwrap_err().to_string();
|
||||
assert!(error.contains("emboss face"), "{error}");
|
||||
value["format"] = Value::from(format_string(FORMAT + 1));
|
||||
let error = read(&value.to_string()).unwrap_err().to_string();
|
||||
assert!(error.contains("newer SolidCraft"), "{error}");
|
||||
assert!(matches!(read(&value.to_string()).unwrap().features[0].kind, crate::FeatureKind::NewerFileFeature { .. }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2207,7 +2227,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_seven_styles_upgrade_and_newer_versions_are_refused_before_columns() {
|
||||
fn version_seven_styles_upgrade_and_a_newer_versions_columns_are_kept() {
|
||||
use crate::config::{Column, ConfigRow};
|
||||
let mut d = Document::new("configured styles");
|
||||
d.configs.columns = vec![Column::Material("Body".into()), Column::Appearance("Body".into())];
|
||||
@@ -2227,9 +2247,11 @@ mod tests {
|
||||
value["format"] = Value::from(format_string(FORMAT + 1));
|
||||
value["configs"]["columns"] = serde_json::json!([{"unknown_future_column":"Body"}]);
|
||||
value["configs"]["rules"] = serde_json::json!({"unknown_future_rule": true});
|
||||
let error = read(&value.to_string()).unwrap_err().to_string();
|
||||
assert!(error.contains("newer SolidCraft") && error.contains("update SolidCraft"), "{error}");
|
||||
assert!(!error.contains("unknown variant"), "format refusal precedes decoding new variants");
|
||||
let opened = read(&value.to_string()).unwrap();
|
||||
let said = opened.newer_file.as_ref().unwrap();
|
||||
assert!(said.not_understood.iter().any(|what| what.contains("configs")), "{:?}", said.not_understood);
|
||||
let saved: Value = serde_json::from_str(&opened.to_json()).unwrap();
|
||||
assert_eq!(saved["configs"], value["configs"], "kept for the build that reads them");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2284,8 +2306,10 @@ mod tests {
|
||||
assert_eq!(read(&saved.to_string()).unwrap(), d);
|
||||
saved["format"] = Value::from(format_string(FORMAT + 1));
|
||||
saved["features"][0]["sketch"]["constraints"][0]["type"] = Value::from("future_dimension");
|
||||
let error = read(&saved.to_string()).unwrap_err().to_string();
|
||||
assert!(error.contains("newer SolidCraft") && !error.contains("unknown variant"), "{error}");
|
||||
let opened = read(&saved.to_string()).unwrap();
|
||||
assert!(matches!(opened.features[0].kind, crate::FeatureKind::NewerFileFeature { .. }));
|
||||
let again: Value = serde_json::from_str(&opened.to_json()).unwrap();
|
||||
assert_eq!(again["features"][0], saved["features"][0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2392,15 +2416,14 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn older_upgraded_newer_refused_others_rejected() {
|
||||
fn older_upgraded_newer_opened_others_rejected() {
|
||||
let mut v: Value = serde_json::from_str(&Document::new("A").to_json()).unwrap();
|
||||
v["format"] = Value::from("solidcraft/1");
|
||||
assert_eq!(read(&v.to_string()).unwrap().format, format_string(FORMAT));
|
||||
v.as_object_mut().unwrap().remove("format");
|
||||
assert!(read(&v.to_string()).is_ok(), "a file without a format is format 1");
|
||||
v["format"] = Value::from(format_string(FORMAT + 1));
|
||||
let e = read(&v.to_string()).unwrap_err().to_string();
|
||||
assert!(e.contains("newer SolidCraft"), "{e}");
|
||||
assert_eq!(read(&v.to_string()).unwrap().newer_file.unwrap().format, FORMAT + 1);
|
||||
for bad in ["solidcraft/0", "acad/3", "solidcraft/-1", "solidcraft/99999999999999999999"] {
|
||||
v["format"] = Value::from(bad);
|
||||
assert!(read(&v.to_string()).is_err(), "{bad}");
|
||||
|
||||
@@ -5,7 +5,7 @@ here is the whole format bump: never edit `FORMAT`, `JSON_STEPS` or another chan
|
||||
|
||||
- Name it after the change, lowercase with dashes: `placed-plane-origins.rs`.
|
||||
- Start it with a `//!` note: what is saved, what an older design gets without it, and what
|
||||
the last release does with a design that has it (it refuses the newer format number).
|
||||
the last release does with a design that has it (it warns about the newer format number, opens what it can read and keeps the rest).
|
||||
- If older designs must be rewritten as they open, add the step to the same file:
|
||||
|
||||
```rust
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
//! Saves exact encoded texture bytes, inert provenance and explicit saved surface roles.
|
||||
//! Older designs have no embedded textures and retain their built-in surface bindings.
|
||||
//! The last release asks for an update instead of silently dropping the new resources.
|
||||
@@ -0,0 +1,5 @@
|
||||
//! A sketch's intersection link can save a curve of another sketch as either side (`a` or `b`,
|
||||
//! `{"type": "sketch_curve", sketch, curve}`), standing in for that curve's extrusion along its
|
||||
//! sketch's normal (#11048). An older design has only bodies and faces there, and opens as before.
|
||||
//! The last release refuses a design that has one (it asks for an update) instead of opening the
|
||||
//! link as lost.
|
||||
@@ -0,0 +1,5 @@
|
||||
//! An at-angle construction plane may save `direction_only` and an optional `pivot` point: the
|
||||
//! plane turns about the line through the pivot (or the base origin) using only the axis
|
||||
//! direction projected onto the base. An older design has neither and keeps turning about the
|
||||
//! axis as a full line. The last release refuses a design that has this change (the newer
|
||||
//! format number) instead of opening it without the option.
|
||||
@@ -0,0 +1,6 @@
|
||||
//! A point cloud keeps the segments and triangles its file draws over its points (#8817):
|
||||
//! `segments` and `triangles` in its `complex`, the indices of their points, and a column of named
|
||||
//! numbers may lie on them (`on`: `segment` or `triangle`; a column that does not say is on the
|
||||
//! points, as every design saved before this has it). Older designs need no transformation: their
|
||||
//! clouds are points alone. The last release refuses a design saved with this (the newer format
|
||||
//! number) instead of opening its clouds as loose points without their edges and faces.
|
||||
@@ -0,0 +1,61 @@
|
||||
//! In a two-sided extrude (`distance2`) with entered distances, Direction fixes the sides and the
|
||||
//! sign of a distance only turns its own side over; `distance2` no longer follows the first
|
||||
//! side across the sketch plane. Older builds sent `distance2` to the other side of a negative
|
||||
//! first distance, so this step marks every saved two-sided extrude with entered distances
|
||||
//! `second_follows_first`, which keeps that reading. The distances themselves are left as they
|
||||
//! were, so an expression keeps the old sides whatever it evaluates to and a feature's named
|
||||
//! inputs keep their values for the expressions that use them. An extent that reaches to an
|
||||
//! object or through all already lets the second side follow the first, and a symmetric one has
|
||||
//! no second side. The last release asks for an update instead of opening a design that carries
|
||||
//! this change.
|
||||
use serde_json::Value;
|
||||
|
||||
pub fn step(v: &mut Value) -> crate::Result<()> {
|
||||
let Some(Value::Array(features)) = v.get_mut("features") else {
|
||||
return Ok(());
|
||||
};
|
||||
for feature in features {
|
||||
if !matches!(feature.get("type").and_then(Value::as_str), Some("extrude" | "surface_extrude")) {
|
||||
continue;
|
||||
}
|
||||
let Some(extent) = feature.get_mut("extent").and_then(Value::as_object_mut) else { continue };
|
||||
let plain = extent.get("distance2").is_some_and(|d| !d.is_null())
|
||||
&& extent.get("to").is_none_or(Value::is_null)
|
||||
&& !extent.get("through_all").and_then(Value::as_bool).unwrap_or(false)
|
||||
&& extent.get("direction").and_then(Value::as_str) != Some("symmetric");
|
||||
if plain {
|
||||
extent.insert("second_follows_first".into(), Value::Bool(true));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn saved_two_sided_extrudes_keep_their_sides_and_distances() {
|
||||
let mut v = json!({"features": [
|
||||
{"type": "extrude", "extent": {"distance": "-10 mm", "distance2": "5 mm"}},
|
||||
{"type": "extrude", "extent": {"distance": "-10 mm", "direction": "negative", "distance2": "5 mm"}},
|
||||
{"type": "extrude", "extent": {"distance": "-0 mm", "distance2": "5 mm"}},
|
||||
{"type": "extrude", "extent": {"distance": "-width", "distance2": "10 mm"}},
|
||||
{"type": "extrude", "extent": {"distance": "-10 mm"}},
|
||||
{"type": "extrude", "extent": {"distance": "-10 mm", "through_all": true, "distance2": "5 mm"}},
|
||||
{"type": "extrude", "extent": {"distance": "-10 mm", "to": [0.0, 0.0, 1.0], "distance2": "5 mm"}},
|
||||
{"type": "sketch"}
|
||||
]});
|
||||
let before = v.clone();
|
||||
super::step(&mut v).unwrap();
|
||||
let features = v["features"].as_array().unwrap();
|
||||
let marked: Vec<bool> = features.iter().map(|f| f["extent"]["second_follows_first"] == json!(true)).collect();
|
||||
assert_eq!(marked, [true, true, true, true, false, false, false, false]);
|
||||
for (f, b) in features.iter().zip(before["features"].as_array().unwrap()) {
|
||||
assert_eq!((&f["extent"]["distance"], &f["extent"]["direction"]), (&b["extent"]["distance"], &b["extent"]["direction"]));
|
||||
}
|
||||
let mut empty = json!({"name": "Untitled"});
|
||||
super::step(&mut empty).unwrap();
|
||||
assert_eq!(empty, json!({"name": "Untitled"}));
|
||||
}
|
||||
}
|
||||
@@ -399,7 +399,7 @@ mod tests {
|
||||
damaged["features"][1]["profiles"]["associations"] = associations;
|
||||
assert!(validate_raw(&damaged).is_err());
|
||||
damaged["format"] = json!(crate::format::format_string(crate::format::FORMAT + 1));
|
||||
assert!(crate::format::read(&damaged.to_string()).unwrap_err().to_string().contains("newer SolidCraft"));
|
||||
assert!(crate::format::read(&damaged.to_string()).unwrap().newer_file.is_some());
|
||||
}
|
||||
// Absent metadata preserves the historical one-to-one chain representation.
|
||||
let mut legacy = fixture();
|
||||
@@ -463,6 +463,7 @@ mod tests {
|
||||
distance2: None,
|
||||
start_offset: None,
|
||||
through_all: false,
|
||||
second_follows_first: false,
|
||||
taper: None,
|
||||
corner: Default::default(),
|
||||
to: None,
|
||||
|
||||
@@ -11,6 +11,7 @@ mod assembly;
|
||||
mod body_transfer;
|
||||
mod body_transfer_sheet;
|
||||
mod browser_tree;
|
||||
pub mod newer_file;
|
||||
pub mod pbr_appearance;
|
||||
pub use body_transfer::{valid_transfer_matrix, validate_body_transfer};
|
||||
pub mod canvas;
|
||||
@@ -48,6 +49,7 @@ pub mod motion;
|
||||
mod name_lookup;
|
||||
mod names;
|
||||
pub mod naming;
|
||||
mod occurrence_tree;
|
||||
mod params;
|
||||
pub mod persistent_name;
|
||||
pub mod physical_material;
|
||||
@@ -77,6 +79,7 @@ pub mod render_optics;
|
||||
pub mod render_resources;
|
||||
pub mod render_settings;
|
||||
pub mod render_surface;
|
||||
pub mod render_texture_resources;
|
||||
pub mod sheet;
|
||||
pub mod sketch_equation;
|
||||
pub mod text_params;
|
||||
@@ -92,8 +95,8 @@ pub use eval::{
|
||||
blend_target, curve_pick_error, draft_corner_options, edge_names_for, edge_refs, face_point_names, face_point_state, hole_direction_at,
|
||||
hole_on_surface, import_body_names, join_pieces_added, map_wire, metric_thread_sizes, object_height, parse_metric_thread, pattern_matrices,
|
||||
revolve_extent, revolve_extent_for_handle, revolve_extent_with_convention, select_profiles, sketch_hole_direction, sketch_line_plane,
|
||||
state_in_frame, state_in_frame_at, suggested_thread, suggested_thread_in, tangent_face_chain, through_all_distance, world_state,
|
||||
world_state_reusing,
|
||||
state_in_frame, state_in_frame_at, suggested_thread, suggested_thread_in, tangent_face_chain, through_all_distance, with_placed_copies,
|
||||
world_state, world_state_reusing,
|
||||
};
|
||||
pub use eval::{EvalCache, KeptResults};
|
||||
pub use expr::Expr;
|
||||
|
||||
@@ -429,6 +429,24 @@ pub(crate) fn cylinder_tool(body: Body) -> Traced {
|
||||
Traced::made(body, roles)
|
||||
}
|
||||
|
||||
/// The roles of a cone primitive's faces, from the body revolved from its half section (base
|
||||
/// side first, then the sloped side, then the top side of a cut-off cone; the side on the axis
|
||||
/// makes no face): `bottom`, `side` and `top`, as a cylinder's are (#7476). `None` when the body
|
||||
/// has no history of its own.
|
||||
pub(crate) fn cone_roles(body: &Body) -> Option<Vec<Vec<String>>> {
|
||||
body.history().filter(|h| h.operands == 0).map(|h| {
|
||||
h.faces
|
||||
.iter()
|
||||
.map(|f| match f.origins.as_slice() {
|
||||
[FaceOrigin::Profile { segment: 0 }] => vec!["bottom".to_string()],
|
||||
[FaceOrigin::Profile { segment: 1 }] => vec!["side".to_string()],
|
||||
[FaceOrigin::Profile { segment: 2 }] => vec!["top".to_string()],
|
||||
_ => Vec::new(),
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
/// A tool a feature builds without a sketch (a boss's post, a rib, a lip's band), its faces named
|
||||
/// by the part it makes: `<part>:top` (its end cap), `<part>:base` (its start cap) and
|
||||
/// `<part>:side:<k>` (swept from segment `k` of its profile), as its construction records them.
|
||||
|
||||
@@ -30,6 +30,39 @@ const MAX_HELD: usize = 50_000_000;
|
||||
/// evaluated again on every open (#8541).
|
||||
const MAX_TEXT: usize = 1 << 24;
|
||||
|
||||
/// Whether two tap markings are the same to the bit: then they write the same record. (`==` takes
|
||||
/// 0.0 and -0.0 for the same number, whose records differ.) Every field is named, so a new one
|
||||
/// cannot be left out.
|
||||
fn same_marking(a: &ThreadInfo, b: &ThreadInfo) -> bool {
|
||||
let numbers = |t: &ThreadInfo| {
|
||||
let ThreadInfo {
|
||||
feature: _,
|
||||
body: _,
|
||||
designation: _,
|
||||
major_diameter,
|
||||
radius,
|
||||
pitch,
|
||||
internal: _,
|
||||
axis_point,
|
||||
axis,
|
||||
start,
|
||||
end,
|
||||
form: _,
|
||||
hand: _,
|
||||
starts: _,
|
||||
} = t;
|
||||
[*major_diameter, *radius, *pitch, axis_point.x, axis_point.y, axis_point.z, axis.x, axis.y, axis.z, *start, *end]
|
||||
};
|
||||
a.feature == b.feature
|
||||
&& a.body == b.body
|
||||
&& a.designation == b.designation
|
||||
&& a.internal == b.internal
|
||||
&& a.form == b.form
|
||||
&& a.hand == b.hand
|
||||
&& a.starts == b.starts
|
||||
&& numbers(a).iter().zip(numbers(b).iter()).all(|(x, y)| x.to_bits() == y.to_bits())
|
||||
}
|
||||
|
||||
fn hex(v: u64) -> String {
|
||||
format!("{v:016x}")
|
||||
}
|
||||
@@ -120,6 +153,14 @@ struct Writer {
|
||||
/// share them (writing them with every state made a sheet-metal design's model too large to keep).
|
||||
sheets: Vec<Json>,
|
||||
sheet_index: HashMap<String, usize>,
|
||||
/// Tap markings (cosmetic threads) by their record, as the sheets: every state lists the
|
||||
/// markings of all the features before it, so writing them with each state grew with the
|
||||
/// square of the features (400 marked cylinders: 34 of a 37 MB manifest, past its limit).
|
||||
threads: Vec<Json>,
|
||||
thread_index: HashMap<String, usize>,
|
||||
/// The state written last and the places of its markings: a state mostly keeps the markings
|
||||
/// of the one before it, which are found again without writing them as text.
|
||||
last_threads: Option<(Arc<ModelState>, Vec<usize>)>,
|
||||
states: Vec<Json>,
|
||||
state_index: HashMap<usize, usize>,
|
||||
/// Each kernel body's place in `bodies`, by its record.
|
||||
@@ -258,6 +299,32 @@ impl Writer {
|
||||
Some(self.sheets.len() - 1)
|
||||
}
|
||||
|
||||
/// The places of `st`'s markings among the written ones. A marking at the same place in the
|
||||
/// state written last, the same to the bit, is that one (no text built); any other is keyed by
|
||||
/// its text, as a sheet is.
|
||||
fn threads(&mut self, st: &Arc<ModelState>) -> Option<Vec<usize>> {
|
||||
let mut out = Vec::with_capacity(st.threads.len());
|
||||
for (i, t) in st.threads.iter().enumerate() {
|
||||
let before = self.last_threads.as_ref().and_then(|(s, at)| Some((s.threads.get(i)?, *at.get(i)?)));
|
||||
if let Some((_, at)) = before.filter(|(b, _)| same_marking(b, t)) {
|
||||
out.push(at);
|
||||
continue;
|
||||
}
|
||||
let key = serde_json::to_string(t).ok()?;
|
||||
let at = match self.thread_index.get(&key) {
|
||||
Some(at) => *at,
|
||||
None => {
|
||||
self.threads.push(serde_json::to_value(t).ok()?);
|
||||
self.thread_index.insert(key, self.threads.len() - 1);
|
||||
self.threads.len() - 1
|
||||
}
|
||||
};
|
||||
out.push(at);
|
||||
}
|
||||
self.last_threads = Some((st.clone(), out.clone()));
|
||||
Some(out)
|
||||
}
|
||||
|
||||
fn state(&mut self, st: &Arc<ModelState>) -> Option<usize> {
|
||||
let id = Arc::as_ptr(st) as usize;
|
||||
if let Some(i) = self.state_index.get(&id) {
|
||||
@@ -266,11 +333,12 @@ impl Writer {
|
||||
let bodies = st.bodies.iter().map(|b| self.model_body(b)).collect::<Option<Vec<_>>>()?;
|
||||
let sketches = st.sketches.iter().map(|s| self.sketch(s)).collect::<Option<Vec<_>>>()?;
|
||||
let sheets = st.sheets.iter().map(|s| self.sheet(s)).collect::<Option<Vec<_>>>()?;
|
||||
let threads = self.threads(st)?;
|
||||
let r = json!({
|
||||
"bodies": runs(&bodies), "sketches": runs(&sketches), "body_counter": st.body_counter,
|
||||
"reserved_body_names": st.reserved_body_names.iter().collect::<Vec<_>>(),
|
||||
"sheets": sheets, "construct": serde_json::to_value(&st.construct).ok()?,
|
||||
"threads": serde_json::to_value(&st.threads).ok()?,
|
||||
"threads": runs(&threads),
|
||||
"sketch_key_set": self.sketch_key_set(&st.sketch_keys),
|
||||
"blend_edges": serde_json::to_value(&st.blend_edges).ok()?, "fitted": serde_json::to_value(&st.fitted).ok()?,
|
||||
"tapered": serde_json::to_value(&st.tapered).ok()?,
|
||||
@@ -306,7 +374,8 @@ impl Model {
|
||||
// The check mode the results were evaluated under: they are another mode's results.
|
||||
let records = json!({
|
||||
"features": features, "states": w.states, "model_bodies": w.model_bodies, "names": w.names, "sketches": w.sketches,
|
||||
"sheets": w.sheets, "validation": self.validation().id(), "rounds": w.rounds, "sketch_key_sets": w.sketch_key_sets,
|
||||
"sheets": w.sheets, "threads": w.threads, "validation": self.validation().id(), "rounds": w.rounds,
|
||||
"sketch_key_sets": w.sketch_key_sets,
|
||||
});
|
||||
Ok(SavedModel { records, bodies: w.bodies })
|
||||
})
|
||||
@@ -388,6 +457,10 @@ impl SavedModel {
|
||||
for s in list("sheets")? {
|
||||
sheets.push(serde_json::from_value::<crate::sheet::SheetBody>(s.clone()).map_err(|_| bad("saved model: sheet"))?);
|
||||
}
|
||||
let mut threads = Vec::new();
|
||||
for t in list("threads")? {
|
||||
threads.push(serde_json::from_value::<ThreadInfo>(t.clone()).map_err(|_| bad("saved model: tap marking"))?);
|
||||
}
|
||||
// The sketch-key sets, each the changes from an earlier one (`base`): a short record can
|
||||
// stand for a large set, so what they hold counts towards MAX_HELD as they are built.
|
||||
let mut key_sets: Vec<BTreeMap<u64, u64>> = Vec::new();
|
||||
@@ -413,6 +486,12 @@ impl SavedModel {
|
||||
}
|
||||
key_sets.push(keys);
|
||||
}
|
||||
// Running totals of what each marking costs to clone (its items and text), by place.
|
||||
let mut thread_weight = vec![0usize];
|
||||
for t in &threads {
|
||||
let w = 1usize.saturating_add((t.body.len().saturating_add(t.designation.len())) / 64);
|
||||
thread_weight.push(thread_weight.last().copied().unwrap_or(0).saturating_add(w));
|
||||
}
|
||||
let records = list("states")?;
|
||||
// What every state holds, counted from its runs and its sketch-key set before any is
|
||||
// built (a hostile file cannot make the states clone more than MAX_HELD items).
|
||||
@@ -422,6 +501,14 @@ impl SavedModel {
|
||||
held = held.saturating_add(run.get(1).and_then(Json::as_u64).and_then(|n| usize::try_from(n).ok()).unwrap_or(usize::MAX));
|
||||
}
|
||||
}
|
||||
// A marking is cloned deeply into every state that lists it, so each counts by its
|
||||
// text, not as one item: a run of a large marking repeated cannot exhaust memory.
|
||||
for run in s.get("threads").and_then(Json::as_array).ok_or_else(|| bad("saved model: state"))? {
|
||||
let first = index(run.get(0), threads.len())?;
|
||||
let count = run.get(1).and_then(Json::as_u64).and_then(|n| usize::try_from(n).ok()).ok_or_else(|| bad("saved model: run"))?;
|
||||
let end = first.checked_add(count).filter(|end| *end <= threads.len()).ok_or_else(|| bad("saved model: run"))?;
|
||||
held = held.saturating_add(thread_weight[end].saturating_sub(thread_weight[first]));
|
||||
}
|
||||
held = held.saturating_add(s.get("sheets").and_then(Json::as_array).map_or(0, Vec::len));
|
||||
held = held.saturating_add(key_sets.get(index(s.get("sketch_key_set"), key_sets.len())?).map_or(0, BTreeMap::len));
|
||||
if held > MAX_HELD {
|
||||
@@ -465,7 +552,7 @@ impl SavedModel {
|
||||
.map(|i| index(Some(i), sheets.len()).and_then(|i| sheets.get(i).cloned().ok_or_else(|| bad("saved model: sheet"))))
|
||||
.collect::<Result<Vec<_>>>()?,
|
||||
construct: serde_json::from_value(field("construct")?).map_err(invalid)?,
|
||||
threads: serde_json::from_value(field("threads")?).map_err(invalid)?,
|
||||
threads: ids("threads", threads.len())?.into_iter().filter_map(|i| threads.get(i).cloned()).collect(),
|
||||
sketch_keys,
|
||||
blend_edges: serde_json::from_value(field("blend_edges")?).map_err(invalid)?,
|
||||
fitted: serde_json::from_value(field("fitted")?).map_err(invalid)?,
|
||||
@@ -613,10 +700,11 @@ impl SavedModel {
|
||||
let mut kept_bodies = vec![false; model_bodies.len()];
|
||||
let (mut names_used, mut sketches_used) = (vec![false; names.len()], vec![false; sketches.len()]);
|
||||
let mut stored = std::collections::HashSet::new();
|
||||
// The sheet bodies, each written once and named by place in the states: all kept.
|
||||
// The sheet bodies and tap markings, each written once and named by place in the states: all kept.
|
||||
let sheets = list("sheets").cloned().unwrap_or_default();
|
||||
let threads = list("threads").cloned().unwrap_or_default();
|
||||
let key_sets = list("sketch_key_sets").cloned().unwrap_or_default();
|
||||
let mut used = bytes(sheets.as_slice()).iter().chain(bytes(key_sets.as_slice()).iter()).fold(0usize, |t, b| t.saturating_add(*b));
|
||||
let mut used = [&sheets, &threads, &key_sets].iter().flat_map(|l| bytes(l.as_slice())).fold(0usize, |t, b| t.saturating_add(b));
|
||||
for s in std::iter::once(last).chain(order) {
|
||||
let (mut adds, mut new_names, mut new_sketches) = (HashMap::new(), std::collections::HashSet::new(), std::collections::HashSet::new());
|
||||
let mut records = *state_bytes.get(s)?;
|
||||
@@ -722,7 +810,8 @@ impl SavedModel {
|
||||
}
|
||||
let mut records = json!({
|
||||
"features": out_features, "states": out_states, "model_bodies": out_bodies, "names": kept_of(names.as_slice(), &names_used),
|
||||
"sketches": kept_of(sketches.as_slice(), &sketches_used), "sheets": sheets, "rounds": out_rounds, "sketch_key_sets": key_sets,
|
||||
"sketches": kept_of(sketches.as_slice(), &sketches_used), "sheets": sheets, "threads": threads, "rounds": out_rounds,
|
||||
"sketch_key_sets": key_sets,
|
||||
});
|
||||
// The check mode the results were evaluated under, as the model records it (a model
|
||||
// that records none stays without one: adopting it is refused either way).
|
||||
|
||||
@@ -0,0 +1,530 @@
|
||||
//! A design a newer SolidCraft saved: it opens, with a warning, and saving it loses nothing.
|
||||
//!
|
||||
//! A newer format number, or a change the file names that this build doesn't have, says a newer
|
||||
//! build saved the file. It may use nothing this build lacks, so it is read as usual. When part of
|
||||
//! it can't be read, that part stays out of the way and the rest opens:
|
||||
//! - a feature this build can't read keeps its place on the timeline as
|
||||
//! [`FeatureKind::NewerFileFeature`](crate::FeatureKind::NewerFileFeature), which reports an error when evaluated;
|
||||
//! - a parameter it can't read is left out;
|
||||
//! - any other top-level section it can't read is left out whole;
|
||||
//! - a field it doesn't know, which typed reading would drop, is noticed by comparing the file
|
||||
//! with what was read.
|
||||
//!
|
||||
//! All of these are named in [`NewerFile::not_understood`] and go back into the file when it is
|
||||
//! saved ([`restore`]), as they came. Saving keeps the file's own format number and change list:
|
||||
//! the file still holds what the newer build wrote, and a lower number would make that build
|
||||
//! upgrade its own data as if it were old. What can't be put back because the design changed around
|
||||
//! it ([`not_kept`]) is said in the reply to the save.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Map, Value, json};
|
||||
|
||||
use crate::format::{self, DESIGN, Newer};
|
||||
use crate::{DocError, Document, Result};
|
||||
|
||||
/// How a [`RawFeature`] is written while it is inside this build; [`restore`] unwraps it.
|
||||
const FOREIGN_TAG: &str = "newer_file_feature";
|
||||
/// Sections a design can't open without: filled in when the file's can't be read.
|
||||
const CORE: [&str; 3] = ["name", "params", "features"];
|
||||
/// The most sections left out one at a time to find the one a design can't be read with.
|
||||
const MAX_SECTION_TRIES: usize = 64;
|
||||
/// Fields a design container adds beside what a design holds; not unknown to this build.
|
||||
const CONTAINER_ONLY: [&str; 6] = ["step_blob", "mesh_blob", "image_blob", "points_blob", "hdr_blob", "texture_blob"];
|
||||
|
||||
/// A feature as a newer build wrote it.
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct RawFeature(pub Box<Value>);
|
||||
|
||||
impl RawFeature {
|
||||
/// The kind of feature it says it is.
|
||||
pub fn kind(&self) -> &str {
|
||||
self.0.get("type").and_then(Value::as_str).unwrap_or("unnamed")
|
||||
}
|
||||
|
||||
/// Why it doesn't build here.
|
||||
pub fn why(&self) -> String {
|
||||
format!("saved by a newer SolidCraft: this version can't read a `{}` feature. Update SolidCraft to build it; saving keeps it", self.kind())
|
||||
}
|
||||
}
|
||||
|
||||
/// What the file says and holds that this build doesn't read.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct NewerFile {
|
||||
/// The number the file says.
|
||||
pub format: u32,
|
||||
/// The number this build reads up to.
|
||||
pub reads_up_to: u32,
|
||||
/// The changes the file names that this build doesn't have.
|
||||
pub unknown_changes: Vec<String>,
|
||||
/// What this build couldn't read, by name.
|
||||
pub not_understood: Vec<String>,
|
||||
/// Fields known records lost in typed reading.
|
||||
kept: Option<Kept>,
|
||||
/// Top-level sections left out whole.
|
||||
sections: Vec<Section>,
|
||||
/// Elements left out of a top-level list: its key and the element.
|
||||
elements: Vec<(String, Value)>,
|
||||
/// The parts of a design container this build has no use for: name and bytes. A blob only one
|
||||
/// of the kept pieces refers to is one of them.
|
||||
pub parts: Vec<(String, Arc<[u8]>)>,
|
||||
/// The file's `container.json`, when this build doesn't know what it says.
|
||||
pub container: Option<Value>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
struct Section {
|
||||
key: String,
|
||||
value: Value,
|
||||
/// This build's record of the section, without the file's: while it is unchanged, the file's
|
||||
/// goes back.
|
||||
empty: Option<Value>,
|
||||
}
|
||||
|
||||
/// The fields of a record that typed reading dropped.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
enum Kept {
|
||||
Field(Value),
|
||||
Fields(BTreeMap<String, Kept>),
|
||||
Items(Vec<(Anchor, Kept)>),
|
||||
}
|
||||
|
||||
/// Which element of a list some kept fields belong to.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
enum Anchor {
|
||||
Id(u64),
|
||||
Name(String),
|
||||
/// Its place, good only while the list keeps its length.
|
||||
Index {
|
||||
at: usize,
|
||||
of: usize,
|
||||
},
|
||||
}
|
||||
|
||||
impl NewerFile {
|
||||
/// What to tell the user, whatever else is said.
|
||||
pub fn notice(&self) -> String {
|
||||
let reads = self.reads_up_to;
|
||||
let format = self.format;
|
||||
match self.unknown_changes.first() {
|
||||
Some(change) if format <= reads => {
|
||||
format!(
|
||||
"Saved by a newer SolidCraft (format {format} with `{change}`, which this version doesn't know). If something's missing, update."
|
||||
)
|
||||
}
|
||||
_ => format!("Saved by a newer SolidCraft (format {format}, this version reads up to {reads}). If something's missing, update."),
|
||||
}
|
||||
}
|
||||
|
||||
/// Every line to report: the notice, then what couldn't be read, by name.
|
||||
pub fn warnings(&self) -> Vec<String> {
|
||||
let unread = self.not_understood.iter().map(|what| format!("Not read: {what}. Saving keeps it."));
|
||||
std::iter::once(self.notice()).chain(unread).collect()
|
||||
}
|
||||
|
||||
/// What a reply says about it.
|
||||
pub fn report(&self) -> Value {
|
||||
json!({
|
||||
"format": self.format,
|
||||
"reads_up_to": self.reads_up_to,
|
||||
"unknown_changes": self.unknown_changes,
|
||||
"not_understood": self.not_understood,
|
||||
"notice": self.notice(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Whether the next save needs a container to keep what the file holds: plain JSON has no room
|
||||
/// for parts beside the design.
|
||||
pub fn needs_container(&self) -> bool {
|
||||
!self.parts.is_empty()
|
||||
}
|
||||
|
||||
/// Whether anything kept back names this text (a blob's name in a container).
|
||||
pub fn mentions(&self, text: &str) -> bool {
|
||||
fn value(v: &Value, text: &str) -> bool {
|
||||
match v {
|
||||
Value::String(s) => s == text,
|
||||
Value::Array(a) => a.iter().any(|x| value(x, text)),
|
||||
Value::Object(o) => o.values().any(|x| value(x, text)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
fn kept(k: &Kept, text: &str) -> bool {
|
||||
match k {
|
||||
Kept::Field(v) => value(v, text),
|
||||
Kept::Fields(m) => m.values().any(|x| kept(x, text)),
|
||||
Kept::Items(l) => l.iter().any(|(_, x)| kept(x, text)),
|
||||
}
|
||||
}
|
||||
self.kept.as_ref().is_some_and(|k| kept(k, text))
|
||||
|| self.sections.iter().any(|s| value(&s.value, text))
|
||||
|| self.elements.iter().any(|(_, v)| value(v, text))
|
||||
}
|
||||
|
||||
/// Say that a section the container reader couldn't finish reading is left out.
|
||||
/// `empty` is what the design writes for the section while it holds nothing, if anything.
|
||||
pub fn keep_section(&mut self, key: &str, value: Value, empty: Option<Value>, why: &str) {
|
||||
self.not_understood.push(format!("section `{key}` ({why})"));
|
||||
self.sections.push(Section { key: key.into(), value, empty });
|
||||
}
|
||||
}
|
||||
|
||||
/// Read a design a newer build saved, or say why it truly can't be read.
|
||||
pub(crate) fn read(mut raw: Value, version: u32, applied: Option<Vec<String>>, newer: Newer) -> Result<Document> {
|
||||
let applied_names = applied.as_deref();
|
||||
let mut file =
|
||||
NewerFile { format: newer.format, reads_up_to: newer.reads_up_to, unknown_changes: newer.unknown_changes.clone(), ..NewerFile::default() };
|
||||
let (mut doc, read_as) = match format::decode(raw.clone(), version, applied_names) {
|
||||
Ok(doc) => (doc, raw),
|
||||
Err(_) => {
|
||||
let mut foreign = BTreeMap::new();
|
||||
let mut doc = salvage(&mut raw, version, applied_names, &mut file, &mut foreign).map_err(|why| {
|
||||
DocError::Invalid(format!(
|
||||
"this design was saved by a newer SolidCraft (format {}, this version reads up to {}) and this version can't read it: {why}. Update SolidCraft to open it",
|
||||
newer.format, newer.reads_up_to
|
||||
))
|
||||
})?;
|
||||
fill_foreign(&mut doc, foreign);
|
||||
(doc, raw)
|
||||
}
|
||||
};
|
||||
let mut upgraded = read_as;
|
||||
// Compared with what was read, the file's own shape after the same upgrade.
|
||||
DESIGN.upgrade(&mut upgraded, version, applied_names)?;
|
||||
let known = serde_json::to_value(&doc).map_err(|e| DocError::Invalid(format!("design file: {e}")))?;
|
||||
let mut paths = Vec::new();
|
||||
file.kept = vanished(&upgraded, &known, "", &mut paths);
|
||||
file.not_understood.extend(paths);
|
||||
for section in &mut file.sections {
|
||||
section.empty = known.get(§ion.key).cloned();
|
||||
}
|
||||
if newer.format > newer.reads_up_to {
|
||||
// The file's own number and list: it still holds what that build wrote.
|
||||
doc.format = DESIGN.format_string(newer.format);
|
||||
doc.format_pending = applied.unwrap_or_default();
|
||||
} else {
|
||||
// Upgraded to this build's format; the changes it doesn't know are still in it.
|
||||
doc.format_pending.extend(newer.unknown_changes.iter().cloned());
|
||||
}
|
||||
doc.newer_file = Some(Arc::new(file));
|
||||
Ok(doc)
|
||||
}
|
||||
|
||||
/// Read the design around what can't be read, which moves to `file`. `raw` ends as what was read.
|
||||
fn salvage(
|
||||
raw: &mut Value,
|
||||
version: u32,
|
||||
applied: Option<&[String]>,
|
||||
file: &mut NewerFile,
|
||||
foreign: &mut BTreeMap<u64, Value>,
|
||||
) -> std::result::Result<Document, String> {
|
||||
let piece = |key: &str, value: &Value| {
|
||||
let mut stub = json!({"name": "", "params": [], "features": []});
|
||||
stub[key] = value.clone();
|
||||
format::check_piece(stub, version, applied)
|
||||
};
|
||||
let Some(top) = raw.as_object_mut() else { return Err("expected a JSON object".into()) };
|
||||
// A feature it can't read keeps its place; a parameter it can't read stays out.
|
||||
if let Some(Value::Array(features)) = top.get_mut("features") {
|
||||
let mut free = features.iter().filter_map(|f| f.get("id").and_then(Value::as_u64)).max().unwrap_or(0).saturating_add(1);
|
||||
for feature in features.iter_mut() {
|
||||
if let Err(why) = piece("features", &json!([feature])) {
|
||||
file.not_understood.push(feature_note(feature, &why.to_string()));
|
||||
*feature = wrapped(std::mem::take(feature), &mut free, foreign);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(Value::Array(params)) = top.get_mut("params") {
|
||||
params.retain(|param| match piece("params", &json!([param])) {
|
||||
Ok(()) => true,
|
||||
Err(why) => {
|
||||
let name = param.get("name").and_then(Value::as_str).unwrap_or("unnamed");
|
||||
file.not_understood.push(format!("parameter `{name}` ({})", short(&why.to_string())));
|
||||
file.elements.push(("params".into(), param.clone()));
|
||||
false
|
||||
}
|
||||
});
|
||||
}
|
||||
// Any other section, by itself.
|
||||
let keys: Vec<String> = top.keys().cloned().collect();
|
||||
for key in &keys {
|
||||
let value = top.get(key).cloned().unwrap_or_default();
|
||||
if let Err(why) = piece(key, &value) {
|
||||
move_out(top, key, &why.to_string(), file);
|
||||
}
|
||||
}
|
||||
if !top.get("name").is_some_and(Value::is_string) {
|
||||
top.insert("name".into(), json!("Untitled"));
|
||||
}
|
||||
for key in ["params", "features"] {
|
||||
top.entry(key).or_insert_with(|| json!([]));
|
||||
}
|
||||
let attempt = |top: &Map<String, Value>| format::decode(Value::Object(top.clone()), version, applied);
|
||||
let first = match attempt(top) {
|
||||
Ok(doc) => return Ok(doc),
|
||||
Err(why) => why,
|
||||
};
|
||||
// Sections that are fine by themselves but not together with the rest.
|
||||
let keys: Vec<String> = top.keys().filter(|k| !CORE.contains(&k.as_str())).cloned().collect();
|
||||
// Each try reads the whole design: a file of many sections goes straight to the last resort.
|
||||
for key in keys.iter().filter(|_| keys.len() <= MAX_SECTION_TRIES) {
|
||||
let mut without = top.clone();
|
||||
without.remove(key);
|
||||
if let Ok(doc) = attempt(&without) {
|
||||
move_out(top, key, &first.to_string(), file);
|
||||
return Ok(doc);
|
||||
}
|
||||
}
|
||||
let mut bare = top.clone();
|
||||
bare.retain(|key, _| CORE.contains(&key.as_str()));
|
||||
match attempt(&bare) {
|
||||
Ok(doc) => {
|
||||
for key in &keys {
|
||||
move_out(top, key, &first.to_string(), file);
|
||||
}
|
||||
Ok(doc)
|
||||
}
|
||||
Err(_) => Err(first.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Leave a section out and keep it. A section the design needs is left empty.
|
||||
fn move_out(top: &mut Map<String, Value>, key: &str, why: &str, file: &mut NewerFile) {
|
||||
let Some(value) = top.remove(key) else { return };
|
||||
file.not_understood.push(format!("section `{key}` ({})", short(why)));
|
||||
file.sections.push(Section { key: key.into(), value, empty: None });
|
||||
}
|
||||
|
||||
/// The first line of a reader's error, without the list of every kind it knows.
|
||||
fn short(why: &str) -> &str {
|
||||
let why = why.split(", expected").next().unwrap_or(why);
|
||||
why.strip_prefix("design file: ").unwrap_or(why)
|
||||
}
|
||||
|
||||
fn feature_note(feature: &Value, why: &str) -> String {
|
||||
let name = feature.get("name").and_then(Value::as_str).unwrap_or("unnamed");
|
||||
let kind = feature.get("type").and_then(Value::as_str).unwrap_or("unnamed");
|
||||
let why = short(why);
|
||||
if why.starts_with("unknown variant") {
|
||||
format!("feature `{name}`, a `{kind}` feature this version doesn't know")
|
||||
} else {
|
||||
format!("feature `{name}`, a `{kind}` feature ({why})")
|
||||
}
|
||||
}
|
||||
|
||||
/// A feature this build can't read as this build reads it: the fields every feature has, which stay
|
||||
/// editable. Its `raw` is filled in after reading ([`fill_foreign`]), so nothing in a feature this
|
||||
/// build can't read is judged by this build's rules. `free` gives an id to one the file left
|
||||
/// without.
|
||||
fn wrapped(raw: Value, free: &mut u64, foreign: &mut BTreeMap<u64, Value>) -> Value {
|
||||
let id = raw.get("id").and_then(Value::as_u64).unwrap_or_else(|| {
|
||||
let id = *free;
|
||||
*free = free.saturating_add(1);
|
||||
id
|
||||
});
|
||||
let name = raw.get("name").and_then(Value::as_str).unwrap_or("Unknown feature").to_string();
|
||||
let suppressed = raw.get("suppressed").and_then(Value::as_bool).unwrap_or(false);
|
||||
let component = raw.get("component").and_then(Value::as_u64).unwrap_or(0);
|
||||
foreign.insert(id, raw);
|
||||
json!({"id": id, "name": name, "suppressed": suppressed, "component": component, "type": FOREIGN_TAG, "raw": null})
|
||||
}
|
||||
|
||||
/// Give the features [`wrapped`] left without one what the file said.
|
||||
fn fill_foreign(doc: &mut Document, mut foreign: BTreeMap<u64, Value>) {
|
||||
for feature in &mut doc.features {
|
||||
if let crate::FeatureKind::NewerFileFeature { raw } = &mut feature.kind
|
||||
&& let Some(value) = foreign.remove(&feature.id)
|
||||
{
|
||||
*raw.0 = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A feature back as the file had it, with the changes this build can make to any feature.
|
||||
fn unwrapped(feature: &mut Value) {
|
||||
let Some(wrapper) = feature.as_object_mut() else { return };
|
||||
let Some(Value::Object(mut raw)) = wrapper.remove("raw") else { return };
|
||||
for key in ["id", "name", "suppressed", "component"] {
|
||||
match wrapper.get(key) {
|
||||
// Left out as a writer leaves it out, unless the file said it.
|
||||
Some(v) if key != "id" && !raw.contains_key(key) && looks_default(v) => {}
|
||||
Some(v) => {
|
||||
raw.insert(key.into(), v.clone());
|
||||
}
|
||||
None => {
|
||||
raw.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
*feature = Value::Object(raw);
|
||||
}
|
||||
|
||||
/// A value a writer leaves out without losing anything: not worth telling anyone about.
|
||||
fn looks_default(v: &Value) -> bool {
|
||||
match v {
|
||||
Value::Null => true,
|
||||
Value::Bool(b) => !b,
|
||||
Value::Number(n) => n.as_f64() == Some(0.0),
|
||||
Value::String(s) => s.is_empty(),
|
||||
Value::Array(a) => a.is_empty(),
|
||||
Value::Object(o) => o.is_empty(),
|
||||
}
|
||||
}
|
||||
|
||||
/// What `raw` has that `known`, what was read of it, doesn't. `paths` gets those fields' names.
|
||||
fn vanished(raw: &Value, known: &Value, path: &str, paths: &mut Vec<String>) -> Option<Kept> {
|
||||
match (raw, known) {
|
||||
(Value::Object(raw), Value::Object(known)) => {
|
||||
if known.get("type").and_then(Value::as_str) == Some(FOREIGN_TAG) {
|
||||
return None;
|
||||
}
|
||||
let mut kept = BTreeMap::new();
|
||||
for (key, value) in raw {
|
||||
if CONTAINER_ONLY.contains(&key.as_str()) || (path.is_empty() && matches!(key.as_str(), "format" | "format_pending")) {
|
||||
continue;
|
||||
}
|
||||
let inner = if path.is_empty() { key.clone() } else { format!("{path}.{key}") };
|
||||
match known.get(key) {
|
||||
None => {
|
||||
if !looks_default(value) {
|
||||
paths.push(format!("field `{inner}`"));
|
||||
}
|
||||
kept.insert(key.clone(), Kept::Field(value.clone()));
|
||||
}
|
||||
Some(known) => {
|
||||
if let Some(k) = vanished(value, known, &inner, paths) {
|
||||
kept.insert(key.clone(), k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(!kept.is_empty()).then_some(Kept::Fields(kept))
|
||||
}
|
||||
(Value::Array(raw), Value::Array(known)) => {
|
||||
let mut items = Vec::new();
|
||||
for (at, element) in raw.iter().enumerate() {
|
||||
let anchor = anchor_of(element).unwrap_or(Anchor::Index { at, of: raw.len() });
|
||||
let Some(partner) = find(known, &anchor) else { continue };
|
||||
let label = match (element.get("name").and_then(Value::as_str), &anchor) {
|
||||
(Some(name), _) => format!("{path}[{name}]"),
|
||||
(None, Anchor::Id(id)) => format!("{path}[{id}]"),
|
||||
_ => format!("{path}[{at}]"),
|
||||
};
|
||||
if let Some(k) = vanished(element, partner, &label, paths) {
|
||||
items.push((anchor, k));
|
||||
}
|
||||
}
|
||||
(!items.is_empty()).then_some(Kept::Items(items))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn anchor_of(element: &Value) -> Option<Anchor> {
|
||||
if let Some(id) = element.get("id").and_then(Value::as_u64) {
|
||||
return Some(Anchor::Id(id));
|
||||
}
|
||||
element.get("name").and_then(Value::as_str).map(|name| Anchor::Name(name.to_string()))
|
||||
}
|
||||
|
||||
fn find<'a>(list: &'a [Value], anchor: &Anchor) -> Option<&'a Value> {
|
||||
match anchor {
|
||||
Anchor::Id(id) => list.iter().find(|x| x.get("id").and_then(Value::as_u64) == Some(*id)),
|
||||
Anchor::Name(name) => list.iter().find(|x| x.get("name").and_then(Value::as_str) == Some(name)),
|
||||
Anchor::Index { at, of } => (list.len() == *of).then(|| list.get(*at)).flatten(),
|
||||
}
|
||||
}
|
||||
|
||||
fn find_mut<'a>(list: &'a mut [Value], anchor: &Anchor) -> Option<&'a mut Value> {
|
||||
match anchor {
|
||||
Anchor::Id(id) => list.iter_mut().find(|x| x.get("id").and_then(Value::as_u64) == Some(*id)),
|
||||
Anchor::Name(name) => list.iter_mut().find(|x| x.get("name").and_then(Value::as_str) == Some(name)),
|
||||
Anchor::Index { at, of } => (list.len() == *of).then(|| list.get_mut(*at)).flatten(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Put the kept fields back. An element that is gone was deleted here, its fields with it; one
|
||||
/// whose place can't be told any more, and a field whose record is gone, go into `lost`.
|
||||
fn apply(kept: &Kept, value: &mut Value, path: &str, lost: &mut Vec<String>) {
|
||||
match kept {
|
||||
Kept::Field(_) => {}
|
||||
Kept::Fields(fields) => {
|
||||
let Some(object) = value.as_object_mut() else {
|
||||
lost.push(format!("field `{path}`"));
|
||||
return;
|
||||
};
|
||||
for (key, sub) in fields {
|
||||
let inner = if path.is_empty() { key.clone() } else { format!("{path}.{key}") };
|
||||
match (sub, object.get_mut(key)) {
|
||||
(Kept::Field(v), None) => {
|
||||
object.insert(key.clone(), v.clone());
|
||||
}
|
||||
(Kept::Field(_), Some(_)) => {}
|
||||
(sub, Some(inner_value)) => apply(sub, inner_value, &inner, lost),
|
||||
// The list it was in is empty and not written: its elements were deleted.
|
||||
(Kept::Items(_), None) => {}
|
||||
(_, None) => lost.push(format!("field `{inner}`")),
|
||||
}
|
||||
}
|
||||
}
|
||||
Kept::Items(items) => {
|
||||
let Some(list) = value.as_array_mut() else {
|
||||
lost.push(format!("field `{path}`"));
|
||||
return;
|
||||
};
|
||||
for (anchor, sub) in items {
|
||||
match (find_mut(list, anchor), anchor) {
|
||||
(Some(element), _) => apply(sub, element, path, lost),
|
||||
(None, Anchor::Index { at, .. }) => lost.push(format!("element {at} of `{path}`")),
|
||||
(None, _) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Write back what the design holds of the file a newer build saved, into `value`, the design's
|
||||
/// JSON. Returns what couldn't go back ([`not_kept`]).
|
||||
pub fn restore(doc: &Document, value: &mut Value) -> Vec<String> {
|
||||
let mut lost = Vec::new();
|
||||
let Some(file) = &doc.newer_file else { return lost };
|
||||
if let Some(Value::Array(features)) = value.get_mut("features") {
|
||||
for feature in features.iter_mut().filter(|f| f.get("type").and_then(Value::as_str) == Some(FOREIGN_TAG)) {
|
||||
unwrapped(feature);
|
||||
}
|
||||
}
|
||||
if let Some(kept) = &file.kept {
|
||||
apply(kept, value, "", &mut lost);
|
||||
}
|
||||
if let Some(top) = value.as_object_mut() {
|
||||
for section in &file.sections {
|
||||
if top.get(§ion.key) == section.empty.as_ref() {
|
||||
top.insert(section.key.clone(), section.value.clone());
|
||||
} else {
|
||||
lost.push(format!("section `{}`, replaced by what was edited here", section.key));
|
||||
}
|
||||
}
|
||||
for (key, element) in &file.elements {
|
||||
let name = element.get("name");
|
||||
match top.entry(key.clone()).or_insert_with(|| json!([])) {
|
||||
Value::Array(list) if name.is_none() || !list.iter().any(|x| x.get("name") == name) => list.push(element.clone()),
|
||||
_ => lost.push(format!("`{key}` element {}, replaced by what was made here", name.and_then(Value::as_str).unwrap_or("unnamed"))),
|
||||
}
|
||||
}
|
||||
}
|
||||
lost
|
||||
}
|
||||
|
||||
/// What of the file a newer build saved the next save can't keep, because the design was changed
|
||||
/// around it. Empty almost always.
|
||||
pub fn not_kept(doc: &Document) -> Vec<String> {
|
||||
let Ok(mut value) = serde_json::to_value(doc) else { return Vec::new() };
|
||||
restore(doc, &mut value)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "newer_file_tests.rs"]
|
||||
mod tests;
|
||||
@@ -0,0 +1,236 @@
|
||||
//! Designs a newer SolidCraft saved: they open, say so, and saving loses nothing.
|
||||
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use crate::format::{DESIGN, FORMAT, format_string};
|
||||
use crate::{Document, FeatureKind, Model, PlaneRef};
|
||||
|
||||
/// A real design: a sketched disc and its extrusion, and a parameter.
|
||||
fn design() -> Document {
|
||||
use solidcraft_sketch::Sketch;
|
||||
let mut d = Document::new("Plate");
|
||||
d.set_param("depth", "5 mm", Some("mm"), None).unwrap();
|
||||
let mut sketch = Sketch::new();
|
||||
sketch.add_circle(solidcore_record::Vec2::new(0.0, 0.0), 10.0, None, None).unwrap();
|
||||
let base = d.add_feature(FeatureKind::Sketch { plane: PlaneRef::Origin { name: "XY".into() }, sketch }, Some("Base")).unwrap();
|
||||
d.add_feature(extrude(base), Some("Pad")).unwrap();
|
||||
d
|
||||
}
|
||||
|
||||
fn extrude(sketch: u64) -> FeatureKind {
|
||||
FeatureKind::Extrude {
|
||||
thin: None,
|
||||
sketch: crate::SketchId(sketch),
|
||||
profiles: crate::ProfileSel::All,
|
||||
extent: serde_json::from_value(json!({"distance": "depth"})).unwrap(),
|
||||
operation: crate::Operation::NewBody,
|
||||
targets: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn saved(d: &Document) -> Value {
|
||||
serde_json::from_str(&d.to_json()).unwrap()
|
||||
}
|
||||
|
||||
/// `(body name, volume)` of everything the design builds, and the error of each feature.
|
||||
fn built(d: &Document) -> (Vec<(String, f64)>, Vec<Option<String>>) {
|
||||
let mut model = Model::default();
|
||||
model.evaluate(d);
|
||||
let bodies = model.state().bodies.iter().map(|b| (b.name.clone(), b.body.volume_at(0.01).unwrap())).collect();
|
||||
(bodies, model.results.iter().map(|r| r.error.clone()).collect())
|
||||
}
|
||||
|
||||
fn newer() -> String {
|
||||
format_string(FORMAT + 1)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_format_newer_with_nothing_unknown_opens_with_the_same_bodies() {
|
||||
let original = design();
|
||||
let mut file = saved(&original);
|
||||
file["format"] = json!(newer());
|
||||
let opened = Document::from_json(&file.to_string()).unwrap();
|
||||
let (bodies, errors) = built(&opened);
|
||||
assert_eq!((bodies.clone(), errors), built(&original));
|
||||
assert_eq!(bodies.len(), 1);
|
||||
assert_eq!(opened.features, original.features);
|
||||
let notice = opened.newer_file.as_ref().unwrap().notice();
|
||||
assert_eq!(
|
||||
notice,
|
||||
format!("Saved by a newer SolidCraft (format {}, this version reads up to {FORMAT}). If something's missing, update.", FORMAT + 1)
|
||||
);
|
||||
assert!(opened.newer_file.as_ref().unwrap().not_understood.is_empty());
|
||||
// Saved, it still says what it is; read again, the same.
|
||||
let again = saved(&opened);
|
||||
assert_eq!(again["format"], json!(newer()));
|
||||
assert_eq!(Document::from_json(&opened.to_json()).unwrap(), opened);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_unknown_change_opens_with_a_warning_and_stays_on_the_file() {
|
||||
let original = design();
|
||||
let mut file = saved(&original);
|
||||
file["format_pending"] = json!(["a-later-change"]);
|
||||
let opened = Document::from_json(&file.to_string()).unwrap();
|
||||
let report = opened.newer_file.as_ref().unwrap();
|
||||
assert_eq!(report.unknown_changes, ["a-later-change"]);
|
||||
assert!(report.notice().contains("a-later-change") && report.notice().starts_with("Saved by a newer SolidCraft"), "{}", report.notice());
|
||||
assert_eq!(built(&opened), built(&original));
|
||||
let again = saved(&opened);
|
||||
assert_eq!(again["format"], json!(format_string(FORMAT)));
|
||||
assert!(again["format_pending"].as_array().unwrap().contains(&json!("a-later-change")));
|
||||
assert_eq!(Document::from_json(&opened.to_json()).unwrap(), opened);
|
||||
}
|
||||
|
||||
/// The file, with a feature of a kind this build doesn't have between the sketch and the pad.
|
||||
fn with_unknown_feature() -> Value {
|
||||
let mut file = saved(&design());
|
||||
file["format"] = json!(newer());
|
||||
file["format_pending"] = json!(["a-later-change"]);
|
||||
let future = json!({"id": 900, "name": "Lattice1", "type": "gyroid_lattice", "cell": 2.5, "nested": {"big": 1e300}, "suppressed": false});
|
||||
file["features"].as_array_mut().unwrap().insert(1, future);
|
||||
file
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_unknown_feature_kind_opens_the_rest_and_reports_that_feature() {
|
||||
let file = with_unknown_feature();
|
||||
let opened = Document::from_json(&file.to_string()).unwrap();
|
||||
let report = opened.newer_file.as_ref().unwrap();
|
||||
assert!(report.not_understood.iter().any(|what| what.contains("Lattice1") && what.contains("gyroid_lattice")), "{:?}", report.not_understood);
|
||||
assert_eq!(opened.features.len(), 3);
|
||||
assert!(matches!(&opened.features[1].kind, FeatureKind::NewerFileFeature { raw } if raw.kind() == "gyroid_lattice"));
|
||||
let (bodies, errors) = built(&opened);
|
||||
assert_eq!(bodies.len(), 1, "the pad after it still builds");
|
||||
assert!(errors[0].is_none() && errors[2].is_none(), "{errors:?}");
|
||||
let why = errors[1].as_deref().unwrap();
|
||||
assert!(why.contains("newer SolidCraft") && why.contains("gyroid_lattice"), "{why}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saving_keeps_an_unknown_feature_as_the_file_had_it_and_the_files_number() {
|
||||
let file = with_unknown_feature();
|
||||
let opened = Document::from_json(&file.to_string()).unwrap();
|
||||
let again = saved(&opened);
|
||||
assert_eq!(again["format"], file["format"], "never a lower number");
|
||||
assert_eq!(again["format_pending"], file["format_pending"]);
|
||||
assert_eq!(again["features"][1], file["features"][1], "the feature as the file had it");
|
||||
assert_eq!(
|
||||
again["features"].as_array().unwrap().iter().map(|f| f["type"].clone()).collect::<Vec<_>>(),
|
||||
[json!("sketch"), json!("gyroid_lattice"), json!("extrude")]
|
||||
);
|
||||
assert_eq!(Document::from_json(&opened.to_json()).unwrap(), opened, "saved and read again, the same");
|
||||
// A rename here goes with it; the rest stays.
|
||||
let mut renamed = opened.clone();
|
||||
renamed.features[1].name = "Infill".into();
|
||||
let again = saved(&renamed);
|
||||
assert_eq!(again["features"][1]["name"], "Infill");
|
||||
assert_eq!(again["features"][1]["nested"], json!({"big": 1e300}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saving_keeps_fields_and_sections_this_version_doesnt_know() {
|
||||
let mut file = saved(&design());
|
||||
file["format"] = json!(newer());
|
||||
file["future_section"] = json!({"anything": [1, 2, 3]});
|
||||
file["features"][1]["draft_v2"] = json!({"angle": 3.0});
|
||||
file["params"][0]["future_note"] = json!("keep me");
|
||||
let opened = Document::from_json(&file.to_string()).unwrap();
|
||||
assert_eq!(built(&opened).0.len(), 1);
|
||||
let report = opened.newer_file.as_ref().unwrap();
|
||||
for field in ["future_section", "draft_v2", "future_note"] {
|
||||
assert!(report.not_understood.iter().any(|what| what.contains(field)), "{field}: {:?}", report.not_understood);
|
||||
}
|
||||
let again = saved(&opened);
|
||||
assert_eq!(again["future_section"], file["future_section"]);
|
||||
assert_eq!(again["features"][1]["draft_v2"], file["features"][1]["draft_v2"]);
|
||||
assert_eq!(again["params"][0]["future_note"], "keep me");
|
||||
// They follow their records when the design changes around them.
|
||||
let mut changed = opened.clone();
|
||||
let id = changed.features[1].id;
|
||||
changed.features.insert(0, changed.features[0].clone());
|
||||
changed.features[0].id = 777;
|
||||
assert_eq!(changed.feature(id).unwrap().name, "Pad");
|
||||
let again = saved(&changed);
|
||||
assert_eq!(again["features"][2]["draft_v2"], file["features"][1]["draft_v2"]);
|
||||
assert!(crate::newer_file::not_kept(&changed).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_section_that_doesnt_read_is_left_out_named_and_kept() {
|
||||
let mut file = saved(&design());
|
||||
file["format"] = json!(newer());
|
||||
file["configs"] = json!({"columns": [{"unknown_future_column": "Body"}], "rules": {"unknown_future_rule": true}});
|
||||
let opened = Document::from_json(&file.to_string()).unwrap();
|
||||
assert_eq!(built(&opened).0.len(), 1);
|
||||
let report = opened.newer_file.as_ref().unwrap();
|
||||
assert!(report.not_understood.iter().any(|what| what.contains("configs")), "{:?}", report.not_understood);
|
||||
assert_eq!(saved(&opened)["configs"], file["configs"]);
|
||||
assert!(crate::newer_file::not_kept(&opened).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_parameter_that_doesnt_read_is_left_out_named_and_kept() {
|
||||
let mut file = saved(&design());
|
||||
file["format"] = json!(newer());
|
||||
file["params"].as_array_mut().unwrap().push(json!({"name": "future", "expression": {"kind": "table"}}));
|
||||
let opened = Document::from_json(&file.to_string()).unwrap();
|
||||
assert_eq!(built(&opened).0.len(), 1);
|
||||
assert!(opened.newer_file.as_ref().unwrap().not_understood.iter().any(|what| what.contains("`future`")));
|
||||
let names: Vec<_> = saved(&opened)["params"].as_array().unwrap().iter().map(|p| p["name"].clone()).collect();
|
||||
assert!(names.contains(&json!("future")) && names.contains(&json!("depth")), "{names:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn what_cant_go_back_after_an_edit_is_said() {
|
||||
let mut file = saved(&design());
|
||||
file["format"] = json!(newer());
|
||||
file["configs"] = json!({"columns": [{"unknown_future_column": "Body"}]});
|
||||
let opened = Document::from_json(&file.to_string()).unwrap();
|
||||
assert!(crate::newer_file::not_kept(&opened).is_empty());
|
||||
let mut edited = opened.clone();
|
||||
edited.favorites.insert("depth".into());
|
||||
assert!(crate::newer_file::not_kept(&edited).is_empty(), "an edit elsewhere costs nothing");
|
||||
let baseline: Value = saved(&edited);
|
||||
assert_eq!(baseline["configs"], file["configs"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_feature_this_version_reads_wrongly_is_kept_whole() {
|
||||
// A kind this build has, in a shape it doesn't: the feature can't read, and is not guessed at.
|
||||
let mut file = saved(&design());
|
||||
file["format"] = json!(newer());
|
||||
file["features"][1]["operation"] = json!("weave");
|
||||
let opened = Document::from_json(&file.to_string()).unwrap();
|
||||
assert!(matches!(&opened.features[1].kind, FeatureKind::NewerFileFeature { raw } if raw.kind() == "extrude"));
|
||||
assert!(built(&opened).1[1].as_deref().unwrap().contains("newer SolidCraft"));
|
||||
assert_eq!(saved(&opened)["features"][1], file["features"][1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn what_truly_cant_be_read_says_why() {
|
||||
for (text, says) in [
|
||||
("not json", "HDR resource admission"),
|
||||
("{\"format\":\"solidcraft/0\",\"name\":\"x\"}", "format 0"),
|
||||
("{\"format\":\"someone-else/9\"}", "not a SolidCraft design (format `someone-else/9`)"),
|
||||
] {
|
||||
let why = Document::from_json(text).unwrap_err().to_string();
|
||||
assert!(why.contains(says), "{text}: {why}");
|
||||
}
|
||||
// Two features of one id are damage whatever build saved them.
|
||||
let mut file = saved(&design());
|
||||
file["format"] = json!(newer());
|
||||
let same = file["features"][0].clone();
|
||||
file["features"].as_array_mut().unwrap().push(same);
|
||||
let why = Document::from_json(&file.to_string()).unwrap_err().to_string();
|
||||
assert!(why.contains(&format!("format {}", FORMAT + 1)) && why.contains("can't read it") && why.contains("damaged"), "{why}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_file_this_build_fully_knows_is_not_flagged_and_damage_is_still_refused() {
|
||||
assert!(Document::from_json(&design().to_json()).unwrap().newer_file.is_none());
|
||||
assert_eq!(DESIGN.newer(FORMAT, None), None);
|
||||
let mut file = saved(&design());
|
||||
file["features"][1]["type"] = json!("gyroid_lattice");
|
||||
assert!(Document::from_json(&file.to_string()).is_err(), "of this build's own format, that is damage");
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//! The occurrence tree of a document: each component's child occurrences, gathered in one pass,
|
||||
//! and the walk of every placement from the root over them. Walking it costs the placements,
|
||||
//! where scanning every occurrence for each component visited cost placements times occurrences.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{Document, IDENTITY, MAX_PLACEMENT_DEPTH, MAX_PLACEMENTS, Mat, Occurrence, mat_mul};
|
||||
|
||||
/// Each component's child occurrences, in document order, as a document held them when made.
|
||||
pub(crate) struct OccurrenceTree<'a> {
|
||||
children: HashMap<u64, Vec<&'a Occurrence>>,
|
||||
}
|
||||
|
||||
impl<'a> OccurrenceTree<'a> {
|
||||
pub(crate) fn new(doc: &'a Document) -> Self {
|
||||
let mut children: HashMap<u64, Vec<&Occurrence>> = HashMap::new();
|
||||
for o in &doc.occurrences {
|
||||
children.entry(o.parent).or_default().push(o);
|
||||
}
|
||||
OccurrenceTree { children }
|
||||
}
|
||||
|
||||
/// The occurrences placed in `component`, in document order.
|
||||
pub(crate) fn children(&self, component: u64) -> &[&'a Occurrence] {
|
||||
self.children.get(&component).map_or(&[], Vec::as_slice)
|
||||
}
|
||||
|
||||
/// Every placement from the root: (component, occurrence path ids, world transform), with
|
||||
/// each occurrence placed in its parent by `transform`. Depth first, children in document
|
||||
/// order. A branch stops past [`MAX_PLACEMENT_DEPTH`] levels, the walk past [`MAX_PLACEMENTS`]
|
||||
/// placements.
|
||||
pub(crate) fn placements_with(&self, transform: impl Fn(&Occurrence) -> Mat) -> Vec<(u64, Vec<u64>, Mat)> {
|
||||
let mut out = vec![(0, Vec::new(), IDENTITY)];
|
||||
let mut stack: Vec<(u64, Vec<u64>, Mat)> = vec![(0, Vec::new(), IDENTITY)];
|
||||
while let Some((comp, path, m)) = stack.pop() {
|
||||
if out.len() > MAX_PLACEMENTS {
|
||||
break;
|
||||
}
|
||||
// Only this branch stops past the depth limit; the others are still walked.
|
||||
if path.len() > MAX_PLACEMENT_DEPTH {
|
||||
continue;
|
||||
}
|
||||
for o in self.children(comp) {
|
||||
let mut p = path.clone();
|
||||
p.push(o.id);
|
||||
let w = mat_mul(&m, &transform(o));
|
||||
out.push((o.component, p.clone(), w));
|
||||
stack.push((o.component, p, w));
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "occurrence_tree_tests.rs"]
|
||||
mod tests;
|
||||
@@ -0,0 +1,253 @@
|
||||
use solidcore_record::Vec3;
|
||||
|
||||
use super::OccurrenceTree;
|
||||
use crate::*;
|
||||
|
||||
/// `placements` as it walked before the tree: a scan of every occurrence for each component
|
||||
/// visited. The tree must give exactly this, in this order, for every document.
|
||||
fn scanning_placements(doc: &Document) -> Vec<(u64, Vec<u64>, Mat)> {
|
||||
let mut out = vec![(0, Vec::new(), IDENTITY)];
|
||||
let mut stack: Vec<(u64, Vec<u64>, Mat)> = vec![(0, Vec::new(), IDENTITY)];
|
||||
while let Some((comp, path, m)) = stack.pop() {
|
||||
if out.len() > MAX_PLACEMENTS {
|
||||
break;
|
||||
}
|
||||
if path.len() > MAX_PLACEMENT_DEPTH {
|
||||
continue;
|
||||
}
|
||||
for o in doc.occurrences.iter().filter(|o| o.parent == comp) {
|
||||
let mut p = path.clone();
|
||||
p.push(o.id);
|
||||
let w = mat_mul(&m, &o.transform);
|
||||
out.push((o.component, p.clone(), w));
|
||||
stack.push((o.component, p, w));
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// `inside_component`'s walk as it was before the tree: is `parent` placed anywhere below `component`?
|
||||
fn scanning_inside(doc: &Document, parent: u64, component: u64) -> bool {
|
||||
if doc.component_within(parent, component) {
|
||||
return true;
|
||||
}
|
||||
let mut stack = vec![component];
|
||||
let mut seen = std::collections::BTreeSet::new();
|
||||
while let Some(c) = stack.pop() {
|
||||
if !seen.insert(c) || seen.len() > MAX_PLACEMENTS {
|
||||
continue;
|
||||
}
|
||||
for o in doc.occurrences.iter().filter(|o| o.parent == c) {
|
||||
if o.component == parent {
|
||||
return true;
|
||||
}
|
||||
stack.push(o.component);
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Every (parent, component) pair over the ids a document names, and one id it doesn't.
|
||||
fn same_inside(doc: &Document) -> std::result::Result<(), String> {
|
||||
let mut ids: Vec<u64> = doc.occurrences.iter().flat_map(|o| [o.parent, o.component]).chain([0, 999]).collect();
|
||||
ids.sort_unstable();
|
||||
ids.dedup();
|
||||
for &parent in &ids {
|
||||
for &component in &ids {
|
||||
if doc.inside_component(parent, component) != scanning_inside(doc, parent, component) {
|
||||
return Err(format!("inside_component({parent}, {component})"));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// An occurrence of `component` in `parent`, moved by its id so every path's transform differs.
|
||||
fn occurrence(id: u64, component: u64, parent: u64) -> Occurrence {
|
||||
let t = Vec3::new(id as f64, (id % 7) as f64, 0.5 * (id % 3) as f64);
|
||||
Occurrence {
|
||||
id,
|
||||
component,
|
||||
parent,
|
||||
name: format!("C{component}:{id}"),
|
||||
transform: rigid(t, Vec3::ZERO, Vec3::Z, 0.01 * id as f64),
|
||||
grounded: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn with(occurrences: Vec<Occurrence>) -> Document {
|
||||
let mut doc = Document::new("tree");
|
||||
doc.occurrences = occurrences;
|
||||
doc
|
||||
}
|
||||
|
||||
/// Component `k` placed once in component `k - 1`, `depth` levels below the root.
|
||||
fn chain(depth: u64) -> Document {
|
||||
with((1..=depth).map(|k| occurrence(k, k, k - 1)).collect())
|
||||
}
|
||||
|
||||
/// The documents whose walks must not move: the walk's boundaries and the hostile shapes a file
|
||||
/// can hold.
|
||||
fn shapes() -> Vec<(&'static str, Document)> {
|
||||
// 2 under the root, each holding 400 of component 3, each holding 150 of component 4: past
|
||||
// MAX_PLACEMENTS, so the walk stops part way.
|
||||
let mut wide = vec![occurrence(1, 1, 0), occurrence(2, 1, 0)];
|
||||
wide.extend((0..400).map(|i| occurrence(10 + i, 3, 1)));
|
||||
wide.extend((0..150).map(|i| occurrence(1000 + i, 4, 3)));
|
||||
vec![
|
||||
("empty", with(Vec::new())),
|
||||
("one level", with(vec![occurrence(1, 1, 0), occurrence(2, 2, 0), occurrence(3, 1, 0)])),
|
||||
("depth 63", chain(63)),
|
||||
("depth 64", chain(64)),
|
||||
("depth 65", chain(65)),
|
||||
("depth 66", chain(66)),
|
||||
("a component twice under one parent", with(vec![occurrence(1, 1, 0), occurrence(2, 2, 1), occurrence(3, 2, 1), occurrence(4, 3, 2)])),
|
||||
("an occurrence whose parent is missing", with(vec![occurrence(1, 1, 0), occurrence(2, 2, 99), occurrence(3, 3, 2)])),
|
||||
("a cycle", with(vec![occurrence(1, 1, 0), occurrence(2, 2, 1), occurrence(3, 1, 2)])),
|
||||
("a component inside itself", with(vec![occurrence(1, 1, 0), occurrence(2, 1, 1)])),
|
||||
("duplicated occurrence ids", with(vec![occurrence(1, 1, 0), occurrence(1, 2, 0), occurrence(2, 3, 1), occurrence(2, 3, 2)])),
|
||||
("children listed out of order", with(vec![occurrence(5, 2, 1), occurrence(1, 1, 0), occurrence(4, 3, 2), occurrence(2, 2, 1)])),
|
||||
("past MAX_PLACEMENTS", with(wide)),
|
||||
]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_walk_is_the_scanning_walk_for_every_shape() {
|
||||
for (name, doc) in shapes() {
|
||||
let walked = doc.placements();
|
||||
assert_eq!(walked, scanning_placements(&doc), "{name}");
|
||||
assert_eq!(OccurrenceTree::new(&doc).placements_with(|o| o.transform), walked, "{name}");
|
||||
if doc.occurrences.len() < 100 {
|
||||
assert_eq!(same_inside(&doc), Ok(()), "{name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_walk_stops_at_its_caps_as_before() {
|
||||
let deepest = |doc: &Document| doc.placements().iter().map(|(_, path, _)| path.len()).max();
|
||||
// A branch is walked to MAX_PLACEMENT_DEPTH levels and one past it (today's 65-id path), no further.
|
||||
assert_eq!(deepest(&chain(64)), Some(64));
|
||||
assert_eq!(deepest(&chain(66)), Some(MAX_PLACEMENT_DEPTH + 1));
|
||||
let (_, wide) = shapes().pop().unwrap();
|
||||
let n = wide.placements().len();
|
||||
assert!(n > MAX_PLACEMENTS && n <= MAX_PLACEMENTS + 400, "stops once past the cap: {n}");
|
||||
// A cycle ends at the depth cap, not in a loop.
|
||||
let (_, cycle) = shapes().into_iter().find(|(name, _)| *name == "a cycle").unwrap();
|
||||
assert_eq!(deepest(&cycle), Some(MAX_PLACEMENT_DEPTH + 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn children_are_listed_in_document_order() {
|
||||
let doc = with(vec![occurrence(5, 2, 1), occurrence(1, 1, 0), occurrence(4, 3, 1), occurrence(2, 2, 1), occurrence(3, 9, 7)]);
|
||||
let tree = OccurrenceTree::new(&doc);
|
||||
let ids = |c: u64| tree.children(c).iter().map(|o| o.id).collect::<Vec<_>>();
|
||||
assert_eq!(ids(1), [5, 4, 2]);
|
||||
assert_eq!(ids(0), [1]);
|
||||
assert_eq!(ids(7), [3], "an occurrence under a missing parent is still its child");
|
||||
assert!(ids(2).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_walk_uses_the_transform_it_is_given() {
|
||||
let doc = with(vec![occurrence(1, 1, 0), occurrence(2, 2, 1)]);
|
||||
let moved = rigid(Vec3::new(0.0, 0.0, 9.0), Vec3::ZERO, Vec3::Z, 0.0);
|
||||
let walked = OccurrenceTree::new(&doc).placements_with(|o| if o.id == 1 { moved } else { o.transform });
|
||||
let child = &doc.occurrences[1].transform;
|
||||
assert_eq!(walked[1], (1, vec![1], moved));
|
||||
assert_eq!(walked[2], (2, vec![1, 2], mat_mul(&moved, child)));
|
||||
}
|
||||
|
||||
/// splitmix64, so each run makes the same documents.
|
||||
fn next(state: &mut u64) -> u64 {
|
||||
*state = state.wrapping_add(0x9e37_79b9_7f4a_7c15);
|
||||
let mut z = *state;
|
||||
z = (z ^ (z >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9);
|
||||
z = (z ^ (z >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb);
|
||||
z ^ (z >> 31)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hostile_documents_walk_as_before() {
|
||||
// Few component ids and parents drawn from them, so repeats, missing parents, cycles and
|
||||
// duplicated ids all come up.
|
||||
let mut state = 4032;
|
||||
for round in 0..300 {
|
||||
let n = next(&mut state) % 40;
|
||||
let components = 1 + next(&mut state) % 12;
|
||||
let occurrences = (0..n)
|
||||
.map(|i| {
|
||||
let id = if next(&mut state).is_multiple_of(9) { i / 2 } else { i + 1 };
|
||||
let component = 1 + next(&mut state) % components;
|
||||
let parent = next(&mut state) % (components + 2);
|
||||
occurrence(id, component, parent)
|
||||
})
|
||||
.collect();
|
||||
let doc = with(occurrences);
|
||||
assert_eq!(doc.placements(), scanning_placements(&doc), "round {round}");
|
||||
assert_eq!(same_inside(&doc), Ok(()), "round {round}");
|
||||
}
|
||||
}
|
||||
|
||||
/// A generated assembly of `top` modules, each holding `parts` parts, each part holding `pins`
|
||||
/// pins: the shape of a large imported board.
|
||||
fn assembly(top: u64, parts: u64, pins: u64) -> Document {
|
||||
let mut occurrences = Vec::new();
|
||||
let mut id = 0;
|
||||
let mut add = |component: u64, parent: u64, n: u64| {
|
||||
for _ in 0..n {
|
||||
id += 1;
|
||||
occurrences.push(occurrence(id, component, parent));
|
||||
}
|
||||
};
|
||||
add(1, 0, top);
|
||||
for part in 0..parts {
|
||||
add(10 + part, 1, 1);
|
||||
add(5, 10 + part, pins);
|
||||
}
|
||||
with(occurrences)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "manual optimized walk microbenchmark; no timing assertion"]
|
||||
fn benchmark_placements_walk() {
|
||||
// About 10,000 and 97,500 placements, under MAX_PLACEMENTS.
|
||||
for (top, parts, pins) in [(10, 100, 9), (39, 250, 9)] {
|
||||
let doc = assembly(top, parts, pins);
|
||||
let time = |walk: &dyn Fn() -> Vec<(u64, Vec<u64>, Mat)>| {
|
||||
let start = std::time::Instant::now();
|
||||
let walked = walk();
|
||||
let elapsed = start.elapsed().as_secs_f64() * 1000.;
|
||||
std::hint::black_box(walked);
|
||||
elapsed
|
||||
};
|
||||
let median = |mut ms: Vec<f64>| {
|
||||
ms.sort_by(f64::total_cmp);
|
||||
ms[ms.len() / 2]
|
||||
};
|
||||
let walked = doc.placements();
|
||||
assert_eq!(walked, scanning_placements(&doc));
|
||||
let (scan, tree) = (|| scanning_placements(&doc), || doc.placements());
|
||||
// Each order in turn, so neither walk always runs on a cache the other warmed.
|
||||
for scanning_first in [true, false] {
|
||||
let (mut old, mut new) = (Vec::new(), Vec::new());
|
||||
for _ in 0..9 {
|
||||
if scanning_first {
|
||||
old.push(time(&scan));
|
||||
new.push(time(&tree));
|
||||
} else {
|
||||
new.push(time(&tree));
|
||||
old.push(time(&scan));
|
||||
}
|
||||
}
|
||||
eprintln!(
|
||||
"{} occurrences, {} placements, {} first: scanning={:.3}ms tree={:.3}ms (median of 9, tree includes building it)",
|
||||
doc.occurrences.len(),
|
||||
walked.len(),
|
||||
if scanning_first { "scanning" } else { "tree" },
|
||||
median(old),
|
||||
median(new)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ use std::collections::{BTreeMap, BTreeSet};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::expr::{self, Expr, Kind, ParamDef, Value};
|
||||
use crate::sheet::rule_references;
|
||||
use crate::walk::Walk;
|
||||
use crate::{DocError, Document, FeatureKind, MAX_PARAMS, Result};
|
||||
|
||||
@@ -797,13 +798,14 @@ impl Document {
|
||||
}
|
||||
}
|
||||
let uses = |e: &str| expr::references(e).iter().any(|r| r == name);
|
||||
let rule_uses = |field: &str, e: &str| rule_references(field, e).iter().any(|r| r == name);
|
||||
for r in &self.sheet.rules {
|
||||
for (field, _, _) in r.fields().into_iter().filter(|(_, e, _)| uses(e)) {
|
||||
for (field, _, _) in r.fields().into_iter().filter(|(f, e, _)| rule_uses(f, e)) {
|
||||
out.push(ParamUser::Rule { kind: "sheet metal".into(), name: r.name.clone(), field: field.into() });
|
||||
}
|
||||
}
|
||||
for r in &self.plastic.rules {
|
||||
for (field, _, _) in r.fields().into_iter().filter(|(_, e, _)| uses(e)) {
|
||||
for (field, _, _) in r.fields().into_iter().filter(|(f, e, _)| rule_uses(f, e)) {
|
||||
out.push(ParamUser::Rule { kind: "plastic".into(), name: r.name.clone(), field: field.into() });
|
||||
}
|
||||
}
|
||||
@@ -855,6 +857,21 @@ impl Document {
|
||||
if self.name_taken(new) {
|
||||
return Err(DocError::Invalid(format!("there is already a parameter `{new}`")));
|
||||
}
|
||||
// Outside a rule's own thickness, `Thickness` is the rule's thickness (#9623): renaming to
|
||||
// it would turn those references into the rule's thickness and change their values.
|
||||
if new == "Thickness" {
|
||||
let uses_old = |field: &str, e: &str| field != "thickness" && expr::references(e).iter().any(|r| r == old);
|
||||
let sheet = self.sheet.rules.iter().flat_map(|r| r.fields().into_iter().map(move |(f, e, _)| ("sheet metal", &r.name, f, e)));
|
||||
let plastic = self.plastic.rules.iter().flat_map(|r| r.fields().into_iter().map(move |(f, e, _)| ("plastic", &r.name, f, e)));
|
||||
let list: Vec<String> =
|
||||
sheet.chain(plastic).filter(|(_, _, f, e)| uses_old(f, e)).map(|(kind, name, f, _)| format!("{kind} rule {name} ({f})")).collect();
|
||||
if !list.is_empty() {
|
||||
return Err(DocError::Invalid(format!(
|
||||
"`{old}` can't be renamed to `Thickness`: {} would read the rule's thickness instead",
|
||||
list.join(", ")
|
||||
)));
|
||||
}
|
||||
}
|
||||
let mut n = 0;
|
||||
let text = self.text_param(old).is_some();
|
||||
for p in &mut self.text_params {
|
||||
@@ -964,7 +981,8 @@ impl Document {
|
||||
}
|
||||
// Sheet metal and plastic rules and canvas planes hold expressions too (#4420).
|
||||
let rules = self.sheet.rules.iter_mut().flat_map(|r| r.fields_mut()).chain(self.plastic.rules.iter_mut().flat_map(|r| r.fields_mut()));
|
||||
for (_, e) in rules {
|
||||
// `Thickness` outside a rule's own thickness is the rule's, not the parameter (#9623).
|
||||
for (_, e) in rules.filter(|(field, _)| old != "Thickness" || *field == "thickness") {
|
||||
let r = expr::rename_reference(e, old, new);
|
||||
if r != *e {
|
||||
*e = r;
|
||||
@@ -1025,10 +1043,10 @@ impl Document {
|
||||
}
|
||||
// Sheet metal and plastic features rebuild when their rules' parameters change (#4420).
|
||||
if f.kind.is_sheet() {
|
||||
names.extend(self.sheet.rules.iter().flat_map(|r| r.fields()).flat_map(|(_, e, _)| expr::references(e)));
|
||||
names.extend(self.sheet.rules.iter().flat_map(|r| r.fields()).flat_map(|(f, e, _)| rule_references(f, e)));
|
||||
}
|
||||
if f.kind.is_plastic() {
|
||||
names.extend(self.plastic.rules.iter().flat_map(|r| r.fields()).flat_map(|(_, e, _)| expr::references(e)));
|
||||
names.extend(self.plastic.rules.iter().flat_map(|r| r.fields()).flat_map(|(f, e, _)| rule_references(f, e)));
|
||||
}
|
||||
out.extend(names.into_iter().map(|n| (format!("Feature:{}", f.name), n)));
|
||||
}
|
||||
@@ -1052,9 +1070,9 @@ impl Document {
|
||||
}
|
||||
// A rule that uses it changes every sheet metal (or plastic) feature: the evaluation
|
||||
// hashes the rules into their fingerprint.
|
||||
let uses = |e: &String| expr::references(e).iter().any(|r| affected.contains(r));
|
||||
let sheet = self.sheet.rules.iter().any(|r| r.fields().iter().any(|(_, e, _)| uses(e)));
|
||||
let plastic = self.plastic.rules.iter().any(|r| r.fields().iter().any(|(_, e, _)| uses(e)));
|
||||
let uses = |field: &str, e: &str| rule_references(field, e).iter().any(|r| affected.contains(r));
|
||||
let sheet = self.sheet.rules.iter().any(|r| r.fields().iter().any(|(f, e, _)| uses(f, e)));
|
||||
let plastic = self.plastic.rules.iter().any(|r| r.fields().iter().any(|(f, e, _)| uses(f, e)));
|
||||
let mut out = Vec::new();
|
||||
for f in &self.features {
|
||||
let mut hit = f.param_names.iter().any(|n| affected.contains(n));
|
||||
|
||||
@@ -84,6 +84,16 @@ impl PlacedMesh {
|
||||
})
|
||||
}
|
||||
|
||||
/// Identity of the definition's display cells, shared by every placement of it.
|
||||
pub(super) fn definition_id(&self) -> usize {
|
||||
Arc::as_ptr(&self.mesh) as usize
|
||||
}
|
||||
|
||||
/// Where this placement puts the definition.
|
||||
pub(super) fn transform(&self) -> &crate::Mat {
|
||||
&self.transform
|
||||
}
|
||||
|
||||
pub(super) fn mesh(&self) -> Arc<Mesh> {
|
||||
Arc::new(self.moved(&reference_mesh(&self.body, &self.mesh, self.tolerance)))
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ fn local_plane(plane: &PlaneRef, depth: usize) -> bool {
|
||||
PlaneRef::Origin { name: _ } | PlaneRef::Custom { plane: _ } | PlaneRef::Placed { origin: _, x_dir: _, y_dir: _ } => true,
|
||||
PlaneRef::Offset { base, distance: _ }
|
||||
| PlaneRef::Flipped { base, about_y: _ }
|
||||
| PlaneRef::AtAngle { base, axis_origin: _, axis_dir: _, angle: _, line: None } => local_plane(base, depth + 1),
|
||||
| PlaneRef::AtAngle { base, axis_origin: _, axis_dir: _, angle: _, line: None, pivot: None, .. } => local_plane(base, depth + 1),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -106,6 +106,7 @@ pub(super) fn local_geometry(f: &Feature, components: &std::collections::HashMap
|
||||
distance2: _,
|
||||
start_offset: _,
|
||||
through_all,
|
||||
second_follows_first: _,
|
||||
taper: _,
|
||||
to,
|
||||
to_offset: _,
|
||||
@@ -123,6 +124,7 @@ pub(super) fn local_geometry(f: &Feature, components: &std::collections::HashMap
|
||||
/// Assembly records do not feed any of the feature kinds accepted by `local_geometry`.
|
||||
fn same_geometry_inputs(a: &Document, b: &Document) -> bool {
|
||||
let Document {
|
||||
newer_file: _,
|
||||
render,
|
||||
format,
|
||||
format_pending,
|
||||
|
||||
@@ -167,7 +167,7 @@ impl Document {
|
||||
return Err(DocError::Invalid("the plastic thickness must be positive".into()));
|
||||
}
|
||||
let with_t = |e: &str, k: Kind| -> Result<f64> {
|
||||
let f = |n: &str| if n == "Thickness" && !vals.contains_key("Thickness") { Ok(Value::length(t)) } else { look(n) };
|
||||
let f = |n: &str| if n == "Thickness" { Ok(Value::length(t)) } else { look(n) };
|
||||
let v = expr::eval_with(e, &f)?.to_kind(k)?;
|
||||
if !v.is_finite() || v < 0.0 {
|
||||
return Err(DocError::Invalid(format!("plastic rule `{}`: `{e}` must not be negative", r.name)));
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user