mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 02:25:55 +00:00
Compare commits
244 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8e2b0cd60d | |||
| 6cee7a91b9 | |||
| 15e7615ce3 | |||
| 70b2466965 | |||
| ec1f78c6fa | |||
| 01b592e0c8 | |||
| f6e932f61a | |||
| 8ab33bb80c | |||
| e33b8cfb07 | |||
| 768dc253dd | |||
| 1d98b59883 | |||
| abea79d8ab | |||
| c4808e4715 | |||
| 6649cf4cfe | |||
| 9b431fea16 | |||
| f484457c3b | |||
| 2339510e41 | |||
| a9fa61ce77 | |||
| ac9d4e8a1d | |||
| 39163148d9 | |||
| 3ca311f922 | |||
| b978eeddcd | |||
| 2ccca96929 | |||
| 8ac4c03602 | |||
| 063f481ea0 | |||
| 3c806394b0 | |||
| 3ddd73728b | |||
| ef939b52eb | |||
| c1612e9234 | |||
| 544de55baa | |||
| 29a6bbdb43 | |||
| a169ad4194 | |||
| d907078434 | |||
| 073bbc3f00 | |||
| 9e5e143ac3 | |||
| 0dd8d9e5ec | |||
| 9fdf153054 | |||
| 5336007c2b | |||
| 21fc384e40 | |||
| 5d6a80ff35 | |||
| 9636188a0f | |||
| ec39a8ebb4 | |||
| 450cc8b389 | |||
| 3c12c36bef | |||
| 6e67ae34b0 | |||
| afd81380ab | |||
| 3125a79827 | |||
| 1cbebe21bf | |||
| 25a19cdefe | |||
| 12ebc80e15 | |||
| b470d46ffc | |||
| 6e98bd474e | |||
| ad23882080 | |||
| 63fb818f33 | |||
| 96509412e9 | |||
| f2ffd8d233 | |||
| 2b08e0c6a7 | |||
| 1885fa739b | |||
| cacfbbc578 | |||
| 2ae13ba9cf | |||
| e91f5d73bd | |||
| da501178aa | |||
| 3231f049d0 | |||
| a22ea76ab6 | |||
| a735620aa8 | |||
| 059119b345 | |||
| e016c40f43 | |||
| 1fb9dadcd9 | |||
| 91a19548d3 | |||
| a277839236 | |||
| b5d931ff4c | |||
| d7a17de3d9 | |||
| a33619fd2b | |||
| e4e3c8609c | |||
| 325917f2ae | |||
| 5ad2503503 | |||
| f43198988d | |||
| 6f73bd95c4 | |||
| 9d7544929e | |||
| eb0cecee31 | |||
| 27951f40c3 | |||
| 9e3cf05c4f | |||
| 6459c9fcf0 | |||
| 5e7f3651fb | |||
| da586bc048 | |||
| 062a7b8288 | |||
| 5bbfa5ea6c | |||
| 75c46dad7b | |||
| af54b09bb1 | |||
| dcbca523a0 | |||
| e9b08f4af4 | |||
| 6a7275a5af | |||
| 666356dba0 | |||
| 3fe2b3386f | |||
| afe06a1a3d | |||
| eaf34d2528 | |||
| 2b9261050f | |||
| eac1425661 | |||
| 88dbda42a7 | |||
| 9e30ff5878 | |||
| 4c4c768d57 | |||
| 83fe1f874f | |||
| 0734616104 | |||
| d68d86dae9 | |||
| 7d86a8cfd3 | |||
| cea07cbc66 | |||
| 0b90731096 | |||
| afa4f72f7d | |||
| cfc46448a8 | |||
| 58c64ca4d4 | |||
| 5c7e50cb67 | |||
| b6aabb88ff | |||
| a1dc849e8d | |||
| 446590dca1 | |||
| 303236e524 | |||
| 97336ccfe7 | |||
| e1d4cb433c | |||
| 0ad74f2577 | |||
| 835e261593 | |||
| 84c3b6356c | |||
| d730a273dc | |||
| 89cb651084 | |||
| 9efcb461f8 | |||
| 958ab867cd | |||
| cffa6e1580 | |||
| ce296a1ad5 | |||
| 0515dee9ce | |||
| 0478f7e401 | |||
| 52f0b52b45 | |||
| 1ae93218a7 | |||
| 3017641b41 | |||
| 17b9a8e67f | |||
| 90c09eaaf9 | |||
| 251573a5a4 | |||
| bdce595fb3 | |||
| 862807ffc9 | |||
| 6f368a5b77 | |||
| a0ed5a9923 | |||
| 38ae728538 | |||
| 147d1cfadd | |||
| d135e1659c | |||
| 3ccf04d4b2 | |||
| 2014e60ae6 | |||
| c454c05f88 | |||
| 1cc90036fb | |||
| 176c22acaa | |||
| 2ab5dedfc1 | |||
| e77c55549f | |||
| e7bb038795 | |||
| a5a561caae | |||
| 462de5b9d2 | |||
| be425c6102 | |||
| bc4d35b097 | |||
| 78c5b8de42 | |||
| 9738861d61 | |||
| 0cf4da606c | |||
| 074cc758ee | |||
| 47e8b96c2f | |||
| 7b3d84fb33 | |||
| 59a40aabc0 | |||
| b557f22f97 | |||
| 29acaf8642 | |||
| 36bdf4ea3c | |||
| ddd65cc4bb | |||
| 2a410c2d76 | |||
| 01b43999a5 | |||
| bcb5efcaa4 | |||
| 74ee047d3f | |||
| ab6e6941c7 | |||
| a93d7b207c | |||
| 2b65164159 | |||
| fbb00dc7b2 | |||
| 7702316039 | |||
| bb0b9a1d2f | |||
| 74eda80b5d | |||
| ea63eec845 | |||
| 64a55392d8 | |||
| 3a6920d10f | |||
| 228b311a6f | |||
| 16f00d4cf9 | |||
| 9fa3c85c68 | |||
| cda0277c3e | |||
| f233f000e8 | |||
| fcb0a925e2 | |||
| 71624cf7fa | |||
| ce72abacf9 | |||
| f25a8e6abc | |||
| 24b91bd453 | |||
| 22f12e80e4 | |||
| 1b02f38732 | |||
| 7239c95858 | |||
| f90b6116d5 | |||
| 38b9ff223b | |||
| d96d547f6b | |||
| 05a8911f83 | |||
| 1581d85cba | |||
| ad54bdf8eb | |||
| 7bafc99390 | |||
| e537a63988 | |||
| 984a5de001 | |||
| a799318b0f | |||
| f989b3309f | |||
| 8771b734e4 | |||
| ef081519d5 | |||
| 2a3d29766e | |||
| 058f17cc37 | |||
| 6475418169 | |||
| 1a76f80bae | |||
| f7ceba9cca | |||
| 249ca9267a | |||
| 119168cb3e | |||
| 2eb81a79b5 | |||
| b503340b06 | |||
| 63123e9237 | |||
| c843d1fb28 | |||
| 483e21ab1b | |||
| 9f37003dfa | |||
| 4c44126a4d | |||
| 5c1aad8a09 | |||
| 9d01f6e005 | |||
| 127373a950 | |||
| 64fe82b6c1 | |||
| fafa575c9f | |||
| 7e701aabc1 | |||
| 9475e0e1a4 | |||
| a8be28563c | |||
| a69a7c6ffe | |||
| 7366fe0ecc | |||
| 60d79e7ea2 | |||
| 653bc00be4 | |||
| 915dd9a83f | |||
| b99d4ddde9 | |||
| 425417b36a | |||
| b7a6967ef2 | |||
| 68105938e2 | |||
| 11e283ba77 | |||
| c25c2f3a21 | |||
| e0582668bb | |||
| df04e7742f | |||
| 56b77b405d | |||
| e4a67d1e30 | |||
| ed9599d1e4 | |||
| 80fb02f0b8 | |||
| a5c8fa7be7 |
@@ -1,6 +1,6 @@
|
||||
## Change
|
||||
|
||||
Describe the problem and resulting behavior. Link the issue when applicable.
|
||||
Start with what this is, in plain words someone new to the project would understand: what it does and who it's for. Don't assume anyone knows the name of your tool, project or feature. Then describe the problem and resulting behavior. Link the issue when applicable.
|
||||
|
||||
Changes a default: no
|
||||
|
||||
@@ -14,6 +14,8 @@ What did you run, and what were the results? Include expected and measured value
|
||||
|
||||
## Screenshots of the behavior (when observable)
|
||||
|
||||
Put the picture or video here in the description. A file in the diff doesn't count: a maintainer has to be able to see what this is without opening the changed files.
|
||||
|
||||
For observable CAD, UI or model behavior, include actual descriptive screenshots or a short recording from a small reproduction. This includes kernel, geometry, assembly and backend effects that can be shown in the app. Coding agents should run the app and capture the evidence themselves, rather than asking a human for evidence they can produce. Show the actual failure/result, preferably before and after, with brief steps, expected/actual behavior and annotations where helpful.
|
||||
|
||||
Flowcharts and Mermaid diagrams cannot substitute for evidence of visible behavior. Use diagrams for architectural reasoning when actual screenshots cannot illustrate the point. Changes with no useful observable behavior may omit screenshot/video evidence. Exclude private designs and sensitive details; attach media or link to an accessible recording rather than committing generated review artifacts.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# While a new geometry kernel is evaluated, new issues, pull requests and discussions about kernel
|
||||
# work are labelled `kernel-paused` for a maintainer to answer (CONTRIBUTING.md, "Kernel work is
|
||||
# paused"). The logic is in scripts/kernel-paused/ (tested in ci.yml).
|
||||
# While a new geometry kernel is evaluated, new issues and discussions about kernel work, and new
|
||||
# pull requests that change the kernel directories, are labelled `kernel-paused` for a maintainer to
|
||||
# answer (CONTRIBUTING.md, "Kernel work is paused"). The logic is in scripts/kernel-paused/ (tested
|
||||
# in ci.yml).
|
||||
name: Kernel paused
|
||||
|
||||
on:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Two quick checks on every pull request (CONTRIBUTING.md, "Sending a change"): no second open PR
|
||||
# for an issue a lower-numbered one already closes, and the "Changes a default:" line is filled in.
|
||||
# Neither is a required status check. The logic is in scripts/pr-checks/pr-checks.js (tested in ci.yml).
|
||||
# Three quick checks on every pull request (CONTRIBUTING.md, "Sending a change"): no second open PR
|
||||
# for an issue a lower-numbered one already closes, the "Changes a default:" line is filled in, and
|
||||
# a PR adding to the showcase shows a picture. None is a required status check. The logic is in scripts/pr-checks/pr-checks.js (tested in ci.yml).
|
||||
name: PR checks
|
||||
|
||||
on:
|
||||
@@ -48,6 +48,21 @@ jobs:
|
||||
const r = await checks.checkDefaults({ github, context, core });
|
||||
core.info(JSON.stringify(r));
|
||||
|
||||
picture:
|
||||
name: Picture check
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
sparse-checkout: scripts/pr-checks
|
||||
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const checks = require('./scripts/pr-checks/pr-checks.js');
|
||||
const r = await checks.checkPicture({ github, context, core });
|
||||
core.info(JSON.stringify(r));
|
||||
|
||||
# Labelling needs write access, which a fork PR's token never has, so it is its own job and best
|
||||
# effort: a fork PR just doesn't get the `changes-default` label from here.
|
||||
label:
|
||||
|
||||
+125
@@ -630,6 +630,131 @@ Original SolidCraft drawings, Open for owner review; no runtime assignments.
|
||||
| docs/book/src/generated/equation-wave.png | SolidCraft contributors | rendered by `cargo xtask book` (`solidcraft-cli snapshot docs/book/scripts/equation-wave.json`; gitignored) | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/model/print_face.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
|
||||
| assets/locales/fi.json | SolidCraft contributors | Original Finnish translation of SolidCraft's English catalog, AI-assisted (Claude Code) clean-room from the English and not yet reviewed line by line by a native speaker; no commercial CAD text used | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/model/plane_axis_point.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/aa-2x.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/aa-2x.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/aa-4x.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/aa-4x.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/aa-8x.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/aa-8x.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/aa-off.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/aa-off.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/agent-pointer-speed.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/agent-pointer-speed.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/ao-on.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/ao-on.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/assembly-move.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/assembly-move.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/browser-card.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/browser-card.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/browser-column.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/browser-column.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/colour-vision-axes.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/colour-vision-axes.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/colour-vision-bends.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/colour-vision-bends.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/colour-vision-draft.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/colour-vision-draft.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/colour-vision-failed.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/colour-vision-failed.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/colour-vision-fixed.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/colour-vision-fixed.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/colour-vision-projected.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/colour-vision-projected.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/direct-interaction.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/direct-interaction.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/frame-rate.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/frame-rate.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/history-bottom.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/history-bottom.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/history-in-browser.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/history-in-browser.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/history-left.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/history-left.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/history-under-browser.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/history-under-browser.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/layout-schematic.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/layout-schematic.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/measure-axis-distances.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/measure-axis-distances.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/mouse-mapping.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/mouse-mapping.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/navigator-axes.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/navigator-axes.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/navigator-cube.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/navigator-cube.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/orbit-centre.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/orbit-centre.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/orbit-cursor.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/orbit-cursor.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/orbit-origin.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/orbit-origin.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/orientation-y-up.dark.svg | SolidCraft contributors | original SolidCraft artwork: the Orientation figure copied from the app and extracted from the Preferences redesign board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/orientation-y-up.light.svg | SolidCraft contributors | original SolidCraft artwork: the Orientation figure copied from the app and extracted from the Preferences redesign board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/orientation-z-up.dark.svg | SolidCraft contributors | original SolidCraft artwork: the Orientation figure copied from the app and extracted from the Preferences redesign board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/orientation-z-up.light.svg | SolidCraft contributors | original SolidCraft artwork: the Orientation figure copied from the app and extracted from the Preferences redesign board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/quick-measure.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/quick-measure.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/select-bodies.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/select-bodies.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/select-faces-edges.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/select-faces-edges.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/sketch-conflict.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/sketch-conflict.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/sketch-exit-keep.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/sketch-exit-keep.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/sketch-exit-previous.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/sketch-exit-previous.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/sketch-exit-unless-turned.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/sketch-exit-unless-turned.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/sketch-guides.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/sketch-guides.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/sketch-hide-glyphs.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/sketch-hide-glyphs.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/sketch-state-icons.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/sketch-state-icons.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/style-shaded-edges.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/style-shaded-edges.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/tess-coarse.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/tess-coarse.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/tess-fine.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/tess-fine.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/tess-normal.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/tess-normal.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/tess-ultra.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/tess-ultra.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/theme-dark.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/theme-dark.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/theme-gruvbox.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/theme-gruvbox.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/theme-imported.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/theme-imported.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/theme-light.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/theme-light.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/theme-system-dark-half.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/theme-system-dark-half.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/theme-system-light-half.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/theme-system-light-half.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/tool-cursor.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/tool-cursor.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/tool-extrude.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/tool-extrude.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/tool-hole.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/tool-hole.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/tool-placement.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/tool-placement.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/vertex-markers.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/vertex-markers.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/view-angles.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/view-angles.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/view-grid.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/view-grid.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/view-perspective.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/view-perspective.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/view-shadow.dark.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| docs/design/preferences/figures/view-shadow.light.svg | SolidCraft contributors | original SolidCraft artwork drawn for the Preferences redesign (tracker #9863), extracted from the design board as a standalone SVG | MIT OR Apache-2.0 |
|
||||
| `assets/locales/pending/sketch-offset-instances.json` | SolidCraft contributors | Original offset instance-count label and translations | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/flat/settings_profile.svg | SolidCraft contributors | single-colour application/navigation icon master (64 grid), original analytic head and shoulder geometry for settings profiles | MIT OR Apache-2.0 |
|
||||
|
||||
## Code adapted from sibling projects
|
||||
|
||||
|
||||
+14
-6
@@ -19,12 +19,13 @@ Questions, ideas or want to say what you're working on? Join the [Discord](https
|
||||
|
||||
## Kernel work is paused
|
||||
|
||||
We're evaluating a new geometry kernel. Until that's decided, please don't open issues, pull
|
||||
requests or discussions about kernel work: booleans, fillets, chamfers, shell, offsets, sweeps and
|
||||
lofts, threads and holes, STEP/IGES import and export, tessellation, or anything else that changes
|
||||
`crates/kernel/`. The pinned issue on the tracker explains it. New ones get the `kernel-paused`
|
||||
label (`.github/workflows/kernel-paused.yml`) and a maintainer answers them. Crashes and data loss
|
||||
are still taken as issues, and everything outside the kernel carries on as normal.
|
||||
We're moving to a new geometry kernel. Until that's done, please don't open pull requests that
|
||||
change `crates/kernel/`, `crates/geom/` or `vendor/truck*`, or issues and discussions about kernel
|
||||
work: booleans, fillets, chamfers, shell, offsets, sweeps and lofts, threads and holes, STEP/IGES
|
||||
import and export, or tessellation. The pinned issue on the tracker explains it. New ones get the
|
||||
`kernel-paused` label (`.github/workflows/kernel-paused.yml`) and a maintainer answers them. Crashes
|
||||
and data loss are still taken as issues. Pull requests outside those directories carry on as normal,
|
||||
including the dialogs and tools for these features.
|
||||
|
||||
## Get it running (about 10 minutes on a fast machine)
|
||||
|
||||
@@ -346,6 +347,13 @@ without pushes. These automatic cleanup deadlines are separate from the daily pr
|
||||
`/unclaim` releases the bot claim early (only you or a maintainer can). Issues a maintainer has
|
||||
taken are never freed automatically.
|
||||
|
||||
**A PR has to explain itself.** Start the description with what it is, in plain words someone new
|
||||
to the project would understand: what it does and who it's for. Don't assume anyone knows the name
|
||||
of your tool, project or feature. If there's anything to look at, show it in the description: a
|
||||
picture or video has to be visible there without opening the changed files. "The GIF is the change"
|
||||
doesn't count. A PR that doesn't do this waits until it does. For a PR that adds to
|
||||
`docs/showcase.md` or `docs/screenshots/`, the Picture check fails until the description shows one.
|
||||
|
||||
Help reviewers see what the words mean. For observable CAD, UI or model behavior, please
|
||||
include actual descriptive screenshots or a short recording in the issue or PR. This evidence
|
||||
is required for changes or bugs that can be demonstrated in the app, including kernel,
|
||||
|
||||
Generated
+24
-17
@@ -4208,7 +4208,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft"
|
||||
version = "0.29.0"
|
||||
version = "0.30.1"
|
||||
dependencies = [
|
||||
"block2 0.6.2",
|
||||
"eframe",
|
||||
@@ -4245,11 +4245,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-3dx-client"
|
||||
version = "0.29.0"
|
||||
version = "0.30.1"
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-cli"
|
||||
version = "0.29.0"
|
||||
version = "0.30.1"
|
||||
dependencies = [
|
||||
"image",
|
||||
"log",
|
||||
@@ -4261,7 +4261,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-derive"
|
||||
version = "0.29.0"
|
||||
version = "0.30.1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4270,7 +4270,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-doc"
|
||||
version = "0.29.0"
|
||||
version = "0.30.1"
|
||||
dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
@@ -4287,7 +4287,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-engine"
|
||||
version = "0.29.0"
|
||||
version = "0.30.1"
|
||||
dependencies = [
|
||||
"image",
|
||||
"log",
|
||||
@@ -4308,7 +4308,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-geom"
|
||||
version = "0.29.0"
|
||||
version = "0.30.1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4317,7 +4317,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-gpu"
|
||||
version = "0.29.0"
|
||||
version = "0.30.1"
|
||||
dependencies = [
|
||||
"image",
|
||||
"log",
|
||||
@@ -4337,7 +4337,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-io"
|
||||
version = "0.29.0"
|
||||
version = "0.30.1"
|
||||
dependencies = [
|
||||
"cadmpeg-core",
|
||||
"cadmpeg-ir",
|
||||
@@ -4355,13 +4355,14 @@ dependencies = [
|
||||
"solidcraft-doc",
|
||||
"solidcraft-geom",
|
||||
"solidcraft-kernel",
|
||||
"solidcraft-testkit",
|
||||
"thiserror 2.0.21",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-kernel"
|
||||
version = "0.29.0"
|
||||
version = "0.30.1"
|
||||
dependencies = [
|
||||
"log",
|
||||
"proptest",
|
||||
@@ -4381,8 +4382,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-mcp"
|
||||
version = "0.29.0"
|
||||
version = "0.30.1"
|
||||
dependencies = [
|
||||
"image",
|
||||
"serde_json",
|
||||
"solidcraft-engine",
|
||||
"solidcraft-gpu",
|
||||
@@ -4390,7 +4392,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-render"
|
||||
version = "0.29.0"
|
||||
version = "0.30.1"
|
||||
dependencies = [
|
||||
"image",
|
||||
"moxcms",
|
||||
@@ -4399,12 +4401,13 @@ dependencies = [
|
||||
"serde_json",
|
||||
"sha2 0.10.9",
|
||||
"solidcraft-geom",
|
||||
"web-time",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-sketch"
|
||||
version = "0.29.0"
|
||||
version = "0.30.1"
|
||||
dependencies = [
|
||||
"harfrust",
|
||||
"serde",
|
||||
@@ -4417,14 +4420,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-space-mouse"
|
||||
version = "0.29.0"
|
||||
version = "0.30.1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-testkit"
|
||||
version = "0.30.1"
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-ui-egui"
|
||||
version = "0.29.0"
|
||||
version = "0.30.1"
|
||||
dependencies = [
|
||||
"arboard",
|
||||
"egui",
|
||||
@@ -4455,7 +4462,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-web"
|
||||
version = "0.29.0"
|
||||
version = "0.30.1"
|
||||
dependencies = [
|
||||
"eframe",
|
||||
"egui",
|
||||
@@ -6010,7 +6017,7 @@ checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7"
|
||||
|
||||
[[package]]
|
||||
name = "xtask"
|
||||
version = "0.29.0"
|
||||
version = "0.30.1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"serde",
|
||||
|
||||
+3
-1
@@ -5,7 +5,7 @@ members = ["crates/*", "apps/*", "xtask"]
|
||||
exclude = ["vendor"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.29.0"
|
||||
version = "0.30.1"
|
||||
edition = "2024"
|
||||
license = "MIT OR Apache-2.0"
|
||||
rust-version = "1.95"
|
||||
@@ -28,6 +28,8 @@ solidcraft-space-mouse = { path = "crates/space-mouse" }
|
||||
solidcraft-mcp = { path = "crates/mcp" }
|
||||
solidcraft-derive = { path = "crates/derive" }
|
||||
solidcraft-3dx-client = { path = "crates/threedx-client" }
|
||||
# test tooling: a dev-dependency only (xtask/src/layers.rs)
|
||||
solidcraft-testkit = { path = "crates/testkit" }
|
||||
|
||||
# foundation
|
||||
thiserror = "2"
|
||||
|
||||
+3
-1
@@ -662,7 +662,9 @@ completed drawings publish atomically. The browser still exports synchronously.
|
||||
pieces survive unfold/refold as one logical sheet group, including split flanged sheets.
|
||||
Cut-outs made in the flat pattern cross bends at any angle or curved: their edges fold to
|
||||
helices and curves on the bend, their walls square to the sheet. Free-form bases use bounded
|
||||
sampling.
|
||||
sampling. A fold crosses the sheet's own outer edges the same way, slanted or on any curve,
|
||||
with the outline exact in the flat pattern and its DXF, and flanges and other folds still go
|
||||
on the panels.
|
||||
Parallel prismatic partial-depth cuts retain pocket side and depth through transfer and
|
||||
unfold/refold, with separate pocket contours in preview and DXF. Planar Intersect preserves
|
||||
exact holes and retained islands, including the union of multiple selected profiles, and
|
||||
|
||||
+40
-12
@@ -3,7 +3,7 @@
|
||||
## Supported versions
|
||||
|
||||
SolidCraft is young and moves quickly, so only the latest release gets security fixes. Please
|
||||
update to the [latest release](https://github.com/bherbruck/solidcraft/releases/latest) before
|
||||
update to the [latest release](https://github.com/tangency-labs/solidcraft/releases/latest) before
|
||||
reporting, and check whether the problem is still on `main`.
|
||||
|
||||
| Version | Supported |
|
||||
@@ -15,18 +15,46 @@ reporting, and check whether the problem is still on `main`.
|
||||
## Reporting a vulnerability
|
||||
|
||||
Please **don't open a public issue** for a security problem. Report it privately through GitHub:
|
||||
open the repository's [Security tab](https://github.com/bherbruck/solidcraft/security) and choose
|
||||
open the repository's [Security tab](https://github.com/tangency-labs/solidcraft/security) and choose
|
||||
**Report a vulnerability**
|
||||
([direct link](https://github.com/bherbruck/solidcraft/security/advisories/new)).
|
||||
([direct link](https://github.com/tangency-labs/solidcraft/security/advisories/new)).
|
||||
|
||||
Please include what you found, how to reproduce it (a design file, command or MCP request), the
|
||||
version and platform, and the impact you expect. I'll acknowledge the report as soon as I can,
|
||||
keep you updated in the advisory, and credit you when the fix is published unless you'd rather
|
||||
stay anonymous.
|
||||
Please include what you found, the steps you ran to reproduce it (a design file, command or MCP
|
||||
request), the version and platform, and what an attacker gains. I'll acknowledge the report as soon
|
||||
as I can, keep you updated in the advisory, and credit you when the fix is published unless you'd
|
||||
rather stay anonymous.
|
||||
|
||||
Areas where reports are especially useful:
|
||||
## What counts as a security issue
|
||||
|
||||
- opening untrusted design, STEP, STL or 3MF files (crashes, excessive memory or CPU, path tricks)
|
||||
- the MCP server and the control API (file access outside the allowed folders, command injection)
|
||||
- the web app (anything reaching beyond the page's own storage)
|
||||
- release packages and the update paths (AppImage, Flatpak, MSI, deb, rpm, snap)
|
||||
Advisories are for problems where someone gets something they shouldn't, without already having
|
||||
access to your computer or your account. For example:
|
||||
|
||||
- opening a design, STEP, STL or 3MF file from someone else runs code, or reads or overwrites files
|
||||
it shouldn't
|
||||
- a website or another computer can reach or control SolidCraft
|
||||
- an agent gets past a prompt that promised to ask you first
|
||||
- the web app reaches beyond the page's own storage
|
||||
- a release package or an update can be tampered with
|
||||
- an API key or token gets sent somewhere it shouldn't go
|
||||
|
||||
## What to report as a normal bug
|
||||
|
||||
Please [open an issue](https://github.com/tangency-labs/solidcraft/issues/new) for these. They're
|
||||
still worth fixing, but they aren't security issues:
|
||||
|
||||
- anything that needs a program already running on your computer, or the control token. A program
|
||||
running as you can already do what you can.
|
||||
- MCP's allowed folders. They guard against accidents, they aren't a security boundary, and an agent
|
||||
with a shell of its own can already reach those files. Getting around them isn't a vulnerability.
|
||||
- crashes, hangs, or heavy memory or CPU use, unless they also lead to something in the list above
|
||||
- anything that only happens in an unusual setup you'd have to choose on purpose
|
||||
|
||||
## Before you send it
|
||||
|
||||
- **Run it.** Reproduce the problem on the latest release. If you only read the code, say so, and
|
||||
expect it to wait.
|
||||
- **Check it yourself.** If a scanner or an AI agent found it, read the code and confirm it first.
|
||||
These tools tend to overrate what they find.
|
||||
- **Say what's gained.** Name what the attacker needs and what they get. I set the final severity.
|
||||
|
||||
Reports that don't fit will be closed with a short note, or moved to the issue tracker.
|
||||
|
||||
@@ -172,7 +172,11 @@ fn main() -> ExitCode {
|
||||
Some("oracle") => oracle::run(&args[1..]),
|
||||
Some("fuzz") => fuzz::run(&args[1..]),
|
||||
Some("fuzz-one") => fuzz::one(&args[1..]),
|
||||
Some("mcp") => cmd_mcp(&args[1..]),
|
||||
Some("mcp") => {
|
||||
let result = cmd_mcp(&args[1..]);
|
||||
solidcraft_mcp::cleanup_scratch_dir();
|
||||
result
|
||||
}
|
||||
Some("crashes") => crashes::run(&args[1..]),
|
||||
Some("icon") => cmd_icon(&args[1..]),
|
||||
Some("licences" | "licenses") => {
|
||||
|
||||
@@ -263,3 +263,110 @@ mod setup {
|
||||
assert!(json_of(&pkg.join("claude_desktop_config.json"))["mcpServers"]["solidcraft"].is_object());
|
||||
}
|
||||
}
|
||||
|
||||
/// Scratch files belong to the CLI process; exercise real startup, requests and shutdown.
|
||||
mod scratch_cleanup {
|
||||
use super::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
fn command(case: &str) -> (PathBuf, Command) {
|
||||
let dir = std::env::temp_dir().join(format!("solidcraft-mcp-lifecycle-{case}-{}", std::process::id()));
|
||||
std::fs::create_dir(&dir).unwrap();
|
||||
let mut command = Command::new(env!("CARGO_BIN_EXE_solidcraft-cli"));
|
||||
command
|
||||
.args(["mcp", "--headless"])
|
||||
.env("TMPDIR", &dir)
|
||||
.env("TMP", &dir)
|
||||
.env("TEMP", &dir)
|
||||
.env("SOLIDCRAFT_RECOVERY_DIR", dir.join("recovery"))
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped());
|
||||
(dir, command)
|
||||
}
|
||||
|
||||
fn scratch_folders(dir: &Path) -> Vec<PathBuf> {
|
||||
std::fs::read_dir(dir)
|
||||
.unwrap()
|
||||
.map(|entry| entry.unwrap().path())
|
||||
.filter(|path| path.file_name().unwrap().to_string_lossy().starts_with("solidcraft-mcp-"))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn initialize() -> Value {
|
||||
json!({"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2025-06-18"}})
|
||||
}
|
||||
|
||||
fn run(mut command: Command, request: Value) -> std::process::Output {
|
||||
let mut child = command.spawn().unwrap();
|
||||
writeln!(child.stdin.take().unwrap(), "{request}").unwrap();
|
||||
child.wait_with_output().unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eof_removes_only_the_process_scratch_folder() {
|
||||
let (dir, command) = command("eof");
|
||||
let unrelated = dir.join("solidcraft-mcp-unrelated");
|
||||
std::fs::create_dir(&unrelated).unwrap();
|
||||
let out = run(command, initialize());
|
||||
assert!(out.status.success(), "{}", String::from_utf8_lossy(&out.stderr));
|
||||
let reply: Value = serde_json::from_slice(&out.stdout).unwrap();
|
||||
assert!(reply.get("result").is_some(), "{reply}");
|
||||
assert_eq!(scratch_folders(&dir), vec![unrelated]);
|
||||
assert!(dir.join("recovery").is_dir(), "cleanup must leave the recovery folder alone");
|
||||
std::fs::remove_dir_all(dir).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn startup_error_removes_the_process_scratch_folder() {
|
||||
let (dir, mut command) = command("startup-error");
|
||||
let out = command.args(["--param-check", "invalid"]).stdin(Stdio::null()).output().unwrap();
|
||||
let stderr = String::from_utf8_lossy(&out.stderr);
|
||||
assert!(!out.status.success());
|
||||
assert!(stderr.contains("--param-check wants off, warn or strict"), "{stderr}");
|
||||
assert!(scratch_folders(&dir).is_empty(), "startup error left a scratch folder in {dir:?}");
|
||||
std::fs::remove_dir_all(dir).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shutdown_preserves_nonempty_scratch_folder() {
|
||||
let (dir, mut command) = command("nonempty");
|
||||
let mut child = command.spawn().unwrap();
|
||||
let mut input = child.stdin.take().unwrap();
|
||||
writeln!(input, "{}", initialize()).unwrap();
|
||||
let mut reply = String::new();
|
||||
BufReader::new(child.stdout.take().unwrap()).read_line(&mut reply).unwrap();
|
||||
let reply: Value = serde_json::from_str(&reply).unwrap();
|
||||
assert!(reply.get("result").is_some(), "{reply}");
|
||||
let scratch = scratch_folders(&dir);
|
||||
assert_eq!(scratch.len(), 1);
|
||||
let saved = scratch[0].join("keep.txt");
|
||||
std::fs::write(&saved, "saved output").unwrap();
|
||||
drop(input);
|
||||
let out = child.wait_with_output().unwrap();
|
||||
assert!(out.status.success(), "{}", String::from_utf8_lossy(&out.stderr));
|
||||
assert_eq!(std::fs::read_to_string(saved).unwrap(), "saved output");
|
||||
std::fs::remove_dir_all(dir).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_screenshot_scratch_folder_is_removed_without_a_path_scope() {
|
||||
let (dir, mut command) = command("screenshot");
|
||||
command.arg("--allow-any-path");
|
||||
// A headless server cannot capture a window. The request still allocates a scratch
|
||||
// path before the backend refuses it, without relying on a GPU or installed renderer.
|
||||
let out = run(
|
||||
command,
|
||||
json!({
|
||||
"jsonrpc": "2.0", "id": 1, "method": "tools/call",
|
||||
"params": {"name": "screenshot", "arguments": {"source": "window"}},
|
||||
}),
|
||||
);
|
||||
assert!(out.status.success(), "{}", String::from_utf8_lossy(&out.stderr));
|
||||
let reply: Value = serde_json::from_slice(&out.stdout).unwrap();
|
||||
assert_eq!(reply["result"]["isError"], true, "{reply}");
|
||||
assert!(reply["result"]["content"][0]["text"].as_str().unwrap().contains("the window gave no picture"), "{reply}");
|
||||
assert!(scratch_folders(&dir).is_empty(), "failed screenshot left a scratch folder in {dir:?}");
|
||||
std::fs::remove_dir_all(dir).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@ type Reply = Result<(String, Vec<u8>), String>;
|
||||
pub struct Download {
|
||||
reply: Rc<RefCell<Option<Reply>>>,
|
||||
pending: Option<(String, Vec<u8>)>,
|
||||
/// The downloaded design being opened (a few features a frame, #10198): the answer to come,
|
||||
/// the name it takes and the file it was read from.
|
||||
opening: Option<(std::sync::mpsc::Receiver<serde_json::Value>, String, String)>,
|
||||
}
|
||||
|
||||
impl Download {
|
||||
@@ -33,13 +36,14 @@ impl Download {
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, app: &mut SolveApp, ctx: &egui::Context) {
|
||||
self.opened(app);
|
||||
if let Some(reply) = self.reply.borrow_mut().take() {
|
||||
match reply {
|
||||
Ok(file) => self.pending = Some(file),
|
||||
Err(error) => app.set_status(format!("Could not open design link: {error}"), true),
|
||||
}
|
||||
}
|
||||
if self.pending.is_none() || app.file_picker_busy() {
|
||||
if self.pending.is_none() || self.opening.is_some() || app.file_picker_busy() {
|
||||
return;
|
||||
}
|
||||
if solidcraft_ui_egui::documents::any_dirty(app) {
|
||||
@@ -71,18 +75,33 @@ impl Download {
|
||||
}
|
||||
}
|
||||
let Some((name, bytes)) = self.pending.take() else { return };
|
||||
// The extension selects the design loader even when the URL has no extension.
|
||||
let path = &format!("/linked/design.{}", solidcraft_engine::io::DESIGN_EXT);
|
||||
vfs::insert(path, bytes);
|
||||
app.open_path(path);
|
||||
if app.session.path.as_deref() == Some(path.as_str()) {
|
||||
// The extension selects the design loader even when the URL has no extension. Named
|
||||
// after the link, as the progress card shows the file's name.
|
||||
let file: String = name.chars().map(|c| if matches!(c, '/' | '\\') { '_' } else { c }).collect();
|
||||
let path = format!("/linked/{}.{}", if file.is_empty() { "design" } else { &file }, solidcraft_engine::io::DESIGN_EXT);
|
||||
vfs::insert(&path, bytes);
|
||||
// Opened as any design is, with its progress and Cancel; finished in `opened`.
|
||||
self.opening = Some((app.open_path_answered(&path), name, path));
|
||||
self.opened(app);
|
||||
}
|
||||
|
||||
/// The downloaded design is in: it is an unsaved copy under the link's name.
|
||||
fn opened(&mut self, app: &mut SolveApp) {
|
||||
let Some((rx, _, _)) = &self.opening else { return };
|
||||
let (ok, cancelled) = match rx.try_recv() {
|
||||
Ok(reply) => (reply["ok"].as_bool() == Some(true), reply["error"].as_str().is_some_and(|e| e.contains("cancelled"))),
|
||||
Err(std::sync::mpsc::TryRecvError::Empty) => return,
|
||||
Err(std::sync::mpsc::TryRecvError::Disconnected) => (false, false),
|
||||
};
|
||||
let Some((_, name, path)) = self.opening.take() else { return };
|
||||
if ok && app.session.path.as_deref() == Some(path.as_str()) {
|
||||
app.session.path = None;
|
||||
app.session.doc_mut().name = name;
|
||||
app.session.mark_unsaved();
|
||||
} else {
|
||||
} else if !ok && !cancelled {
|
||||
app.set_status("Could not open design link: the downloaded file is not a valid SolidCraft design", true);
|
||||
}
|
||||
let _ = vfs::remove(path);
|
||||
let _ = vfs::remove(&path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -375,7 +375,8 @@ mod web {
|
||||
// Designs opened from the computer's disk (the files window): open them.
|
||||
let opened: Vec<_> = self.files.opened.borrow_mut().drain(..).collect();
|
||||
for path in opened {
|
||||
self.app.open_path(&path);
|
||||
// A few features a frame, with progress and Cancel (#10198).
|
||||
self.app.open_path_async(&path);
|
||||
self.files.panel.set(false);
|
||||
}
|
||||
// Recent designs whose file is gone are dropped from the list.
|
||||
@@ -416,7 +417,7 @@ mod web {
|
||||
path
|
||||
};
|
||||
match intent {
|
||||
Intent::Open => self.app.open_path(&path),
|
||||
Intent::Open => self.app.open_path_async(&path),
|
||||
Intent::Insert | Intent::Theme => self.app.drop_path(&path),
|
||||
}
|
||||
self.files.panel.set(false);
|
||||
@@ -580,7 +581,7 @@ mod web {
|
||||
let path = format!("{}{name}", files::OPFS);
|
||||
match what {
|
||||
"open" => {
|
||||
self.app.open_path(&path);
|
||||
self.app.open_path_async(&path);
|
||||
self.files.panel.set(false);
|
||||
}
|
||||
"insert" => {
|
||||
|
||||
@@ -22,6 +22,10 @@ const MAX_BAD_LINES: usize = 8;
|
||||
/// How long a subscribed connection waits after a `design.changed` line before the next: what
|
||||
/// changes meanwhile (a parameter dragged) goes out as one line, the latest (#6985).
|
||||
const EVENT_GAP: Duration = Duration::from_millis(100);
|
||||
/// How long a new connection has to show the token. A client connects when it has a request to
|
||||
/// send, so this is plenty; one that doesn't send it loses the connection, and its seat among
|
||||
/// [`MAX_CONNECTIONS`] goes to someone else.
|
||||
const TOKEN_WAIT: Duration = Duration::from_secs(5);
|
||||
|
||||
static OPEN: AtomicUsize = AtomicUsize::new(0);
|
||||
/// The token files this launch wrote (its control port, the agent chat's endpoint) and the
|
||||
@@ -225,23 +229,53 @@ fn unauthorized() -> Value {
|
||||
/// Refuse the connection: the reply, then the end of what we send, then what the client had
|
||||
/// already sent read and dropped (a little, briefly). Closing a socket with unread input resets
|
||||
/// the connection, and a reset can reach the client before it has read the refusal.
|
||||
fn refuse(out: &mut TcpStream, reader: &mut BufReader<TcpStream>) {
|
||||
fn refuse(out: &mut TcpStream, reader: &mut BufReader<ClientInput>) {
|
||||
use std::io::Read;
|
||||
let _ = send(out, &unauthorized());
|
||||
let _ = out.shutdown(std::net::Shutdown::Write);
|
||||
let _ = reader.get_ref().set_read_timeout(Some(Duration::from_millis(500)));
|
||||
reader.get_mut().deadline = Some(Instant::now() + Duration::from_millis(500));
|
||||
let _ = std::io::copy(&mut reader.by_ref().take(64 * 1024), &mut std::io::sink());
|
||||
}
|
||||
|
||||
/// What a client sends. Until it shows the token, all of its reads share one deadline, so neither
|
||||
/// blank lines nor a line sent a byte at a time buy it more time. After that it may stay idle as
|
||||
/// long as it likes.
|
||||
struct ClientInput {
|
||||
socket: TcpStream,
|
||||
deadline: Option<Instant>,
|
||||
}
|
||||
|
||||
impl ClientInput {
|
||||
/// No deadline any more: the client showed the token.
|
||||
fn admit(&mut self) -> std::io::Result<()> {
|
||||
self.deadline = None;
|
||||
self.socket.set_read_timeout(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::io::Read for ClientInput {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
if let Some(deadline) = self.deadline {
|
||||
let left = deadline.saturating_duration_since(Instant::now());
|
||||
if left.is_zero() {
|
||||
return Err(std::io::ErrorKind::TimedOut.into());
|
||||
}
|
||||
self.socket.set_read_timeout(Some(left))?;
|
||||
}
|
||||
self.socket.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// How long a request waits for the app's reply: most requests, and a batch (or every request,
|
||||
/// on a patient connection).
|
||||
/// on a patient connection); and how long a new connection has to show the token.
|
||||
#[derive(Clone, Copy)]
|
||||
struct Waits {
|
||||
short: Duration,
|
||||
long: Duration,
|
||||
token: Duration,
|
||||
}
|
||||
|
||||
const WAITS: Waits = Waits { short: Duration::from_secs(60), long: Duration::from_secs(3600) };
|
||||
const WAITS: Waits = Waits { short: Duration::from_secs(60), long: Duration::from_secs(3600), token: TOKEN_WAIT };
|
||||
|
||||
/// A request that timed out but is still running in the app (#1618): its id and method, and where
|
||||
/// its reply will land.
|
||||
@@ -543,7 +577,7 @@ fn serve_with(
|
||||
let connection = running.connections.fetch_add(1, Ordering::Relaxed) + 1;
|
||||
let Ok(read) = stream.try_clone() else { return };
|
||||
let mut out = stream;
|
||||
let mut reader = BufReader::new(read);
|
||||
let mut reader = BufReader::new(ClientInput { socket: read, deadline: Some(Instant::now() + waits.token) });
|
||||
let mut bad = 0usize;
|
||||
let mut authenticated = false;
|
||||
let mut pending: Option<Pending> = None;
|
||||
@@ -554,6 +588,10 @@ fn serve_with(
|
||||
let line = match read_line(&mut reader, MAX_LINE) {
|
||||
Ok(Some(l)) => String::from_utf8_lossy(&l).into_owned(),
|
||||
Ok(None) => break,
|
||||
// Closed without a word: a client that connected early and only now sends its first
|
||||
// request would read a line here as the reply to it. It finds the connection closed
|
||||
// and opens another.
|
||||
Err(e) if !authenticated && matches!(e.kind(), std::io::ErrorKind::TimedOut | std::io::ErrorKind::WouldBlock) => break,
|
||||
Err(e) => {
|
||||
let _ = answer(&mut out, events.as_ref(), &json!({"ok": false, "error": e.to_string()}));
|
||||
break;
|
||||
@@ -565,7 +603,12 @@ fn serve_with(
|
||||
// The first request must carry the token; later ones on the same connection may.
|
||||
if !authenticated {
|
||||
match token_of(&line) {
|
||||
Some(t) if control_auth::tokens_match(&t, token) => authenticated = true,
|
||||
Some(t) if control_auth::tokens_match(&t, token) => {
|
||||
if reader.get_mut().admit().is_err() {
|
||||
break;
|
||||
}
|
||||
authenticated = true;
|
||||
}
|
||||
_ => {
|
||||
refuse(&mut out, &mut reader);
|
||||
break;
|
||||
@@ -675,6 +718,80 @@ fn serve_with(
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// A connection that doesn't show the token in time is closed without a word, however it fills
|
||||
/// the time. One that does keeps working when it idles for longer than that.
|
||||
#[test]
|
||||
fn the_token_has_a_time_limit_and_an_authenticated_client_may_idle() {
|
||||
use std::io::Read;
|
||||
let token = "0123456789abcdef0123456789abcdef";
|
||||
let limit = Duration::from_millis(500);
|
||||
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||
let addr = listener.local_addr().unwrap();
|
||||
let (tx, rx) = channel::<ControlRequest>();
|
||||
std::thread::spawn(move || {
|
||||
for req in rx {
|
||||
let _ = req.reply.send(json!({"ok": true, "result": req.method}));
|
||||
}
|
||||
});
|
||||
static RUNNING: Running = Running::new();
|
||||
let waits = Waits { short: Duration::from_secs(10), long: Duration::from_secs(10), token: limit };
|
||||
std::thread::spawn(move || {
|
||||
for stream in listener.incoming().flatten() {
|
||||
let tx = tx.clone();
|
||||
std::thread::spawn(move || serve_with(stream, token, tx, egui::Context::default(), false, waits, &RUNNING, &DESIGN_WATCH));
|
||||
}
|
||||
});
|
||||
let open = || {
|
||||
let s = TcpStream::connect(addr).unwrap();
|
||||
s.set_read_timeout(Some(Duration::from_secs(20))).unwrap();
|
||||
s
|
||||
};
|
||||
// Sends `drip` every 50 ms until the server closes; what it read, and when it closed.
|
||||
let closed_after = |drip: &[u8]| {
|
||||
let mut s = open();
|
||||
let start = Instant::now();
|
||||
s.set_read_timeout(Some(Duration::from_millis(50))).unwrap();
|
||||
let mut got = Vec::new();
|
||||
loop {
|
||||
let mut buf = [0u8; 256];
|
||||
match s.read(&mut buf) {
|
||||
Ok(0) => break,
|
||||
Ok(n) => got.extend_from_slice(buf.get(..n).unwrap()),
|
||||
Err(e) if matches!(e.kind(), std::io::ErrorKind::WouldBlock | std::io::ErrorKind::TimedOut) => {
|
||||
assert!(start.elapsed() < Duration::from_secs(20), "still open");
|
||||
let _ = s.write_all(drip);
|
||||
}
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
(got, start.elapsed())
|
||||
};
|
||||
for drip in [&b""[..], b"\n", b"\r\n \n", b" ", b"{"] {
|
||||
let (got, took) = closed_after(drip);
|
||||
assert!(got.is_empty(), "{drip:?} was answered: {}", String::from_utf8_lossy(&got));
|
||||
assert!(took >= limit.saturating_sub(Duration::from_millis(50)), "{drip:?} closed after {took:?}");
|
||||
}
|
||||
|
||||
let mut s = open();
|
||||
let mut lines = BufReader::new(s.try_clone().unwrap());
|
||||
let mut ask = |line: Value| -> Value {
|
||||
writeln!(s, "{line}").unwrap();
|
||||
let mut l = String::new();
|
||||
lines.read_line(&mut l).unwrap();
|
||||
serde_json::from_str(&l).unwrap()
|
||||
};
|
||||
assert_eq!(ask(json!({"id": 1, "method": "ui.view", "token": token}))["ok"], true);
|
||||
std::thread::sleep(limit * 3);
|
||||
assert_eq!(ask(json!({"id": 2, "method": "ui.view"}))["ok"], true, "idle for longer than the limit");
|
||||
|
||||
// A wrong token is still answered, at once.
|
||||
let mut wrong = open();
|
||||
writeln!(wrong, "{}", json!({"id": 1, "method": "ui.view", "token": "nope"})).unwrap();
|
||||
let mut l = String::new();
|
||||
BufReader::new(wrong).read_line(&mut l).unwrap();
|
||||
assert!(l.contains("unauthorized"), "{l}");
|
||||
}
|
||||
|
||||
/// #1618: a request that outlasts its wait says it is still running; the connection keeps its
|
||||
/// reply for `control.pending`, and refuses other requests as `busy` until it lands, so none
|
||||
/// is queued to run unseen behind it.
|
||||
@@ -698,7 +815,7 @@ mod tests {
|
||||
let _ = req.reply.send(json!({"ok": true, "result": req.method}));
|
||||
}
|
||||
});
|
||||
let waits = Waits { short: Duration::from_millis(100), long: Duration::from_millis(100) };
|
||||
let waits = Waits { short: Duration::from_millis(100), long: Duration::from_millis(100), token: TOKEN_WAIT };
|
||||
static RUNNING: Running = Running::new();
|
||||
std::thread::spawn(move || {
|
||||
if let Some(stream) = listener.incoming().flatten().next() {
|
||||
@@ -764,7 +881,7 @@ mod tests {
|
||||
let _ = req.reply.send(json!({"ok": true, "result": req.method}));
|
||||
}
|
||||
});
|
||||
let waits = Waits { short: Duration::from_millis(100), long: Duration::from_secs(60) };
|
||||
let waits = Waits { short: Duration::from_millis(100), long: Duration::from_secs(60), token: TOKEN_WAIT };
|
||||
std::thread::spawn(move || {
|
||||
for stream in listener.incoming().flatten() {
|
||||
let tx = tx.clone();
|
||||
|
||||
@@ -181,6 +181,7 @@ pipe false
|
||||
plane false
|
||||
plane_along_path false
|
||||
plane_angle false
|
||||
plane_axis_point false
|
||||
plane_midplane false
|
||||
plane_perpendicular false
|
||||
plane_tangent false
|
||||
@@ -236,6 +237,7 @@ settings_monitor false
|
||||
settings_mouse false
|
||||
settings_palette false
|
||||
settings_plug false
|
||||
settings_profile false
|
||||
sheet false
|
||||
sheet_rules false
|
||||
shell false
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<title>Settings profile</title>
|
||||
<circle cx="32" cy="19" r="8" fill="none" stroke="currentColor" stroke-width="5.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 53C12 44.7157 20.9543 38 32 38C43.0457 38 52 44.7157 52 53" fill="none" stroke="currentColor" stroke-width="5.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 413 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<title>Plane through axis and point</title>
|
||||
<path d="M26 15L56 32.32L38 42.71L8 25.39Z" fill="#b9ddf8" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round" stroke-dasharray="0.1 5"/>
|
||||
<path d="M15 18.4L53 40.34" fill="none" stroke="currentColor" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<circle cx="22" cy="29" r="3.1" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 475 B |
+124
-18
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "مستطيلي",
|
||||
"Rectangular Tube": "أنبوب مستطيل",
|
||||
"Reference face": "الوجه المرجعي",
|
||||
"Reference plane": "المستوى المرجعي",
|
||||
"Relief Depth": "عمق التفريغ",
|
||||
"Relief Width": "عرض التفريغ",
|
||||
"Remove Tools": "إزالة الأدوات",
|
||||
@@ -494,7 +495,7 @@
|
||||
"Rename Folder": "إعادة تسمية المجلد",
|
||||
"Render Stage": "مرحلة التصيير",
|
||||
"Reopen designs": "إعادة فتح التصاميم",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "إعادة فتح التصاميم التي كانت مفتوحة عند آخر إغلاق لـ SolidCraft",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "إعادة فتح التصاميم التي كانت مفتوحة آخر مرة",
|
||||
"Replace existing library file": "استبدال ملف المكتبة الموجود",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "الإبلاغ عن خطأ — راجع لقطة الشاشة وافتح مسودة GitHub",
|
||||
"Report copied. Keep this text to submit on GitHub when you are online.": "تم نسخ التقرير. احتفظ بهذا النص لإرساله على GitHub عند الاتصال بالإنترنت.",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "مقطع",
|
||||
"Section (optional)": "المقطع (اختياري)",
|
||||
"Section position": "موضع المقطع",
|
||||
"Sections": "المقاطع",
|
||||
"Sections (one sketch each)": "المقاطع (رسم واحد لكل منها)",
|
||||
"Select Other": "تحديد آخر",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "تحديد آخر: أبقِ الزر الأيسر ثابتًا على النموذج (أو انقر بالزر الأيمن › تحديد آخر…) لعرض كل ما تحت المؤشر؛ وانقر مرة أخرى في الموضع نفسه لتحديد العنصر التالي هناك. انقر نقرًا مزدوجًا بالزر الأوسط للانتقال إلى العرض الرئيسي (التفضيلات › التنقل).",
|
||||
@@ -793,6 +795,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (فُقد الارتباط)",
|
||||
"browser.sketch_editing": "{name} (قيد التحرير)",
|
||||
"browser.sketch_name": "رسم {number}",
|
||||
"browser.sketches_to_parts_already_in_part": "موجودة بالفعل في جزئها",
|
||||
"browser.sketches_to_parts_design_body": "على جسم من التصميم نفسه",
|
||||
"browser.sketches_to_parts_moved": "نُقلت {moved} رسومات إلى الجزء الذي رُسمت عليه",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "على وجه بلا ارتباط دقيق",
|
||||
"browser.sketches_to_parts_not_on_face": "ليست على وجه جزء",
|
||||
"browser.sketches_to_parts_part_missing": "جزؤها لم يعد موجودًا",
|
||||
"browser.sketches_to_parts_result": "نُقلت {moved} رسومات إلى الجزء الذي رُسمت عليه؛ وبقيت {stayed}: {why}",
|
||||
"browser.units": "الوحدات: {units}",
|
||||
"cam.flat": "وجه مسطح",
|
||||
"cam.follower": "التابع",
|
||||
@@ -852,6 +861,7 @@
|
||||
"cmd.component_extract": "استخراج إلى ملف",
|
||||
"cmd.component_finish_edit": "إنهاء التحرير",
|
||||
"cmd.component_from_bodies": "إنشاء مكوّنات من الأجسام",
|
||||
"cmd.component_hide_history": "إخفاء سجل المكوّن",
|
||||
"cmd.component_insert_design": "إدراج تصميم",
|
||||
"cmd.component_links": "قائمة الروابط",
|
||||
"cmd.component_list": "قائمة المكوّنات",
|
||||
@@ -860,6 +870,7 @@
|
||||
"cmd.component_move_bodies": "نقل الأجسام إلى مكوّن",
|
||||
"cmd.component_move_features": "نقل الميزات إلى مكوّن",
|
||||
"cmd.component_move_sketches": "نقل الرسومات إلى مكوّن",
|
||||
"cmd.component_move_sketches_to_parts": "نقل الرسومات إلى أجزائها",
|
||||
"cmd.component_paste_from_design": "لصق من تصميم آخر",
|
||||
"cmd.component_paste_new": "لصق كجديد",
|
||||
"cmd.component_properties": "الخصائص الفيزيائية للمكوّن",
|
||||
@@ -867,6 +878,7 @@
|
||||
"cmd.component_rename": "إعادة تسمية المكوّن",
|
||||
"cmd.component_replace_with_link": "استبدال برابط",
|
||||
"cmd.component_revert_position": "إرجاع الموضع",
|
||||
"cmd.component_show_history": "إظهار سجل المكوّن",
|
||||
"cmd.component_unlock": "فتح قفل المحتويات",
|
||||
"cmd.component_update_links": "تحديث الروابط",
|
||||
"cmd.config_activate": "تنشيط التكوين",
|
||||
@@ -877,6 +889,7 @@
|
||||
"cmd.config_rule": "قاعدة التكوين",
|
||||
"cmd.config_rules": "قواعد التكوين",
|
||||
"cmd.config_table": "عرض جدول التكوينات",
|
||||
"cmd.construct.plane.axis_point": "مستوى عبر محور ونقطة",
|
||||
"cmd.construct_axis_edge": "محور عبر حافة",
|
||||
"cmd.construct_axis_normal_to_face": "محور عمودي على وجه",
|
||||
"cmd.construct_axis_through_cylinder": "محور عبر أسطوانة/مخروط/طارة",
|
||||
@@ -925,6 +938,11 @@
|
||||
"cmd.explode_show": "إظهار العرض المفكك",
|
||||
"cmd.expr_evaluate": "تقييم تعبير",
|
||||
"cmd.face_delete": "حذف وجه",
|
||||
"cmd.file.export_3mf": "تصدير 3MF",
|
||||
"cmd.file.export_obj": "تصدير OBJ",
|
||||
"cmd.file.export_step": "تصدير STEP",
|
||||
"cmd.file.export_stl": "تصدير STL",
|
||||
"cmd.file.export_urdf": "تصدير URDF",
|
||||
"cmd.file_export": "تصدير",
|
||||
"cmd.file_insert_mesh": "إدراج شبكة",
|
||||
"cmd.file_insert_step": "إدراج STEP",
|
||||
@@ -942,6 +960,7 @@
|
||||
"cmd.inspect_colored_components": "تفعيل ألوان المكوّنات",
|
||||
"cmd.inspect_colored_components_disable": "تعطيل ألوان المكوّنات",
|
||||
"cmd.inspect_compare": "مقارنة الأجسام",
|
||||
"cmd.inspect_compare_picture": "مقارنة بصورة مرجعية",
|
||||
"cmd.inspect_continuity": "تحليل الاستمرارية",
|
||||
"cmd.inspect_curvature_comb": "تحليل مشط الانحناء",
|
||||
"cmd.inspect_curvature_map": "تحليل خريطة الانحناء",
|
||||
@@ -1032,6 +1051,9 @@
|
||||
"cmd.pointcloud_edit": "تحرير سحابة النقاط",
|
||||
"cmd.pointcloud_insert": "إدراج سحابة نقاط",
|
||||
"cmd.pointcloud_list": "سحب النقاط",
|
||||
"cmd.render.compare.divider": "تحريك فاصل مقارنة التصيير",
|
||||
"cmd.render.result.discard": "تجاهل نتيجة التصيير",
|
||||
"cmd.render.result.select": "تحديد نتيجة التصيير",
|
||||
"cmd.render_aging_describe": "تقادم السطح بالطبقات",
|
||||
"cmd.render_backends": "محركات التصيير",
|
||||
"cmd.render_batch": "تصيير كل اللقطات",
|
||||
@@ -1195,6 +1217,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "منحنى شريحي بنقاط تحكم (الدرجة 5)",
|
||||
"cmd.sketch_spline_fit_point": "منحنى شريحي بنقاط ملاءمة",
|
||||
"cmd.sketch_spline_insert_point": "إضافة نقطة إلى المنحنى الشريحي",
|
||||
"cmd.sketch_spline_remove_point": "إزالة نقطة من المنحنى الشريحي",
|
||||
"cmd.sketch_spline_reverse": "عكس اتجاه المنحنى الشريحي",
|
||||
"cmd.sketch_spun_profile": "مقطع دوراني",
|
||||
"cmd.sketch_toggle_diameter": "تبديل نصف القطر / القطر",
|
||||
@@ -1245,6 +1268,7 @@
|
||||
"cmd.timeline_rollback": "نقل مؤشر الخط الزمني",
|
||||
"cmd.timeline_stop": "إيقاف السجل",
|
||||
"cmd.timeline_suppress": "تعطيل الميزة",
|
||||
"cmd.ui.assistant": "إظهار المساعد أو إخفاؤه",
|
||||
"cmd.view.render_browser": "متصفح التصيير",
|
||||
"cmd.view.render_dock": "نقل لوحة التصيير",
|
||||
"cmd.view.render_focus": "عرض تركيز التصيير",
|
||||
@@ -1279,9 +1303,11 @@
|
||||
"cmd.view_tool": "أداة التنقل",
|
||||
"cmd.view_top": "العرض العلوي",
|
||||
"cmd.view_visual_style": "نمط العرض",
|
||||
"color.blue": "أزرق",
|
||||
"color.copy": "انقر لنسخ قيم اللون",
|
||||
"color.edit": "انقر لتعديل اللون",
|
||||
"color.gamma": "عرض قيم اللون في نطاق جاما 0-255",
|
||||
"color.green": "أخضر",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "أدخل #rrggbb أو #rrggbbaa؛ مع الخانات الست تبقى العتامة كما هي",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1289,6 +1315,7 @@
|
||||
"color.linear": "عرض قيم اللون في النطاق الخطي 0-1",
|
||||
"color.recent": "الألوان الأخيرة",
|
||||
"color.recent_help": "تظهر هنا الألوان التي تختارها في أي حقل لون",
|
||||
"color.red": "أحمر",
|
||||
"color.saturation": "التشبع (بالمئة)",
|
||||
"color.saturation_value": "التشبع والقيمة",
|
||||
"color.selected": "اللون المحدد",
|
||||
@@ -1296,6 +1323,8 @@
|
||||
"color.step_up": "زيادة خطوة",
|
||||
"color.value": "القيمة، أي السطوع (بالمئة)",
|
||||
"config.column_filter_empty": "لا توجد أعمدة مطابقة",
|
||||
"config.rule_parameter_columns": "لا تظهر هنا إلا المعاملات التي لها عمود في الجدول. أضف عمودًا عبر «إضافة عمود» (+) لاستخدام المعامل هنا.",
|
||||
"config.rule_suppression_columns": "لا تظهر هنا إلا الميزات التي لها عمود «تعطيل» في الجدول. أضف عمودًا عبر «إضافة عمود» (+) للتحكم بالميزة هنا.",
|
||||
"contact.components_count": "{count} مكوّنات",
|
||||
"contact.enabled_label": "مفعّل",
|
||||
"coupling.gear_1": "الترس 1",
|
||||
@@ -1344,7 +1373,11 @@
|
||||
"dialog.pattern_sketch_not_object": "اختر أجسامًا أو ميزات: الرسم ليس أيًّا منهما. كرّر منحنياته بأداة النمط أو المرآة داخل الرسم، أو ابثقه أولًا.",
|
||||
"dialog.select": "↖ تحديد",
|
||||
"dialog.selected_count": "↖ {count} محدد",
|
||||
"dialog.selection_constraint": "قيد {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · منحنى {id}",
|
||||
"dialog.selection_guidance": "حدد مُدخلًا لتغييره. انقر على العناصر المحددة لإزالتها؛ Ctrl/Cmd مع النقر يبدّل تحديدها أيضًا. راجع المعاينة ثم اضغط موافق.",
|
||||
"dialog.selection_midpoint": "نقطة المنتصف · {id}",
|
||||
"dialog.selection_profile": "{sketch} · مقطع {number}",
|
||||
"dialog.selection_step": "{step} من {total}: حدد {input}",
|
||||
"dialog.step": "الخطوة",
|
||||
"document.changed_on_disk": "تغيّر {name} على القرص",
|
||||
@@ -1427,6 +1460,7 @@
|
||||
"help.construct.axis.two_points": "ينشئ محورًا إنشائيًا يمر بنقطتين أو رأسين أو نقطتي رسم. استخدمه لمحور يمتد بين موضعين معروفين.",
|
||||
"help.construct.plane.along_path": "ينشئ مستوى إنشائيًا عموديًا على حافة أو منحنى عند موضع على امتداده. استخدمه لرسم مقطع من أجل كنس أو لمقطع عرضي للمسار.",
|
||||
"help.construct.plane.at_angle": "ينشئ مستوى إنشائيًا مائلًا بزاوية عن مستوى أو وجه، حول محور أو حافة. استخدمه للرسوم المائلة، مثل حامل مائل.",
|
||||
"help.construct.plane.axis_point": "ينشئ مستوى إنشاء يحتوي على محور ونقطة خارجه. اختر محور الأصل أو محور إنشاء أو حافة مستقيمة أو خط رسم، ثم رأسًا أو نقطة رسم أو نقطة إنشاء. النقطة الواقعة على المحور لا تحدد مستوى وحيدًا.",
|
||||
"help.construct.plane.midplane": "ينشئ مستوى إنشائيًا في منتصف المسافة بين مستويين أو وجهين مسطحين، أو منصّفًا لهما حيث يلتقيان بزاوية. استخدمه كمستوى تناظر عند عمل مرآة.",
|
||||
"help.construct.plane.offset": "ينشئ مستوى إنشائيًا موازيًا لمستوى أو وجه مسطح، على مسافة محددة أو عبر نقطة. استخدمه للرسم على ارتفاع محدد فوق وجه.",
|
||||
"help.construct.plane.perpendicular": "ينشئ مستوى إنشائيًا يحوي خطًا أو حافة ويكون عموديًا على مستوى أو وجه. استخدمه للرسم بشكل رأسي على النموذج على امتداد حافة.",
|
||||
@@ -1549,7 +1583,7 @@
|
||||
"help.sketch.constrainer": "يطبق القيد الذي يكاد منحنى أو منحنيان محددان يحققانه، مثل الأفقي أو التوازي أو التماس أو التساوي. استخدمه لتهذيب رسم يدوي علاقة بعد أخرى.",
|
||||
"help.sketch.constraint.coincident": "يجعل نقطة تقع على نقطة أخرى أو على منحنى أو على محور الرسم. استخدمه لسد الفجوات في المقطع أو لوضع نقطة على خط.",
|
||||
"help.sketch.constraint.collinear": "يجعل خطين يقعان على خط مستقيم واحد. استخدمه لمحاذاة الحواف المتقطعة، مثل جانبي حز.",
|
||||
"help.sketch.constraint.concentric": "يعطي دائرتين أو قوسين المركز نفسه. استخدمه للحلقات والتجاويف المتدرجة والأعمدة ذات الأكتاف.",
|
||||
"help.sketch.constraint.concentric": "يجعل دائرتين أو قوسين يشتركان في المركز نفسه، أو يضع مركز دائرة أو قوس على نقطة في الرسم التخطيطي، بما في ذلك نقطة الأصل. اختر العنصرين بأي ترتيب.",
|
||||
"help.sketch.constraint.curvature": "يجعل منحنيين يلتقيان عند نقطة نهاية يتابعان بالانحناء نفسه (G2). استخدمه حيث لا يكفي الوصل المماسي نعومة، مثلًا على الأسطح الظاهرة.",
|
||||
"help.sketch.constraint.equal": "يجعل الخطوط بالطول نفسه، أو الدوائر والأقواس بنصف القطر نفسه. استخدمه ليتحكم بُعد واحد في عدة ميزات متماثلة.",
|
||||
"help.sketch.constraint.fix": "يثبّت النقاط أو المنحنيات في مكانها، أو يفك تثبيتها. استخدمه للهندسة التي يجب ألا تتحرك عند سحب الرسم أو حله.",
|
||||
@@ -1577,7 +1611,7 @@
|
||||
"help.sketch.dimension": "يضيف بُعدًا حاكمًا: طولًا أو مسافة أو زاوية أو نصف قطر أو قطرًا بحسب ما تختاره. اكتب قيمة أو اسم معامل فيتبعها الرسم.",
|
||||
"help.sketch.edit": "يفتح رسمًا موجودًا للتعديل. تتحدّث الميزات المبنية عليه عند إنهاء الرسم.",
|
||||
"help.sketch.ellipse": "يرسم قطعًا ناقصًا من مركزه ونهاية محوره الأكبر ونقطة على جانبه الأصغر. استخدمه للفتحات البيضاوية والكامات وغيرها من الأشكال المستديرة المتطاولة.",
|
||||
"help.sketch.equation_curve": "يرسم منحنى من صيغ على مدى: x(t) و y(t)، أو y = f(x). يمكن أن تستخدم الصيغ المعلمات فيتبعها المنحنى: استخدمه لموجة جيبية أو لولب أو منحنى انفلات لسن ترس.",
|
||||
"help.sketch.equation_curve": "يرسم منحنى من صيغ على مدى: x(t) و y(t)، أو y = f(x). يمكن أن تستخدم الصيغ المعاملات فيتبعها المنحنى: استخدمه لموجة جيبية أو لولب أو منحنى انفلات لسن ترس.",
|
||||
"help.sketch.extend": "يمدد خطًا أو قوسًا حتى أقرب منحنى في طريقه، أو ينمّي منحنيين حتى يلتقيا. استخدمه لسد الفجوات في المقطع بعد القص أو التعديل.",
|
||||
"help.sketch.fillet": "يقوّس الزاوية التي يلتقي فيها خطان أو قوسان بقوس ذي نصف قطر محدد. الزوايا المقوّسة معًا تشترك في نصف قطر واحد.",
|
||||
"help.sketch.finish": "يغادر الرسم ويعود إلى النموذج. تصبح مقاطعه المغلقة جاهزة للبثق والدوران وغيرهما من الميزات.",
|
||||
@@ -1622,7 +1656,7 @@
|
||||
"help.sketch.spline.fit_point": "يرسم منحنى شريحيًا ناعمًا يمر بكل نقطة تضعها، مفتوحًا أو مغلقًا. استخدمه لتتبع شكل عبر نقاط معروفة.",
|
||||
"help.sketch.spline3d": "يرسم منحنى ناعمًا في الفراغ عبر نقاط، مع تفعيل الرسم ثلاثي الأبعاد. استخدمه لمسارات الكنس ومسارات الكابلات الخارجة عن مستوى الرسم.",
|
||||
"help.sketch.spun_profile": "ينشئ في مستوى الرسم المحيط الذي يمسحه جسم عند دورانه حول محور. استخدمه للحصول على صورة ظلية لقطعة مشغولة بالخراطة من أجل أمر «دوران» أو مخطط.",
|
||||
"help.sketch.text": "يكتب نصًا في الرسم بخط وارتفاع وزاوية تختارها، ويمكن لكلماته أن تتبع المعلمات. ابثقه أو حوّله إلى نقش بارز للملصقات والكتابة.",
|
||||
"help.sketch.text": "يكتب نصًا في الرسم بخط وارتفاع وزاوية تختارها، ويمكن لكلماته أن تتبع المعاملات. ابثقه أو حوّله إلى نقش بارز للملصقات والكتابة.",
|
||||
"help.sketch.transform": "يغيّر مقياس هندسة الرسم ويدوّرها ويحرّكها حول نقطة مركز بخطوة واحدة. استخدمه لإعادة وضع تحديد وتغيير حجمه معًا.",
|
||||
"help.sketch.trim": "يزيل الجزء من المنحنى بين أقرب نقطتي تقاطع حول موضع نقرتك. استخدمه لتنظيف الخطوط المتداخلة وتحويلها إلى مقطع مغلق.",
|
||||
"help.solid.align": "يحرّك الأجسام المحددة بحيث تلتقي نقطة أو وجه أو مستوى منها بنقطة أو وجه أو مستوى في مكان آخر. استخدمه لوضع قطعة ملاصقة لأخرى دون قياس المسافة.",
|
||||
@@ -1800,9 +1834,11 @@
|
||||
"joint.one_component": "هذه القطع أجسام لمكوّن واحد: {bodies}. المفصل يحرّك مكوّنًا مقابل آخر، لذا تحتاج كل قطعة إلى مكوّن خاص بها.",
|
||||
"joint.rigid_group_one_component": "هذه الأجسام وعددها {count} كلها في مكوّن واحد (أو في التصميم نفسه): استخدم {command} أولًا.",
|
||||
"joint.rigid_group_select_components": "اختر مكوّنين أو أكثر.",
|
||||
"joint_move.blocked_hint": "تحقق من نوع المفصل (مثل المفصل الثابت)، وتثبيت الجزء، وحدود المفصل، والقيم التي تتحكم بها المعلمات.",
|
||||
"joint_move.blocked_hint": "تحقق من نوع المفصل (مثل المفصل الثابت)، وتثبيت الجزء، وحدود المفصل، والقيم التي تتحكم بها المعاملات.",
|
||||
"joint_move.blocked_title": "لا يمكن تحريك هذا الجزء",
|
||||
"joint_move.coarse": "خطوة الدوران قرب المحور (°)",
|
||||
"joint_move.conflict": "يتعارض أحد مفاصل هذا الجزء مع المفاصل الأخرى: {joint}.",
|
||||
"joint_move.conflict_hint": "عدّل المفصل المتعارض أو عطّله أو احذفه حتى تتمكن المفاصل الأخرى من وضع الجزء.",
|
||||
"joint_move.distance": "المسافة من نقطة البداية",
|
||||
"joint_move.done": "تم",
|
||||
"joint_move.fine": "خطوة الدوران بعيدًا عن المحور (°)",
|
||||
@@ -1902,6 +1938,8 @@
|
||||
"motion.track_name": "المسار {number}",
|
||||
"nav.alias.help": "Shift+Alt مع السحب بالزر الأيسر يدوّر العرض، وShift+Alt مع الأوسط يُزيحه، وShift+Alt مع الأيمن يكبّره؛ والسحب بالزر الأوسط يُزيح أيضًا والسحب بالأيمن يدوّر",
|
||||
"nav.blender.help": "السحب بالزر الأوسط يدوّر العرض، وShift مع الأوسط يُزيحه، وCtrl مع الأوسط يكبّره؛ والسحب بالأيمن لا يحرك الكاميرا",
|
||||
"nav.creo.help": "السحب بالزر الأوسط يدوّر العرض، وShift مع الأوسط يُزيحه، وCtrl مع السحب العمودي بالأوسط يكبّره أو يصغّره (السحب لأسفل للتكبير)؛ وتدوير العجلة يكبّره أو يصغّره",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "الفأرة والفأرة ثلاثية الأبعاد تدوّران العرض عبر القطبين وتحافظان على اللف. يُعطَّل التقاط المحاور بـ Shift.",
|
||||
"nav.free_orbit.label": "تدوير حر",
|
||||
"nav.free_orbit.snap_help": "الفأرة والفأرة ثلاثية الأبعاد تدوّران العرض عبر القطبين وتحافظان على اللف. اضغط مطولًا على Shift أثناء سحب التدوير لالتقاط اتجاه العرض واللف.",
|
||||
@@ -2054,12 +2092,19 @@
|
||||
"parts.family.button_head_cap_screw": "برغي برأس زرّي بتجويف سداسي",
|
||||
"parts.family.dowel_pin": "مسمار توجيه أسطواني",
|
||||
"parts.family.hex_bolt": "برغي سداسي الرأس",
|
||||
"parts.family.hex_cap_screw_inch": "برغي سداسي الرأس (بوصة)",
|
||||
"parts.family.hex_nut": "صامولة سداسية",
|
||||
"parts.family.hex_nut_inch": "صامولة سداسية (بوصة)",
|
||||
"parts.family.set_screw": "برغي تثبيت بتجويف سداسي، طرف مسطح",
|
||||
"parts.family.socket_head_cap_screw": "برغي برأس أسطواني بتجويف سداسي",
|
||||
"parts.family.washer": "حلقة مسطحة",
|
||||
"parts.family.washer_sae": "حلقة مسطحة، SAE (ضيقة)",
|
||||
"parts.family.washer_uss": "حلقة مسطحة، USS (عريضة)",
|
||||
"parts.holes": "الثقوب (اختياري)",
|
||||
"parts.placement_hint": "اختر ثقبًا أو أكثر لتثبيت قطعة في كل منها، أو اضغط موافق لوضعها عند نقطة الأصل",
|
||||
"parts.system": "نظام الوحدات",
|
||||
"parts.system.inch": "بوصة",
|
||||
"parts.system.metric": "متري",
|
||||
"pattern.alignment_2": "المحاذاة 2",
|
||||
"pattern.alignment_hint": "العدد يشمل الأصل. «توسيط» يوازن النسخ؛ و«قلب» يختار الجانب الإضافي عندما يكون العدد زوجيًا.",
|
||||
"pattern.direction_1": "الاتجاه 1",
|
||||
@@ -2121,6 +2166,8 @@
|
||||
"prefs.button_left": "سحب بالزر الأيسر",
|
||||
"prefs.button_middle": "سحب بالزر الأوسط",
|
||||
"prefs.button_right": "سحب بالزر الأيمن",
|
||||
"prefs.carousel_next": "التالي",
|
||||
"prefs.carousel_previous": "السابق",
|
||||
"prefs.choose_font": "اختيار ملف خط…",
|
||||
"prefs.click": "نقر",
|
||||
"prefs.contact_shading": "تظليل التلامس الناعم",
|
||||
@@ -2133,6 +2180,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "الأسطح المنحنية",
|
||||
"prefs.curves": "الأسطح المنحنية",
|
||||
"prefs.cut_preview_tint": "إظهار المادة المُزالة باللون الأحمر",
|
||||
"prefs.cut_preview_tint_help": "تملأ المعاينة المادة التي تزيلها الميزة، والشكل الذي تستبدله، بطيف أحمر شبه شفاف. عند الإيقاف تكون هذه التعبئة شفافة تمامًا ولا يُرسم سوى محيطها.",
|
||||
"prefs.dark_parts_shading": "الحفاظ على شكل الأجزاء الداكنة",
|
||||
"prefs.dark_parts_shading_help": "تظليل العرض بإضاءة خطية مع إضاءة سماء ناعمة، لتحتفظ الأوجه الداكنة بتدرج بدل أن تصبح سوداء",
|
||||
"prefs.dead_zone_note": "الشريط المخطط هو المنطقة الميتة: تُتجاهل الحركة داخلها.",
|
||||
@@ -2157,7 +2206,19 @@
|
||||
"prefs.font_sample": "نص تجريبي للخط · Ø12.50 mm · 45.0° · 0123456789",
|
||||
"prefs.font_unreadable": "تعذّرت قراءة الخط المحدد.",
|
||||
"prefs.general": "عام",
|
||||
"prefs.general_note": "اللغة والحفظ والتحديثات.",
|
||||
"prefs.general.active": "نشط",
|
||||
"prefs.general.crash_reports": "تقارير الأعطال",
|
||||
"prefs.general.edited": "معدّل",
|
||||
"prefs.general.held": "المحفوظ",
|
||||
"prefs.general.import": "استيراد...",
|
||||
"prefs.general.last_check": "آخر تحقق: {time}",
|
||||
"prefs.general.minutes": "دقائق",
|
||||
"prefs.general.name_setup": "سمِّ هذا الإعداد",
|
||||
"prefs.general.profile_name": "اسم ملف التعريف",
|
||||
"prefs.general.save_new": "حفظ كجديد",
|
||||
"prefs.general.step_import": "STEP والاستيراد",
|
||||
"prefs.general.this_computer": "هذا الكمبيوتر",
|
||||
"prefs.general_note": "ملفات التعريف واللغة والملفات والتحديثات.",
|
||||
"prefs.gesture_orbit": "تدوير العرض",
|
||||
"prefs.gesture_pan": "إزاحة العرض",
|
||||
"prefs.gesture_zoom": "تكبير",
|
||||
@@ -2191,8 +2252,10 @@
|
||||
"prefs.interface_language": "لغة الواجهة",
|
||||
"prefs.interface_note": "شكل SolidCraft وأماكن أجزائه.",
|
||||
"prefs.interval_minutes": "{n} دقيقة",
|
||||
"prefs.invert_curved_roll_arrows": "عكس سهمي التدوير المنحنيين",
|
||||
"prefs.invert_curved_roll_arrows_note": "سهما التدوير المنحنيان عند زاوية المكعب يدوران العرض داخل مستوى الشاشة. عند التشغيل: سهم التدوير مع عقارب الساعة يدور عكسها، وسهم العكس يدور معها. عند الإيقاف: يدور كل سهم بالعرض حسب انحنائه.",
|
||||
"prefs.invert_navigator_arrows": "عكس أسهم تدوير مكعب العرض",
|
||||
"prefs.invert_navigator_arrows_note": "تدور الأسهم حول المكعب بالكاميرا حسب اتجاه السهم، كما في Fusion وInventor: السهم على اليسار يشير يميناً ويحرّك الكاميرا يميناً. عند الإيقاف: يدور السهم بالمكعب نحو الجهة التي يقع عليها، فيحرّك سهم اليسار الكاميرا يساراً. لا يتأثر سهما التدوير المنحنيان.",
|
||||
"prefs.invert_navigator_arrows_note": "تدوّر الأسهم المحيطة بالمكعب الكاميرا في الاتجاه الذي تشير إليه، كما في Fusion وInventor: السهم الموجود على اليسار يشير إلى اليمين ويدوّر الكاميرا إلى اليمين. عند الإيقاف: يدوّر السهم المكعب نحو الجهة التي يقع فيها، فيدوّر السهم الأيسر الكاميرا إلى اليسار. لا يتأثر سهما الدوران المنحنيان.",
|
||||
"prefs.keep_work_on_tab_switch": "إبقاء مربعات الحوار والأدوات مفتوحة عند التبديل بين التصاميم",
|
||||
"prefs.keep_work_on_tab_switch_note": "تحتفظ كل علامة تبويب تصميم بمربع الحوار أو الأداة المفتوحة، بما حددته وكتبته فيها، وبعلامة تبويب شريط الأدوات. وتجدها كما هي عند العودة إلى التصميم. عند الإيقاف: يؤدي التبديل بين التصاميم إلى إلغائها وعرض علامة التبويب «النموذج».",
|
||||
"prefs.labels": "التسميات",
|
||||
@@ -2212,10 +2275,10 @@
|
||||
"prefs.logo_accent": "لون التمييز",
|
||||
"prefs.logo_theme": "ألوان السمة",
|
||||
"prefs.max_file_size": "أكبر ملف يمكن فتحه",
|
||||
"prefs.max_file_size_design": "ملفات التصميم",
|
||||
"prefs.max_file_size_design": "أكبر ملف للفتح: التصميم",
|
||||
"prefs.max_file_size_flag": "بدأ هذا التشغيل بالخيار --max-file-size {size}، ويسري حتى إغلاق SolidCraft. تسري هذه الإعدادات عند تشغيله دون هذا الخيار.",
|
||||
"prefs.max_file_size_note": "القيم الافتراضية: 512 MB و256 MB؛ الحد الأقصى 16 GB. يتطلب فتح ملف ذاكرة تعادل حجمه عدة مرات. قد يحتاج التصميم الذي يحتفظ باستيراد كبير إلى حد أعلى لملف التصميم لحفظه وإعادة فتحه.",
|
||||
"prefs.max_file_size_step": "ملفات STEP والاستيراد",
|
||||
"prefs.max_file_size_step": "أكبر ملف للفتح: STEP والاستيراد",
|
||||
"prefs.max_file_size_web": "يفتح تطبيق الويب ملفات STEP والاستيراد حتى 512 MB والتصاميم حتى 256 MB؛ لا يتسع تبويب المتصفح لملفات أكبر. يمكن رفع هذه الحدود في تطبيق سطح المكتب.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "أيقونات كبيرة",
|
||||
@@ -2232,7 +2295,7 @@
|
||||
"prefs.navigator_fit_note": "النقر على وجه أو ركن أو سهم في المكعب، أو على محور في أداة المحاور، يوجّه العرض أيضًا نحو منتصف النموذج من المسافة الواحدة التي تُظهره كاملًا من كل جهة (كرته المحيطة). يستخدم العرض الرئيسي التكبير والمركز ذاتهما ما دام هذا مفعّلًا، فلا تغيّر العروض المختارة بعده سوى الاتجاه. عند الإيقاف: يدور العرض في مكانه محتفظًا بالهدف والتكبير.",
|
||||
"prefs.network_host_remove": "إزالة",
|
||||
"prefs.network_locations": "السماح بمواقع الشبكة في التصاميم",
|
||||
"prefs.network_locations_note": "عند الإيقاف: الملفات المرتبطة بالتصميم على حاسوب آخر (\\\\server\\share) لا تُفتح إلا من الخوادم المدرجة هنا، أو بعد أن تسمح لأحدها عند السؤال.",
|
||||
"prefs.network_locations_note": "للمؤسسات التي تحفظ التصاميم على خوادم الملفات. عند الإيقاف: الخوادم التي تسمح بها فقط.",
|
||||
"prefs.network_locations_section": "مواقع الشبكة",
|
||||
"prefs.not_on_gpu": "غير متاح على هذا المعالج الرسومي",
|
||||
"prefs.on": "تشغيل",
|
||||
@@ -2250,15 +2313,17 @@
|
||||
"prefs.placement": "الموضع",
|
||||
"prefs.prefer_free_sketch_points": "تفضيل نقاط الرسم القابلة للتحريك عند تداخل النقاط",
|
||||
"prefs.preview_choices": "معاينة اختياراتك",
|
||||
"prefs.preview_tint_new_faces": "تلوين السطح الجديد في المعاينة",
|
||||
"prefs.preview_tint_new_faces_help": "يمكن لمعاينة ميزة تغيّر جسمًا أن ترسم السطح المضاف بلون المعاينة. عند الإيقاف يُرسم الجسم بلونه الأصلي.",
|
||||
"prefs.print_face_highlight": "إبراز وجه الطباعة",
|
||||
"prefs.print_face_highlight_tip": "مدى قوة تلوين وجه الطباعة للجسم ورسم حدوده في العرض. القيمة 0 % لا تُظهر أي إبراز.",
|
||||
"prefs.profile_active": "ملف التعريف النشط",
|
||||
"prefs.profile_applied": "تم تطبيق ملف التعريف.",
|
||||
"prefs.profile_changed": "تغيّرت الإعدادات منذ حفظ ملف التعريف. «حفظ» يحفظها فيه.",
|
||||
"prefs.profile_deleted": "تم حذف ملف التعريف.",
|
||||
"prefs.profile_export": "تصدير…",
|
||||
"prefs.profile_export": "تصدير",
|
||||
"prefs.profile_exported": "تم تصدير ملف التعريف.",
|
||||
"prefs.profile_imported": "تم استيراد ملف التعريف. اختره في «ملف التعريف النشط» للتبديل إليه.",
|
||||
"prefs.profile_imported": "تم استيراد ملف التعريف.",
|
||||
"prefs.profile_name_hint": "اسم ملف التعريف، مثل: حاسوب محمول",
|
||||
"prefs.profile_rename": "إعادة تسمية",
|
||||
"prefs.profile_renamed": "تمت إعادة تسمية ملف التعريف.",
|
||||
@@ -2278,12 +2343,13 @@
|
||||
"prefs.render_previous_geometry_budget": "استخدام ميزانية الهندسة السابقة لمعاينة التصيير",
|
||||
"prefs.render_previous_geometry_budget_help": "استعادة حد الهندسة الثابت السابق (512 ميبيبايت على سطح المكتب 64 بت، و128 ميبيبايت على المنصات الأخرى) بدلًا من الميزانية المعتمدة على الجهاز.",
|
||||
"prefs.reports_saved": "{n} محفوظة",
|
||||
"prefs.restore_page_defaults": "استعادة الإعدادات الافتراضية لهذه الصفحة",
|
||||
"prefs.ribbon_click_ends_tool": "النقر على زر الأداة قيد التشغيل ينهيها",
|
||||
"prefs.ribbon_click_ends_tool_note": "النقر على زر الشريط المضيء لأداة الرسم المستخدمة ينهي الأداة، فيعود النقر في العرض إلى التحديد. عند الإيقاف: يبدأ النقر الأداة من جديد.",
|
||||
"prefs.ribbon_reveal": "إظهار مكان نتائج البحث في الشريط",
|
||||
"prefs.ribbon_reveal_note": "عند تمرير المؤشر فوق نتيجة في «البحث في الأوامر» يظهر مكان الأمر في الشريط: تبويبه أو زره أو صفه في القائمة المنسدلة للوحة. ويومض هناك الأمر الذي بدأ من البحث، ويُعلِّم الأمر الجاري من قائمة منسدلة لوحتَه.",
|
||||
"prefs.right_click": "نقر أيمن",
|
||||
"prefs.saving": "الحفظ التلقائي والاستعادة",
|
||||
"prefs.saving": "البدء والحفظ",
|
||||
"prefs.scale_note": "أو Ctrl + / Ctrl − في النافذة. الأزرار 28 px عند 100%.",
|
||||
"prefs.scale_text": "الحجم والنص",
|
||||
"prefs.search": "البحث في الإعدادات",
|
||||
@@ -2347,7 +2413,7 @@
|
||||
"prefs.structure.in_profile": "ضمن ملف التعريف",
|
||||
"prefs.structure.layout": "التخطيط",
|
||||
"prefs.structure.layout_note": "مواضع أجزاء النافذة.",
|
||||
"prefs.structure.look": "المظهر",
|
||||
"prefs.structure.look": "العرض",
|
||||
"prefs.structure.measure_pointer": "القياس والمؤشر",
|
||||
"prefs.structure.mouse3d": "فأرة ثلاثية الأبعاد",
|
||||
"prefs.structure.mouse3d_note": "SpaceMouse وSpaceNavigator.",
|
||||
@@ -2547,6 +2613,10 @@
|
||||
"render.base_color": "اللون الأساسي · sRGB",
|
||||
"render.base_metallic": "النسبة المعدنية",
|
||||
"render.base_opaque_only": "يحافظ هذا المحرر على دلالات الجسم المعتم. استخدم عناصر التحكم الصريحة للزجاج من أجل النفاذ البصري.",
|
||||
"render.batch_all_shots": "كل اللقطات ({count})",
|
||||
"render.batch_empty": "احفظ لقطة قبل تصيير كل اللقطات.",
|
||||
"render.batch_limit": "تصيير كل اللقطات محدود بـ 32 لقطة.",
|
||||
"render.batch_status": "{running} قيد التشغيل · {waiting} في الانتظار",
|
||||
"render.bloom_enable": "توهج العرض",
|
||||
"render.bloom_help": "توهج إبداعي للإخراج فقط من الإضاءات الخطية الموجبة، يُنشر بثلاث تمريرات صندوقية مُطبّعة. نصف القطر ببكسلات الإخراج. يُضاف في ضوء العرض الخطي قبل sRGB؛ وليس عدسة ولا تأثيرًا حجميًا ولا تحسينًا للتقارب. يبقى EXR الخام دون تغيير.",
|
||||
"render.bloom_opaque": "يتطلب التوهج إخراجًا معتمًا. يُرفض PNG الشفاف بدل تسطيحه بصمت.",
|
||||
@@ -2657,6 +2727,13 @@
|
||||
"render.choose_look": "اختر مظهرًا من المظاهر أولًا",
|
||||
"render.chromatic_offset": "إزاحة لونية عند الحواف",
|
||||
"render.clear_stage": "إزالة المنصة المحفوظة",
|
||||
"render.compare_divider": "فاصل المقارنة",
|
||||
"render.compare_live": "حي",
|
||||
"render.compare_live_preview": "معاينة حية",
|
||||
"render.compare_render": "التصيير {number}",
|
||||
"render.compare_side": "التصيير {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} ث · {renderer} · التُقطت عند المراجعة {revision}",
|
||||
"render.compare_swap": "تبديل",
|
||||
"render.copy_request": "نسخ JSON",
|
||||
"render.cpu_aa_help": "العيّنات المكانية: المزيد منها يحسّن الحواف ويزيد زمن التصيير.",
|
||||
"render.cpu_area_add": "إضافة ضوء مستطيل على CPU",
|
||||
@@ -3406,6 +3483,10 @@
|
||||
"render.result_retained": "تُعرض آخر صورة مكتملة؛ ولم يستبدلها تصيير أحدث.",
|
||||
"render.result_sequence": "نقطة التحقق",
|
||||
"render.result_title": "نتيجة التصيير",
|
||||
"render.results_choose": "النتائج المحتفظ بها",
|
||||
"render.results_label": "التصيير {number} · {shot} · {seconds} ث",
|
||||
"render.results_stale_label": "{result} [قديم]",
|
||||
"render.results_unnamed": "صورة",
|
||||
"render.return_design": "العودة إلى التصميم",
|
||||
"render.review_stale": "هذه المراجعة قديمة. حُلّها أو افحص مرة أخرى قبل التطبيق.",
|
||||
"render.ribbon_aging": "التقادم",
|
||||
@@ -4066,6 +4147,7 @@
|
||||
"ui.agent_chat_auth_terminal": "شغّل هذا في الطرفية، ثم اضغط «اتصال» مرة أخرى:",
|
||||
"ui.agent_chat_auth_title": "تسجيل الدخول إلى الوكيل",
|
||||
"ui.agent_chat_cancel": "إلغاء",
|
||||
"ui.agent_chat_cancel_timeout": "لم يستجب الوكيل لطلب الإيقاف، فتم قطع الاتصال به. أعد الاتصال للمتابعة.",
|
||||
"ui.agent_chat_cannot_start": "مثبّت، لكنه لا يبدأ: {error}",
|
||||
"ui.agent_chat_changes": "التغييرات: {n}، خطوة تراجع واحدة",
|
||||
"ui.agent_chat_changes_n": "{n} تغييرات",
|
||||
@@ -4075,11 +4157,13 @@
|
||||
"ui.agent_chat_command": "أمر الوكيل",
|
||||
"ui.agent_chat_connect": "اتصال",
|
||||
"ui.agent_chat_copy": "نسخ",
|
||||
"ui.agent_chat_copy_message": "نسخ الرسالة",
|
||||
"ui.agent_chat_ctx_bodies": "الأجسام",
|
||||
"ui.agent_chat_ctx_elements": "العناصر",
|
||||
"ui.agent_chat_ctx_features": "الميزات",
|
||||
"ui.agent_chat_ctx_more": "+{n} أخرى",
|
||||
"ui.agent_chat_ctx_other": "أخرى",
|
||||
"ui.agent_chat_custom_heading": "نموذج مخصص",
|
||||
"ui.agent_chat_custom_hint": "أي برنامج يتحدث بروتوكول Agent Client Protocol عبر stdin وstdout. لا تشغّله أي صدفة أوامر.",
|
||||
"ui.agent_chat_denied": "مرفوض",
|
||||
"ui.agent_chat_deny": "رفض",
|
||||
@@ -4123,8 +4207,16 @@
|
||||
"ui.agent_chat_mode_all": "الكل",
|
||||
"ui.agent_chat_mode_ask": "السؤال",
|
||||
"ui.agent_chat_mode_edits": "التعديلات",
|
||||
"ui.agent_chat_model_custom": "استخدام المخصص: {model}",
|
||||
"ui.agent_chat_model_no_tools": "لا يستطيع {model} استخدام أدوات SolidCraft، لذا يمكنه المحادثة فقط. اختر نموذجًا يدعم الأدوات.",
|
||||
"ui.agent_chat_model_rejected": "لم يقبل الوكيل {model}: {error}",
|
||||
"ui.agent_chat_model_search": "البحث عن النماذج…",
|
||||
"ui.agent_chat_model_tip": "النموذج الذي يستخدمه الوكيل. يُحفظ اختيارك لهذا الوكيل.",
|
||||
"ui.agent_chat_models_failed": "تعذّر جلب قائمة النماذج: {error}",
|
||||
"ui.agent_chat_models_heading": "النماذج المتاحة",
|
||||
"ui.agent_chat_models_listing": "جارٍ جلب قائمة النماذج…",
|
||||
"ui.agent_chat_models_none": "لا تسرد نقطة النهاية أي نماذج",
|
||||
"ui.agent_chat_models_not_a_list": "لم يكن الرد قائمة نماذج",
|
||||
"ui.agent_chat_new": "محادثة جديدة",
|
||||
"ui.agent_chat_new_confirm": "بدء محادثة جديدة؟ ستُمسح المحادثة الحالية.",
|
||||
"ui.agent_chat_next_picture": "الصورة التالية",
|
||||
@@ -4260,7 +4352,7 @@
|
||||
"ui.ai_permissions": "الأذونات",
|
||||
"ui.ai_preset": "إعداد مسبق",
|
||||
"ui.ai_protocol": "البروتوكول",
|
||||
"ui.ai_reasoning_effort": "مستوى الاستدلال",
|
||||
"ui.ai_reasoning_effort": "جهد الاستدلال",
|
||||
"ui.ai_reasoning_effort_field_hint": "فارغ = الافتراضي للمزوّد",
|
||||
"ui.ai_reasoning_effort_hint": "اختياري. يُرسل إلى نقطة النهاية على أنه `reasoning_effort`؛ تختلف القيم المقبولة حسب المزوّد (مثل `low` أو `high` أو `max`). لا تستبدل SolidCraft القيمة التي تحددها هنا أبدًا. اتركه فارغًا لعدم إرسال شيء والاحتفاظ بالافتراضي للمزوّد.",
|
||||
"ui.ai_remove": "إزالة",
|
||||
@@ -4386,6 +4478,8 @@
|
||||
"ui.coming_later": "قريبًا",
|
||||
"ui.command_done": "تم {command}",
|
||||
"ui.command_palette_hint": "اكتب أمرًا، مثل {example} — أو اضغط S للبحث",
|
||||
"ui.components_from_bodies_hint": "أنشئ مكونًا لكل جسم محدد، باسم ذلك الجسم، ضمن المكون النشط.",
|
||||
"ui.components_from_bodies_required": "حدد جسمًا واحدًا على الأقل.",
|
||||
"ui.concentric": "متحد المركز",
|
||||
"ui.cone": "مخروط",
|
||||
"ui.conic_curve": "منحنى مخروطي",
|
||||
@@ -4398,9 +4492,11 @@
|
||||
"ui.construction.contact_hint": "مماس: اختر دائرة/قوسًا أو منحنى شريحيًا/قطعًا مخروطيًا مفتوحًا ثم نقطة التماس (للمنحنيات الحرة: النهايات فقط)",
|
||||
"ui.construction.external_hint": "مماس: اختر دائرة أو قوسًا ثم نقطة خارجية ثم الحل المطلوب",
|
||||
"ui.construction.free_hint": "خط إنشائي: انقر نقطتين؛ انقر بزر الفأرة الأيمن للبدء من جديد",
|
||||
"ui.construction.normal_needs_length": "الخط اللانهائي إنشائي دائمًا: اكتب طولًا لرسم خط عادي",
|
||||
"ui.construction.point_hint": "خط إنشائي: انقر نقطة المرور",
|
||||
"ui.construction.reference_hint": "خط إنشائي: اختر خطًا مستقيمًا ثم نقطة؛ انقر بزر الفأرة الأيمن لتغيير المرجع",
|
||||
"ui.construction.ribbon_label": "خطوط مساعدة",
|
||||
"ui.construction.side_hint": "خط إنشائي: انقر الجهة التي يمتد نحوها الخط؛ انقر بزر الفأرة الأيمن للبدء من جديد",
|
||||
"ui.context_menu": "القائمة السياقية",
|
||||
"ui.continuity_g0": "G0 تجعّد",
|
||||
"ui.continuity_g1": "G1 مماس",
|
||||
@@ -4666,6 +4762,7 @@
|
||||
"ui.hideOrigin": "إخفاء نقطة الأصل",
|
||||
"ui.hidePlanes": "إخفاء المستويات",
|
||||
"ui.hideProfile": "إخفاء المقطع",
|
||||
"ui.hide_component_history": "إخفاء سجله",
|
||||
"ui.hide_origin_by_default": "إخفاء نقطة الأصل افتراضيًا",
|
||||
"ui.hide_origin_note": "تبدأ التصاميم الجديدة ومجلد نقطة الأصل مخفي. يحتفظ كل تصميم بإعداد نقطة الأصل الخاص به بمجرد إظهارها أو إخفائها.",
|
||||
"ui.history_back": "الرجوع خطوة",
|
||||
@@ -4673,6 +4770,7 @@
|
||||
"ui.history_edge_hint": "انقر مرتين على الحافة",
|
||||
"ui.history_first": "الذهاب إلى البداية",
|
||||
"ui.history_forward": "التقدم خطوة",
|
||||
"ui.history_hide_hidden": "إخفاء المكونات المخفية",
|
||||
"ui.history_hide_names": "إخفاء الأسماء",
|
||||
"ui.history_last": "الذهاب إلى النهاية",
|
||||
"ui.history_marker_tip": "مؤشر السجل: اسحبه لإرجاع النموذج، والميزات الجديدة تُضاف هنا",
|
||||
@@ -4682,10 +4780,12 @@
|
||||
"ui.history_play_button_show": "إظهار زر التشغيل",
|
||||
"ui.history_play_finish_action": "أكمل الإجراء الحالي قبل تشغيل السجل",
|
||||
"ui.history_play_roll_back": "أعد مؤشر السجل إلى الخلف قبل التشغيل",
|
||||
"ui.history_show_hidden": "إظهار المكونات المخفية",
|
||||
"ui.history_show_names": "إظهار الأسماء",
|
||||
"ui.history_sketch_listed_under": "مدرج تحت {feature}",
|
||||
"ui.history_title": "السجل",
|
||||
"ui.history_toggle_names": "إظهار أسماء الميزات أو إخفاؤها",
|
||||
"ui.history_waits_for_editor": "ينتظر التراجع والإعادة والسجل حتى يكتمل فتح محرر الميزة",
|
||||
"ui.hole": "ثقب",
|
||||
"ui.hole_tap_type": "نوع لولبة الثقب",
|
||||
"ui.hole_type": "نوع الثقب",
|
||||
@@ -4701,6 +4801,7 @@
|
||||
"ui.insert_design.saved_file": "في {name} تغييرات غير محفوظة، لذا أُدرج ملفه المحفوظ. احفظه، ثم استخدم تحديث الروابط لإدخال التغييرات.",
|
||||
"ui.insert_design.unsaved": "تغييرات غير محفوظة",
|
||||
"ui.inspect": "فحص",
|
||||
"ui.instances": "النسخ",
|
||||
"ui.intersect": "تقاطع",
|
||||
"ui.isolate": "عزل",
|
||||
"ui.join": "ضم",
|
||||
@@ -4851,9 +4952,9 @@
|
||||
"ui.polygon": "مضلع",
|
||||
"ui.preferences": "التفضيلات",
|
||||
"ui.press_a_key": "اضغط مفتاحًا…",
|
||||
"ui.print_face_body_not_sent": "جسم هذا الوجه لا يُرسل: أضفه أولًا إلى الأجسام",
|
||||
"ui.print_face_cleared": "{body}: تم مسح وجه الطباعة",
|
||||
"ui.print_face_input": "وجه الطباعة",
|
||||
"ui.print_face_one_body": "يُختار وجه الطباعة لجسم واحد: أرسل ذلك الجسم وحده لتغييره",
|
||||
"ui.print_face_pick": "اختر وجهًا مستويًا واحدًا لجسم صلب لجعله وجه الطباعة",
|
||||
"ui.print_face_set": "{body}: تم تعيين وجه الطباعة؛ الإرسال إلى برنامج التقطيع يضع الجسم عليه",
|
||||
"ui.profile": "المقطع",
|
||||
@@ -4965,6 +5066,7 @@
|
||||
"ui.showDimension": "إظهار البُعد",
|
||||
"ui.showOrigin": "إظهار نقطة الأصل",
|
||||
"ui.showPlanes": "إظهار المستويات",
|
||||
"ui.show_component_history": "إظهار سجله",
|
||||
"ui.show_constraints": "القيود",
|
||||
"ui.show_construction_geometries": "الهندسة المساعدة",
|
||||
"ui.show_dimensions": "الأبعاد",
|
||||
@@ -4996,7 +5098,7 @@
|
||||
"ui.slicer_no_response": "لم يستجب {slicer} في الوقت المحدد. الملف محفوظ مسبقًا في {path}: أرسله مرة أخرى بعد أن يبدأ برنامج التقطيع.",
|
||||
"ui.slicer_resend_hint": "الإرسال مرة أخرى يستبدل الملف المفتوح في برنامج التقطيع.",
|
||||
"ui.slicer_single_instance": "فتح الملفات في برنامج تقطيع يعمل مسبقًا",
|
||||
"ui.slicer_single_instance_tip": "يحمّل PrusaSlicer وSuperSlicer وCura الملف في النافذة المفتوحة بدلًا من تشغيل نسخة أخرى. أما برامج التقطيع الأخرى فتُشغَّل كما هي. يفعل PrusaSlicer 3.0 هذا دائمًا: لا يقبل مشغّله الملف بأي طريقة أخرى.",
|
||||
"ui.slicer_single_instance_tip": "لبرامج التقطيع التي تفتح الملف في نافذة قيد التشغيل.",
|
||||
"ui.slicer_starting": "جارٍ تشغيل {slicer}…",
|
||||
"ui.slicer_still_starting": "لا يزال الانتظار جاريًا لبدء {slicer}. لم يُرسل أي شيء آخر.",
|
||||
"ui.slicer_stl_fallback": "تعذّرت كتابة ملف 3MF، لذا هذا ملف STL.",
|
||||
@@ -5081,12 +5183,15 @@
|
||||
"ui.view_angles": "زوايا العرض",
|
||||
"ui.view_angles_convention": "درجات حول المحور العلوي. الانعراج 0° هو العرض الأمامي، و90° الأيسر، و−90° الأيمن، و180° الخلفي؛ الانحدار 90° هو العرض العلوي؛ والدوران الموجب يدير النموذج على الشاشة باتجاه عقارب الساعة.",
|
||||
"ui.view_angles_help": "يعرض زوايا الانحدار والانعراج والدوران للعرض بالدرجات ونوع الإسقاط أسفل أداة الاتجاه. انقر على القراءة لكتابة زوايا دقيقة.",
|
||||
"ui.view_angles_lock": "قفل {axis}",
|
||||
"ui.view_angles_ortho": "متعامد",
|
||||
"ui.view_angles_persp": "منظور",
|
||||
"ui.view_angles_pitch": "الانحدار",
|
||||
"ui.view_angles_readout": "الانحدار {pitch} · الانعراج {yaw} · الدوران {roll}",
|
||||
"ui.view_angles_roll": "الدوران",
|
||||
"ui.view_angles_unlock": "إلغاء قفل {axis}",
|
||||
"ui.view_angles_yaw": "الانعراج",
|
||||
"ui.view_angles_zero": "تعيين {axis} إلى 0°",
|
||||
"ui.view_centre": "مركز العرض",
|
||||
"ui.viewport_status_clear": "إيقاف {name}",
|
||||
"ui.viewport_status_clear_hint": "إيقاف {name} · Esc",
|
||||
@@ -5138,7 +5243,7 @@
|
||||
"update.available": "SolidCraft {version} متاح",
|
||||
"update.button": "تحديث",
|
||||
"update.button_version": "التحديث إلى {version}",
|
||||
"update.check_automatically": "التحقق من التحديثات تلقائيًا",
|
||||
"update.check_automatically": "التحقق عند البدء ومرة يوميًا",
|
||||
"update.check_for_updates": "التحقق من وجود تحديثات",
|
||||
"update.check_now": "تحقق الآن",
|
||||
"update.check_unavailable": "التحقق من التحديثات غير متاح لهذا التثبيت.",
|
||||
@@ -5188,6 +5293,7 @@
|
||||
"web.open_link": "فتح التصميم المرتبط؟",
|
||||
"web.open_link_confirm": "توجد تغييرات غير محفوظة. هل تريد فتح التصميم المرتبط في تبويب جديد؟",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ اختيار · Enter / Tab إكمال · Esc إغلاق",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ اختيار · Enter يطبّق هذه القيمة",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ اختيار · Enter يطبّق ما كتبته · Esc إغلاق",
|
||||
"⌘Enter continues on GitHub.": "⌘Enter يتابع في GitHub."
|
||||
}
|
||||
|
||||
+71
-1
@@ -793,6 +793,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (pričvršćenje izgubljeno)",
|
||||
"browser.sketch_editing": "{name} (uređuje se)",
|
||||
"browser.sketch_name": "Skica{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "već u svom dijelu",
|
||||
"browser.sketches_to_parts_design_body": "na tijelu samog dizajna",
|
||||
"browser.sketches_to_parts_moved": "Skice premještene u dio na kojem se nalaze: {moved}",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "na licu bez tačnog pričvršćenja",
|
||||
"browser.sketches_to_parts_not_on_face": "ne na licu dijela",
|
||||
"browser.sketches_to_parts_part_missing": "njihov dio više ne postoji",
|
||||
"browser.sketches_to_parts_result": "Skice premještene u dio na kojem se nalaze: {moved}; ostale na mjestu: {stayed} ({why})",
|
||||
"browser.units": "Jedinice: {units}",
|
||||
"cam.flat": "Ravno lice",
|
||||
"cam.follower": "Pratilac",
|
||||
@@ -860,6 +867,7 @@
|
||||
"cmd.component_move_bodies": "Premjesti tijela u komponentu",
|
||||
"cmd.component_move_features": "Premjesti operacije u komponentu",
|
||||
"cmd.component_move_sketches": "Premjesti skice u komponentu",
|
||||
"cmd.component_move_sketches_to_parts": "Premjesti skice u njihove dijelove",
|
||||
"cmd.component_paste_from_design": "Zalijepi iz drugog dizajna",
|
||||
"cmd.component_paste_new": "Zalijepi kao novo",
|
||||
"cmd.component_properties": "Fizička svojstva komponente",
|
||||
@@ -1195,6 +1203,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Splajn kontrolnih tačaka (stepen 5)",
|
||||
"cmd.sketch_spline_fit_point": "Splajn kroz tačke",
|
||||
"cmd.sketch_spline_insert_point": "Dodaj tačku splajna",
|
||||
"cmd.sketch_spline_remove_point": "Ukloni tačku splajna",
|
||||
"cmd.sketch_spline_reverse": "Obrni smjer splajna",
|
||||
"cmd.sketch_spun_profile": "Rotirani profil",
|
||||
"cmd.sketch_toggle_diameter": "Prebaci poluprečnik / prečnik",
|
||||
@@ -1287,8 +1296,14 @@
|
||||
"color.hex_hint": "#rrggbb",
|
||||
"color.hue": "Nijansa",
|
||||
"color.linear": "Prikaz vrijednosti boje u linearnom prostoru 0-1",
|
||||
"color.recent": "Nedavne boje",
|
||||
"color.recent_help": "Ovdje se pojavljuju boje koje izaberete u bilo kojem polju boje",
|
||||
"color.saturation": "Zasićenost (procenat)",
|
||||
"color.saturation_value": "Zasićenost i vrijednost",
|
||||
"color.selected": "Izabrana boja",
|
||||
"color.step_down": "Smanji za korak",
|
||||
"color.step_up": "Povećaj za korak",
|
||||
"color.value": "Vrijednost, svjetlina (procenat)",
|
||||
"config.column_filter_empty": "Nema odgovarajućih kolona",
|
||||
"contact.components_count": "Komponente: {count}",
|
||||
"contact.enabled_label": "omogućeno",
|
||||
@@ -1335,6 +1350,7 @@
|
||||
"dialog.dimension_name": "Kota: {name}",
|
||||
"dialog.edit_title": "UREDI {title}",
|
||||
"dialog.no_shown_body": "Nema prikazanog tijela za izmjenu: prikažite jedno ili izaberite Novo tijelo",
|
||||
"dialog.pattern_sketch_not_object": "Izaberite tijela ili operacije: skica nije ni jedno ni drugo. Ponovite njene krive pomoću alata Niz ili Preslikaj u samoj skici ili je prvo ekstrudirajte.",
|
||||
"dialog.select": "↖ Izaberi",
|
||||
"dialog.selected_count": "↖ Izabrano: {count}",
|
||||
"dialog.selection_guidance": "Izaberite ulaz da ga promijenite. Kliknite izabrane objekte da ih uklonite; Ctrl/Cmd-klik ih također uključuje i isključuje. Pogledajte pregled, a zatim pritisnite U redu.",
|
||||
@@ -1904,6 +1920,7 @@
|
||||
"nav.hfss.help": "Alt+lijevo povlačenje pomjera prikaz, Shift+Alt+lijevo zumira, Ctrl+lijevo povlačenje na praznom prostoru orbitira",
|
||||
"nav.inventor.help": "Povlačenje srednjim dugmetom pomjera, Shift+srednje orbitira, Ctrl+srednje zumira, povlačenje desnim orbitira",
|
||||
"nav.maya.help": "Alt+lijevo povlačenje orbitira, Alt+srednje pomjera, Alt+desno zumira; srednje povlačenje također pomjera",
|
||||
"nav.nx.help": "Povlačenje srednjim dugmetom orbitira, Shift+srednje pomjera (u NX-u srednje + desno), Ctrl+srednje zumira (u NX-u srednje + lijevo); povlačenje desnim dugmetom ne dira kameru",
|
||||
"nav.openscad.help": "Povlačenje lijevim dugmetom po praznom prostoru orbitira, Shift+povlačenje lijevim bira okvirom, povlačenje geometrije skice je uređuje; povlačenje desnim ili srednjim dugmetom pomjera, Shift+desno ili Shift+srednje zumira",
|
||||
"nav.perspective.sketch": "Uređivanje skice koristi ortografski prikaz. Završite skicu da prebacite na perspektivu.",
|
||||
"nav.right_drag_orbits.help": "Isključeno: povlačenje desnim dugmetom ne dira kameru, a desno dugme samo otvara kontekstni meni. Izbor mapiranja miša SolidWorks to isključuje.",
|
||||
@@ -2125,6 +2142,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Zakrivljene površi",
|
||||
"prefs.curves": "Zakrivljene površi",
|
||||
"prefs.dark_parts_shading": "Zadrži oblik tamnih dijelova",
|
||||
"prefs.dark_parts_shading_help": "Sjenči prikaz u linearnom svjetlu s mekim dopunskim svjetlom neba, tako da tamna lica zadrže gradijent umjesto da pocrne",
|
||||
"prefs.dead_zone_note": "Šrafirana traka je mrtva zona: kretanje unutar nje se zanemaruje.",
|
||||
"prefs.default": "podrazumijevano",
|
||||
"prefs.design": "Podrazumijevane postavke dizajna",
|
||||
@@ -2139,6 +2158,8 @@
|
||||
"prefs.edges": "Ivice i sjenčenje",
|
||||
"prefs.extend": "Produži",
|
||||
"prefs.extra_navigation": "Ctrl + povlačenje lijevim dugmetom po praznom prostoru orbitira; držite razmaknicu za pomjeranje.",
|
||||
"prefs.extrude_view_isometric": "Okreni prikaz u izometrijski kada se otvori Ekstrudiraj",
|
||||
"prefs.extrude_view_return": "Vrati se na prethodni prikaz kada se Ekstrudiraj zatvori",
|
||||
"prefs.follows_os": "Prati operativni sistem",
|
||||
"prefs.font": "Font",
|
||||
"prefs.font_fallback": "Znakovi koji nedostaju izabranom fontu prikazuju se ugrađenim fontom, tako da nijedna oznaka ne prikazuje prazan kvadrat.",
|
||||
@@ -2154,6 +2175,7 @@
|
||||
"prefs.graphics_card_auto": "Automatski",
|
||||
"prefs.graphics_card_missing": "Sačuvana grafička kartica nije dostupna; koristi se Automatski.",
|
||||
"prefs.graphics_card_restart": "Stupa na snagu nakon ponovnog pokretanja SolidCrafta. WGPU_ADAPTER_NAME ima prednost nad ovim izborom.",
|
||||
"prefs.graphics_card_unused": "Sačuvana grafička kartica nije korištena za ovaj prozor; kartica koja se koristi prikazana je u Pomoć › O programu i u statusnoj traci. Pokušajte s opcijom Automatski ili postavite WGPU_ADAPTER_NAME na jedinstveni dio naziva kartice.",
|
||||
"prefs.graphics_note": "Žrtvujte brzinu za glatkoću. Svaki izbor pokazuje svoj učinak na primjeru.",
|
||||
"prefs.graphics_presets_note": "Unaprijed postavljene vrijednosti podešavaju sve izbore ispod odjednom.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Sakrij simbole ograničenja kada je prikaz umanjen",
|
||||
@@ -2168,6 +2190,8 @@
|
||||
"prefs.history_sketches_note": "Kada je Skice postavljeno na Ispod svoje operacije, vremenska traka lijevo prikazuje svaku skicu jedan nivo uvučeno, ispod prve operacije napravljene iz nje, kad god je dovoljno široka za nazive. Vremenska traka na dnu uvijek zadržava redoslijed nastanka.",
|
||||
"prefs.history_sketches_under_feature": "Ispod svoje operacije",
|
||||
"prefs.hold_stop_snapping": "Držite da isključite privlačenje",
|
||||
"prefs.home_fit_size": "Veličina za Početni prikaz i Uklopi",
|
||||
"prefs.home_fit_size_note": "Koliki dio prikaza model ispunjava kada ga uokviri Početni prikaz, Uklopi, otvaranje dizajna ili promjena prikaza u navigatoru, bez obzira na to je li postavka ispod uključena. 100 %: ispunjava prikaz između panela, kompasa i navigacijske trake.",
|
||||
"prefs.icons_only": "Samo ikone",
|
||||
"prefs.imported_file": "Uvezena datoteka",
|
||||
"prefs.in_browser": "U stablu modela",
|
||||
@@ -2176,13 +2200,20 @@
|
||||
"prefs.interface_language": "Jezik interfejsa",
|
||||
"prefs.interface_note": "Kako SolidCraft izgleda i gdje su njegovi dijelovi.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_navigator_arrows": "Obrni strelice za rotaciju kocke prikaza",
|
||||
"prefs.invert_navigator_arrows_note": "Strelice oko kocke orbitiraju kameru u smjeru u kojem pokazuju, kao u programima Fusion i Inventor: strelica s lijeve strane pokazuje udesno i zakreće kameru udesno. Isključeno: strelica okreće kocku prema strani na kojoj se nalazi, pa lijeva strelica zakreće kameru ulijevo. Postavka ne utiče na zakrivljene strelice za valjanje.",
|
||||
"prefs.keep_work_on_tab_switch": "Zadrži dijaloge i alate otvorenima pri prebacivanju između dizajna",
|
||||
"prefs.keep_work_on_tab_switch_note": "Svaka kartica dizajna zadržava svoj otvoreni dijalog ili alat, s onim što ste u njemu izabrali i upisali, te svoju karticu trake s alatima. Kada se vratite na dizajn, ponovo su tu. Isključeno: prebacivanje između dizajna otkazuje ih i prikazuje karticu Model.",
|
||||
"prefs.labels": "Oznake",
|
||||
"prefs.language_system": "Sistem",
|
||||
"prefs.layout": "Raspored",
|
||||
"prefs.layout_note": "Kompaktno pričvršćuje panele uz ivice prozora. Prozirnost stabla modela dostupna je u opciji Plutajuće. Stranu Asistenta promijenite na stranici Asistent.",
|
||||
"prefs.left": "Lijevo",
|
||||
"prefs.left_click": "Lijevi klik",
|
||||
"prefs.line_drag_draws_one": "Pritisak i povlačenje crta jednu liniju",
|
||||
"prefs.line_drag_draws_one_note": "U alatu Linija pritisak, povlačenje i puštanje crta jednu liniju od mjesta pritiska do mjesta puštanja i tu se završava. Klikovi i dalje crtaju lanac. Isključeno: povlačenje postavlja tačku tamo gdje se pusti, kao i klik.",
|
||||
"prefs.line_hover_tangent_arc": "Zadržite pokazivač na posljednjoj tački linije da pređete na tangentni luk",
|
||||
"prefs.line_hover_tangent_arc_note": "U alatu Linija, nakon što je nacrtan segment, odmaknite pokazivač od posljednje tačke lanca, vratite ga na nju i držite ga mirno: sljedeći klik završava tangentni luk, a isti pokret vraća na linije. Isključeno: zadržavanje na toj tački ne radi ništa.",
|
||||
"prefs.linked_designs": "Povezani dizajni",
|
||||
"prefs.live_input": "Ulaz uživo",
|
||||
"prefs.live_preview": "Pregled uživo",
|
||||
@@ -2206,6 +2237,8 @@
|
||||
"prefs.mouse_mapping": "Mapiranje miša",
|
||||
"prefs.navigation": "Navigacija",
|
||||
"prefs.navigation_note": "Kako miš, trackpad i 3D miš pomjeraju prikaz.",
|
||||
"prefs.navigator_fit": "Uklopi model nakon promjene prikaza u navigatoru",
|
||||
"prefs.navigator_fit_note": "Lice, ugao ili strelica kocke prikaza, odnosno osa alata za orijentaciju, također usmjerava pogled na sredinu modela s jedne udaljenosti na kojoj se uklapa iz svakog smjera (njegova granična sfera). Početni prikaz koristi isti zum i centar dok je ova opcija uključena, pa se prikazi koje izaberete poslije njega samo okreću. Isključeno: prikaz se okreće na mjestu i zadržava svoj cilj i zum.",
|
||||
"prefs.network_host_remove": "Ukloni",
|
||||
"prefs.network_locations": "Dozvoli mrežne lokacije u dizajnima",
|
||||
"prefs.network_locations_note": "Isključeno: povezane datoteke dizajna na drugom računaru (\\\\server\\share) otvaraju se samo sa servera navedenih ovdje ili nakon što ga dozvolite kada se to zatraži.",
|
||||
@@ -2254,6 +2287,8 @@
|
||||
"prefs.render_previous_geometry_budget": "Koristi prethodni budžet geometrije za pregled renderovanja",
|
||||
"prefs.render_previous_geometry_budget_help": "Vraća prethodno fiksno ograničenje geometrije (512 MiB na 64-bitnim desktop računarima, 128 MiB drugdje) umjesto budžeta prema uređaju.",
|
||||
"prefs.reports_saved": "Sačuvano: {n}",
|
||||
"prefs.ribbon_click_ends_tool": "Klik na dugme aktivnog alata završava ga",
|
||||
"prefs.ribbon_click_ends_tool_note": "Klik na istaknuto dugme na traci alata za skiciranje koji je u upotrebi završava taj alat, pa klikovi u prikazu ponovo služe za biranje. Isključeno: klik ponovo pokreće alat.",
|
||||
"prefs.ribbon_reveal": "Prikaži rezultate pretrage na traci",
|
||||
"prefs.ribbon_reveal_note": "Zadržavanje pokazivača iznad rezultata u Pretraži komande pokazuje gdje se komanda nalazi na traci: njenu karticu, njeno dugme ili njen red u padajućem meniju panela. Komanda pokrenuta iz pretrage tamo trepće, a pokrenuta komanda iz padajućeg menija označava svoj panel.",
|
||||
"prefs.right_click": "Desni klik",
|
||||
@@ -2283,6 +2318,8 @@
|
||||
"prefs.sketch_exit_view.previous": "Vrati se na prethodni prikaz",
|
||||
"prefs.sketch_exit_view.unless_rotated": "Vrati se osim ako je rotirano",
|
||||
"prefs.sketch_exit_view_note": "Kuda prikaz ide nakon Završi skicu, Otkaži ili dvostrukog Esc.",
|
||||
"prefs.sketch_face_activates_part": "Skiciranje na licu dijela čini taj dio aktivnim",
|
||||
"prefs.sketch_face_activates_part_note": "Skica započeta na licu tijela druge komponente prvo aktivira tu komponentu, pa skica pripada njoj i u stablu modela ostaje uz taj dio. Taj dio nakon toga ostaje aktivan. Isključeno: skica pripada aktivnoj komponenti.",
|
||||
"prefs.sketch_open_look_at": "Okreni kameru prema skici kad se otvori",
|
||||
"prefs.sketch_open_look_at_note": "Isključeno: kad napravite ili uredite skicu, prikaz ostaje onakav kakav jest. Pogledaj okreće prikaz prema skici kad to želite.",
|
||||
"prefs.sketch_recent_snap": "Automatski privlači na nedavne centre i središnje tačke",
|
||||
@@ -2314,6 +2351,24 @@
|
||||
"prefs.status_error": "Greška",
|
||||
"prefs.status_selected": "Izabrano",
|
||||
"prefs.status_warning": "Upozorenje",
|
||||
"prefs.structure.appearance": "Izgled",
|
||||
"prefs.structure.appearance_note": "Boje, tekst i pristupačnost.",
|
||||
"prefs.structure.in_profile": "U profilu",
|
||||
"prefs.structure.layout": "Raspored",
|
||||
"prefs.structure.layout_note": "Gdje se nalaze dijelovi prozora.",
|
||||
"prefs.structure.look": "Vizuelno",
|
||||
"prefs.structure.measure_pointer": "Mjerenje i pokazivač",
|
||||
"prefs.structure.mouse3d": "3D miš",
|
||||
"prefs.structure.mouse3d_note": "SpaceMouse i SpaceNavigator.",
|
||||
"prefs.structure.navigation_note": "Miš i trackpad. 3D miš ima vlastitu stranicu.",
|
||||
"prefs.structure.profile": "Profil",
|
||||
"prefs.structure.profile_edited": "Promijenjeno otkako je sačuvano",
|
||||
"prefs.structure.profile_hover": "Sačuvano u profilima postavki",
|
||||
"prefs.structure.scale_percent": "{percent}%",
|
||||
"prefs.structure.sketch": "Skica",
|
||||
"prefs.structure.tools": "Alati",
|
||||
"prefs.structure.tools_note": "Kako se ponašaju alati za modeliranje.",
|
||||
"prefs.structure.work": "Rad",
|
||||
"prefs.supersampling_brief": "Iscrtava prikaz većim i smanjuje ga filtriranjem: vrlo oštro i zahtjevnije.",
|
||||
"prefs.tab_accent_note": "Naglasna boja prati aktivnu karticu",
|
||||
"prefs.theme_file": "datoteka teme",
|
||||
@@ -3811,6 +3866,7 @@
|
||||
"sketch_text.formula": "Počnite znakom = da riječi složite iz parametara: ='W' + number(Width)",
|
||||
"sketch_text.italic": "Kurziv",
|
||||
"sketch_text.loading_fonts": "Traženje instaliranih fontova…",
|
||||
"sketch_text.no_bold": "Ugrađeni font nema podebljanu varijantu: izaberite instalirani font",
|
||||
"sketch_text.on_path": "Tekst prati svoju putanju: kriva ga postavlja",
|
||||
"sketch_text.path_clear": "Skini tekst s njegove krive",
|
||||
"sketch_text.path_hint": "Kriva skice koju tekst prati: kliknite ovdje, a zatim krivu",
|
||||
@@ -4213,6 +4269,9 @@
|
||||
"ui.ai_permissions": "Dozvole",
|
||||
"ui.ai_preset": "Unaprijed postavljeno",
|
||||
"ui.ai_protocol": "Protokol",
|
||||
"ui.ai_reasoning_effort": "Nivo zaključivanja",
|
||||
"ui.ai_reasoning_effort_field_hint": "prazno = podrazumijevana vrijednost pružaoca",
|
||||
"ui.ai_reasoning_effort_hint": "Neobavezno. Šalje se krajnjoj tački kao `reasoning_effort`; prihvaćene vrijednosti razlikuju se od pružaoca do pružaoca (na primjer `low`, `high` ili `max`). SolidCraft nikad ne mijenja vrijednost koju ovdje postavite. Ostavite prazno da se ništa ne šalje i da se zadrži podrazumijevana vrijednost pružaoca.",
|
||||
"ui.ai_remove": "Ukloni",
|
||||
"ui.ai_request_timed_out": "Model je trajao duže od {seconds} s. Vremensko ograničenje zahtjeva za ovu krajnju tačku možete povećati u Postavke › Asistent.",
|
||||
"ui.ai_request_timeout": "Vremensko ograničenje zahtjeva",
|
||||
@@ -4378,6 +4437,7 @@
|
||||
"ui.dark": "Tamna",
|
||||
"ui.default_modelling_orientation": "Podrazumijevana orijentacija modeliranja",
|
||||
"ui.delete": "Izbriši",
|
||||
"ui.delete_select_first": "Prvo izaberite šta želite izbrisati: operacije, tijela, komponente ili geometriju skice",
|
||||
"ui.depth": "Dubina",
|
||||
"ui.design": "Dizajn",
|
||||
"ui.design_properties.author": "Autor",
|
||||
@@ -4470,6 +4530,7 @@
|
||||
"ui.edit_in_place_saved_elsewhere": "Dio je sačuvan u drugu datoteku; sklop i dalje povezuje: {name}",
|
||||
"ui.edit_in_place_wait": "Pričekajte da se završi ono što se izvršava u dizajnu",
|
||||
"ui.edit_section_analysis": "Uredi analizu presjeka",
|
||||
"ui.edit_sketch_empty": "Nema skica za uređivanje",
|
||||
"ui.ellipse": "Elipsa",
|
||||
"ui.emboss_wrap_note": "Ortogonalni linijski profili koji tangiraju jedan podržani cilindar obavijaju se automatski. Izaberite zid da izaberete cilj.",
|
||||
"ui.end_radius": "Krajnji poluprečnik",
|
||||
@@ -4480,7 +4541,9 @@
|
||||
"ui.equation_explicit": "Eksplicitno",
|
||||
"ui.equation_form": "Oblik",
|
||||
"ui.equation_note": "Varijabla je običan broj: sin(t) je uzima u radijanima. Rezultat bez jedinice je u jedinici dizajna.",
|
||||
"ui.equation_note_range": "Varijabla je dužina, kao i njen raspon: kombinujte je s parametrima dužine, a za sin() je podijelite dužinom. Rezultat bez jedinice je u jedinici dizajna.",
|
||||
"ui.equation_parametric": "Parametarski",
|
||||
"ui.equation_variable_unit": "Varijabla ima jedinicu raspona",
|
||||
"ui.equation_x_of_t": "x(t)",
|
||||
"ui.equation_y_of_t": "y(t)",
|
||||
"ui.equation_y_of_x": "y(x)",
|
||||
@@ -4647,6 +4710,7 @@
|
||||
"ui.insert_design.saved_file": "{name} ima nesačuvane promjene, pa je umetnuta njegova sačuvana datoteka. Sačuvajte ga, a zatim pokrenite Ažuriraj veze da unesete promjene.",
|
||||
"ui.insert_design.unsaved": "Nesačuvane promjene",
|
||||
"ui.inspect": "Pregledaj",
|
||||
"ui.instances": "Instance",
|
||||
"ui.intersect": "Presjek",
|
||||
"ui.isolate": "Izoluj",
|
||||
"ui.join": "Spoji",
|
||||
@@ -4655,12 +4719,14 @@
|
||||
"ui.joints": "Spojevi",
|
||||
"ui.keyboard_shortcuts": "Prečice na tastaturi",
|
||||
"ui.language": "Jezik",
|
||||
"ui.lasso_select": "Laso (povucite petlju slobodnom rukom: uzima sve što je u cijelosti unutra)",
|
||||
"ui.layout_compact": "Kompaktno",
|
||||
"ui.layout_floating": "Plutajuće",
|
||||
"ui.length": "Dužina",
|
||||
"ui.length_decimals": "Decimale dužine",
|
||||
"ui.light": "Svijetla",
|
||||
"ui.line": "Linija",
|
||||
"ui.line_arc_hint": "Tangentni luk: kliknite kraj luka. Ponovo se zadržite na posljednjoj tački za linije; desni klik ili Esc za završetak",
|
||||
"ui.link_copies": "Poveži identične kopije",
|
||||
"ui.link_copies_keep": "Zadrži",
|
||||
"ui.link_copies_link": "Poveži",
|
||||
@@ -4749,6 +4815,7 @@
|
||||
"ui.part_size_shared": "Ponovo gradi dio, zamjenjujući sve dodane operacije. Svi primjerci ove komponente se ažuriraju.",
|
||||
"ui.paste": "Zalijepi",
|
||||
"ui.path": "Putanja",
|
||||
"ui.pattern_object_type_tip": "Tijela: puni oblici, biraju se u prikazu ili u stablu modela; svako se kopira u cijelosti.\nOperacije: koraci vremenske trake, poput ekstruzije ili rupe, biraju se na vremenskoj traci ili na licu koje su napravili; korak se ponavlja.\nSkica nije ni jedno ni drugo: ponovite njene krive pomoću alata Niz ili Preslikaj u samoj skici ili je prvo ekstrudirajte.",
|
||||
"ui.pattern_on_path": "Niz duž putanje",
|
||||
"ui.perforate_margin_default": "iz prečnika",
|
||||
"ui.perforate_pitch": "Korak",
|
||||
@@ -4794,9 +4861,9 @@
|
||||
"ui.polygon": "Poligon",
|
||||
"ui.preferences": "Postavke",
|
||||
"ui.press_a_key": "pritisnite tipku…",
|
||||
"ui.print_face_body_not_sent": "Tijelo te površine se ne šalje: prvo ga dodajte u Tijela",
|
||||
"ui.print_face_cleared": "{body}: površina za štampu uklonjena",
|
||||
"ui.print_face_input": "Površina za štampu",
|
||||
"ui.print_face_one_body": "Površina za štampu pripada jednom tijelu: pošaljite to tijelo samo da biste je promijenili",
|
||||
"ui.print_face_pick": "Odaberite ravnu površinu punog tijela da postane površina za štampu",
|
||||
"ui.print_face_set": "{body}: površina za štampu postavljena; Pošalji u slajser polaže tijelo na nju",
|
||||
"ui.profile": "Profil",
|
||||
@@ -5024,12 +5091,15 @@
|
||||
"ui.view_angles": "Uglovi prikaza",
|
||||
"ui.view_angles_convention": "Stepeni oko ose prema gore. Skretanje 0° je pogled sprijeda, 90° slijeva, −90° zdesna, 180° straga; nagib 90° je pogled odozgo; pozitivno valjanje okreće model u smjeru kazaljke na satu.",
|
||||
"ui.view_angles_help": "Prikazuje nagib, skretanje i valjanje prikaza u stepenima i njegovu projekciju ispod alata za orijentaciju. Kliknite očitanje da upišete tačne uglove.",
|
||||
"ui.view_angles_lock": "Zaključaj {axis}",
|
||||
"ui.view_angles_ortho": "Ortogr.",
|
||||
"ui.view_angles_persp": "Persp.",
|
||||
"ui.view_angles_pitch": "Nagib",
|
||||
"ui.view_angles_readout": "Nagib {pitch} · Skretanje {yaw} · Valjanje {roll}",
|
||||
"ui.view_angles_roll": "Valjanje",
|
||||
"ui.view_angles_unlock": "Otključaj {axis}",
|
||||
"ui.view_angles_yaw": "Skretanje",
|
||||
"ui.view_angles_zero": "Postavi {axis} na 0°",
|
||||
"ui.view_centre": "Centar prikaza",
|
||||
"ui.viewport_status_clear": "Isključi: {name}",
|
||||
"ui.viewport_status_clear_hint": "Isključi: {name} · Esc",
|
||||
|
||||
+71
-1
@@ -793,6 +793,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (uchycení ztraceno)",
|
||||
"browser.sketch_editing": "{name} (upravuje se)",
|
||||
"browser.sketch_name": "Náčrt{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "už jsou ve svém dílu",
|
||||
"browser.sketches_to_parts_design_body": "leží na tělese samotného návrhu",
|
||||
"browser.sketches_to_parts_moved": "Přesunuto náčrtů do dílu, na kterém leží: {moved}",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "leží na ploše bez přesného připojení",
|
||||
"browser.sketches_to_parts_not_on_face": "neleží na ploše dílu",
|
||||
"browser.sketches_to_parts_part_missing": "jejich díl už neexistuje",
|
||||
"browser.sketches_to_parts_result": "Přesunuto náčrtů do dílu, na kterém leží: {moved}; na místě zůstalo: {stayed} — {why}",
|
||||
"browser.units": "Jednotky: {units}",
|
||||
"cam.flat": "Ploché čelo",
|
||||
"cam.follower": "Zdvihátko",
|
||||
@@ -860,6 +867,7 @@
|
||||
"cmd.component_move_bodies": "Přesunout tělesa do komponenty",
|
||||
"cmd.component_move_features": "Přesunout prvky do komponenty",
|
||||
"cmd.component_move_sketches": "Přesunout náčrty do komponenty",
|
||||
"cmd.component_move_sketches_to_parts": "Přesunout náčrty do jejich dílů",
|
||||
"cmd.component_paste_from_design": "Vložit z jiného návrhu",
|
||||
"cmd.component_paste_new": "Vložit jako nové",
|
||||
"cmd.component_properties": "Fyzikální vlastnosti komponenty",
|
||||
@@ -1195,6 +1203,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Splajn řídicími body (stupeň 5)",
|
||||
"cmd.sketch_spline_fit_point": "Splajn procházející body",
|
||||
"cmd.sketch_spline_insert_point": "Přidat bod splajnu",
|
||||
"cmd.sketch_spline_remove_point": "Odebrat bod splajnu",
|
||||
"cmd.sketch_spline_reverse": "Obrátit směr splajnu",
|
||||
"cmd.sketch_spun_profile": "Rotační obrys",
|
||||
"cmd.sketch_toggle_diameter": "Přepnout poloměr / průměr",
|
||||
@@ -1287,8 +1296,14 @@
|
||||
"color.hex_hint": "#rrggbb",
|
||||
"color.hue": "Odstín",
|
||||
"color.linear": "Hodnoty barvy v lineárním prostoru 0–1",
|
||||
"color.recent": "Nedávné barvy",
|
||||
"color.recent_help": "Barvy, které vyberete v libovolném poli barvy, se zobrazí zde",
|
||||
"color.saturation": "Sytost (v procentech)",
|
||||
"color.saturation_value": "Sytost a jas",
|
||||
"color.selected": "Vybraná barva",
|
||||
"color.step_down": "Snížit o krok",
|
||||
"color.step_up": "Zvýšit o krok",
|
||||
"color.value": "Hodnota, tedy jas (v procentech)",
|
||||
"config.column_filter_empty": "Žádné odpovídající sloupce",
|
||||
"contact.components_count": "Počet komponent: {count}",
|
||||
"contact.enabled_label": "povoleno",
|
||||
@@ -1335,6 +1350,7 @@
|
||||
"dialog.dimension_name": "Kóta: {name}",
|
||||
"dialog.edit_title": "UPRAVIT {title}",
|
||||
"dialog.no_shown_body": "Není zobrazeno žádné těleso ke změně: zobrazte nějaké nebo zvolte Nové těleso",
|
||||
"dialog.pattern_sketch_not_object": "Vyberte tělesa nebo prvky: náčrt není ani jedno. Opakujte jeho křivky vlastním nástrojem Pole nebo Zrcadlit v náčrtu, nebo jej nejprve vysuňte.",
|
||||
"dialog.select": "↖ Vybrat",
|
||||
"dialog.selected_count": "↖ Vybráno: {count}",
|
||||
"dialog.selection_guidance": "Vstup změníte jeho výběrem. Kliknutím na vybraný objekt jej odeberete; Ctrl/Cmd+klik výběr také přepíná. Zkontrolujte náhled a stiskněte OK.",
|
||||
@@ -1904,6 +1920,7 @@
|
||||
"nav.hfss.help": "Alt+tažení levým tlačítkem posouvá, Shift+Alt+levé tlačítko přibližuje, Ctrl+tažení levým tlačítkem na prázdném místě otáčí",
|
||||
"nav.inventor.help": "Tažení prostředním tlačítkem posouvá, Shift+prostřední otáčí, Ctrl+prostřední přibližuje, tažení pravým tlačítkem otáčí",
|
||||
"nav.maya.help": "Alt+tažení levým tlačítkem otáčí, Alt+prostřední tlačítko posouvá, Alt+pravé tlačítko přibližuje; tažení prostředním tlačítkem také posouvá",
|
||||
"nav.nx.help": "Tažení prostředním tlačítkem otáčí, Shift+prostřední posouvá (v NX prostřední + pravé), Ctrl+prostřední přibližuje (v NX prostřední + levé); tažení pravým tlačítkem kameru nemění",
|
||||
"nav.openscad.help": "Tažení levým tlačítkem na prázdném místě otáčí, Shift+tažení levým vybírá obdélníkem, tažení geometrie náčrtu ji upravuje; tažení pravým nebo prostředním tlačítkem posouvá, Shift+tažení pravým nebo prostředním přibližuje",
|
||||
"nav.perspective.sketch": "Úpravy náčrtu používají ortografický pohled. Dokončením náčrtu přepnete na perspektivu.",
|
||||
"nav.right_drag_orbits.help": "Vypnuto: tažení pravým tlačítkem kameru nemění a pravé tlačítko jen otevírá kontextovou nabídku. Volba přiřazení myši SolidWorks tuto možnost vypne.",
|
||||
@@ -2125,6 +2142,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Zakřivené plochy",
|
||||
"prefs.curves": "Zakřivené plochy",
|
||||
"prefs.dark_parts_shading": "Zachovat tvar tmavých dílů",
|
||||
"prefs.dark_parts_shading_help": "Stínuje pohled v lineárním světle s jemným světlem oblohy, takže tmavé plochy zachovají přechod a nezčernají",
|
||||
"prefs.dead_zone_note": "Šrafované pásmo je mrtvá zóna: pohyb uvnitř něj se ignoruje.",
|
||||
"prefs.default": "výchozí",
|
||||
"prefs.design": "Výchozí nastavení návrhu",
|
||||
@@ -2139,6 +2158,8 @@
|
||||
"prefs.edges": "Hrany a stínování",
|
||||
"prefs.extend": "Prodloužit",
|
||||
"prefs.extra_navigation": "Ctrl+tažení levým tlačítkem v prázdném prostoru otáčí pohled; podržením mezerníku pohled posunete.",
|
||||
"prefs.extrude_view_isometric": "Při otevření Vysunutí otočit pohled do izometrického",
|
||||
"prefs.extrude_view_return": "Po zavření Vysunutí vrátit dřívější pohled",
|
||||
"prefs.follows_os": "Podle systému",
|
||||
"prefs.font": "Písmo",
|
||||
"prefs.font_fallback": "Znaky, které vybrané písmo neobsahuje, se nahradí vestavěným písmem, takže žádný popisek nezobrazí prázdný čtverec.",
|
||||
@@ -2154,6 +2175,7 @@
|
||||
"prefs.graphics_card_auto": "Automaticky",
|
||||
"prefs.graphics_card_missing": "Uložená grafická karta není k dispozici; použije se možnost Automaticky.",
|
||||
"prefs.graphics_card_restart": "Projeví se po restartování aplikace SolidCraft. Tuto volbu přepíše WGPU_ADAPTER_NAME.",
|
||||
"prefs.graphics_card_unused": "Uložená grafická karta se pro toto okno nepoužila; použitá karta je uvedena v nabídce Nápověda › O aplikaci SolidCraft a ve stavovém řádku. Zkuste možnost Automaticky, nebo nastavte WGPU_ADAPTER_NAME na jedinečnou část názvu karty.",
|
||||
"prefs.graphics_note": "Rychlost, nebo hladkost obrazu: každá volba ukazuje svůj účinek na ukázce.",
|
||||
"prefs.graphics_presets_note": "Předvolby nastaví níže uvedené volby najednou.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Skrýt symboly vazeb při oddálení",
|
||||
@@ -2168,6 +2190,8 @@
|
||||
"prefs.history_sketches_note": "Při nastavení Náčrty na Pod svým prvkem zobrazuje Historie vlevo každý náčrt o úroveň níž, pod prvním prvkem, který z něj vychází, pokud je dost široká pro názvy. Časová osa dole vždy zachovává pořadí vytvoření.",
|
||||
"prefs.history_sketches_under_feature": "Pod svým prvkem",
|
||||
"prefs.hold_stop_snapping": "Podržením vypnout přichytávání",
|
||||
"prefs.home_fit_size": "Velikost pro Domů a Přizpůsobit",
|
||||
"prefs.home_fit_size_note": "Kolik pohledu model vyplní, když jej zarámuje Domů, Přizpůsobit, otevřený návrh nebo změna pohledu v navigátoru, ať je nastavení níže zapnuté, nebo ne. 100 %: vyplní pohled mezi panely, kompasem a navigační lištou.",
|
||||
"prefs.icons_only": "Pouze ikony",
|
||||
"prefs.imported_file": "Importovaný soubor",
|
||||
"prefs.in_browser": "V prohlížeči",
|
||||
@@ -2176,13 +2200,20 @@
|
||||
"prefs.interface_language": "Jazyk rozhraní",
|
||||
"prefs.interface_note": "Vzhled aplikace SolidCraft a rozmístění jejích částí.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_navigator_arrows": "Obrátit šipky otáčení krychle pohledu",
|
||||
"prefs.invert_navigator_arrows_note": "Šipky kolem krychle otáčejí kameru tím směrem, kam míří, jako ve Fusion a Inventor: šipka vlevo míří doprava a stáčí kameru doprava. Vypnuto: šipka otáčí krychli na stranu, na které leží, takže šipka vlevo stáčí kameru doleva. Zakřivené šipky natočení tím nejsou dotčeny.",
|
||||
"prefs.keep_work_on_tab_switch": "Ponechat dialogy a nástroje otevřené při přepínání návrhů",
|
||||
"prefs.keep_work_on_tab_switch_note": "Každá karta návrhu si ponechá otevřený dialog nebo nástroj s tím, co jste v něm vybrali a zadali, i svou kartu panelu nástrojů. Po návratu k návrhu jsou zase tam. Vypnuto: přepnutí návrhu je zruší a zobrazí kartu Model.",
|
||||
"prefs.labels": "Popisky",
|
||||
"prefs.language_system": "Systém",
|
||||
"prefs.layout": "Rozvržení",
|
||||
"prefs.layout_note": "Kompaktní ukotví panely k okrajům okna. Průsvitnost prohlížeče je k dispozici v režimu Plovoucí. Stranu asistenta změníte na stránce Asistent.",
|
||||
"prefs.left": "Vlevo",
|
||||
"prefs.left_click": "Levé kliknutí",
|
||||
"prefs.line_drag_draws_one": "Stisknutí a tažení nakreslí jednu úsečku",
|
||||
"prefs.line_drag_draws_one_note": "V nástroji Úsečka nakreslí stisknutí, tažení a puštění jednu úsečku od místa stisknutí po místo puštění a tím kreslení skončí. Kliknutí stále kreslí řetězec. Vypnuto: tažení umístí bod tam, kde tlačítko pustíte, stejně jako kliknutí.",
|
||||
"prefs.line_hover_tangent_arc": "Setrvání na posledním bodu úsečky přepne na tečný oblouk",
|
||||
"prefs.line_hover_tangent_arc_note": "V nástroji Úsečka po nakreslení úseku odsuňte ukazatel od posledního bodu řetězce, vraťte se a podržte jej na něm v klidu: další kliknutí ukončí tečný oblouk a stejné gesto se vrací k úsečkám. Vypnuto: setrvání tam nic nedělá.",
|
||||
"prefs.linked_designs": "Propojené návrhy",
|
||||
"prefs.live_input": "Živý vstup",
|
||||
"prefs.live_preview": "Živý náhled",
|
||||
@@ -2206,6 +2237,8 @@
|
||||
"prefs.mouse_mapping": "Přiřazení myši",
|
||||
"prefs.navigation": "Navigace",
|
||||
"prefs.navigation_note": "Jak myš, trackpad a 3D myš pohybují pohledem.",
|
||||
"prefs.navigator_fit": "Přizpůsobit model po změně pohledu v navigátoru",
|
||||
"prefs.navigator_fit_note": "Plocha, roh nebo šipka krychle pohledu, případně osa orientačního ovladače, také zaměří pohled na střed modelu z jediné vzdálenosti, ze které je celý vidět ze všech stran (jeho ohraničující koule). Domů při zapnutí používá stejné přiblížení a střed, takže pohledy zvolené po něm se jen otáčejí. Vypnuto: pohled se otáčí na místě a zachovává svůj cíl a přiblížení.",
|
||||
"prefs.network_host_remove": "Odebrat",
|
||||
"prefs.network_locations": "Povolit síťová umístění v návrzích",
|
||||
"prefs.network_locations_note": "Vypnuto: propojené soubory návrhu na jiném počítači (\\\\server\\share) se otevírají jen ze zde uvedených serverů nebo poté, co jeden povolíte na výzvu.",
|
||||
@@ -2254,6 +2287,8 @@
|
||||
"prefs.render_previous_geometry_budget": "Použít předchozí rozpočet geometrie náhledu renderu",
|
||||
"prefs.render_previous_geometry_budget_help": "Obnoví předchozí pevný limit geometrie (512 MiB na 64bitovém desktopu, 128 MiB jinde) místo rozpočtu podle zařízení.",
|
||||
"prefs.reports_saved": "{n} uloženo",
|
||||
"prefs.ribbon_click_ends_tool": "Kliknutím na tlačítko spuštěného nástroje jej ukončíte",
|
||||
"prefs.ribbon_click_ends_tool_note": "Kliknutí na zvýrazněné tlačítko právě používaného nástroje náčrtu na pásu karet nástroj ukončí, takže kliknutí v pohledu opět vybírají. Vypnuto: kliknutí nástroj spustí znovu.",
|
||||
"prefs.ribbon_reveal": "Ukazovat výsledky hledání na pásu karet",
|
||||
"prefs.ribbon_reveal_note": "Najetí na výsledek v Hledat příkazy ukáže, kde se příkaz na pásu karet nachází: jeho kartu, tlačítko nebo řádek v rozbalovací nabídce panelu. Příkaz spuštěný z hledání tam zabliká a běžící příkaz z rozbalovací nabídky označí svůj panel.",
|
||||
"prefs.right_click": "Pravé kliknutí",
|
||||
@@ -2283,6 +2318,8 @@
|
||||
"prefs.sketch_exit_view.previous": "Vrátit se k předchozímu pohledu",
|
||||
"prefs.sketch_exit_view.unless_rotated": "Vrátit, pokud nebyl pohled otočen",
|
||||
"prefs.sketch_exit_view_note": "Kam se pohled přesune po příkazu Dokončit náčrt, Zrušit nebo dvojitém Esc.",
|
||||
"prefs.sketch_face_activates_part": "Náčrt na ploše dílu tento díl aktivuje",
|
||||
"prefs.sketch_face_activates_part_note": "Náčrt zahájený na ploše tělesa jiné komponenty nejprve aktivuje tuto komponentu, takže jí náčrt patří a v prohlížeči zůstává u tohoto dílu. Díl zůstane aktivní i poté. Vypnuto: náčrt patří aktivní komponentě.",
|
||||
"prefs.sketch_open_look_at": "Při otevření náčrtu natočit pohled k němu",
|
||||
"prefs.sketch_open_look_at_note": "Vypnuto: vytvoření nebo úprava náčrtu ponechá pohled beze změny. Pohled kolmo natočí pohled k náčrtu, kdykoli ho chcete.",
|
||||
"prefs.sketch_recent_snap": "Automaticky přichytávat k nedávným středům a středním bodům",
|
||||
@@ -2314,6 +2351,24 @@
|
||||
"prefs.status_error": "Chyba",
|
||||
"prefs.status_selected": "Vybráno",
|
||||
"prefs.status_warning": "Varování",
|
||||
"prefs.structure.appearance": "Vzhled",
|
||||
"prefs.structure.appearance_note": "Barvy, text a usnadnění přístupu.",
|
||||
"prefs.structure.in_profile": "V profilu",
|
||||
"prefs.structure.layout": "Rozvržení",
|
||||
"prefs.structure.layout_note": "Kde jsou umístěné části okna.",
|
||||
"prefs.structure.look": "Zobrazení",
|
||||
"prefs.structure.measure_pointer": "Měření a ukazatel",
|
||||
"prefs.structure.mouse3d": "3D myš",
|
||||
"prefs.structure.mouse3d_note": "SpaceMouse a SpaceNavigator.",
|
||||
"prefs.structure.navigation_note": "Myš a touchpad. 3D myš má vlastní stránku.",
|
||||
"prefs.structure.profile": "Profil",
|
||||
"prefs.structure.profile_edited": "Změněno od uložení",
|
||||
"prefs.structure.profile_hover": "Uloženo v profilech nastavení",
|
||||
"prefs.structure.scale_percent": "{percent}%",
|
||||
"prefs.structure.sketch": "Náčrt",
|
||||
"prefs.structure.tools": "Nástroje",
|
||||
"prefs.structure.tools_note": "Jak se chovají nástroje pro modelování.",
|
||||
"prefs.structure.work": "Práce",
|
||||
"prefs.supersampling_brief": "Vykreslí pohled větší a pak jej zmenší s filtrováním: velmi ostré, ale náročnější.",
|
||||
"prefs.tab_accent_note": "Zvýrazňovací barva se řídí aktivní kartou",
|
||||
"prefs.theme_file": "soubor motivu",
|
||||
@@ -3811,6 +3866,7 @@
|
||||
"sketch_text.formula": "Začněte znakem = a sestavte slova z parametrů: ='W' + number(Width)",
|
||||
"sketch_text.italic": "Kurzíva",
|
||||
"sketch_text.loading_fonts": "Hledání nainstalovaných písem…",
|
||||
"sketch_text.no_bold": "Vestavěné písmo nemá tučný řez: zvolte nainstalované písmo",
|
||||
"sketch_text.on_path": "Text sleduje svou cestu: umístění určuje křivka",
|
||||
"sketch_text.path_clear": "Odpojit text od křivky",
|
||||
"sketch_text.path_hint": "Křivka náčrtu, kterou text sleduje: klikněte sem a poté na křivku",
|
||||
@@ -4213,6 +4269,9 @@
|
||||
"ui.ai_permissions": "Oprávnění",
|
||||
"ui.ai_preset": "Předvolba",
|
||||
"ui.ai_protocol": "Protokol",
|
||||
"ui.ai_reasoning_effort": "Úroveň uvažování",
|
||||
"ui.ai_reasoning_effort_field_hint": "prázdné = výchozí hodnota poskytovatele",
|
||||
"ui.ai_reasoning_effort_hint": "Volitelné. Odesílá se na endpoint jako `reasoning_effort`; přípustné hodnoty se u jednotlivých poskytovatelů liší (například `low`, `high` nebo `max`). Hodnotu, kterou zde nastavíte, SolidCraft nikdy nenahradí. Ponechte prázdné, aby se nic neodeslalo a zůstala výchozí hodnota poskytovatele.",
|
||||
"ui.ai_remove": "Odebrat",
|
||||
"ui.ai_request_timed_out": "Model odpovídal déle než {seconds} s. Časový limit požadavku pro tento endpoint můžete zvýšit v Předvolbách › Asistent.",
|
||||
"ui.ai_request_timeout": "Časový limit požadavku",
|
||||
@@ -4378,6 +4437,7 @@
|
||||
"ui.dark": "Tmavý",
|
||||
"ui.default_modelling_orientation": "Výchozí orientace modelování",
|
||||
"ui.delete": "Smazat",
|
||||
"ui.delete_select_first": "Nejprve vyberte, co smazat: prvky, tělesa, komponenty nebo geometrii náčrtu",
|
||||
"ui.depth": "Hloubka",
|
||||
"ui.design": "Návrh",
|
||||
"ui.design_properties.author": "Autor",
|
||||
@@ -4470,6 +4530,7 @@
|
||||
"ui.edit_in_place_saved_elsewhere": "Díl byl uložen do jiného souboru; sestava stále odkazuje na {name}",
|
||||
"ui.edit_in_place_wait": "Počkejte, až skončí to, co v návrhu běží",
|
||||
"ui.edit_section_analysis": "Upravit analýzu řezu",
|
||||
"ui.edit_sketch_empty": "Žádné náčrty k úpravě",
|
||||
"ui.ellipse": "Elipsa",
|
||||
"ui.emboss_wrap_note": "Profily z navzájem kolmých úseček, tečné k jednomu podporovanému válci, se obalí automaticky. Cíl zvolíte výběrem stěny.",
|
||||
"ui.end_radius": "Koncový poloměr",
|
||||
@@ -4480,7 +4541,9 @@
|
||||
"ui.equation_explicit": "Explicitní",
|
||||
"ui.equation_form": "Tvar",
|
||||
"ui.equation_note": "Proměnná je prosté číslo: sin(t) ji bere v radiánech. Výsledek bez jednotky je v jednotce návrhu.",
|
||||
"ui.equation_note_range": "Proměnná je délka, stejně jako její rozsah: kombinujte ji s parametry délky a pro sin() ji vydělte délkou. Výsledek bez jednotky je v jednotce návrhu.",
|
||||
"ui.equation_parametric": "Parametrický",
|
||||
"ui.equation_variable_unit": "Proměnná má jednotku rozsahu",
|
||||
"ui.equation_x_of_t": "x(t)",
|
||||
"ui.equation_y_of_t": "y(t)",
|
||||
"ui.equation_y_of_x": "y(x)",
|
||||
@@ -4647,6 +4710,7 @@
|
||||
"ui.insert_design.saved_file": "{name} obsahuje neuložené změny, proto byl vložen jeho uložený soubor. Uložte jej a pak použijte Aktualizovat odkazy, aby se změny převzaly.",
|
||||
"ui.insert_design.unsaved": "Neuložené změny",
|
||||
"ui.inspect": "Analýza",
|
||||
"ui.instances": "Instance",
|
||||
"ui.intersect": "Průnik",
|
||||
"ui.isolate": "Izolovat",
|
||||
"ui.join": "Sjednotit",
|
||||
@@ -4655,12 +4719,14 @@
|
||||
"ui.joints": "Klouby",
|
||||
"ui.keyboard_shortcuts": "Klávesové zkratky",
|
||||
"ui.language": "Jazyk",
|
||||
"ui.lasso_select": "Laso (tažením nakreslete volnou smyčku: vybere vše, co leží celé uvnitř)",
|
||||
"ui.layout_compact": "Kompaktní",
|
||||
"ui.layout_floating": "Plovoucí",
|
||||
"ui.length": "Délka",
|
||||
"ui.length_decimals": "Desetinná místa délky",
|
||||
"ui.light": "Světlý",
|
||||
"ui.line": "Úsečka",
|
||||
"ui.line_arc_hint": "Tečný oblouk: klikněte na konec oblouku. Znovu setrvejte na posledním bodu pro úsečky; dokončíte pravým kliknutím nebo klávesou Esc",
|
||||
"ui.link_copies": "Propojit shodné kopie",
|
||||
"ui.link_copies_keep": "Ponechat",
|
||||
"ui.link_copies_link": "Propojit",
|
||||
@@ -4749,6 +4815,7 @@
|
||||
"ui.part_size_shared": "Přestaví díl a nahradí všechny přidané prvky. Aktualizují se všechny výskyty této komponenty.",
|
||||
"ui.paste": "Vložit",
|
||||
"ui.path": "Cesta",
|
||||
"ui.pattern_object_type_tip": "Tělesa: pevné tvary vybrané v pohledu nebo v prohlížeči; každé se zkopíruje celé.\nPrvky: kroky Historie, například vysunutí nebo otvor, vybrané v Historii nebo na ploše, kterou vytvořily; krok se zopakuje.\nNáčrt není ani jedno: opakujte jeho křivky vlastním nástrojem Pole nebo Zrcadlit v náčrtu, nebo jej nejprve vysuňte.",
|
||||
"ui.pattern_on_path": "Pole po cestě",
|
||||
"ui.perforate_margin_default": "z průměru",
|
||||
"ui.perforate_pitch": "Rozteč",
|
||||
@@ -4794,9 +4861,9 @@
|
||||
"ui.polygon": "Mnohoúhelník",
|
||||
"ui.preferences": "Předvolby",
|
||||
"ui.press_a_key": "stiskněte klávesu…",
|
||||
"ui.print_face_body_not_sent": "Těleso této plochy se neodesílá: nejprve ho přidejte do Tělesa",
|
||||
"ui.print_face_cleared": "{body}: tisková plocha zrušena",
|
||||
"ui.print_face_input": "Tisková plocha",
|
||||
"ui.print_face_one_body": "Tisková plocha patří jednomu tělesu: odešlete toto těleso samostatně, abyste ji změnili",
|
||||
"ui.print_face_pick": "Vyberte rovinnou plochu objemového tělesa, aby se stala tiskovou plochou",
|
||||
"ui.print_face_set": "{body}: tisková plocha nastavena; Odeslat do sliceru položí těleso na ni",
|
||||
"ui.profile": "Profil",
|
||||
@@ -5024,12 +5091,15 @@
|
||||
"ui.view_angles": "Úhly pohledu",
|
||||
"ui.view_angles_convention": "Úhly ve stupních kolem svislé osy. Stáčení 0° je pohled zepředu, 90° zleva, −90° zprava, 180° zezadu; náklon 90° je pohled shora; kladné natočení otáčí model ve směru hodinových ručiček.",
|
||||
"ui.view_angles_help": "Zobrazuje náklon, stáčení a natočení pohledu ve stupních a jeho projekci pod orientačním ovladačem. Kliknutím na údaj zadáte přesné úhly.",
|
||||
"ui.view_angles_lock": "Zamknout {axis}",
|
||||
"ui.view_angles_ortho": "Orto",
|
||||
"ui.view_angles_persp": "Persp.",
|
||||
"ui.view_angles_pitch": "Náklon",
|
||||
"ui.view_angles_readout": "Náklon {pitch} · Stáčení {yaw} · Natočení {roll}",
|
||||
"ui.view_angles_roll": "Natočení",
|
||||
"ui.view_angles_unlock": "Odemknout {axis}",
|
||||
"ui.view_angles_yaw": "Stáčení",
|
||||
"ui.view_angles_zero": "Nastavit {axis} na 0°",
|
||||
"ui.view_centre": "Střed pohledu",
|
||||
"ui.viewport_status_clear": "Vypnout {name}",
|
||||
"ui.viewport_status_clear_hint": "Vypnout {name} · Esc",
|
||||
|
||||
+72
-1
@@ -793,6 +793,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (tilknytning mistet)",
|
||||
"browser.sketch_editing": "{name} (redigeres)",
|
||||
"browser.sketch_name": "Skitse{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "allerede i deres del",
|
||||
"browser.sketches_to_parts_design_body": "på et emne i selve designet",
|
||||
"browser.sketches_to_parts_moved": "Flyttede {moved} skitser til den del, de ligger på",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "på en flade uden nøjagtig tilknytning",
|
||||
"browser.sketches_to_parts_not_on_face": "ikke på en dels flade",
|
||||
"browser.sketches_to_parts_part_missing": "deres del findes ikke længere",
|
||||
"browser.sketches_to_parts_result": "Flyttede {moved} skitser til den del, de ligger på; {stayed} blev, hvor de var: {why}",
|
||||
"browser.units": "Enheder: {units}",
|
||||
"cam.flat": "Flad kontaktflade",
|
||||
"cam.follower": "Følger",
|
||||
@@ -860,6 +867,7 @@
|
||||
"cmd.component_move_bodies": "Flyt emner til komponent",
|
||||
"cmd.component_move_features": "Flyt features til komponent",
|
||||
"cmd.component_move_sketches": "Flyt skitser til komponent",
|
||||
"cmd.component_move_sketches_to_parts": "Flyt skitser til deres dele",
|
||||
"cmd.component_paste_from_design": "Indsæt fra et andet design",
|
||||
"cmd.component_paste_new": "Indsæt som ny",
|
||||
"cmd.component_properties": "Komponentens fysiske egenskaber",
|
||||
@@ -1195,6 +1203,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Spline med kontrolpunkter (grad 5)",
|
||||
"cmd.sketch_spline_fit_point": "Spline gennem punkter",
|
||||
"cmd.sketch_spline_insert_point": "Tilføj splinepunkt",
|
||||
"cmd.sketch_spline_remove_point": "Fjern splinepunkt",
|
||||
"cmd.sketch_spline_reverse": "Vend splinens retning",
|
||||
"cmd.sketch_spun_profile": "Omdrejningsprofil",
|
||||
"cmd.sketch_toggle_diameter": "Skift mellem radius og diameter",
|
||||
@@ -1287,8 +1296,14 @@
|
||||
"color.hex_hint": "#rrggbb",
|
||||
"color.hue": "Farvetone",
|
||||
"color.linear": "Viser farveværdier i 0-1 lineært rum",
|
||||
"color.recent": "Seneste farver",
|
||||
"color.recent_help": "Farver, du vælger i et farvefelt, vises her",
|
||||
"color.saturation": "Mætning (procent)",
|
||||
"color.saturation_value": "Mætning og værdi",
|
||||
"color.selected": "Valgt farve",
|
||||
"color.step_down": "Ét trin ned",
|
||||
"color.step_up": "Ét trin op",
|
||||
"color.value": "Værdi, lysstyrken (procent)",
|
||||
"config.column_filter_empty": "Ingen matchende kolonner",
|
||||
"contact.components_count": "{count} komponenter",
|
||||
"contact.enabled_label": "aktiveret",
|
||||
@@ -1335,6 +1350,7 @@
|
||||
"dialog.dimension_name": "Mål: {name}",
|
||||
"dialog.edit_title": "REDIGÉR {title}",
|
||||
"dialog.no_shown_body": "Intet vist emne at ændre: Vis et, eller vælg Nyt emne",
|
||||
"dialog.pattern_sketch_not_object": "Vælg emner eller features: En skitse er ingen af delene. Gentag dens kurver med skitsens eget Mønster eller Spejling, eller ekstruder den først.",
|
||||
"dialog.select": "↖ Vælg",
|
||||
"dialog.selected_count": "↖ Valgte: {count}",
|
||||
"dialog.selection_guidance": "Vælg et input for at ændre det. Klik på valgte objekter for at fjerne dem; Ctrl/Cmd-klik skifter også deres valgstatus. Gennemgå forhåndsvisningen, og tryk derefter på OK.",
|
||||
@@ -1904,6 +1920,7 @@
|
||||
"nav.hfss.help": "Alt+træk med venstre museknap panorerer, Shift+Alt+venstre zoomer, Ctrl+træk med venstre fra et tomt område roterer visningen",
|
||||
"nav.inventor.help": "Træk med midterste museknap panorerer, Shift+midterste roterer visningen, Ctrl+midterste zoomer, træk med højre roterer visningen",
|
||||
"nav.maya.help": "Alt+træk med venstre museknap roterer visningen, Alt+midterste panorerer, Alt+højre zoomer; træk med midterste museknap panorerer også",
|
||||
"nav.nx.help": "Træk med midterste museknap roterer visningen, Shift+midterste panorerer (NX's midterste + højre), Ctrl+midterste zoomer (NX's midterste + venstre); træk med højre museknap påvirker ikke kameraet",
|
||||
"nav.openscad.help": "Træk med venstre museknap i et tomt område roterer visningen, Shift+venstre markerer med en ramme, træk i skitsegeometri redigerer den; træk med højre eller midterste museknap panorerer, Shift+højre eller Shift+midterste zoomer",
|
||||
"nav.perspective.sketch": "Skitseredigering bruger ortografisk visning. Afslut skitsen for at skifte til perspektiv.",
|
||||
"nav.right_drag_orbits.help": "Slået fra: Træk med højre museknap påvirker ikke kameraet, og højre knap åbner kun genvejsmenuen. Hvis du vælger SolidWorks-musetildelingen, slås dette fra.",
|
||||
@@ -2125,6 +2142,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Krumme overflader",
|
||||
"prefs.curves": "Krumme overflader",
|
||||
"prefs.dark_parts_shading": "Bevar formen på mørke dele",
|
||||
"prefs.dark_parts_shading_help": "Skygger visningen i lineært lys med en blød himmelfyld, så mørke flader beholder et farveforløb i stedet for at blive sorte",
|
||||
"prefs.dead_zone_note": "Det skraverede bånd er dødzonen: Bevægelser inden for det ignoreres.",
|
||||
"prefs.default": "standard",
|
||||
"prefs.design": "Standardindstillinger for design",
|
||||
@@ -2139,6 +2158,8 @@
|
||||
"prefs.edges": "Kanter og skygning",
|
||||
"prefs.extend": "Udvid",
|
||||
"prefs.extra_navigation": "Ctrl+træk med venstre museknap i et tomt område roterer visningen; hold mellemrumstasten nede for at panorere.",
|
||||
"prefs.extrude_view_isometric": "Drej visningen til isometrisk, når Ekstrudering åbnes",
|
||||
"prefs.extrude_view_return": "Gå tilbage til den tidligere visning, når Ekstrudering lukkes",
|
||||
"prefs.follows_os": "Følger operativsystemet",
|
||||
"prefs.font": "Skrifttype",
|
||||
"prefs.font_fallback": "Tegn, som den valgte skrifttype mangler, vises med den indbyggede skrifttype, så ingen tekst viser en tom firkant.",
|
||||
@@ -2154,6 +2175,7 @@
|
||||
"prefs.graphics_card_auto": "Automatisk",
|
||||
"prefs.graphics_card_missing": "Det gemte grafikkort er ikke tilgængeligt. Automatisk bruges i stedet.",
|
||||
"prefs.graphics_card_restart": "Træder i kraft efter genstart af SolidCraft. WGPU_ADAPTER_NAME tilsidesætter dette valg.",
|
||||
"prefs.graphics_card_unused": "Det gemte grafikkort blev ikke brugt til dette vindue. Kortet i brug vises i Hjælp › Om og i statuslinjen. Prøv Automatisk, eller sæt WGPU_ADAPTER_NAME til en entydig del af kortets navn.",
|
||||
"prefs.graphics_note": "Afvej hastighed mod udjævning. Hvert valg viser sin virkning på et eksempel.",
|
||||
"prefs.graphics_presets_note": "Forudindstillinger angiver de nedenstående valg samlet.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Skjul bindingssymboler, når der zoomes ud",
|
||||
@@ -2168,6 +2190,8 @@
|
||||
"prefs.history_sketches_note": "Når Skitser er sat til Under deres feature, viser historikken til venstre hver skitse ét niveau inde under den første feature, der er bygget på den, så snart den er bred nok til navne. Tidslinjen nederst bevarer altid oprettelsesrækkefølgen.",
|
||||
"prefs.history_sketches_under_feature": "Under deres feature",
|
||||
"prefs.hold_stop_snapping": "Hold nede for at slå snapping fra",
|
||||
"prefs.home_fit_size": "Størrelse ved Hjem og Tilpas",
|
||||
"prefs.home_fit_size_note": "Hvor meget af visningen modellen fylder, når Hjem, Tilpas, et åbnet design eller et skift af visning i navigatoren rammer den ind, uanset om indstillingen nedenfor er slået til. 100 %: Fylder visningen mellem panelerne, kompasset og navigationslinjen.",
|
||||
"prefs.icons_only": "Kun ikoner",
|
||||
"prefs.imported_file": "Importeret fil",
|
||||
"prefs.in_browser": "I modeltræet",
|
||||
@@ -2176,13 +2200,20 @@
|
||||
"prefs.interface_language": "Sprog i brugergrænsefladen",
|
||||
"prefs.interface_note": "SolidCrafts udseende og placeringen af dets dele.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_navigator_arrows": "Vend visningskubens rotationspile om",
|
||||
"prefs.invert_navigator_arrows_note": "Pilene rundt om kuben roterer kameraet i den retning, de peger, som i Fusion og Inventor: Pilen til venstre peger mod højre og svinger kameraet til højre. Slået fra: En pil drejer kuben mod den side, den sidder på, så venstre pil svinger kameraet til venstre. De buede rulningspile påvirkes ikke.",
|
||||
"prefs.keep_work_on_tab_switch": "Behold dialoger og værktøjer åbne, når du skifter design",
|
||||
"prefs.keep_work_on_tab_switch_note": "Hver designfane beholder sin åbne dialog eller sit åbne værktøj med det, du har valgt og skrevet i den, og sin fane på båndet. De er der igen, når du vender tilbage til designet. Slået fra: Skift af design annullerer dem og viser fanen Model.",
|
||||
"prefs.labels": "Etiketter",
|
||||
"prefs.language_system": "System",
|
||||
"prefs.layout": "Layout",
|
||||
"prefs.layout_note": "Kompakt fastgør panelerne til vinduets kanter. Gennemsigtighed i modelbrowseren er tilgængelig i Flydende. Skift assistentens side på siden Assistent.",
|
||||
"prefs.left": "Venstre",
|
||||
"prefs.left_click": "Venstreklik",
|
||||
"prefs.line_drag_draws_one": "Tryk og træk tegner én linje",
|
||||
"prefs.line_drag_draws_one_note": "I værktøjet Linje tegner tryk, træk og slip én linje fra trykket til slippet, og linjen slutter dér. Klik tegner stadig en kæde. Slået fra: Et træk placerer et punkt, hvor det slippes, ligesom et klik gør.",
|
||||
"prefs.line_hover_tangent_arc": "Hvil markøren på en linjes sidste punkt for at skifte til en tangentbue",
|
||||
"prefs.line_hover_tangent_arc_note": "Når et segment er tegnet i værktøjet Linje, så flyt markøren væk fra kædens sidste punkt, kom tilbage, og hold den stille på punktet: Næste klik afslutter en tangentbue, og samme bevægelse går tilbage til linjer. Slået fra: Det gør ingenting at hvile markøren der.",
|
||||
"prefs.linked_designs": "Linkede designs",
|
||||
"prefs.live_input": "Løbende input",
|
||||
"prefs.live_preview": "Løbende forhåndsvisning",
|
||||
@@ -2206,6 +2237,8 @@
|
||||
"prefs.mouse_mapping": "Musetildeling",
|
||||
"prefs.navigation": "Navigation",
|
||||
"prefs.navigation_note": "Hvordan musen, pegefeltet og 3D-musen styrer visningen.",
|
||||
"prefs.navigator_fit": "Tilpas modellen efter et skift af visning i navigatoren",
|
||||
"prefs.navigator_fit_note": "En flade, et hjørne eller en pil på visningskuben eller en akse på orienteringsgizmoen ser også mod modellens midte fra den ene afstand, der får den til at passe fra alle sider (dens omsluttende kugle). Hjem bruger samme zoom og centrum, mens dette er slået til, så visninger, der vælges bagefter, kun drejer. Slået fra: Visningen drejer på stedet og beholder sit mål og sin zoom.",
|
||||
"prefs.network_host_remove": "Fjern",
|
||||
"prefs.network_locations": "Tillad netværksplaceringer i designs",
|
||||
"prefs.network_locations_note": "Slået fra: Et designs linkede filer på en anden computer (\\\\server\\share) åbnes kun fra de servere, der er angivet her, eller efter at du har tilladt en, når du bliver spurgt.",
|
||||
@@ -2254,6 +2287,8 @@
|
||||
"prefs.render_previous_geometry_budget": "Brug det tidligere geometribudget til forhåndsvisning af rendering",
|
||||
"prefs.render_previous_geometry_budget_help": "Gendan den tidligere faste geometrigrænse (512 MiB på 64-bit-computere, 128 MiB andre steder) i stedet for det enhedsbaserede budget.",
|
||||
"prefs.reports_saved": "{n} gemt",
|
||||
"prefs.ribbon_click_ends_tool": "Et klik på det kørende værktøjs knap afslutter det",
|
||||
"prefs.ribbon_click_ends_tool_note": "Et klik på den lysende knap på båndet for det skitseværktøj, der er i brug, afslutter værktøjet, så klik i visningen markerer igen. Slået fra: Klikket starter værktøjet igen.",
|
||||
"prefs.ribbon_reveal": "Vis søgeresultater på båndet",
|
||||
"prefs.ribbon_reveal_note": "Når du holder musen over et resultat i Søg efter kommandoer, vises det, hvor kommandoen sidder på båndet: dens fane, dens knap eller dens række i et panels rullemenu. En kommando, der startes fra søgningen, blinker dér, og en kørende kommando fra en rullemenu markerer sit panel.",
|
||||
"prefs.right_click": "Højreklik",
|
||||
@@ -2283,6 +2318,8 @@
|
||||
"prefs.sketch_exit_view.previous": "Vend tilbage til den forrige visning",
|
||||
"prefs.sketch_exit_view.unless_rotated": "Vend tilbage, medmindre visningen er roteret",
|
||||
"prefs.sketch_exit_view_note": "Hvor visningen går hen efter Afslut skitse, Annullér eller et dobbelt tryk på Esc.",
|
||||
"prefs.sketch_face_activates_part": "Når du skitserer på en dels flade, bliver delen aktiv",
|
||||
"prefs.sketch_face_activates_part_note": "En skitse, der startes på en flade af en anden komponents emne, aktiverer først den komponent, så skitsen hører til den og bliver hos den del i modeltræet. Delen forbliver aktiv bagefter. Slået fra: Skitsen hører til den aktive komponent.",
|
||||
"prefs.sketch_open_look_at": "Vend visningen mod en skitse, når den åbnes",
|
||||
"prefs.sketch_open_look_at_note": "Slået fra: Når en skitse oprettes eller redigeres, forbliver visningen, som den er. Se på vender visningen mod skitsen, når du ønsker det.",
|
||||
"prefs.sketch_recent_snap": "Automatisk snap til nylige centre og midtpunkter",
|
||||
@@ -2314,6 +2351,24 @@
|
||||
"prefs.status_error": "Fejl",
|
||||
"prefs.status_selected": "Valgt",
|
||||
"prefs.status_warning": "Advarsel",
|
||||
"prefs.structure.appearance": "Udseende",
|
||||
"prefs.structure.appearance_note": "Farver, tekst og tilgængelighed.",
|
||||
"prefs.structure.in_profile": "I profil",
|
||||
"prefs.structure.layout": "Layout",
|
||||
"prefs.structure.layout_note": "Hvor vinduets dele sidder.",
|
||||
"prefs.structure.look": "Look",
|
||||
"prefs.structure.measure_pointer": "Mål og markør",
|
||||
"prefs.structure.mouse3d": "3D-mus",
|
||||
"prefs.structure.mouse3d_note": "SpaceMouse og SpaceNavigator.",
|
||||
"prefs.structure.navigation_note": "Mus og pegefelt. 3D-musen har sin egen side.",
|
||||
"prefs.structure.profile": "Profil",
|
||||
"prefs.structure.profile_edited": "Ændret, siden den blev gemt",
|
||||
"prefs.structure.profile_hover": "Gemt i indstillingsprofiler",
|
||||
"prefs.structure.scale_percent": "{percent}%",
|
||||
"prefs.structure.sketch": "Skitse",
|
||||
"prefs.structure.tools": "Værktøjer",
|
||||
"prefs.structure.tools_note": "Hvordan modelleringsværktøjerne opfører sig.",
|
||||
"prefs.structure.work": "Arbejde",
|
||||
"prefs.supersampling_brief": "Tegner visningen i større størrelse og nedskalerer den med filtrering: meget skarpt og mere krævende.",
|
||||
"prefs.tab_accent_note": "Accentfarven følger den aktive fane",
|
||||
"prefs.theme_file": "temafil",
|
||||
@@ -3811,6 +3866,7 @@
|
||||
"sketch_text.formula": "Start med = for at bygge ordene ud fra parametre: ='W' + number(Width)",
|
||||
"sketch_text.italic": "Kursiv",
|
||||
"sketch_text.loading_fonts": "Søger efter installerede skrifttyper…",
|
||||
"sketch_text.no_bold": "Den indbyggede skrifttype har ingen fed udgave: Vælg en installeret skrifttype",
|
||||
"sketch_text.on_path": "Teksten følger sin bane: Kurven bestemmer placeringen",
|
||||
"sketch_text.path_clear": "Frigør teksten fra dens kurve",
|
||||
"sketch_text.path_hint": "En kurve i skitsen, som teksten følger: Klik her og derefter på kurven",
|
||||
@@ -4213,6 +4269,9 @@
|
||||
"ui.ai_permissions": "Tilladelser",
|
||||
"ui.ai_preset": "Forudindstilling",
|
||||
"ui.ai_protocol": "Protokol",
|
||||
"ui.ai_reasoning_effort": "Ræsonnementsindsats",
|
||||
"ui.ai_reasoning_effort_field_hint": "tom = udbyderens standard",
|
||||
"ui.ai_reasoning_effort_hint": "Valgfrit. Sendes til slutpunktet som `reasoning_effort`; accepterede værdier varierer fra udbyder til udbyder (for eksempel `low`, `high` eller `max`). En værdi, du angiver her, erstattes aldrig af SolidCraft. Lad feltet stå tomt for ikke at sende noget og beholde udbyderens standard.",
|
||||
"ui.ai_remove": "Fjern",
|
||||
"ui.ai_request_timed_out": "Modellen brugte mere end {seconds} s. Du kan hæve tidsgrænsen for anmodninger til dette endepunkt under Indstillinger › Assistent.",
|
||||
"ui.ai_request_timeout": "Tidsgrænse for anmodning",
|
||||
@@ -4378,6 +4437,7 @@
|
||||
"ui.dark": "Mørk",
|
||||
"ui.default_modelling_orientation": "Standardorientering for modellering",
|
||||
"ui.delete": "Slet",
|
||||
"ui.delete_select_first": "Vælg først, hvad der skal slettes: features, emner, komponenter eller skitsegeometri",
|
||||
"ui.depth": "Dybde",
|
||||
"ui.design": "Design",
|
||||
"ui.design_properties.author": "Forfatter",
|
||||
@@ -4470,6 +4530,7 @@
|
||||
"ui.edit_in_place_saved_elsewhere": "Delen blev gemt i en anden fil; samlingen linker stadig til {name}",
|
||||
"ui.edit_in_place_wait": "Vent, til det, der kører i designet, er færdigt",
|
||||
"ui.edit_section_analysis": "Redigér snitanalyse",
|
||||
"ui.edit_sketch_empty": "Ingen skitser at redigere",
|
||||
"ui.ellipse": "Ellipse",
|
||||
"ui.emboss_wrap_note": "Ortogonale linjeprofiler, der tangerer én understøttet cylinder, vikles automatisk om. Vælg en væg for at vælge et mål.",
|
||||
"ui.end_radius": "Slutradius",
|
||||
@@ -4480,7 +4541,9 @@
|
||||
"ui.equation_explicit": "Eksplicit",
|
||||
"ui.equation_form": "Form",
|
||||
"ui.equation_note": "Variablen er et almindeligt tal: sin(t) tager den i radianer. Et resultat uden enhed er i designets enhed.",
|
||||
"ui.equation_note_range": "Variablen er en længde, ligesom dens interval er det: Kombinér den med længdeparametre, og divider den med en længde for sin(). Et resultat uden enhed er i designets enhed.",
|
||||
"ui.equation_parametric": "Parametrisk",
|
||||
"ui.equation_variable_unit": "Variablen har intervallets enhed",
|
||||
"ui.equation_x_of_t": "x(t)",
|
||||
"ui.equation_y_of_t": "y(t)",
|
||||
"ui.equation_y_of_x": "y(x)",
|
||||
@@ -4647,6 +4710,7 @@
|
||||
"ui.insert_design.saved_file": "{name} har ændringer, der ikke er gemt, så dens gemte fil blev indsat. Gem den, og kør derefter Opdatér forbindelser for at hente ændringerne.",
|
||||
"ui.insert_design.unsaved": "Ikke-gemte ændringer",
|
||||
"ui.inspect": "Inspicér",
|
||||
"ui.instances": "Forekomster",
|
||||
"ui.intersect": "Fællesvolumen",
|
||||
"ui.isolate": "Isolér",
|
||||
"ui.join": "Foren",
|
||||
@@ -4655,12 +4719,14 @@
|
||||
"ui.joints": "Led",
|
||||
"ui.keyboard_shortcuts": "Tastaturgenveje",
|
||||
"ui.language": "Sprog",
|
||||
"ui.lasso_select": "Lasso (træk en frihåndsløkke: Markerer det, der ligger helt indeni)",
|
||||
"ui.layout_compact": "Kompakt",
|
||||
"ui.layout_floating": "Flydende",
|
||||
"ui.length": "Længde",
|
||||
"ui.length_decimals": "Længdedecimaler",
|
||||
"ui.light": "Lys",
|
||||
"ui.line": "Linje",
|
||||
"ui.line_arc_hint": "Tangentbue: Klik på buens slutpunkt. Hvil markøren på det sidste punkt igen for linjer; højreklik eller Esc for at afslutte",
|
||||
"ui.link_copies": "Link identiske kopier",
|
||||
"ui.link_copies_keep": "Behold",
|
||||
"ui.link_copies_link": "Link",
|
||||
@@ -4749,6 +4815,7 @@
|
||||
"ui.part_size_shared": "Genopbygger delen og erstatter alle tilføjede features. Alle forekomster af komponenten opdateres.",
|
||||
"ui.paste": "Indsæt",
|
||||
"ui.path": "Bane",
|
||||
"ui.pattern_object_type_tip": "Emner: massive former, der vælges i visningen eller modeltræet; hvert kopieres helt.\nFeatures: trin i historikken, f.eks. en ekstrudering eller et hul, der vælges i historikken eller på en flade, de har lavet; trinnet gentages.\nEn skitse er ingen af delene: Gentag dens kurver med skitsens eget Mønster eller Spejling, eller ekstruder den først.",
|
||||
"ui.pattern_on_path": "Mønster langs bane",
|
||||
"ui.perforate_margin_default": "ud fra diameteren",
|
||||
"ui.perforate_pitch": "Deling",
|
||||
@@ -4794,9 +4861,9 @@
|
||||
"ui.polygon": "Polygon",
|
||||
"ui.preferences": "Indstillinger",
|
||||
"ui.press_a_key": "tryk på en tast…",
|
||||
"ui.print_face_body_not_sent": "Den flades emne sendes ikke: føj det først til Emner",
|
||||
"ui.print_face_cleared": "{body}: printflade ryddet",
|
||||
"ui.print_face_input": "Printflade",
|
||||
"ui.print_face_one_body": "En printflade hører til ét legeme: send det legeme alene for at ændre den",
|
||||
"ui.print_face_pick": "Vælg en plan flade på et massivt legeme for at gøre den til printfladen",
|
||||
"ui.print_face_set": "{body}: printflade sat; Send til slicer lægger legemet på den",
|
||||
"ui.profile": "Profil",
|
||||
@@ -4814,6 +4881,7 @@
|
||||
"ui.properties.inertia_y": "Iyx, Iyy, Iyz",
|
||||
"ui.properties.inertia_z": "Izx, Izy, Izz",
|
||||
"ui.properties.measuring": "Måler…",
|
||||
"ui.properties.measuring_bodies": "Måler… {done} af {total} emner",
|
||||
"ui.properties.measuring_failed": "Målingen blev stoppet, før den var færdig.",
|
||||
"ui.properties.principal_axis": "Hovedakse {number}",
|
||||
"ui.properties.principal_moments": "Hovedmomenter",
|
||||
@@ -5023,12 +5091,15 @@
|
||||
"ui.view_angles": "Visningsvinkler",
|
||||
"ui.view_angles_convention": "Grader om den opadgående akse. Krøjning 0° er visningen Forfra, 90° Fra venstre, −90° Fra højre, 180° Bagfra; stampning 90° er visningen Ovenfra; en positiv rulning drejer modellen med uret.",
|
||||
"ui.view_angles_help": "Viser visningens stampning, krøjning og rulning i grader samt dens projektion under orienteringsgizmoen. Klik på visningen for at skrive eksakte vinkler.",
|
||||
"ui.view_angles_lock": "Lås {axis}",
|
||||
"ui.view_angles_ortho": "Orto",
|
||||
"ui.view_angles_persp": "Persp",
|
||||
"ui.view_angles_pitch": "Stampning",
|
||||
"ui.view_angles_readout": "Stampning {pitch} · Krøjning {yaw} · Rulning {roll}",
|
||||
"ui.view_angles_roll": "Rulning",
|
||||
"ui.view_angles_unlock": "Lås {axis} op",
|
||||
"ui.view_angles_yaw": "Krøjning",
|
||||
"ui.view_angles_zero": "Sæt {axis} til 0°",
|
||||
"ui.view_centre": "Visningens centrum",
|
||||
"ui.viewport_status_clear": "Slå {name} fra",
|
||||
"ui.viewport_status_clear_hint": "Slå {name} fra · Esc",
|
||||
|
||||
+118
-12
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "Rechteckig",
|
||||
"Rectangular Tube": "Rechteckrohr",
|
||||
"Reference face": "Referenzfläche",
|
||||
"Reference plane": "Referenzebene",
|
||||
"Relief Depth": "Freistellungstiefe",
|
||||
"Relief Width": "Freistellungsbreite",
|
||||
"Remove Tools": "Werkzeuge entfernen",
|
||||
@@ -494,7 +495,7 @@
|
||||
"Rename Folder": "Ordner umbenennen",
|
||||
"Render Stage": "Renderphase",
|
||||
"Reopen designs": "Designs erneut öffnen",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "Die Designs erneut öffnen, die beim letzten Beenden von SolidCraft geöffnet waren",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "Die zuletzt geöffneten Entwürfe erneut öffnen",
|
||||
"Replace existing library file": "Vorhandene Bibliotheksdatei ersetzen",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "Fehler melden — Screenshot prüfen und einen GitHub-Entwurf öffnen",
|
||||
"Report copied. Keep this text to submit on GitHub when you are online.": "Bericht kopiert. Bewahren Sie diesen Text auf, um ihn auf GitHub einzureichen, sobald Sie online sind.",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "Schnitt",
|
||||
"Section (optional)": "Abschnitt (optional)",
|
||||
"Section position": "Lage des Querschnitts",
|
||||
"Sections": "Querschnitte",
|
||||
"Sections (one sketch each)": "Querschnitte (je eine Skizze)",
|
||||
"Select Other": "Andere auswählen",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Anderes auswählen: Halten Sie die linke Taste ruhig auf dem Modell gedrückt (oder Rechtsklick › Anderes auswählen…), um alles unter dem Cursor aufzulisten; klicken Sie erneut an dieselbe Stelle, um das nächste Element dort auszuwählen. Doppelklick mit der mittleren Taste wechselt zur Startansicht (Einstellungen › Navigation).",
|
||||
@@ -793,6 +795,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (Bezug verloren)",
|
||||
"browser.sketch_editing": "{name} (in Bearbeitung)",
|
||||
"browser.sketch_name": "Skizze {number}",
|
||||
"browser.sketches_to_parts_already_in_part": "bereits in ihrem Teil",
|
||||
"browser.sketches_to_parts_design_body": "auf einem Körper des Designs selbst",
|
||||
"browser.sketches_to_parts_moved": "{moved} Skizzen in das Teil verschoben, auf dem sie liegen",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "auf einer Fläche ohne exakte Anbindung",
|
||||
"browser.sketches_to_parts_not_on_face": "nicht auf der Fläche eines Teils",
|
||||
"browser.sketches_to_parts_part_missing": "ihr Teil gibt es nicht mehr",
|
||||
"browser.sketches_to_parts_result": "{moved} Skizzen in das Teil verschoben, auf dem sie liegen; {stayed} geblieben: {why}",
|
||||
"browser.units": "Einheiten: {units}",
|
||||
"cam.flat": "Flachstößel",
|
||||
"cam.follower": "Stößel",
|
||||
@@ -860,6 +869,7 @@
|
||||
"cmd.component_move_bodies": "Körper in Komponente verschieben",
|
||||
"cmd.component_move_features": "Merkmale in Komponente verschieben",
|
||||
"cmd.component_move_sketches": "Skizzen in Komponente verschieben",
|
||||
"cmd.component_move_sketches_to_parts": "Skizzen in ihre Teile verschieben",
|
||||
"cmd.component_paste_from_design": "Aus einem anderen Design einfügen",
|
||||
"cmd.component_paste_new": "Neu einfügen",
|
||||
"cmd.component_properties": "Physikalische Eigenschaften der Komponente",
|
||||
@@ -877,6 +887,7 @@
|
||||
"cmd.config_rule": "Konfigurationsregel",
|
||||
"cmd.config_rules": "Konfigurationsregeln",
|
||||
"cmd.config_table": "Konfigurationstabelle anzeigen",
|
||||
"cmd.construct.plane.axis_point": "Ebene durch Achse und Punkt",
|
||||
"cmd.construct_axis_edge": "Achse durch Kante",
|
||||
"cmd.construct_axis_normal_to_face": "Achse senkrecht zur Fläche",
|
||||
"cmd.construct_axis_through_cylinder": "Achse durch Zylinder/Kegel/Torus",
|
||||
@@ -925,6 +936,11 @@
|
||||
"cmd.explode_show": "Explosionsdarstellung anzeigen",
|
||||
"cmd.expr_evaluate": "Ausdruck auswerten",
|
||||
"cmd.face_delete": "Fläche löschen",
|
||||
"cmd.file.export_3mf": "Als 3MF exportieren",
|
||||
"cmd.file.export_obj": "Als OBJ exportieren",
|
||||
"cmd.file.export_step": "Als STEP exportieren",
|
||||
"cmd.file.export_stl": "Als STL exportieren",
|
||||
"cmd.file.export_urdf": "Als URDF exportieren",
|
||||
"cmd.file_export": "Exportieren",
|
||||
"cmd.file_insert_mesh": "Netz einfügen",
|
||||
"cmd.file_insert_step": "STEP einfügen",
|
||||
@@ -1032,6 +1048,9 @@
|
||||
"cmd.pointcloud_edit": "Punktwolke bearbeiten",
|
||||
"cmd.pointcloud_insert": "Punktwolke einfügen",
|
||||
"cmd.pointcloud_list": "Punktwolken",
|
||||
"cmd.render.compare.divider": "Render-Vergleichstrenner verschieben",
|
||||
"cmd.render.result.discard": "Renderergebnis verwerfen",
|
||||
"cmd.render.result.select": "Renderergebnis auswählen",
|
||||
"cmd.render_aging_describe": "Mehrschichtige Oberflächenalterung",
|
||||
"cmd.render_backends": "Render-Backends",
|
||||
"cmd.render_batch": "Alle Aufnahmen rendern",
|
||||
@@ -1195,6 +1214,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Spline mit Kontrollpunkten (Grad 5)",
|
||||
"cmd.sketch_spline_fit_point": "Spline mit Fitpunkten",
|
||||
"cmd.sketch_spline_insert_point": "Splinepunkt hinzufügen",
|
||||
"cmd.sketch_spline_remove_point": "Splinepunkt entfernen",
|
||||
"cmd.sketch_spline_reverse": "Splinerichtung umkehren",
|
||||
"cmd.sketch_spun_profile": "Gedrehtes Profil",
|
||||
"cmd.sketch_toggle_diameter": "Radius / Durchmesser umschalten",
|
||||
@@ -1245,6 +1265,7 @@
|
||||
"cmd.timeline_rollback": "Zeitleistenmarkierung verschieben",
|
||||
"cmd.timeline_stop": "Verlauf anhalten",
|
||||
"cmd.timeline_suppress": "Merkmal unterdrücken",
|
||||
"cmd.ui.assistant": "Assistenten ein-/ausblenden",
|
||||
"cmd.view.render_browser": "Render-Browser",
|
||||
"cmd.view.render_dock": "Render-Panel verschieben",
|
||||
"cmd.view.render_focus": "Render-Fokusansicht",
|
||||
@@ -1279,9 +1300,11 @@
|
||||
"cmd.view_tool": "Navigationswerkzeug",
|
||||
"cmd.view_top": "Draufsicht",
|
||||
"cmd.view_visual_style": "Darstellungsstil",
|
||||
"color.blue": "Blau",
|
||||
"color.copy": "Klicken, um Farbwerte zu kopieren",
|
||||
"color.edit": "Klicken, um Farbe zu bearbeiten",
|
||||
"color.gamma": "Farbwerte im Gammaraum 0-255 anzeigen",
|
||||
"color.green": "Grün",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "#rrggbb oder #rrggbbaa eingeben; sechs Ziffern behalten die Deckkraft bei",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1289,6 +1312,7 @@
|
||||
"color.linear": "Farbwerte im linearen Raum 0-1 anzeigen",
|
||||
"color.recent": "Zuletzt verwendete Farben",
|
||||
"color.recent_help": "Farben, die Sie in einem beliebigen Farbfeld wählen, erscheinen hier",
|
||||
"color.red": "Rot",
|
||||
"color.saturation": "Sättigung (Prozent)",
|
||||
"color.saturation_value": "Sättigung und Wert",
|
||||
"color.selected": "Ausgewählte Farbe",
|
||||
@@ -1296,6 +1320,8 @@
|
||||
"color.step_up": "Schritt nach oben",
|
||||
"color.value": "Hellwert (Prozent)",
|
||||
"config.column_filter_empty": "Keine passenden Spalten",
|
||||
"config.rule_parameter_columns": "Hier stehen nur Parameter mit einer Spalte in der Tabelle. Fügen Sie eine mit „Spalte hinzufügen“ (+) hinzu, um den Parameter hier zu verwenden.",
|
||||
"config.rule_suppression_columns": "Hier stehen nur Funktionen mit einer Spalte „Unterdrücken“ in der Tabelle. Fügen Sie eine mit „Spalte hinzufügen“ (+) hinzu, um die Funktion hier zu steuern.",
|
||||
"contact.components_count": "{count} Komponenten",
|
||||
"contact.enabled_label": "aktiviert",
|
||||
"coupling.gear_1": "Zahnrad 1",
|
||||
@@ -1344,7 +1370,11 @@
|
||||
"dialog.pattern_sketch_not_object": "Körper oder Funktionen wählen: Eine Skizze ist keines von beiden. Ihre Kurven mit Muster oder Spiegeln in der Skizze wiederholen oder sie zuerst extrudieren.",
|
||||
"dialog.select": "↖ Auswählen",
|
||||
"dialog.selected_count": "↖ {count} ausgewählt",
|
||||
"dialog.selection_constraint": "Bedingung {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · Kurve {id}",
|
||||
"dialog.selection_guidance": "Wählen Sie eine Eingabe aus, um sie zu ändern. Klicken Sie auf ausgewählte Objekte, um sie zu entfernen; Strg/Cmd-Klick schaltet sie ebenfalls um. Prüfen Sie die Vorschau und drücken Sie dann OK.",
|
||||
"dialog.selection_midpoint": "Mittelpunkt · {id}",
|
||||
"dialog.selection_profile": "{sketch} · Profil {number}",
|
||||
"dialog.selection_step": "{step} von {total}: {input} auswählen",
|
||||
"dialog.step": "Schritt",
|
||||
"document.changed_on_disk": "{name} wurde auf dem Datenträger geändert",
|
||||
@@ -1427,6 +1457,7 @@
|
||||
"help.construct.axis.two_points": "Erzeugt eine Konstruktionsachse durch zwei Punkte, Eckpunkte oder Skizzenpunkte. Für eine Achse zwischen zwei bekannten Lagen.",
|
||||
"help.construct.plane.along_path": "Erzeugt eine Konstruktionsebene senkrecht zu einer Kante oder Kurve, an einer Stelle entlang dieser. Damit skizzieren Sie ein Profil für eine Austragung oder einen Querschnitt eines Pfads.",
|
||||
"help.construct.plane.at_angle": "Erzeugt eine Konstruktionsebene, die gegenüber einer Ebene oder Fläche um eine Achse oder Kante gedreht ist. Für schräge Skizzen, etwa eines Winkelhalters.",
|
||||
"help.construct.plane.axis_point": "Erstellt eine Konstruktionsebene durch eine Achse und einen Punkt außerhalb der Achse. Zuerst eine Ursprungs- oder Konstruktionsachse, gerade Kante oder Skizzenlinie wählen, dann einen Eckpunkt, Skizzenpunkt oder Konstruktionspunkt. Ein Punkt auf der Achse legt keine eindeutige Ebene fest.",
|
||||
"help.construct.plane.midplane": "Erzeugt eine Konstruktionsebene mittig zwischen zwei Ebenen oder ebenen Flächen oder als Winkelhalbierende, wo sie schräg zueinander stehen. Als Symmetrieebene zum Spiegeln.",
|
||||
"help.construct.plane.offset": "Erzeugt eine Konstruktionsebene parallel zu einer Ebene oder ebenen Fläche, in einem Abstand oder durch einen Punkt. Damit skizzieren Sie in fester Höhe über einer Fläche.",
|
||||
"help.construct.plane.perpendicular": "Erzeugt eine Konstruktionsebene, die eine Linie oder Kante enthält und senkrecht auf einer Ebene oder Fläche steht. Damit skizzieren Sie aufrecht entlang einer Kante am Modell.",
|
||||
@@ -1549,7 +1580,7 @@
|
||||
"help.sketch.constrainer": "Setzt die Bedingung, die eine oder zwei gewählte Kurven schon fast erfüllen, etwa horizontal, parallel, tangential oder gleich. Damit räumen Sie eine frei gezeichnete Skizze Beziehung für Beziehung auf.",
|
||||
"help.sketch.constraint.coincident": "Legt einen Punkt auf einen anderen Punkt, eine Kurve oder eine Skizzenachse. Damit schließen Sie Lücken in einem Profil oder setzen einen Punkt auf eine Linie.",
|
||||
"help.sketch.constraint.collinear": "Legt zwei Linien auf eine gemeinsame Gerade. Damit richten Sie unterbrochene Kanten aus, etwa die beiden Seiten einer Kerbe.",
|
||||
"help.sketch.constraint.concentric": "Gibt zwei Kreisen oder Bögen denselben Mittelpunkt. Für Ringe, Senkungen und abgesetzte Wellen.",
|
||||
"help.sketch.constraint.concentric": "Gibt zwei Kreisen oder Bögen denselben Mittelpunkt oder zentriert einen Kreis oder Bogen auf einem Skizzenpunkt, auch dem Ursprung. Die Auswahlreihenfolge ist beliebig.",
|
||||
"help.sketch.constraint.curvature": "Lässt zwei Kurven, die sich an einem Endpunkt treffen, mit gleicher Krümmung ineinander übergehen (G2). Wo ein tangentialer Übergang nicht glatt genug ist, etwa an Sichtflächen.",
|
||||
"help.sketch.constraint.equal": "Macht Linien gleich lang oder Kreise und Bögen gleich groß im Radius. So steuert ein Maß mehrere gleiche Elemente.",
|
||||
"help.sketch.constraint.fix": "Fixiert Punkte oder Kurven an ihrer Stelle oder löst sie wieder. Für Geometrie, die sich beim Ziehen oder Lösen der Skizze nicht bewegen darf.",
|
||||
@@ -1803,6 +1834,8 @@
|
||||
"joint_move.blocked_hint": "Prüfen Sie den Gelenktyp (z. B. „Starr“), die Fixierung, die Gelenkgrenzen und die durch Parameter gesteuerten Werte.",
|
||||
"joint_move.blocked_title": "Dieses Bauteil kann nicht bewegt werden",
|
||||
"joint_move.coarse": "Drehschritt nahe der Achse (°)",
|
||||
"joint_move.conflict": "Ein Gelenk dieses Bauteils steht im Konflikt mit den anderen: {joint}.",
|
||||
"joint_move.conflict_hint": "Bearbeiten, unterdrücken oder löschen Sie das widersprüchliche Gelenk, damit die anderen Gelenke das Bauteil platzieren können.",
|
||||
"joint_move.distance": "Abstand vom Ausgangspunkt",
|
||||
"joint_move.done": "Fertig",
|
||||
"joint_move.fine": "Drehschritt weit von der Achse entfernt (°)",
|
||||
@@ -1902,6 +1935,8 @@
|
||||
"motion.track_name": "Spur {number}",
|
||||
"nav.alias.help": "Umschalt+Alt+Linksziehen dreht, Umschalt+Alt+Mitte verschiebt, Umschalt+Alt+Rechtsziehen zoomt; mittleres Ziehen verschiebt ebenfalls und rechtes Ziehen dreht",
|
||||
"nav.blender.help": "Mittlere Maustaste dreht, Umschalt+Mitte verschiebt, Strg+Mitte zoomt; rechtes Ziehen lässt die Kamera unverändert",
|
||||
"nav.creo.help": "Ziehen mit der mittleren Maustaste dreht, Umschalt+Mitte verschiebt, Strg+Mitte zoomt beim vertikalen Ziehen (nach unten zum Vergrößern); das Mausrad zoomt",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "Maus und 3D-Maus drehen über beide Pole hinweg und behalten die Rollneigung bei. Das Einrasten mit Umschalt ist deaktiviert.",
|
||||
"nav.free_orbit.label": "Freie Drehung",
|
||||
"nav.free_orbit.snap_help": "Maus und 3D-Maus drehen über beide Pole hinweg und behalten die Rollausrichtung bei. Halten Sie beim Ziehen zum Drehen die Umschalttaste gedrückt, um Blickrichtung und Rollausrichtung einrasten zu lassen.",
|
||||
@@ -1911,6 +1946,7 @@
|
||||
"nav.hfss.help": "Alt+Linksziehen verschiebt, Umschalt+Alt+Linksziehen zoomt, Strg+Linksziehen auf leerer Fläche dreht",
|
||||
"nav.inventor.help": "Mittlere Maustaste verschiebt, Umschalt+Mitte dreht, Strg+Mitte zoomt, rechte Maustaste dreht",
|
||||
"nav.maya.help": "Alt+Linksziehen dreht, Alt+Mitte verschiebt, Alt+Rechtsziehen zoomt; mittleres Ziehen verschiebt ebenfalls",
|
||||
"nav.nx.help": "Mittlere Maustaste dreht, Umschalt+Mitte verschiebt (in NX Mitte + rechts), Strg+Mitte zoomt (in NX Mitte + links); rechtes Ziehen lässt die Kamera unverändert",
|
||||
"nav.openscad.help": "Linksziehen auf leerem Hintergrund dreht, Umschalt+Linksziehen zieht ein Auswahlrechteck auf, Skizzengeometrie wird durch Ziehen bearbeitet; rechtes oder mittleres Ziehen verschiebt, Umschalt+rechtes oder Umschalt+mittleres Ziehen zoomt",
|
||||
"nav.perspective.sketch": "Beim Bearbeiten einer Skizze wird die orthografische Ansicht verwendet. Beenden Sie die Skizze, um die Perspektive umzuschalten.",
|
||||
"nav.right_drag_orbits.help": "Aus: rechtes Ziehen lässt die Kamera unverändert, die rechte Maustaste öffnet nur das Kontextmenü. Die Mausbelegung SolidWorks schaltet es beim Auswählen aus.",
|
||||
@@ -2053,12 +2089,19 @@
|
||||
"parts.family.button_head_cap_screw": "Linsenkopfschraube mit Innensechskant",
|
||||
"parts.family.dowel_pin": "Zylinderstift",
|
||||
"parts.family.hex_bolt": "Sechskantschraube",
|
||||
"parts.family.hex_cap_screw_inch": "Sechskantschraube (Zoll)",
|
||||
"parts.family.hex_nut": "Sechskantmutter",
|
||||
"parts.family.hex_nut_inch": "Sechskantmutter (Zoll)",
|
||||
"parts.family.set_screw": "Gewindestift mit Innensechskant und Kegelstumpf",
|
||||
"parts.family.socket_head_cap_screw": "Zylinderschraube mit Innensechskant",
|
||||
"parts.family.washer": "Unterlegscheibe",
|
||||
"parts.family.washer_sae": "Unterlegscheibe, SAE (schmal)",
|
||||
"parts.family.washer_uss": "Unterlegscheibe, USS (breit)",
|
||||
"parts.holes": "Bohrungen (optional)",
|
||||
"parts.placement_hint": "Wählen Sie eine oder mehrere Bohrungen, um in jede ein Teil einzusetzen, oder OK platziert es im Ursprung",
|
||||
"parts.system": "Maßsystem",
|
||||
"parts.system.inch": "Zoll",
|
||||
"parts.system.metric": "Metrisch",
|
||||
"pattern.alignment_2": "Ausrichtung 2",
|
||||
"pattern.alignment_hint": "Die Anzahl enthält das Original. Die zentrierte Anordnung verteilt Kopien auf beide Seiten; bei gerader Anzahl legt die Richtungsumkehr die Seite mit der zusätzlichen Kopie fest.",
|
||||
"pattern.direction_1": "Richtung 1",
|
||||
@@ -2120,6 +2163,8 @@
|
||||
"prefs.button_left": "Ziehen mit der linken Maustaste",
|
||||
"prefs.button_middle": "Ziehen mit der mittleren Maustaste",
|
||||
"prefs.button_right": "Ziehen mit der rechten Maustaste",
|
||||
"prefs.carousel_next": "Weiter",
|
||||
"prefs.carousel_previous": "Zurück",
|
||||
"prefs.choose_font": "Schriftdatei auswählen…",
|
||||
"prefs.click": "Klicken",
|
||||
"prefs.contact_shading": "Weiche Kontaktschattierung",
|
||||
@@ -2132,6 +2177,8 @@
|
||||
"prefs.ctrl_cmd": "Strg / Cmd",
|
||||
"prefs.curved_surfaces": "Gekrümmte Flächen",
|
||||
"prefs.curves": "Gekrümmte Flächen",
|
||||
"prefs.cut_preview_tint": "Entferntes Material rot anzeigen",
|
||||
"prefs.cut_preview_tint_help": "Eine Vorschau füllt das Material, das ein Feature entfernt, sowie die ersetzte Form mit einem roten, durchscheinenden Geist. Wenn deaktiviert, sind diese Füllungen völlig transparent und es werden nur ihre Umrisse gezeichnet.",
|
||||
"prefs.dark_parts_shading": "Form dunkler Teile erhalten",
|
||||
"prefs.dark_parts_shading_help": "Die Ansicht in linearem Licht mit sanftem Himmelslicht schattieren, damit dunkle Flächen einen Verlauf behalten statt schwarz zu werden",
|
||||
"prefs.dead_zone_note": "Der schraffierte Bereich kennzeichnet die Totzone. Bewegungen innerhalb dieses Bereichs werden ignoriert.",
|
||||
@@ -2156,7 +2203,19 @@
|
||||
"prefs.font_sample": "Zwölf Boxkämpfer jagen Viktor quer über den großen Sylter Deich · Ø12,50 mm · 45,0° · 0123456789",
|
||||
"prefs.font_unreadable": "Die ausgewählte Schriftdatei konnte nicht gelesen werden.",
|
||||
"prefs.general": "Allgemein",
|
||||
"prefs.general_note": "Sprache, Speichern und Updates.",
|
||||
"prefs.general.active": "Aktiv",
|
||||
"prefs.general.crash_reports": "Absturzberichte",
|
||||
"prefs.general.edited": "Geändert",
|
||||
"prefs.general.held": "Enthalten",
|
||||
"prefs.general.import": "Importieren...",
|
||||
"prefs.general.last_check": "Zuletzt geprüft: {time}",
|
||||
"prefs.general.minutes": "Minuten",
|
||||
"prefs.general.name_setup": "Dieses Setup benennen",
|
||||
"prefs.general.profile_name": "Profilname",
|
||||
"prefs.general.save_new": "Als neu speichern",
|
||||
"prefs.general.step_import": "STEP und Import",
|
||||
"prefs.general.this_computer": "Dieser Computer",
|
||||
"prefs.general_note": "Profile, Sprache, Dateien und Updates.",
|
||||
"prefs.gesture_orbit": "Ansicht drehen",
|
||||
"prefs.gesture_pan": "Ansicht verschieben",
|
||||
"prefs.gesture_zoom": "Ansicht zoomen",
|
||||
@@ -2190,6 +2249,8 @@
|
||||
"prefs.interface_language": "Oberflächensprache",
|
||||
"prefs.interface_note": "Legt das Erscheinungsbild von SolidCraft und die Anordnung seiner Bereiche fest.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_navigator_arrows": "Drehpfeile des Ansichtswürfels umkehren",
|
||||
"prefs.invert_navigator_arrows_note": "Die Pfeile um den Würfel drehen die Kamera in die Richtung, in die sie zeigen, wie in Fusion und Inventor: Der linke Pfeil zeigt nach rechts und schwenkt die Kamera nach rechts. Aus: Ein Pfeil dreht den Würfel zu der Seite hin, an der er sitzt, sodass der linke Pfeil die Kamera nach links schwenkt. Die gebogenen Rollpfeile sind davon nicht betroffen.",
|
||||
"prefs.keep_work_on_tab_switch": "Dialoge und Werkzeuge beim Wechsel zwischen Konstruktionen offen lassen",
|
||||
"prefs.keep_work_on_tab_switch_note": "Jeder Konstruktions-Tab behält seinen offenen Dialog oder sein Werkzeug mit allem, was darin ausgewählt und eingegeben wurde, sowie seinen Werkzeugleisten-Tab. Beim Zurückkehren zur Konstruktion ist alles wieder da. Aus: Ein Wechsel bricht sie ab und zeigt den Tab Modell.",
|
||||
"prefs.labels": "Beschriftungen",
|
||||
@@ -2209,10 +2270,10 @@
|
||||
"prefs.logo_accent": "Akzentfarbe",
|
||||
"prefs.logo_theme": "Designfarben",
|
||||
"prefs.max_file_size": "Maximale Dateigröße zum Öffnen",
|
||||
"prefs.max_file_size_design": "Designdateien",
|
||||
"prefs.max_file_size_design": "Größte zu öffnende Datei: Entwurf",
|
||||
"prefs.max_file_size_flag": "Dieser Lauf wurde mit --max-file-size {size} gestartet. Der Wert gilt, bis SolidCraft geschlossen wird. Diese Einstellungen gelten beim Start ohne diese Option.",
|
||||
"prefs.max_file_size_note": "Standard: 512 MB und 256 MB; höchstens 16 GB. Das Öffnen einer Datei benötigt ein Mehrfaches ihrer Größe im Arbeitsspeicher. Ein Design mit großem Import benötigt zum Speichern und erneuten Öffnen eventuell eine höhere Grenze für Designdateien.",
|
||||
"prefs.max_file_size_step": "STEP- und Importdateien",
|
||||
"prefs.max_file_size_step": "Größte zu öffnende Datei: STEP und Import",
|
||||
"prefs.max_file_size_web": "Die Web-App öffnet STEP- und Importdateien bis 512 MB und Designs bis 256 MB: Ein Browser-Tab kann keine größeren Dateien aufnehmen. In der Desktop-App lassen sich diese Grenzen erhöhen.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "Große Symbole",
|
||||
@@ -2229,7 +2290,7 @@
|
||||
"prefs.navigator_fit_note": "Eine Fläche, Ecke oder ein Pfeil des Würfels oder eine Achse des Gizmos blickt auch auf die Mitte des Modells, aus dem einen Abstand, der es von jeder Seite einpasst (seine Hüllkugel). Home nutzt denselben Zoom und dieselbe Mitte, solange dies an ist, sodass danach gewählte Ansichten sich nur drehen. Aus: Die Ansicht dreht sich an Ort und Stelle und behält Ziel und Zoom.",
|
||||
"prefs.network_host_remove": "Entfernen",
|
||||
"prefs.network_locations": "Netzwerkspeicherorte in Designs zulassen",
|
||||
"prefs.network_locations_note": "Deaktiviert: Verknüpfte Dateien eines Designs auf einem anderen Computer (\\\\server\\share) werden nur von den hier aufgeführten Servern geöffnet oder nachdem Sie einen Server auf Nachfrage ausdrücklich zugelassen haben.",
|
||||
"prefs.network_locations_note": "Für Organisationen, die Entwürfe auf Dateiservern speichern. Aus: nur erlaubte Server.",
|
||||
"prefs.network_locations_section": "Netzwerkspeicherorte",
|
||||
"prefs.not_on_gpu": "Auf dieser GPU nicht verfügbar",
|
||||
"prefs.on": "An",
|
||||
@@ -2247,15 +2308,17 @@
|
||||
"prefs.placement": "Platzierung",
|
||||
"prefs.prefer_free_sketch_points": "Bei überlappenden Punkten bewegliche Skizzenpunkte bevorzugen",
|
||||
"prefs.preview_choices": "Vorschau Ihrer Einstellungen",
|
||||
"prefs.preview_tint_new_faces": "Neue Fläche in der Vorschau einfärben",
|
||||
"prefs.preview_tint_new_faces_help": "Eine Vorschau eines Features, das einen Körper ändert, kann die hinzugefügte Fläche in der Vorschaufarbe zeichnen. Wenn deaktiviert, wird der Körper wie er ist gezeichnet.",
|
||||
"prefs.print_face_highlight": "Hervorhebung der Auflagefläche",
|
||||
"prefs.print_face_highlight_tip": "Wie stark die Auflagefläche eines Körpers in der Ansicht eingefärbt und umrandet wird. 0 % zeigt keine Hervorhebung.",
|
||||
"prefs.profile_active": "Aktives Profil",
|
||||
"prefs.profile_applied": "Profil angewendet.",
|
||||
"prefs.profile_changed": "Die Einstellungen wurden seit dem Speichern des Profils geändert. „Speichern“ übernimmt sie ins Profil.",
|
||||
"prefs.profile_deleted": "Profil gelöscht.",
|
||||
"prefs.profile_export": "Exportieren…",
|
||||
"prefs.profile_export": "Exportieren",
|
||||
"prefs.profile_exported": "Profil exportiert.",
|
||||
"prefs.profile_imported": "Profil importiert. Unter „Aktives Profil“ auswählen, um dazu zu wechseln.",
|
||||
"prefs.profile_imported": "Profil importiert.",
|
||||
"prefs.profile_name_hint": "Profilname, z. B. Laptop",
|
||||
"prefs.profile_rename": "Umbenennen",
|
||||
"prefs.profile_renamed": "Profil umbenannt.",
|
||||
@@ -2275,12 +2338,13 @@
|
||||
"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",
|
||||
"prefs.restore_page_defaults": "Seitenstandardwerte wiederherstellen",
|
||||
"prefs.ribbon_click_ends_tool": "Klick auf die Schaltfläche des laufenden Werkzeugs beendet es",
|
||||
"prefs.ribbon_click_ends_tool_note": "Ein Klick auf die hervorgehobene Schaltfläche des aktiven Skizzenwerkzeugs beendet das Werkzeug, sodass Klicks in die Ansicht wieder auswählen. Aus: Der Klick startet das Werkzeug neu.",
|
||||
"prefs.ribbon_reveal": "Suchergebnisse im Menüband zeigen",
|
||||
"prefs.ribbon_reveal_note": "Steht der Zeiger über einem Ergebnis in Befehle suchen, zeigt das Menüband, wo der Befehl liegt: seine Registerkarte, seine Schaltfläche oder seine Zeile im Aufklappmenü eines Panels. Ein aus der Suche gestarteter Befehl blinkt dort, und ein laufender Befehl aus einem Aufklappmenü markiert sein Panel.",
|
||||
"prefs.right_click": "Rechtsklick",
|
||||
"prefs.saving": "Automatisches Speichern und Wiederherstellung",
|
||||
"prefs.saving": "Starten und Speichern",
|
||||
"prefs.scale_note": "Die Skalierung lässt sich auch mit Strg + / Strg − im Fenster ändern. Bei 100 % sind die Schaltflächen 28 px groß.",
|
||||
"prefs.scale_text": "Skalierung und Text",
|
||||
"prefs.search": "Einstellungen suchen",
|
||||
@@ -2306,6 +2370,8 @@
|
||||
"prefs.sketch_exit_view.previous": "Zur vorherigen Ansicht zurückkehren",
|
||||
"prefs.sketch_exit_view.unless_rotated": "Zurückkehren, sofern nicht gedreht",
|
||||
"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_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",
|
||||
@@ -2542,6 +2608,10 @@
|
||||
"render.base_color": "Grundfarbe · sRGB",
|
||||
"render.base_metallic": "Metallanteil",
|
||||
"render.base_opaque_only": "Dieser Editor lässt Körper undurchsichtig. Für optische Transmission verwenden Sie die gesonderten Glaseinstellungen.",
|
||||
"render.batch_all_shots": "Alle Ansichten ({count})",
|
||||
"render.batch_empty": "Vor dem Rendern aller Ansichten eine Ansicht speichern.",
|
||||
"render.batch_limit": "Es können höchstens 32 Ansichten gerendert werden.",
|
||||
"render.batch_status": "{running} läuft · {waiting} warten",
|
||||
"render.bloom_enable": "Darstellungs-Bloom",
|
||||
"render.bloom_help": "Kreativer Ausgabeglanz aus linearen Glanzlichtern mit drei normierten Durchläufen. Radius in Ausgabepixeln; Addition vor sRGB. Keine Linsen-, Volumen- oder Konvergenzsimulation. Das rohe EXR bleibt unverändert.",
|
||||
"render.bloom_opaque": "Bloom benötigt eine deckende Ausgabe. Transparentes PNG wird abgelehnt, nicht still abgeflacht.",
|
||||
@@ -2652,6 +2722,13 @@
|
||||
"render.choose_look": "Zuerst einen Look in der Bibliothek wählen",
|
||||
"render.chromatic_offset": "Chromatischer Kantenversatz",
|
||||
"render.clear_stage": "Remove saved stage",
|
||||
"render.compare_divider": "Vergleichstrenner",
|
||||
"render.compare_live": "Live",
|
||||
"render.compare_live_preview": "Live-Vorschau",
|
||||
"render.compare_render": "Render {number}",
|
||||
"render.compare_side": "Render {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} s · {renderer} · aufgenommen bei Revision {revision}",
|
||||
"render.compare_swap": "Tauschen",
|
||||
"render.copy_request": "JSON kopieren",
|
||||
"render.cpu_aa_help": "Räumliche Samples: Mehr Samples verbessern die Kantenglättung, verlängern jedoch die Renderzeit.",
|
||||
"render.cpu_area_add": "Rechteckiges CPU-Flächenlicht hinzufügen",
|
||||
@@ -3401,6 +3478,10 @@
|
||||
"render.result_retained": "Das zuletzt fertiggestellte Bild wird angezeigt; ein neueres Rendering hat es noch nicht ersetzt.",
|
||||
"render.result_sequence": "Prüfpunkt",
|
||||
"render.result_title": "Render-Ergebnis",
|
||||
"render.results_choose": "Behaltene Ergebnisse",
|
||||
"render.results_label": "Render {number} · {shot} · {seconds} s",
|
||||
"render.results_stale_label": "{result} [veraltet]",
|
||||
"render.results_unnamed": "Bild",
|
||||
"render.return_design": "Zurück zu Design",
|
||||
"render.review_stale": "Diese Prüfung ist veraltet. Lösen Sie sie auf oder prüfen Sie erneut, bevor Sie sie anwenden.",
|
||||
"render.ribbon_aging": "Alterung",
|
||||
@@ -3852,6 +3933,7 @@
|
||||
"sketch_text.formula": "Mit = beginnen, um den Text aus Parametern aufzubauen: ='W' + number(Width)",
|
||||
"sketch_text.italic": "Kursiv",
|
||||
"sketch_text.loading_fonts": "Installierte Schriftarten werden gesucht…",
|
||||
"sketch_text.no_bold": "Die integrierte Schrift hat keinen fetten Schnitt: Wählen Sie eine installierte Schriftart",
|
||||
"sketch_text.on_path": "Der Text folgt seiner Kurve: sie bestimmt seine Lage",
|
||||
"sketch_text.path_clear": "Text von der Kurve lösen",
|
||||
"sketch_text.path_hint": "Eine Skizzenkurve, der der Text folgt: hier klicken, dann die Kurve",
|
||||
@@ -4060,6 +4142,7 @@
|
||||
"ui.agent_chat_auth_terminal": "Führen Sie dies in einem Terminal aus und klicken Sie dann erneut auf Verbinden:",
|
||||
"ui.agent_chat_auth_title": "Beim Agenten anmelden",
|
||||
"ui.agent_chat_cancel": "Abbrechen",
|
||||
"ui.agent_chat_cancel_timeout": "Der Agent hat nicht auf Stopp reagiert und wurde getrennt. Zum Fortfahren erneut verbinden.",
|
||||
"ui.agent_chat_cannot_start": "Installiert, startet aber nicht: {error}",
|
||||
"ui.agent_chat_changes": "Änderungen: {n}, ein Rückgängig-Schritt",
|
||||
"ui.agent_chat_changes_n": "{n} Änderungen",
|
||||
@@ -4069,11 +4152,13 @@
|
||||
"ui.agent_chat_command": "Agentenbefehl",
|
||||
"ui.agent_chat_connect": "Verbinden",
|
||||
"ui.agent_chat_copy": "Kopieren",
|
||||
"ui.agent_chat_copy_message": "Nachricht kopieren",
|
||||
"ui.agent_chat_ctx_bodies": "Körper",
|
||||
"ui.agent_chat_ctx_elements": "Elemente",
|
||||
"ui.agent_chat_ctx_features": "Funktionen",
|
||||
"ui.agent_chat_ctx_more": "+{n} weitere",
|
||||
"ui.agent_chat_ctx_other": "Sonstiges",
|
||||
"ui.agent_chat_custom_heading": "Eigenes Modell",
|
||||
"ui.agent_chat_custom_hint": "Jedes Programm, das das Agent Client Protocol über stdin und stdout spricht. Es läuft ohne Shell.",
|
||||
"ui.agent_chat_denied": "Abgelehnt",
|
||||
"ui.agent_chat_deny": "Ablehnen",
|
||||
@@ -4117,8 +4202,16 @@
|
||||
"ui.agent_chat_mode_all": "Alles",
|
||||
"ui.agent_chat_mode_ask": "Fragen",
|
||||
"ui.agent_chat_mode_edits": "Ändern",
|
||||
"ui.agent_chat_model_custom": "Eigenes verwenden: {model}",
|
||||
"ui.agent_chat_model_no_tools": "{model} kann die Werkzeuge von SolidCraft nicht verwenden und daher nur chatten. Wählen Sie ein Modell mit Werkzeugunterstützung.",
|
||||
"ui.agent_chat_model_rejected": "Der Agent hat {model} nicht akzeptiert: {error}",
|
||||
"ui.agent_chat_model_search": "Modelle suchen…",
|
||||
"ui.agent_chat_model_tip": "Das vom Agenten verwendete Modell. Ihre Auswahl wird für diesen Agenten gespeichert.",
|
||||
"ui.agent_chat_models_failed": "Modelle konnten nicht abgerufen werden: {error}",
|
||||
"ui.agent_chat_models_heading": "Verfügbare Modelle",
|
||||
"ui.agent_chat_models_listing": "Modelle werden abgerufen…",
|
||||
"ui.agent_chat_models_none": "Der Endpunkt listet keine Modelle",
|
||||
"ui.agent_chat_models_not_a_list": "Die Antwort war keine Modellliste",
|
||||
"ui.agent_chat_new": "Neuer Chat",
|
||||
"ui.agent_chat_new_confirm": "Neuen Chat starten? Der aktuelle wird geleert.",
|
||||
"ui.agent_chat_next_picture": "Nächstes Bild",
|
||||
@@ -4254,6 +4347,9 @@
|
||||
"ui.ai_permissions": "Berechtigungen",
|
||||
"ui.ai_preset": "Vorlage",
|
||||
"ui.ai_protocol": "Protokoll",
|
||||
"ui.ai_reasoning_effort": "Denkaufwand",
|
||||
"ui.ai_reasoning_effort_field_hint": "leer = Standard des Anbieters",
|
||||
"ui.ai_reasoning_effort_hint": "Optional. Wird als `reasoning_effort` an den Endpunkt gesendet; die zulässigen Werte unterscheiden sich je nach Anbieter (zum Beispiel `low`, `high` oder `max`). Einen hier gesetzten Wert ersetzt SolidCraft nie. Leer lassen, um nichts zu senden und den Standard des Anbieters zu behalten.",
|
||||
"ui.ai_remove": "Entfernen",
|
||||
"ui.ai_request_timed_out": "Das Modell hat länger als {seconds} s gebraucht. Sie können das Anfrage-Zeitlimit für diesen Endpunkt unter Einstellungen › Assistent erhöhen.",
|
||||
"ui.ai_request_timeout": "Anfrage-Zeitlimit",
|
||||
@@ -4377,6 +4473,8 @@
|
||||
"ui.coming_later": "bald verfügbar",
|
||||
"ui.command_done": "{command} fertig",
|
||||
"ui.command_palette_hint": "Befehl eingeben, z. B. {example} — oder S drücken, um zu suchen",
|
||||
"ui.components_from_bodies_hint": "Für jeden ausgewählten Körper eine gleichnamige Komponente unter der aktiven Komponente erstellen.",
|
||||
"ui.components_from_bodies_required": "Mindestens einen Körper auswählen.",
|
||||
"ui.concentric": "Konzentrisch",
|
||||
"ui.cone": "Kegel",
|
||||
"ui.conic_curve": "Kegelschnitt",
|
||||
@@ -4389,9 +4487,11 @@
|
||||
"ui.construction.contact_hint": "Tangente: Kreis/Bogen oder offenen Spline/Kegelschnitt wählen, dann Berührpunkt (Freiformkurven: Endpunkte)",
|
||||
"ui.construction.external_hint": "Tangente: Kreis oder Bogen, äußeren Punkt, dann gewünschte Lösung wählen",
|
||||
"ui.construction.free_hint": "Konstruktionslinie: zwei Punkte anklicken; Rechtsklick beginnt neu",
|
||||
"ui.construction.normal_needs_length": "Eine unendliche Linie ist immer Konstruktion: Länge eingeben, um eine normale Linie zu zeichnen",
|
||||
"ui.construction.point_hint": "Konstruktionslinie: Durchgangspunkt anklicken",
|
||||
"ui.construction.reference_hint": "Konstruktionslinie: Gerade wählen, dann Punkt; Rechtsklick wechselt die Referenz",
|
||||
"ui.construction.ribbon_label": "Hilfslinien",
|
||||
"ui.construction.side_hint": "Konstruktionslinie: die Seite anklicken, zu der die Linie läuft; Rechtsklick beginnt neu",
|
||||
"ui.context_menu": "Kontextmenü",
|
||||
"ui.continuity_g0": "G0 Knick",
|
||||
"ui.continuity_g1": "G1 Tangente",
|
||||
@@ -4677,6 +4777,7 @@
|
||||
"ui.history_sketch_listed_under": "Aufgeführt unter {feature}",
|
||||
"ui.history_title": "Verlauf",
|
||||
"ui.history_toggle_names": "Merkmalsnamen ein- oder ausblenden",
|
||||
"ui.history_waits_for_editor": "Rückgängig, Wiederholen und Verlauf warten, bis der Feature-Editor geöffnet ist",
|
||||
"ui.hole": "Bohrung",
|
||||
"ui.hole_tap_type": "Gewindetyp",
|
||||
"ui.hole_type": "Bohrungstyp",
|
||||
@@ -4692,6 +4793,7 @@
|
||||
"ui.insert_design.saved_file": "{name} hat ungespeicherte Änderungen, daher wurde die gespeicherte Datei eingefügt. Speichern Sie sie und wählen Sie dann Verknüpfungen aktualisieren, um die Änderungen zu übernehmen.",
|
||||
"ui.insert_design.unsaved": "Ungespeicherte Änderungen",
|
||||
"ui.inspect": "Prüfen",
|
||||
"ui.instances": "Instanzen",
|
||||
"ui.intersect": "Schnitt",
|
||||
"ui.isolate": "Isolieren",
|
||||
"ui.join": "Verbinden",
|
||||
@@ -4842,9 +4944,9 @@
|
||||
"ui.polygon": "Polygon",
|
||||
"ui.preferences": "Einstellungen",
|
||||
"ui.press_a_key": "Taste drücken…",
|
||||
"ui.print_face_body_not_sent": "Der Körper dieser Fläche wird nicht gesendet: Füge ihn zuerst bei Körper hinzu",
|
||||
"ui.print_face_cleared": "{body}: Auflagefläche entfernt",
|
||||
"ui.print_face_input": "Auflagefläche",
|
||||
"ui.print_face_one_body": "Eine Auflagefläche gehört zu einem Körper: Sende diesen Körper allein, um sie zu ändern",
|
||||
"ui.print_face_pick": "Eine plane Fläche eines Volumenkörpers auswählen, um sie zur Auflagefläche zu machen",
|
||||
"ui.print_face_set": "{body}: Auflagefläche gesetzt; An Slicer senden legt den Körper darauf",
|
||||
"ui.profile": "Profil",
|
||||
@@ -4987,7 +5089,7 @@
|
||||
"ui.slicer_no_response": "{slicer} hat nicht rechtzeitig geantwortet. Die Datei ist bereits unter {path} gespeichert: Senden Sie sie erneut, sobald der Slicer gestartet ist.",
|
||||
"ui.slicer_resend_hint": "Erneutes Senden ersetzt die Datei, die der Slicer geöffnet hat.",
|
||||
"ui.slicer_single_instance": "Dateien in einem bereits laufenden Slicer öffnen",
|
||||
"ui.slicer_single_instance_tip": "PrusaSlicer, SuperSlicer und Cura laden die Datei in das geöffnete Fenster, statt ein weiteres zu starten. Andere Slicer werden wie gewohnt gestartet. PrusaSlicer 3.0 tut das immer: Sein Starter kann eine Datei nicht anders übernehmen.",
|
||||
"ui.slicer_single_instance_tip": "Für Slicer, die Dateien in ein laufendes Fenster laden.",
|
||||
"ui.slicer_starting": "{slicer} wird gestartet…",
|
||||
"ui.slicer_still_starting": "Es wird weiter darauf gewartet, dass {slicer} startet. Sonst wurde nichts gesendet.",
|
||||
"ui.slicer_stl_fallback": "Das 3MF konnte nicht geschrieben werden, daher ist dies ein STL.",
|
||||
@@ -5072,12 +5174,15 @@
|
||||
"ui.view_angles": "Ansichtswinkel",
|
||||
"ui.view_angles_convention": "Grad um die Hochachse. Gieren 0° ist die Vorderansicht, 90° links, −90° rechts, 180° die Rückansicht; Nicken 90° ist die Draufsicht; ein positives Rollen dreht das Modell im Uhrzeigersinn.",
|
||||
"ui.view_angles_help": "Zeigt Nick-, Gier- und Rollwinkel der Ansicht in Grad sowie die Projektion unter dem Orientierungs-Gizmo. Klicken Sie auf die Anzeige, um genaue Winkel einzugeben.",
|
||||
"ui.view_angles_lock": "{axis} sperren",
|
||||
"ui.view_angles_ortho": "Ortho",
|
||||
"ui.view_angles_persp": "Persp",
|
||||
"ui.view_angles_pitch": "Nicken",
|
||||
"ui.view_angles_readout": "Nicken {pitch} · Gieren {yaw} · Rollen {roll}",
|
||||
"ui.view_angles_roll": "Rollen",
|
||||
"ui.view_angles_unlock": "{axis} entsperren",
|
||||
"ui.view_angles_yaw": "Gieren",
|
||||
"ui.view_angles_zero": "{axis} auf 0° setzen",
|
||||
"ui.view_centre": "Ansichtszentrum",
|
||||
"ui.viewport_status_clear": "{name} ausschalten",
|
||||
"ui.viewport_status_clear_hint": "{name} ausschalten · Esc",
|
||||
@@ -5129,7 +5234,7 @@
|
||||
"update.available": "SolidCraft {version} ist verfügbar",
|
||||
"update.button": "Update",
|
||||
"update.button_version": "Auf {version} aktualisieren",
|
||||
"update.check_automatically": "Automatisch nach Updates suchen",
|
||||
"update.check_automatically": "Beim Start und einmal täglich prüfen",
|
||||
"update.check_for_updates": "Nach Updates suchen",
|
||||
"update.check_now": "Jetzt suchen",
|
||||
"update.check_unavailable": "Für diese Installation ist keine Update-Prüfung verfügbar.",
|
||||
@@ -5179,6 +5284,7 @@
|
||||
"web.open_link": "Verknüpftes Design öffnen?",
|
||||
"web.open_link_confirm": "Es gibt nicht gespeicherte Änderungen. Das verknüpfte Design in einem neuen Tab öffnen?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ auswählen · Eingabe / Tab vervollständigen · Esc schließen",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ auswählen · Eingabe übernimmt diesen Wert",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ wählen · Enter wendet das Getippte an · Esc schließt",
|
||||
"⌘Enter continues on GitHub.": "⌘Enter geht weiter zu GitHub."
|
||||
}
|
||||
|
||||
@@ -76,12 +76,11 @@
|
||||
"prefs.bottom_centre": "Bottom centre",
|
||||
"prefs.logo_theme": "Theme's colours",
|
||||
"prefs.max_file_size": "Largest file to open",
|
||||
"prefs.max_file_size_design": "Design files",
|
||||
"prefs.max_file_size_flag": "This run started with --max-file-size {size}, which applies until SolidCraft closes. These settings apply when it starts without it.",
|
||||
"prefs.max_file_size_note": "Defaults: 512 MB and 256 MB; at most 16 GB. Opening a file takes several times its size in memory. A design that keeps a large import may need a higher design file limit to save and reopen.",
|
||||
"prefs.max_file_size_step": "STEP and import files",
|
||||
"prefs.max_file_size_web": "The web app opens STEP and import files up to 512 MB and designs up to 256 MB: a browser tab can't hold larger files. The desktop app can raise these limits.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.network_locations_note": "For organisations that keep designs on file servers. Off: only servers you allow.",
|
||||
"prefs.sketch_conflict_colour": "Conflicting geometry in the conflict colour",
|
||||
"prefs.sketch_conflict_colour_note": "While a sketch's constraints conflict, the curves and points a failing constraint holds are drawn in the conflict colour too, not only its glyph or dimension. Off: they keep their usual colours.",
|
||||
"prefs.sketch_construction_lines": "Extended construction line tools",
|
||||
@@ -249,6 +248,7 @@
|
||||
"ui.details_curvature_caption": "Faces are coloured by their local radius of curvature.",
|
||||
"ui.details_draft_caption": "Faces are coloured by their angle to the pull direction.",
|
||||
"ui.export_keymap_tip": "Keys, S box favourites and the toolbar, as a file",
|
||||
"ui.instances": "Instances",
|
||||
"ui.layout_compact": "Compact",
|
||||
"ui.layout_floating": "Floating",
|
||||
"ui.material_review.claims": "Sources and licences are unverified claims. Missing values remain unknown.",
|
||||
|
||||
+118
-12
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "Rectangular",
|
||||
"Rectangular Tube": "Rectangular Tube",
|
||||
"Reference face": "Reference face",
|
||||
"Reference plane": "Reference plane",
|
||||
"Relief Depth": "Relief Depth",
|
||||
"Relief Width": "Relief Width",
|
||||
"Remove Tools": "Remove Tools",
|
||||
@@ -494,7 +495,7 @@
|
||||
"Rename Folder": "Rename Folder",
|
||||
"Render Stage": "Render Stage",
|
||||
"Reopen designs": "Reopen designs",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "Reopen the designs that were open when SolidCraft was last closed",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "Reopen the designs that were open last time",
|
||||
"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 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.",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "Section",
|
||||
"Section (optional)": "Section (optional)",
|
||||
"Section position": "Section position",
|
||||
"Sections": "Sections",
|
||||
"Sections (one sketch each)": "Sections (one sketch each)",
|
||||
"Select Other": "Select Other",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).",
|
||||
@@ -793,6 +795,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (attachment lost)",
|
||||
"browser.sketch_editing": "{name} (editing)",
|
||||
"browser.sketch_name": "Sketch{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "already in their part",
|
||||
"browser.sketches_to_parts_design_body": "on a body of the design itself",
|
||||
"browser.sketches_to_parts_moved": "Moved {moved} sketches to the part they're on",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "on a face without an exact attachment",
|
||||
"browser.sketches_to_parts_not_on_face": "not on a part's face",
|
||||
"browser.sketches_to_parts_part_missing": "their part is gone",
|
||||
"browser.sketches_to_parts_result": "Moved {moved} sketches to the part they're on; {stayed} stayed: {why}",
|
||||
"browser.units": "Units: {units}",
|
||||
"cam.flat": "Flat face",
|
||||
"cam.follower": "Follower",
|
||||
@@ -852,6 +861,7 @@
|
||||
"cmd.component_extract": "Extract to File",
|
||||
"cmd.component_finish_edit": "Finish Edit",
|
||||
"cmd.component_from_bodies": "Create Components from Bodies",
|
||||
"cmd.component_hide_history": "Hide Component History",
|
||||
"cmd.component_insert_design": "Insert Design",
|
||||
"cmd.component_links": "List Links",
|
||||
"cmd.component_list": "List Components",
|
||||
@@ -860,6 +870,7 @@
|
||||
"cmd.component_move_bodies": "Move Bodies to Component",
|
||||
"cmd.component_move_features": "Move Features to Component",
|
||||
"cmd.component_move_sketches": "Move Sketches to Component",
|
||||
"cmd.component_move_sketches_to_parts": "Move Sketches to Their Parts",
|
||||
"cmd.component_paste_from_design": "Paste from Another Design",
|
||||
"cmd.component_paste_new": "Paste New",
|
||||
"cmd.component_properties": "Component Physical Properties",
|
||||
@@ -867,6 +878,7 @@
|
||||
"cmd.component_rename": "Rename Component",
|
||||
"cmd.component_replace_with_link": "Replace with Link",
|
||||
"cmd.component_revert_position": "Revert Position",
|
||||
"cmd.component_show_history": "Show Component History",
|
||||
"cmd.component_unlock": "Unlock Contents",
|
||||
"cmd.component_update_links": "Update Links",
|
||||
"cmd.config_activate": "Activate Configuration",
|
||||
@@ -877,6 +889,7 @@
|
||||
"cmd.config_rule": "Configuration Rule",
|
||||
"cmd.config_rules": "Configuration Rules",
|
||||
"cmd.config_table": "Display Configuration Table",
|
||||
"cmd.construct.plane.axis_point": "Plane Through Axis And Point",
|
||||
"cmd.construct_axis_edge": "Axis Through Edge",
|
||||
"cmd.construct_axis_normal_to_face": "Axis Perpendicular To Face",
|
||||
"cmd.construct_axis_through_cylinder": "Axis Through Cylinder/Cone/Torus",
|
||||
@@ -925,6 +938,11 @@
|
||||
"cmd.explode_show": "Show Exploded View",
|
||||
"cmd.expr_evaluate": "Evaluate Expression",
|
||||
"cmd.face_delete": "Delete Face",
|
||||
"cmd.file.export_3mf": "Export 3MF",
|
||||
"cmd.file.export_obj": "Export OBJ",
|
||||
"cmd.file.export_step": "Export STEP",
|
||||
"cmd.file.export_stl": "Export STL",
|
||||
"cmd.file.export_urdf": "Export URDF",
|
||||
"cmd.file_export": "Export",
|
||||
"cmd.file_insert_mesh": "Insert Mesh",
|
||||
"cmd.file_insert_step": "Insert STEP",
|
||||
@@ -942,6 +960,7 @@
|
||||
"cmd.inspect_colored_components": "Enable Component Colors",
|
||||
"cmd.inspect_colored_components_disable": "Disable Component Colors",
|
||||
"cmd.inspect_compare": "Compare Bodies",
|
||||
"cmd.inspect_compare_picture": "Compare Picture",
|
||||
"cmd.inspect_continuity": "Continuity Analysis",
|
||||
"cmd.inspect_curvature_comb": "Curvature Comb Analysis",
|
||||
"cmd.inspect_curvature_map": "Curvature Map Analysis",
|
||||
@@ -1032,6 +1051,9 @@
|
||||
"cmd.pointcloud_edit": "Edit Point Cloud",
|
||||
"cmd.pointcloud_insert": "Insert Point Cloud",
|
||||
"cmd.pointcloud_list": "Point Clouds",
|
||||
"cmd.render.compare.divider": "Move Render Comparison Divider",
|
||||
"cmd.render.result.discard": "Discard Render Result",
|
||||
"cmd.render.result.select": "Select Render Result",
|
||||
"cmd.render_aging_describe": "Layered Surface Aging",
|
||||
"cmd.render_backends": "Render Backends",
|
||||
"cmd.render_batch": "Render All Shots",
|
||||
@@ -1195,6 +1217,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Control Point Spline (Degree 5)",
|
||||
"cmd.sketch_spline_fit_point": "Fit Point Spline",
|
||||
"cmd.sketch_spline_insert_point": "Add Spline Point",
|
||||
"cmd.sketch_spline_remove_point": "Remove Spline Point",
|
||||
"cmd.sketch_spline_reverse": "Reverse Spline Direction",
|
||||
"cmd.sketch_spun_profile": "Spun Profile",
|
||||
"cmd.sketch_toggle_diameter": "Toggle Radius / Diameter",
|
||||
@@ -1245,6 +1268,7 @@
|
||||
"cmd.timeline_rollback": "Move Timeline Marker",
|
||||
"cmd.timeline_stop": "Stop History",
|
||||
"cmd.timeline_suppress": "Suppress Feature",
|
||||
"cmd.ui.assistant": "Toggle Assistant",
|
||||
"cmd.view.render_browser": "Render Browser",
|
||||
"cmd.view.render_dock": "Move Render Dock",
|
||||
"cmd.view.render_focus": "Render Focus View",
|
||||
@@ -1279,9 +1303,11 @@
|
||||
"cmd.view_tool": "Navigation tool",
|
||||
"cmd.view_top": "Top View",
|
||||
"cmd.view_visual_style": "Visual style",
|
||||
"color.blue": "Blue",
|
||||
"color.copy": "Click to copy color values",
|
||||
"color.edit": "Click to edit color",
|
||||
"color.gamma": "Showing color values in 0-255 gamma space",
|
||||
"color.green": "Green",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "Enter #rrggbb or #rrggbbaa; six digits preserve opacity",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1289,6 +1315,7 @@
|
||||
"color.linear": "Showing color values in 0-1 linear space",
|
||||
"color.recent": "Recent colors",
|
||||
"color.recent_help": "Colors you pick in any color field appear here",
|
||||
"color.red": "Red",
|
||||
"color.saturation": "Saturation (percent)",
|
||||
"color.saturation_value": "Saturation and value",
|
||||
"color.selected": "Selected color",
|
||||
@@ -1296,6 +1323,8 @@
|
||||
"color.step_up": "Step up",
|
||||
"color.value": "Value, the brightness (percent)",
|
||||
"config.column_filter_empty": "No matching columns",
|
||||
"config.rule_parameter_columns": "Only parameters with a column in the table are listed. Add one with Add Column (+) to use it here.",
|
||||
"config.rule_suppression_columns": "Only features with a Suppress column in the table are listed. Add one with Add Column (+) to drive it here.",
|
||||
"contact.components_count": "{count} components",
|
||||
"contact.enabled_label": "enabled",
|
||||
"coupling.gear_1": "Gear 1",
|
||||
@@ -1344,7 +1373,11 @@
|
||||
"dialog.pattern_sketch_not_object": "Pick bodies or features: a sketch is neither. Repeat its curves with the sketch's own Pattern or Mirror, or extrude it first.",
|
||||
"dialog.select": "↖ Select",
|
||||
"dialog.selected_count": "↖ {count} selected",
|
||||
"dialog.selection_constraint": "Constraint {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · curve {id}",
|
||||
"dialog.selection_guidance": "Select an input to change it. Click selected objects to remove them; Ctrl/Cmd-click also toggles them. Review the preview, then press OK.",
|
||||
"dialog.selection_midpoint": "Midpoint · {id}",
|
||||
"dialog.selection_profile": "{sketch} · profile {number}",
|
||||
"dialog.selection_step": "{step} of {total}: Select {input}",
|
||||
"dialog.step": "Step",
|
||||
"document.changed_on_disk": "{name} changed on disk",
|
||||
@@ -1427,6 +1460,7 @@
|
||||
"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.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.",
|
||||
"help.construct.plane.perpendicular": "Creates a construction plane that holds a line or edge and stands square to a plane or face. Use it to sketch upright on a model along an edge.",
|
||||
@@ -1549,7 +1583,7 @@
|
||||
"help.sketch.constrainer": "Applies the constraint that one or two picked curves nearly have already, such as horizontal, parallel, tangent or equal. Use it to tidy a hand-drawn sketch one relation at a time.",
|
||||
"help.sketch.constraint.coincident": "Makes a point lie on another point, a curve or a sketch axis. Use it to close gaps in a profile or to put a point on a line.",
|
||||
"help.sketch.constraint.collinear": "Makes two lines lie on one straight line. Use it to line up interrupted edges, such as both sides of a notch.",
|
||||
"help.sketch.constraint.concentric": "Gives two circles or arcs the same centre. Use it for rings, counterbores and shafts with shoulders.",
|
||||
"help.sketch.constraint.concentric": "Gives two circles or arcs the same centre, or centres a circle or arc on a sketch point, including the origin. Pick either pair in either order.",
|
||||
"help.sketch.constraint.curvature": "Makes two curves that meet at an end point continue with the same curvature (G2). Use it where a tangent join is not smooth enough, for example on visible surfaces.",
|
||||
"help.sketch.constraint.equal": "Makes lines the same length, or circles and arcs the same radius. Use it so that one dimension drives several matching features.",
|
||||
"help.sketch.constraint.fix": "Fixes points or curves where they are, or frees them again. Use it for geometry that must not move when you drag or solve the sketch.",
|
||||
@@ -1803,6 +1837,8 @@
|
||||
"joint_move.blocked_hint": "Check the joint type (such as Rigid), grounding, joint limits, and parameter-driven values.",
|
||||
"joint_move.blocked_title": "Cannot move this part",
|
||||
"joint_move.coarse": "Near-axis rotation step (°)",
|
||||
"joint_move.conflict": "A joint of this part conflicts with the others: {joint}.",
|
||||
"joint_move.conflict_hint": "Edit, suppress or delete the conflicting joint so the other joints can place the part.",
|
||||
"joint_move.distance": "Distance from start",
|
||||
"joint_move.done": "Done",
|
||||
"joint_move.fine": "Far-axis rotation step (°)",
|
||||
@@ -1902,6 +1938,8 @@
|
||||
"motion.track_name": "Track {number}",
|
||||
"nav.alias.help": "Shift+Alt+left drag orbits, Shift+Alt+middle pans, Shift+Alt+right zooms; middle drag also pans and right drag orbits",
|
||||
"nav.blender.help": "Middle drag orbits, Shift+middle pans, Ctrl+middle zooms; right drag leaves the camera alone",
|
||||
"nav.creo.help": "Middle drag orbits, Shift+middle pans, Ctrl+middle drag vertically zooms (down to zoom in); scroll the wheel to zoom",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "Mouse and 3D mouse rotate through both poles and preserve roll. Shift axis snapping is disabled.",
|
||||
"nav.free_orbit.label": "Free orbit",
|
||||
"nav.free_orbit.snap_help": "Mouse and 3D mouse rotate through both poles and preserve roll. Hold Shift during an orbit drag to snap the view direction and roll.",
|
||||
@@ -2054,12 +2092,19 @@
|
||||
"parts.family.button_head_cap_screw": "Button head cap screw",
|
||||
"parts.family.dowel_pin": "Parallel pin (dowel)",
|
||||
"parts.family.hex_bolt": "Hex head bolt / screw",
|
||||
"parts.family.hex_cap_screw_inch": "Hex cap screw (inch)",
|
||||
"parts.family.hex_nut": "Hex nut",
|
||||
"parts.family.hex_nut_inch": "Hex nut (inch)",
|
||||
"parts.family.set_screw": "Hex socket set screw, flat point",
|
||||
"parts.family.socket_head_cap_screw": "Socket head cap screw",
|
||||
"parts.family.washer": "Plain washer",
|
||||
"parts.family.washer_sae": "Plain washer, SAE (narrow)",
|
||||
"parts.family.washer_uss": "Plain washer, USS (wide)",
|
||||
"parts.holes": "Holes (optional)",
|
||||
"parts.placement_hint": "pick one or more holes to seat a part in each, or OK places it at the origin",
|
||||
"parts.system": "Unit system",
|
||||
"parts.system.inch": "Inch",
|
||||
"parts.system.metric": "Metric",
|
||||
"pattern.alignment_2": "Alignment 2",
|
||||
"pattern.alignment_hint": "Counts include the original. Center balances copies; Flip chooses the extra side for even totals.",
|
||||
"pattern.direction_1": "Direction 1",
|
||||
@@ -2121,6 +2166,8 @@
|
||||
"prefs.button_left": "Left drag",
|
||||
"prefs.button_middle": "Middle drag",
|
||||
"prefs.button_right": "Right drag",
|
||||
"prefs.carousel_next": "Next",
|
||||
"prefs.carousel_previous": "Previous",
|
||||
"prefs.choose_font": "Choose font file…",
|
||||
"prefs.click": "Click",
|
||||
"prefs.contact_shading": "Soft contact shading",
|
||||
@@ -2133,6 +2180,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Curved surfaces",
|
||||
"prefs.curves": "Curved surfaces",
|
||||
"prefs.cut_preview_tint": "Show removed material in red",
|
||||
"prefs.cut_preview_tint_help": "A preview fills the material a feature removes, and the shape it replaces, with a red see-through ghost. When off, those fills are fully transparent and only their outlines are drawn.",
|
||||
"prefs.dark_parts_shading": "Keep the shape of dark parts",
|
||||
"prefs.dark_parts_shading_help": "Shade the view in linear light with a soft sky fill, so dark faces keep a gradient instead of turning black",
|
||||
"prefs.dead_zone_note": "The hatched band is the dead zone: motion inside it is ignored.",
|
||||
@@ -2157,7 +2206,19 @@
|
||||
"prefs.font_sample": "The quick brown fox · Ø12.50 mm · 45.0° · 0123456789",
|
||||
"prefs.font_unreadable": "The selected font could not be read.",
|
||||
"prefs.general": "General",
|
||||
"prefs.general_note": "Language, saving and updates.",
|
||||
"prefs.general.active": "Active",
|
||||
"prefs.general.crash_reports": "Crash reports",
|
||||
"prefs.general.edited": "Edited",
|
||||
"prefs.general.held": "Held",
|
||||
"prefs.general.import": "Import...",
|
||||
"prefs.general.last_check": "Last checked: {time}",
|
||||
"prefs.general.minutes": "Minutes",
|
||||
"prefs.general.name_setup": "Name this setup",
|
||||
"prefs.general.profile_name": "Profile name",
|
||||
"prefs.general.save_new": "Save as new",
|
||||
"prefs.general.step_import": "STEP and import",
|
||||
"prefs.general.this_computer": "This computer",
|
||||
"prefs.general_note": "Profiles, language, files and updates.",
|
||||
"prefs.gesture_orbit": "Orbit",
|
||||
"prefs.gesture_pan": "Pan",
|
||||
"prefs.gesture_zoom": "Zoom",
|
||||
@@ -2191,6 +2252,8 @@
|
||||
"prefs.interface_language": "Interface language",
|
||||
"prefs.interface_note": "How SolidCraft looks and where its parts sit.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_curved_roll_arrows": "Invert the curved roll arrows",
|
||||
"prefs.invert_curved_roll_arrows_note": "The two curved arrows at the cube's corner roll the view within the screen plane. On: the clockwise arrow rolls counterclockwise and the counterclockwise arrow rolls clockwise. Off: each arrow rolls the view the way it curls.",
|
||||
"prefs.invert_navigator_arrows": "Invert the view cube rotation arrows",
|
||||
"prefs.invert_navigator_arrows_note": "The arrows around the cube orbit the camera the way they point, as in Fusion and Inventor: the arrow on the left points right and swings the camera right. Off: an arrow rotates the cube toward the side it sits on, so the left arrow swings the camera left. The curved roll arrows are not affected.",
|
||||
"prefs.keep_work_on_tab_switch": "Keep dialogs and tools open when switching designs",
|
||||
@@ -2212,10 +2275,10 @@
|
||||
"prefs.logo_accent": "Accent",
|
||||
"prefs.logo_theme": "Theme's colors",
|
||||
"prefs.max_file_size": "Largest file to open",
|
||||
"prefs.max_file_size_design": "Design files",
|
||||
"prefs.max_file_size_design": "Largest file to open: Design",
|
||||
"prefs.max_file_size_flag": "This run started with --max-file-size {size}, which applies until SolidCraft closes. These settings apply when it starts without it.",
|
||||
"prefs.max_file_size_note": "Defaults: 512 MB and 256 MB; at most 16 GB. Opening a file takes several times its size in memory. A design that keeps a large import may need a higher design file limit to save and reopen.",
|
||||
"prefs.max_file_size_step": "STEP and import files",
|
||||
"prefs.max_file_size_step": "Largest file to open: STEP and import",
|
||||
"prefs.max_file_size_web": "The web app opens STEP and import files up to 512 MB and designs up to 256 MB: a browser tab can't hold larger files. The desktop app can raise these limits.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "Large icons",
|
||||
@@ -2232,7 +2295,7 @@
|
||||
"prefs.navigator_fit_note": "A face, corner or arrow of the view cube, or an axis of the gizmo, also looks at the middle of the model from the one distance that fits it from every side (its bounding sphere). Home uses the same zoom and centre while this is on, so views chosen after it only turn. Off: the view turns in place, keeping its target and zoom.",
|
||||
"prefs.network_host_remove": "Remove",
|
||||
"prefs.network_locations": "Allow network locations in designs",
|
||||
"prefs.network_locations_note": "Off: a design's linked files on another computer (\\\\server\\share) are opened only from the servers listed here, or after you allow one when asked.",
|
||||
"prefs.network_locations_note": "For organizations that keep designs on file servers. Off: only servers you allow.",
|
||||
"prefs.network_locations_section": "Network locations",
|
||||
"prefs.not_on_gpu": "not on this GPU",
|
||||
"prefs.on": "On",
|
||||
@@ -2250,15 +2313,17 @@
|
||||
"prefs.placement": "Placement",
|
||||
"prefs.prefer_free_sketch_points": "Prefer movable sketch points when points overlap",
|
||||
"prefs.preview_choices": "Preview of your choices",
|
||||
"prefs.preview_tint_new_faces": "Tint new surface in a preview",
|
||||
"prefs.preview_tint_new_faces_help": "A preview of a feature that changes a body can draw the surface it adds in the preview colour. When off, the body is drawn as itself.",
|
||||
"prefs.print_face_highlight": "Print face highlight",
|
||||
"prefs.print_face_highlight_tip": "How strongly the print face of a body is tinted and outlined in the view. 0 % shows no highlight.",
|
||||
"prefs.profile_active": "Active profile",
|
||||
"prefs.profile_applied": "Profile applied.",
|
||||
"prefs.profile_changed": "Settings changed since the profile was saved. Save keeps them in it.",
|
||||
"prefs.profile_deleted": "Profile deleted.",
|
||||
"prefs.profile_export": "Export…",
|
||||
"prefs.profile_export": "Export",
|
||||
"prefs.profile_exported": "Profile exported.",
|
||||
"prefs.profile_imported": "Profile imported. Choose it under Active profile to switch to it.",
|
||||
"prefs.profile_imported": "Profile imported.",
|
||||
"prefs.profile_name_hint": "Profile name, e.g. Laptop",
|
||||
"prefs.profile_rename": "Rename",
|
||||
"prefs.profile_renamed": "Profile renamed.",
|
||||
@@ -2278,12 +2343,13 @@
|
||||
"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",
|
||||
"prefs.restore_page_defaults": "Restore page defaults",
|
||||
"prefs.ribbon_click_ends_tool": "Clicking the running tool's button ends it",
|
||||
"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.right_click": "Right click",
|
||||
"prefs.saving": "Autosave and recovery",
|
||||
"prefs.saving": "Start and save",
|
||||
"prefs.scale_note": "Also Ctrl + / Ctrl − in the window. Buttons are 28 px at 100%.",
|
||||
"prefs.scale_text": "Scale and text",
|
||||
"prefs.search": "Search preferences",
|
||||
@@ -2547,6 +2613,10 @@
|
||||
"render.base_color": "Base color · sRGB",
|
||||
"render.base_metallic": "Metallic fraction",
|
||||
"render.base_opaque_only": "This editor preserves opaque body semantics. Use the explicit glass controls for optical transmission.",
|
||||
"render.batch_all_shots": "All shots ({count})",
|
||||
"render.batch_empty": "Save a shot before rendering all shots.",
|
||||
"render.batch_limit": "Render all shots is limited to 32 shots.",
|
||||
"render.batch_status": "{running} running · {waiting} waiting",
|
||||
"render.bloom_enable": "Display bloom",
|
||||
"render.bloom_help": "Creative output-only glow from positive linear highlights, spread with three normalized box passes. Radius is in output pixels. Added in linear display light before sRGB; not a lens, volumetric effect or convergence improvement. The raw EXR is unchanged.",
|
||||
"render.bloom_opaque": "Bloom requires opaque output. Transparent PNG is refused, not silently flattened.",
|
||||
@@ -2657,6 +2727,13 @@
|
||||
"render.choose_look": "Choose a look in Appearances first",
|
||||
"render.chromatic_offset": "Chromatic edge offset",
|
||||
"render.clear_stage": "Remove saved stage",
|
||||
"render.compare_divider": "Comparison divider",
|
||||
"render.compare_live": "Live",
|
||||
"render.compare_live_preview": "Live preview",
|
||||
"render.compare_render": "Render {number}",
|
||||
"render.compare_side": "Render {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} s · {renderer} · captured at revision {revision}",
|
||||
"render.compare_swap": "Swap",
|
||||
"render.copy_request": "Copy JSON",
|
||||
"render.cpu_aa_help": "Spatial samples: more samples improve edges and increase render time.",
|
||||
"render.cpu_area_add": "Add rectangular CPU light",
|
||||
@@ -3406,6 +3483,10 @@
|
||||
"render.result_retained": "Showing the last completed image; a newer render has not replaced it.",
|
||||
"render.result_sequence": "Checkpoint",
|
||||
"render.result_title": "Render Result",
|
||||
"render.results_choose": "Retained results",
|
||||
"render.results_label": "Render {number} · {shot} · {seconds} s",
|
||||
"render.results_stale_label": "{result} [stale]",
|
||||
"render.results_unnamed": "Image",
|
||||
"render.return_design": "Return to Design",
|
||||
"render.review_stale": "This review is stale. Resolve or check again before applying.",
|
||||
"render.ribbon_aging": "Aging",
|
||||
@@ -4066,6 +4147,7 @@
|
||||
"ui.agent_chat_auth_terminal": "Run this in a terminal, then press Connect again:",
|
||||
"ui.agent_chat_auth_title": "Sign in to the agent",
|
||||
"ui.agent_chat_cancel": "Cancel",
|
||||
"ui.agent_chat_cancel_timeout": "The agent did not respond to Stop and was disconnected. Reconnect to continue.",
|
||||
"ui.agent_chat_cannot_start": "Installed, but it does not start: {error}",
|
||||
"ui.agent_chat_changes": "Changes: {n}, one undo step",
|
||||
"ui.agent_chat_changes_n": "{n} changes",
|
||||
@@ -4075,11 +4157,13 @@
|
||||
"ui.agent_chat_command": "Agent command",
|
||||
"ui.agent_chat_connect": "Connect",
|
||||
"ui.agent_chat_copy": "Copy",
|
||||
"ui.agent_chat_copy_message": "Copy message",
|
||||
"ui.agent_chat_ctx_bodies": "Bodies",
|
||||
"ui.agent_chat_ctx_elements": "Elements",
|
||||
"ui.agent_chat_ctx_features": "Features",
|
||||
"ui.agent_chat_ctx_more": "+{n} more",
|
||||
"ui.agent_chat_ctx_other": "Other",
|
||||
"ui.agent_chat_custom_heading": "Custom model",
|
||||
"ui.agent_chat_custom_hint": "Any program that speaks the Agent Client Protocol on stdin and stdout. No shell runs it.",
|
||||
"ui.agent_chat_denied": "Denied",
|
||||
"ui.agent_chat_deny": "Deny",
|
||||
@@ -4123,8 +4207,16 @@
|
||||
"ui.agent_chat_mode_all": "All",
|
||||
"ui.agent_chat_mode_ask": "Ask",
|
||||
"ui.agent_chat_mode_edits": "Edits",
|
||||
"ui.agent_chat_model_custom": "Use custom: {model}",
|
||||
"ui.agent_chat_model_no_tools": "{model} can't use SolidCraft's tools, so it can only chat. Pick a model with tool support.",
|
||||
"ui.agent_chat_model_rejected": "The agent did not accept {model}: {error}",
|
||||
"ui.agent_chat_model_search": "Search models…",
|
||||
"ui.agent_chat_model_tip": "The model the agent uses. Your choice is remembered for this agent.",
|
||||
"ui.agent_chat_models_failed": "Couldn't list models: {error}",
|
||||
"ui.agent_chat_models_heading": "Available models",
|
||||
"ui.agent_chat_models_listing": "Listing models…",
|
||||
"ui.agent_chat_models_none": "The endpoint lists no models",
|
||||
"ui.agent_chat_models_not_a_list": "The answer was no model list",
|
||||
"ui.agent_chat_new": "New chat",
|
||||
"ui.agent_chat_new_confirm": "Start a new chat? The current one is cleared.",
|
||||
"ui.agent_chat_next_picture": "Next picture",
|
||||
@@ -4386,6 +4478,8 @@
|
||||
"ui.coming_later": "coming later",
|
||||
"ui.command_done": "{command} done",
|
||||
"ui.command_palette_hint": "Type a command, e.g. {example} — or press S to search",
|
||||
"ui.components_from_bodies_hint": "Create one component per selected body, named after that body, under the active component.",
|
||||
"ui.components_from_bodies_required": "Select at least one body.",
|
||||
"ui.concentric": "Concentric",
|
||||
"ui.cone": "Cone",
|
||||
"ui.conic_curve": "Conic Curve",
|
||||
@@ -4398,9 +4492,11 @@
|
||||
"ui.construction.contact_hint": "Tangent: pick a circle/arc or open spline/conic, then the contact point (free-form curves: endpoints)",
|
||||
"ui.construction.external_hint": "Tangent: pick a circle or arc, an external point, then the desired solution",
|
||||
"ui.construction.free_hint": "Construction line: click two points; right-click to restart",
|
||||
"ui.construction.normal_needs_length": "An infinite line is always construction: type a length to draw a normal line",
|
||||
"ui.construction.point_hint": "Construction line: click the point to pass through",
|
||||
"ui.construction.reference_hint": "Construction line: pick a straight line, then a point; right-click to change the reference",
|
||||
"ui.construction.ribbon_label": "X-Lines",
|
||||
"ui.construction.side_hint": "Construction line: click the side the line runs towards; right-click to restart",
|
||||
"ui.context_menu": "Context menu",
|
||||
"ui.continuity_g0": "G0 crease",
|
||||
"ui.continuity_g1": "G1 tangent",
|
||||
@@ -4666,6 +4762,7 @@
|
||||
"ui.hideOrigin": "Hide Origin",
|
||||
"ui.hidePlanes": "Hide Planes",
|
||||
"ui.hideProfile": "Hide Profile",
|
||||
"ui.hide_component_history": "Hide its History",
|
||||
"ui.hide_origin_by_default": "Hide origin by default",
|
||||
"ui.hide_origin_note": "New designs start with the Origin folder hidden. A design keeps its own Origin setting once you show or hide it.",
|
||||
"ui.history_back": "Go back one step",
|
||||
@@ -4673,6 +4770,7 @@
|
||||
"ui.history_edge_hint": "double-click the edge",
|
||||
"ui.history_first": "Go to beginning",
|
||||
"ui.history_forward": "Go forward one step",
|
||||
"ui.history_hide_hidden": "Hide Hidden Components",
|
||||
"ui.history_hide_names": "Hide names",
|
||||
"ui.history_last": "Go to end",
|
||||
"ui.history_marker_tip": "History marker: drag to roll the model back; new features go here",
|
||||
@@ -4682,10 +4780,12 @@
|
||||
"ui.history_play_button_show": "Show Play Button",
|
||||
"ui.history_play_finish_action": "Finish the current action before playing History",
|
||||
"ui.history_play_roll_back": "Move the History marker back before playing",
|
||||
"ui.history_show_hidden": "Show Hidden Components",
|
||||
"ui.history_show_names": "Show names",
|
||||
"ui.history_sketch_listed_under": "Listed under {feature}",
|
||||
"ui.history_title": "History",
|
||||
"ui.history_toggle_names": "Show or hide feature names",
|
||||
"ui.history_waits_for_editor": "Undo, Redo and History wait until the feature editor has opened",
|
||||
"ui.hole": "Hole",
|
||||
"ui.hole_tap_type": "Hole Tap Type",
|
||||
"ui.hole_type": "Hole Type",
|
||||
@@ -4701,6 +4801,7 @@
|
||||
"ui.insert_design.saved_file": "{name} has unsaved changes, so its saved file was inserted. Save it, then Update Links to bring the changes in.",
|
||||
"ui.insert_design.unsaved": "Unsaved changes",
|
||||
"ui.inspect": "Inspect",
|
||||
"ui.instances": "Instances",
|
||||
"ui.intersect": "Intersect",
|
||||
"ui.isolate": "Isolate",
|
||||
"ui.join": "Join",
|
||||
@@ -4851,9 +4952,9 @@
|
||||
"ui.polygon": "Polygon",
|
||||
"ui.preferences": "Preferences",
|
||||
"ui.press_a_key": "press a key…",
|
||||
"ui.print_face_body_not_sent": "That face's body is not being sent: add it to Bodies first",
|
||||
"ui.print_face_cleared": "{body}: print face cleared",
|
||||
"ui.print_face_input": "Print face",
|
||||
"ui.print_face_one_body": "A print face is picked for one body: send that body on its own to change it",
|
||||
"ui.print_face_pick": "Select one flat face of a solid body to make it the print face",
|
||||
"ui.print_face_set": "{body}: print face set; Send to Slicer lays the body on it",
|
||||
"ui.profile": "Profile",
|
||||
@@ -4965,6 +5066,7 @@
|
||||
"ui.showDimension": "Show Dimension",
|
||||
"ui.showOrigin": "Show Origin",
|
||||
"ui.showPlanes": "Show Planes",
|
||||
"ui.show_component_history": "Show its History",
|
||||
"ui.show_constraints": "Constraints",
|
||||
"ui.show_construction_geometries": "Construction Geometries",
|
||||
"ui.show_dimensions": "Dimensions",
|
||||
@@ -4996,7 +5098,7 @@
|
||||
"ui.slicer_no_response": "{slicer} didn't respond in time. The file is already saved at {path}: send it again once the slicer has started.",
|
||||
"ui.slicer_resend_hint": "Sending again replaces the file the slicer has open.",
|
||||
"ui.slicer_single_instance": "Open files in a slicer that is already running",
|
||||
"ui.slicer_single_instance_tip": "PrusaSlicer, SuperSlicer and Cura load the file in the window that is open instead of starting another. Other slicers are started as they are. PrusaSlicer 3.0 always does this: its launcher can't take a file any other way.",
|
||||
"ui.slicer_single_instance_tip": "For slicers that load a file into a running window.",
|
||||
"ui.slicer_starting": "Starting {slicer}…",
|
||||
"ui.slicer_still_starting": "Still waiting for {slicer} to start. Nothing else was sent.",
|
||||
"ui.slicer_stl_fallback": "The 3MF could not be written, so this is an STL.",
|
||||
@@ -5081,12 +5183,15 @@
|
||||
"ui.view_angles": "View angles",
|
||||
"ui.view_angles_convention": "Degrees about the up axis. Yaw 0° is the Front view, 90° Left, −90° Right, 180° Back; pitch 90° is the Top view; a positive roll turns the model clockwise.",
|
||||
"ui.view_angles_help": "Shows the view's pitch, yaw and roll in degrees and its projection under the orientation gizmo. Click the readout to type exact angles.",
|
||||
"ui.view_angles_lock": "Lock {axis}",
|
||||
"ui.view_angles_ortho": "Ortho",
|
||||
"ui.view_angles_persp": "Persp",
|
||||
"ui.view_angles_pitch": "Pitch",
|
||||
"ui.view_angles_readout": "Pitch {pitch} · Yaw {yaw} · Roll {roll}",
|
||||
"ui.view_angles_roll": "Roll",
|
||||
"ui.view_angles_unlock": "Unlock {axis}",
|
||||
"ui.view_angles_yaw": "Yaw",
|
||||
"ui.view_angles_zero": "Set {axis} to 0°",
|
||||
"ui.view_centre": "View center",
|
||||
"ui.viewport_status_clear": "Turn off {name}",
|
||||
"ui.viewport_status_clear_hint": "Turn off {name} · Esc",
|
||||
@@ -5138,7 +5243,7 @@
|
||||
"update.available": "SolidCraft {version} is available",
|
||||
"update.button": "Update",
|
||||
"update.button_version": "Update to {version}",
|
||||
"update.check_automatically": "Check for updates automatically",
|
||||
"update.check_automatically": "Check at start and once a day",
|
||||
"update.check_for_updates": "Check for updates",
|
||||
"update.check_now": "Check now",
|
||||
"update.check_unavailable": "Update checks are not available for this installation.",
|
||||
@@ -5188,6 +5293,7 @@
|
||||
"web.open_link": "Open linked design?",
|
||||
"web.open_link_confirm": "There are unsaved changes. Open the linked design in a new tab?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ choose · Enter / Tab complete · Esc dismiss",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ choose · Enter applies this value",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ choose · Enter applies what you typed · Esc dismiss",
|
||||
"⌘Enter continues on GitHub.": "⌘Enter continues on GitHub."
|
||||
}
|
||||
|
||||
+10
-1
@@ -1195,6 +1195,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Spline por puntos de control (grado 5)",
|
||||
"cmd.sketch_spline_fit_point": "Spline por puntos de ajuste",
|
||||
"cmd.sketch_spline_insert_point": "Añadir punto de spline",
|
||||
"cmd.sketch_spline_remove_point": "Eliminar punto de spline",
|
||||
"cmd.sketch_spline_reverse": "Invertir dirección del spline",
|
||||
"cmd.sketch_spun_profile": "Perfil de revolución",
|
||||
"cmd.sketch_toggle_diameter": "Alternar radio / diámetro",
|
||||
@@ -1805,12 +1806,19 @@
|
||||
"parts.family.button_head_cap_screw": "Tornillo cabeza botón",
|
||||
"parts.family.dowel_pin": "Pasador cilíndrico",
|
||||
"parts.family.hex_bolt": "Tornillo de cabeza hexagonal",
|
||||
"parts.family.hex_cap_screw_inch": "Tornillo de cabeza hexagonal (pulgadas)",
|
||||
"parts.family.hex_nut": "Tuerca hexagonal",
|
||||
"parts.family.hex_nut_inch": "Tuerca hexagonal (pulgadas)",
|
||||
"parts.family.set_screw": "Prisionero con hexágono interior, punta plana",
|
||||
"parts.family.socket_head_cap_screw": "Tornillo cilíndrico con hexágono interior",
|
||||
"parts.family.washer": "Arandela plana",
|
||||
"parts.family.washer_sae": "Arandela plana, SAE (estrecha)",
|
||||
"parts.family.washer_uss": "Arandela plana, USS (ancha)",
|
||||
"parts.holes": "Agujeros (opcional)",
|
||||
"parts.placement_hint": "elige uno o más agujeros para colocar una pieza en cada uno, o Aceptar la coloca en el origen",
|
||||
"parts.system": "Sistema de unidades",
|
||||
"parts.system.inch": "Pulgadas",
|
||||
"parts.system.metric": "Métrico",
|
||||
"pattern.alignment_2": "Alineación 2",
|
||||
"pattern.alignment_hint": "Los recuentos incluyen el original. Centrar equilibra las copias; Invertir elige el lado adicional cuando el total es par.",
|
||||
"pattern.direction_1": "Dirección 1",
|
||||
@@ -4357,6 +4365,7 @@
|
||||
"ui.insert_design.saved_file": "{name} tiene cambios sin guardar, así que se insertó su archivo guardado. Guárdalo y luego Actualizar vínculos para traer los cambios.",
|
||||
"ui.insert_design.unsaved": "Cambios sin guardar",
|
||||
"ui.inspect": "Inspeccionar",
|
||||
"ui.instances": "Instancias",
|
||||
"ui.intersect": "Intersecar",
|
||||
"ui.isolate": "Aislar",
|
||||
"ui.join": "Unir",
|
||||
@@ -4504,9 +4513,9 @@
|
||||
"ui.polygon": "Polígono",
|
||||
"ui.preferences": "Preferencias",
|
||||
"ui.press_a_key": "pulsa una tecla…",
|
||||
"ui.print_face_body_not_sent": "El cuerpo de esa cara no se envía: añádelo primero a Cuerpos",
|
||||
"ui.print_face_cleared": "{body}: cara de impresión limpiada",
|
||||
"ui.print_face_input": "Cara de impresión",
|
||||
"ui.print_face_one_body": "Se ha elegido una cara de impresión para un cuerpo: envía ese cuerpo solo para cambiarla",
|
||||
"ui.print_face_pick": "Selecciona una cara plana de un cuerpo sólido para hacerla la cara de impresión",
|
||||
"ui.print_face_set": "{body}: cara de impresión establecida; Enviar a Laminador coloca el cuerpo sobre ella",
|
||||
"ui.profile": "Perfil",
|
||||
|
||||
@@ -1191,6 +1191,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Ohjauspistesplini (aste 5)",
|
||||
"cmd.sketch_spline_fit_point": "Sovituspistesplini",
|
||||
"cmd.sketch_spline_insert_point": "Lisää splinipiste",
|
||||
"cmd.sketch_spline_remove_point": "Poista splinipiste",
|
||||
"cmd.sketch_spline_reverse": "Käännä splinin suunta",
|
||||
"cmd.sketch_spun_profile": "Pyöräytysprofiili",
|
||||
"cmd.sketch_toggle_diameter": "Vaihda säde / halkaisija",
|
||||
@@ -4321,6 +4322,7 @@
|
||||
"ui.insert_design.saved_file": "Mallissa {name} on tallentamattomia muutoksia, joten sen tallennettu tiedosto lisättiin. Tallenna se ja tuo muutokset valitsemalla Päivitä linkit.",
|
||||
"ui.insert_design.unsaved": "Tallentamattomia muutoksia",
|
||||
"ui.inspect": "Tarkastele",
|
||||
"ui.instances": "Esiintymät",
|
||||
"ui.intersect": "Leikkaus",
|
||||
"ui.isolate": "Eristä",
|
||||
"ui.join": "Yhdistä",
|
||||
@@ -4468,9 +4470,9 @@
|
||||
"ui.polygon": "Monikulmio",
|
||||
"ui.preferences": "Asetukset",
|
||||
"ui.press_a_key": "paina näppäintä…",
|
||||
"ui.print_face_body_not_sent": "Tämän pinnan kappaletta ei lähetetä: lisää se ensin kohtaan Kappaleet",
|
||||
"ui.print_face_cleared": "{body}: tulostuspinta poistettu",
|
||||
"ui.print_face_input": "Tulostuspinta",
|
||||
"ui.print_face_one_body": "Tulostuspinta on valittu yhdelle kappaleelle: lähetä kappale yksinään muuttaaksesi sitä",
|
||||
"ui.print_face_pick": "Valitse kiinteän kappaleen yksi tasainen pinta tulostuspinnaksi",
|
||||
"ui.print_face_set": "{body}: tulostuspinta asetettu; Lähetä viipalointiohjelmaan asettaa kappaleen sen varaan",
|
||||
"ui.profile": "Profiili",
|
||||
|
||||
+10
-1
@@ -1195,6 +1195,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Spline par points de contrôle (degré 5)",
|
||||
"cmd.sketch_spline_fit_point": "Spline par points",
|
||||
"cmd.sketch_spline_insert_point": "Ajouter un point de spline",
|
||||
"cmd.sketch_spline_remove_point": "Supprimer un point de spline",
|
||||
"cmd.sketch_spline_reverse": "Inverser le sens de la spline",
|
||||
"cmd.sketch_spun_profile": "Profil de révolution",
|
||||
"cmd.sketch_toggle_diameter": "Basculer rayon / diamètre",
|
||||
@@ -2053,12 +2054,19 @@
|
||||
"parts.family.button_head_cap_screw": "Vis à tête bombée à six pans creux",
|
||||
"parts.family.dowel_pin": "Goupille cylindrique",
|
||||
"parts.family.hex_bolt": "Vis à tête hexagonale",
|
||||
"parts.family.hex_cap_screw_inch": "Vis à tête hexagonale (pouces)",
|
||||
"parts.family.hex_nut": "Écrou hexagonal",
|
||||
"parts.family.hex_nut_inch": "Écrou hexagonal (pouces)",
|
||||
"parts.family.set_screw": "Vis sans tête à six pans creux, bout plat",
|
||||
"parts.family.socket_head_cap_screw": "Vis à tête cylindrique à six pans creux",
|
||||
"parts.family.washer": "Rondelle plate",
|
||||
"parts.family.washer_sae": "Rondelle plate, SAE (étroite)",
|
||||
"parts.family.washer_uss": "Rondelle plate, USS (large)",
|
||||
"parts.holes": "Trous (facultatif)",
|
||||
"parts.placement_hint": "choisissez un ou plusieurs trous pour y loger une pièce chacun, ou OK la place à l'origine",
|
||||
"parts.system": "Système d'unités",
|
||||
"parts.system.inch": "Pouces",
|
||||
"parts.system.metric": "Métrique",
|
||||
"pattern.alignment_2": "Alignement 2",
|
||||
"pattern.alignment_hint": "La quantité inclut l’original. Le centrage répartit les copies de chaque côté ; si la quantité est paire, l’inversion choisit le côté qui reçoit la copie supplémentaire.",
|
||||
"pattern.direction_1": "Direction 1",
|
||||
@@ -4657,6 +4665,7 @@
|
||||
"ui.insert_design.saved_file": "{name} contient des modifications non enregistrées ; c'est donc son fichier enregistré qui a été inséré. Enregistrez-le, puis utilisez Mettre à jour les liens pour intégrer les modifications.",
|
||||
"ui.insert_design.unsaved": "Modifications non enregistrées",
|
||||
"ui.inspect": "Inspecter",
|
||||
"ui.instances": "Instances",
|
||||
"ui.intersect": "Intersection",
|
||||
"ui.isolate": "Isoler",
|
||||
"ui.join": "Joindre",
|
||||
@@ -4805,9 +4814,9 @@
|
||||
"ui.polygon": "Polygone",
|
||||
"ui.preferences": "Préférences",
|
||||
"ui.press_a_key": "appuyez sur une touche…",
|
||||
"ui.print_face_body_not_sent": "Le corps de cette face n’est pas envoyé : ajoutez-le d’abord à Corps",
|
||||
"ui.print_face_cleared": "{body} : face d’impression effacée",
|
||||
"ui.print_face_input": "Face d’impression",
|
||||
"ui.print_face_one_body": "Une face d’impression appartient à un seul corps : envoyez ce corps seul pour la modifier",
|
||||
"ui.print_face_pick": "Sélectionnez une face plane d’un corps solide pour en faire la face d’impression",
|
||||
"ui.print_face_set": "{body} : face d’impression définie ; Envoyer au slicer pose le corps sur cette face",
|
||||
"ui.profile": "Profil",
|
||||
|
||||
+70
-1
@@ -793,6 +793,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (ancoraxe perdida)",
|
||||
"browser.sketch_editing": "{name} (en edición)",
|
||||
"browser.sketch_name": "Bosquexo{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "xa na súa peza",
|
||||
"browser.sketches_to_parts_design_body": "nun corpo do propio deseño",
|
||||
"browser.sketches_to_parts_moved": "Movéronse {moved} bosquexos á peza na que están",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "nunha cara sen unha ancoraxe exacta",
|
||||
"browser.sketches_to_parts_not_on_face": "non na cara dunha peza",
|
||||
"browser.sketches_to_parts_part_missing": "a súa peza desapareceu",
|
||||
"browser.sketches_to_parts_result": "Movéronse {moved} bosquexos á peza na que están; {stayed} non se moveron: {why}",
|
||||
"browser.units": "Unidades: {units}",
|
||||
"cam.flat": "Cara plana",
|
||||
"cam.follower": "Seguidor",
|
||||
@@ -860,6 +867,7 @@
|
||||
"cmd.component_move_bodies": "Mover corpos ao compoñente",
|
||||
"cmd.component_move_features": "Mover operacións ao compoñente",
|
||||
"cmd.component_move_sketches": "Mover bosquexos ao compoñente",
|
||||
"cmd.component_move_sketches_to_parts": "Mover os bosquexos ás súas pezas",
|
||||
"cmd.component_paste_from_design": "Pegar desde outro deseño",
|
||||
"cmd.component_paste_new": "Pegar como novo",
|
||||
"cmd.component_properties": "Propiedades físicas do compoñente",
|
||||
@@ -1195,6 +1203,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Spline por puntos de control (grao 5)",
|
||||
"cmd.sketch_spline_fit_point": "Spline por puntos de paso",
|
||||
"cmd.sketch_spline_insert_point": "Engadir punto ao spline",
|
||||
"cmd.sketch_spline_remove_point": "Eliminar punto do spline",
|
||||
"cmd.sketch_spline_reverse": "Inverter a dirección do spline",
|
||||
"cmd.sketch_spun_profile": "Perfil de revolución",
|
||||
"cmd.sketch_toggle_diameter": "Alternar raio / diámetro",
|
||||
@@ -1287,8 +1296,14 @@
|
||||
"color.hex_hint": "#rrggbb",
|
||||
"color.hue": "Ton",
|
||||
"color.linear": "Mostrando os valores da cor no espazo lineal 0-1",
|
||||
"color.recent": "Cores recentes",
|
||||
"color.recent_help": "As cores que escolles en calquera campo de cor aparecen aquí",
|
||||
"color.saturation": "Saturación (porcentaxe)",
|
||||
"color.saturation_value": "Saturación e valor",
|
||||
"color.selected": "Cor seleccionada",
|
||||
"color.step_down": "Diminuír un paso",
|
||||
"color.step_up": "Aumentar un paso",
|
||||
"color.value": "Valor, a luminosidade (porcentaxe)",
|
||||
"config.column_filter_empty": "Non hai columnas que coincidan",
|
||||
"contact.components_count": "{count} compoñentes",
|
||||
"contact.enabled_label": "activado",
|
||||
@@ -1335,6 +1350,7 @@
|
||||
"dialog.dimension_name": "Cota: {name}",
|
||||
"dialog.edit_title": "EDITAR {title}",
|
||||
"dialog.no_shown_body": "Non hai ningún corpo visible para modificar: mostra un ou escolle Corpo novo",
|
||||
"dialog.pattern_sketch_not_object": "Escolle corpos ou operacións: un bosquexo non é ningunha das dúas cousas. Repite as súas curvas co Patrón ou a Simetría do propio bosquexo, ou extrúeo primeiro.",
|
||||
"dialog.select": "↖ Seleccionar",
|
||||
"dialog.selected_count": "↖ {count} seleccionados",
|
||||
"dialog.selection_guidance": "Selecciona unha entrada para cambiala. Fai clic nos obxectos seleccionados para quitalos; Ctrl/Cmd-clic tamén os alterna. Revisa a previsualización e preme Aceptar.",
|
||||
@@ -1904,6 +1920,7 @@
|
||||
"nav.hfss.help": "Alt+arrastrar co botón esquerdo desprázase, Shift+Alt+botón esquerdo fai zoom, Ctrl+arrastrar co botón esquerdo sobre un espazo baleiro orbita",
|
||||
"nav.inventor.help": "Arrastrar co botón central desprázase, Shift+botón central orbita, Ctrl+botón central fai zoom, arrastrar co botón dereito orbita",
|
||||
"nav.maya.help": "Alt+arrastrar co botón esquerdo orbita, Alt+botón central desprázase, Alt+botón dereito fai zoom; arrastrar co botón central tamén desprázase",
|
||||
"nav.nx.help": "Arrastrar co botón central orbita, Shift+botón central desprázase (botón central + dereito de NX), Ctrl+botón central fai zoom (botón central + esquerdo de NX); arrastrar co botón dereito non move a cámara",
|
||||
"nav.openscad.help": "Arrastrar co botón esquerdo sobre un espazo baleiro orbita, Shift+arrastrar co botón esquerdo selecciona por caixa, arrastrar a xeometría do bosquexo edítaa; arrastrar co botón dereito ou central desprázase, Shift+arrastrar co botón dereito ou central fai zoom",
|
||||
"nav.perspective.sketch": "A edición de bosquexos usa a vista ortogonal. Remata o bosquexo para cambiar á perspectiva.",
|
||||
"nav.right_drag_orbits.help": "Desactivado: arrastrar co botón dereito non move a cámara e o botón dereito só abre o menú contextual. Escoller a asignación do rato de SolidWorks desactívao.",
|
||||
@@ -2125,6 +2142,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Superficies curvas",
|
||||
"prefs.curves": "Superficies curvas",
|
||||
"prefs.dark_parts_shading": "Conservar a forma das pezas escuras",
|
||||
"prefs.dark_parts_shading_help": "Sombrea a vista en luz lineal cunha luz de recheo suave do ceo, para que as caras escuras manteñan un degradado en vez de volverse negras",
|
||||
"prefs.dead_zone_note": "A banda raiada é a zona morta: o movemento dentro dela ignórase.",
|
||||
"prefs.default": "predeterminado",
|
||||
"prefs.design": "Valores predeterminados do deseño",
|
||||
@@ -2139,6 +2158,8 @@
|
||||
"prefs.edges": "Arestas e sombreado",
|
||||
"prefs.extend": "Estender",
|
||||
"prefs.extra_navigation": "Ctrl+arrastrar co botón esquerdo no espazo baleiro orbita; mantén premida a barra espaciadora para desprazar.",
|
||||
"prefs.extrude_view_isometric": "Cambiar a vista a isométrica ao abrir Extruír",
|
||||
"prefs.extrude_view_return": "Volver á vista anterior ao pechar Extruír",
|
||||
"prefs.follows_os": "Segue o sistema operativo",
|
||||
"prefs.font": "Tipo de letra",
|
||||
"prefs.font_fallback": "Os glifos que lle faltan ao tipo de letra escollido substitúense polos do tipo de letra integrado, así que ningunha etiqueta mostra unha caixa baleira.",
|
||||
@@ -2154,6 +2175,7 @@
|
||||
"prefs.graphics_card_auto": "Automático",
|
||||
"prefs.graphics_card_missing": "A tarxeta gráfica gardada non está dispoñible; úsase Automático.",
|
||||
"prefs.graphics_card_restart": "Aplícase despois de reiniciar SolidCraft. WGPU_ADAPTER_NAME anula esta opción.",
|
||||
"prefs.graphics_card_unused": "A tarxeta gráfica gardada non se usou para esta xanela; a tarxeta en uso móstrase en Axuda › Acerca de e na barra de estado. Proba Automático ou define WGPU_ADAPTER_NAME cunha parte única do nome da tarxeta.",
|
||||
"prefs.graphics_note": "Troca velocidade por suavidade. Cada opción mostra o seu efecto nunha mostra.",
|
||||
"prefs.graphics_presets_note": "Os axustes predefinidos configuran xuntas as opcións de abaixo.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Ocultar as iconas de restricións ao reducir o zoom",
|
||||
@@ -2168,6 +2190,8 @@
|
||||
"prefs.history_sketches_note": "Con Bosquexos configurado en Baixo a súa operación, o Historial da esquerda lista cada bosquexo un nivel máis adentro, baixo a primeira operación construída a partir del, sempre que sexa o bastante ancho para mostrar os nomes. A liña de tempo da parte inferior mantén sempre a orde de creación.",
|
||||
"prefs.history_sketches_under_feature": "Baixo a súa operación",
|
||||
"prefs.hold_stop_snapping": "Manter premido para deixar de axustar",
|
||||
"prefs.home_fit_size": "Tamaño de Inicio e Axustar",
|
||||
"prefs.home_fit_size_note": "Canto da vista ocupa o modelo cando Inicio, Axustar, un deseño aberto ou un cambio de vista do navegador de vistas o encadran, estea ou non activado o axuste de abaixo. 100 %: ocupa a vista entre os paneis, a brúxula e a barra de navegación.",
|
||||
"prefs.icons_only": "Só iconas",
|
||||
"prefs.imported_file": "Ficheiro importado",
|
||||
"prefs.in_browser": "No navegador",
|
||||
@@ -2176,13 +2200,20 @@
|
||||
"prefs.interface_language": "Idioma da interface",
|
||||
"prefs.interface_note": "Como se ve SolidCraft e onde están as súas partes.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_navigator_arrows": "Invertir as frechas de rotación do cubo de vistas",
|
||||
"prefs.invert_navigator_arrows_note": "As frechas arredor do cubo fan orbitar a cámara na dirección na que apuntan, como en Fusion e Inventor: a frecha da esquerda apunta á dereita e move a cámara cara á dereita. Desactivado: unha frecha xira o cubo cara ao lado no que está, así que a frecha da esquerda move a cámara cara á esquerda. As frechas curvas de balanceo non se ven afectadas.",
|
||||
"prefs.keep_work_on_tab_switch": "Manter abertos os diálogos e as ferramentas ao cambiar de deseño",
|
||||
"prefs.keep_work_on_tab_switch_note": "Cada lapela de deseño conserva o seu diálogo ou ferramenta aberto, co que escolliches e escribiches nel, e a súa lapela da barra de ferramentas. Volven estar aí cando regresas ao deseño. Desactivado: cambiar de deseño cancélaos e mostra a lapela Modelo.",
|
||||
"prefs.labels": "Etiquetas",
|
||||
"prefs.language_system": "Sistema",
|
||||
"prefs.layout": "Disposición",
|
||||
"prefs.layout_note": "Compacta acopla os paneis aos bordos da xanela. A translucidez do navegador está dispoñible en Flotante. Cambia o lado do Asistente na páxina do Asistente.",
|
||||
"prefs.left": "Esquerda",
|
||||
"prefs.left_click": "Clic esquerdo",
|
||||
"prefs.line_drag_draws_one": "Premer e arrastrar debuxa unha liña",
|
||||
"prefs.line_drag_draws_one_note": "Na ferramenta Liña, premer, arrastrar e soltar debuxa unha liña desde onde se preme ata onde se solta, e remata alí. Os clics seguen debuxando unha cadea. Desactivado: un arrastre coloca un punto onde se solta, como fai un clic.",
|
||||
"prefs.line_hover_tangent_arc": "Manter o punteiro quieto no último punto dunha liña para cambiar a un arco tanxente",
|
||||
"prefs.line_hover_tangent_arc_note": "Na ferramenta Liña, unha vez debuxado un segmento, afástate do último punto da cadea, volve e mantén o punteiro quieto nel: o seguinte clic remata un arco tanxente, e o mesmo xesto volve ás liñas. Desactivado: manter o punteiro aí non fai nada.",
|
||||
"prefs.linked_designs": "Deseños ligados",
|
||||
"prefs.live_input": "Entrada en directo",
|
||||
"prefs.live_preview": "Vista previa en directo",
|
||||
@@ -2206,6 +2237,8 @@
|
||||
"prefs.mouse_mapping": "Asignación do rato",
|
||||
"prefs.navigation": "Navegación",
|
||||
"prefs.navigation_note": "Como moven a vista o rato, o panel táctil e o rato 3D.",
|
||||
"prefs.navigator_fit": "Axustar o modelo despois dun cambio de vista do navegador",
|
||||
"prefs.navigator_fit_note": "Unha cara, esquina ou frecha do cubo de vistas, ou un eixe do gizmo, tamén mira ao centro do modelo desde a única distancia que o abarca por todos os lados (a súa esfera delimitadora). Inicio usa o mesmo zoom e centro mentres isto está activado, así que as vistas escollidas despois só xiran. Desactivado: a vista xira no sitio, conservando o seu punto de mira e o seu zoom.",
|
||||
"prefs.network_host_remove": "Quitar",
|
||||
"prefs.network_locations": "Permitir ubicacións de rede nos deseños",
|
||||
"prefs.network_locations_note": "Desactivado: os ficheiros ligados dun deseño que están noutro ordenador (\\\\server\\share) só se abren desde os servidores listados aquí, ou despois de que permitas un cando se che pregunte.",
|
||||
@@ -2254,6 +2287,8 @@
|
||||
"prefs.render_previous_geometry_budget": "Usar o orzamento de xeometría anterior da vista previa de Render",
|
||||
"prefs.render_previous_geometry_budget_help": "Restaura o límite fixo de xeometría anterior (512 MiB en escritorio de 64 bits, 128 MiB noutros casos) en lugar do orzamento baseado no dispositivo.",
|
||||
"prefs.reports_saved": "{n} gardados",
|
||||
"prefs.ribbon_click_ends_tool": "Facer clic no botón da ferramenta en uso remata a ferramenta",
|
||||
"prefs.ribbon_click_ends_tool_note": "Un clic no botón resaltado da cinta da ferramenta de bosquexo en uso remata a ferramenta, e así os clics na vista volven seleccionar. Desactivado: o clic inicia a ferramenta de novo.",
|
||||
"prefs.ribbon_reveal": "Mostrar os resultados da busca na cinta",
|
||||
"prefs.ribbon_reveal_note": "Ao pasar o punteiro sobre un resultado en Buscar ordes, móstrase onde está a orde na cinta: a súa lapela, o seu botón ou a súa fila no despregable dun panel. Unha orde iniciada desde a busca escintila alí, e unha orde en execución desde un despregable marca o seu panel.",
|
||||
"prefs.right_click": "Clic dereito",
|
||||
@@ -2283,6 +2318,8 @@
|
||||
"prefs.sketch_exit_view.previous": "Volver á vista anterior",
|
||||
"prefs.sketch_exit_view.unless_rotated": "Volver, salvo que se xirase",
|
||||
"prefs.sketch_exit_view_note": "A onde vai a vista despois de Finalizar o bosquexo, Cancelar ou un dobre Esc.",
|
||||
"prefs.sketch_face_activates_part": "Crear un bosquexo na cara dunha peza activa esa peza",
|
||||
"prefs.sketch_face_activates_part_note": "Un bosquexo iniciado na cara dun corpo doutro compoñente activa primeiro ese compoñente, de modo que o bosquexo lle pertence e queda con esa peza no navegador. Despois a peza segue activa. Desactivado: o bosquexo pertence ao compoñente activo.",
|
||||
"prefs.sketch_open_look_at": "Xirar para mirar cara ao bosquexo cando se abre",
|
||||
"prefs.sketch_open_look_at_note": "Desactivado: crear ou editar un bosquexo deixa a vista tal e como está. Mirar cara a orienta a vista cara ao bosquexo cando o queres.",
|
||||
"prefs.sketch_recent_snap": "Axustar automaticamente aos centros e puntos medios recentes",
|
||||
@@ -2314,6 +2351,24 @@
|
||||
"prefs.status_error": "Erro",
|
||||
"prefs.status_selected": "Seleccionado",
|
||||
"prefs.status_warning": "Aviso",
|
||||
"prefs.structure.appearance": "Aparencia",
|
||||
"prefs.structure.appearance_note": "Cores, texto e accesibilidade.",
|
||||
"prefs.structure.in_profile": "No perfil",
|
||||
"prefs.structure.layout": "Disposición",
|
||||
"prefs.structure.layout_note": "Onde se colocan as partes da xanela.",
|
||||
"prefs.structure.look": "Estilo",
|
||||
"prefs.structure.measure_pointer": "Medición e punteiro",
|
||||
"prefs.structure.mouse3d": "Rato 3D",
|
||||
"prefs.structure.mouse3d_note": "SpaceMouse e SpaceNavigator.",
|
||||
"prefs.structure.navigation_note": "Rato e panel táctil. O rato 3D ten a súa propia páxina.",
|
||||
"prefs.structure.profile": "Perfil",
|
||||
"prefs.structure.profile_edited": "Cambiado desde que se gardou",
|
||||
"prefs.structure.profile_hover": "Gardado nos perfís de axustes",
|
||||
"prefs.structure.scale_percent": "{percent}%",
|
||||
"prefs.structure.sketch": "Bosquexo",
|
||||
"prefs.structure.tools": "Ferramentas",
|
||||
"prefs.structure.tools_note": "Como se comportan as ferramentas de modelado.",
|
||||
"prefs.structure.work": "Traballo",
|
||||
"prefs.supersampling_brief": "Debuxa a vista máis grande e redúcea con filtrado: moi nítida, pero máis pesada.",
|
||||
"prefs.tab_accent_note": "O realce segue a pestana activa",
|
||||
"prefs.theme_file": "ficheiro de tema",
|
||||
@@ -3811,6 +3866,7 @@
|
||||
"sketch_text.formula": "Comeza con = para construír as palabras a partir de parámetros: ='W' + number(Width)",
|
||||
"sketch_text.italic": "Cursiva",
|
||||
"sketch_text.loading_fonts": "Buscando os tipos de letra instalados…",
|
||||
"sketch_text.no_bold": "O tipo de letra integrado non ten versión en negra: escolle un tipo de letra instalado",
|
||||
"sketch_text.on_path": "O texto segue a súa traxectoria: a curva colócao",
|
||||
"sketch_text.path_clear": "Separar o texto da súa curva",
|
||||
"sketch_text.path_hint": "Unha curva do bosquexo que segue o texto: fai clic aquí e despois na curva",
|
||||
@@ -4213,6 +4269,9 @@
|
||||
"ui.ai_permissions": "Permisos",
|
||||
"ui.ai_preset": "Axuste predefinido",
|
||||
"ui.ai_protocol": "Protocolo",
|
||||
"ui.ai_reasoning_effort": "Esforzo de razoamento",
|
||||
"ui.ai_reasoning_effort_field_hint": "baleiro = valor predeterminado do provedor",
|
||||
"ui.ai_reasoning_effort_hint": "Opcional. Envíase ao punto de acceso como `reasoning_effort`; os valores aceptados difiren segundo o provedor (por exemplo `low`, `high` ou `max`). SolidCraft nunca substitúe un valor que definas aquí. Déixao baleiro para non enviar nada e manter o valor predeterminado do provedor.",
|
||||
"ui.ai_remove": "Quitar",
|
||||
"ui.ai_request_timed_out": "O modelo tardou máis de {seconds} s. Podes aumentar o tempo límite da solicitude deste punto de acceso en Preferencias › Asistente.",
|
||||
"ui.ai_request_timeout": "Tempo límite da solicitude",
|
||||
@@ -4378,6 +4437,7 @@
|
||||
"ui.dark": "Escuro",
|
||||
"ui.default_modelling_orientation": "Orientación de modelado predeterminada",
|
||||
"ui.delete": "Eliminar",
|
||||
"ui.delete_select_first": "Selecciona primeiro o que queres eliminar: operacións, corpos, compoñentes ou xeometría do bosquexo",
|
||||
"ui.depth": "Profundidade",
|
||||
"ui.design": "Deseño",
|
||||
"ui.design_properties.author": "Autor",
|
||||
@@ -4470,6 +4530,7 @@
|
||||
"ui.edit_in_place_saved_elsewhere": "A peza gardouse noutro ficheiro; a ensamblaxe segue ligando {name}",
|
||||
"ui.edit_in_place_wait": "Agarda a que remate o que se está a executar no deseño",
|
||||
"ui.edit_section_analysis": "Editar a análise de sección",
|
||||
"ui.edit_sketch_empty": "Non hai bosquexos para editar",
|
||||
"ui.ellipse": "Elipse",
|
||||
"ui.emboss_wrap_note": "Os perfís de liñas ortogonais tanxentes a un cilindro compatible envólvense automaticamente. Escolle unha parede para elixir un destino.",
|
||||
"ui.end_radius": "Raio final",
|
||||
@@ -4480,7 +4541,9 @@
|
||||
"ui.equation_explicit": "Explícita",
|
||||
"ui.equation_form": "Forma",
|
||||
"ui.equation_note": "A variable é un número sen unidade: sin(t) tómaa en radiáns. Un resultado sen unidade está na unidade do deseño.",
|
||||
"ui.equation_note_range": "A variable é unha lonxitude, igual que o seu intervalo: combínaa con parámetros de lonxitude e divídea entre unha lonxitude para sin(). Un resultado sen unidade está na unidade do deseño.",
|
||||
"ui.equation_parametric": "Paramétrica",
|
||||
"ui.equation_variable_unit": "A variable ten a unidade do intervalo",
|
||||
"ui.equation_x_of_t": "x(t)",
|
||||
"ui.equation_y_of_t": "y(t)",
|
||||
"ui.equation_y_of_x": "y(x)",
|
||||
@@ -4655,12 +4718,14 @@
|
||||
"ui.joints": "Unións",
|
||||
"ui.keyboard_shortcuts": "Atallos de teclado",
|
||||
"ui.language": "Idioma",
|
||||
"ui.lasso_select": "Lazo (arrastra un bucle a man alzada: colle o que queda totalmente dentro)",
|
||||
"ui.layout_compact": "Compacta",
|
||||
"ui.layout_floating": "Flotante",
|
||||
"ui.length": "Lonxitude",
|
||||
"ui.length_decimals": "Decimais das lonxitudes",
|
||||
"ui.light": "Claro",
|
||||
"ui.line": "Liña",
|
||||
"ui.line_arc_hint": "Arco tanxente: fai clic no final do arco. Mantén o punteiro quieto de novo no último punto para volver ás liñas; clic dereito ou Esc para finalizar",
|
||||
"ui.link_copies": "Ligar copias idénticas",
|
||||
"ui.link_copies_keep": "Manter",
|
||||
"ui.link_copies_link": "Ligar",
|
||||
@@ -4749,6 +4814,7 @@
|
||||
"ui.part_size_shared": "Reconstrúe a peza, substituíndo as operacións engadidas. Actualízanse todas as ocorrencias deste compoñente.",
|
||||
"ui.paste": "Pegar",
|
||||
"ui.path": "Traxectoria",
|
||||
"ui.pattern_object_type_tip": "Corpos: formas sólidas, escollidas na vista ou no Navegador; cada un cópiase enteiro.\nOperacións: pasos do Historial, como unha extrusión ou un furado, escollidos no Historial ou nunha cara que crearon; repítese o paso.\nUn bosquexo non é ningunha das dúas cousas: repite as súas curvas co Patrón ou a Simetría do propio bosquexo, ou extrúeo primeiro.",
|
||||
"ui.pattern_on_path": "Patrón nunha traxectoria",
|
||||
"ui.perforate_margin_default": "a partir do diámetro",
|
||||
"ui.perforate_pitch": "Paso",
|
||||
@@ -4794,9 +4860,9 @@
|
||||
"ui.polygon": "Polígono",
|
||||
"ui.preferences": "Preferencias",
|
||||
"ui.press_a_key": "preme unha tecla…",
|
||||
"ui.print_face_body_not_sent": "O corpo desa cara non se envía: engádeo primeiro a Corpos",
|
||||
"ui.print_face_cleared": "{body}: cara de impresión quitada",
|
||||
"ui.print_face_input": "Cara de impresión",
|
||||
"ui.print_face_one_body": "Unha cara de impresión pertence a un só corpo: envía ese corpo só para cambiala",
|
||||
"ui.print_face_pick": "Selecciona unha cara plana dun corpo sólido para facela a cara de impresión",
|
||||
"ui.print_face_set": "{body}: cara de impresión definida; Enviar ao laminador apoia o corpo sobre ela",
|
||||
"ui.profile": "Perfil",
|
||||
@@ -5024,12 +5090,15 @@
|
||||
"ui.view_angles": "Ángulos da vista",
|
||||
"ui.view_angles_convention": "Graos arredor do eixe cara arriba. A guiñada 0° é a vista frontal, 90° a esquerda, −90° a dereita, 180° a posterior; un cabeceo de 90° é a vista superior; un balanceo positivo fai xirar o modelo en sentido horario.",
|
||||
"ui.view_angles_help": "Mostra o cabeceo, a guiñada e o balanceo da vista en graos e a súa proxección baixo o indicador de orientación. Fai clic na lectura para escribir ángulos exactos.",
|
||||
"ui.view_angles_lock": "Bloquear {axis}",
|
||||
"ui.view_angles_ortho": "Orto",
|
||||
"ui.view_angles_persp": "Persp.",
|
||||
"ui.view_angles_pitch": "Cabeceo",
|
||||
"ui.view_angles_readout": "Cabeceo {pitch} · Guiñada {yaw} · Balanceo {roll}",
|
||||
"ui.view_angles_roll": "Balanceo",
|
||||
"ui.view_angles_unlock": "Desbloquear {axis}",
|
||||
"ui.view_angles_yaw": "Guiñada",
|
||||
"ui.view_angles_zero": "Poñer {axis} a 0°",
|
||||
"ui.view_centre": "Centro da vista",
|
||||
"ui.viewport_status_clear": "Desactivar {name}",
|
||||
"ui.viewport_status_clear_hint": "Desactivar {name} · Esc",
|
||||
|
||||
+118
-12
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "מלבני",
|
||||
"Rectangular Tube": "צינור מלבני",
|
||||
"Reference face": "פאת ייחוס",
|
||||
"Reference plane": "מישור ייחוס",
|
||||
"Relief Depth": "עומק הקלה",
|
||||
"Relief Width": "רוחב הקלה",
|
||||
"Remove Tools": "הסרת הכלים",
|
||||
@@ -494,7 +495,7 @@
|
||||
"Rename Folder": "שינוי שם תיקייה",
|
||||
"Render Stage": "שלב הרינדור",
|
||||
"Reopen designs": "פתיחה מחדש של תכנים",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "פתיחה מחדש של התכנים שהיו פתוחים כש־SolidCraft נסגרה בפעם האחרונה",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "פתיחה מחדש של התכנונים שהיו פתוחים בפעם הקודמת",
|
||||
"Replace existing library file": "החלפת קובץ הספרייה הקיים",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "דיווח על באג — בדיקת צילום מסך ופתיחת טיוטה ב־GitHub",
|
||||
"Report copied. Keep this text to submit on GitHub when you are online.": "הדיווח הועתק. יש לשמור את הטקסט כדי לשלוח אותו ב-GitHub כשהחיבור לאינטרנט יהיה זמין.",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "חתך",
|
||||
"Section (optional)": "חתך (אופציונלי)",
|
||||
"Section position": "מיקום החתך",
|
||||
"Sections": "חתכים",
|
||||
"Sections (one sketch each)": "חתכים (סקיצה אחת לכל אחד)",
|
||||
"Select Other": "בחירת אחר",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "בחירת אחר: החזקת הכפתור השמאלי ללא תזוזה על המודל (או לחיצה ימנית › בחירת אחר…) מציגה את כל מה שמתחת לסמן; לחיצה נוספת באותה נקודה בוחרת את הפריט הבא שם. לחיצה כפולה על הכפתור האמצעי חוזרת לתצוגת הבית (העדפות › ניווט).",
|
||||
@@ -793,6 +795,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (ההצמדה אבדה)",
|
||||
"browser.sketch_editing": "{name} (בעריכה)",
|
||||
"browser.sketch_name": "סקיצה{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "כבר בחלק שלהם",
|
||||
"browser.sketches_to_parts_design_body": "על גוף של התכן עצמו",
|
||||
"browser.sketches_to_parts_moved": "{moved} שרטוטים הועברו לחלק שעליו הם נמצאים",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "על פאה ללא הצמדה מדויקת",
|
||||
"browser.sketches_to_parts_not_on_face": "לא על פאה של חלק",
|
||||
"browser.sketches_to_parts_part_missing": "החלק שלהם כבר לא קיים",
|
||||
"browser.sketches_to_parts_result": "{moved} שרטוטים הועברו לחלק שעליו הם נמצאים; {stayed} נשארו: {why}",
|
||||
"browser.units": "יחידות: {units}",
|
||||
"cam.flat": "פאה שטוחה",
|
||||
"cam.follower": "עוקב",
|
||||
@@ -852,6 +861,7 @@
|
||||
"cmd.component_extract": "חילוץ לקובץ",
|
||||
"cmd.component_finish_edit": "סיום עריכה",
|
||||
"cmd.component_from_bodies": "יצירת רכיבים מגופים",
|
||||
"cmd.component_hide_history": "הסתר היסטוריית רכיב",
|
||||
"cmd.component_insert_design": "הוספת תכן",
|
||||
"cmd.component_links": "רשימת קישורים",
|
||||
"cmd.component_list": "רשימת רכיבים",
|
||||
@@ -860,6 +870,7 @@
|
||||
"cmd.component_move_bodies": "העברת גופים לרכיב",
|
||||
"cmd.component_move_features": "העברת פיצ'רים לרכיב",
|
||||
"cmd.component_move_sketches": "העברת סקיצות לרכיב",
|
||||
"cmd.component_move_sketches_to_parts": "העברת שרטוטים לחלקים שלהם",
|
||||
"cmd.component_paste_from_design": "הדבק מעיצוב אחר",
|
||||
"cmd.component_paste_new": "הדבקה כחדש",
|
||||
"cmd.component_properties": "תכונות פיזיקליות של רכיב",
|
||||
@@ -867,6 +878,7 @@
|
||||
"cmd.component_rename": "שינוי שם רכיב",
|
||||
"cmd.component_replace_with_link": "החלפה בקישור",
|
||||
"cmd.component_revert_position": "החזרת מיקום",
|
||||
"cmd.component_show_history": "הצג היסטוריית רכיב",
|
||||
"cmd.component_unlock": "ביטול נעילת התוכן",
|
||||
"cmd.component_update_links": "עדכון קישורים",
|
||||
"cmd.config_activate": "הפעלת תצורה",
|
||||
@@ -877,6 +889,7 @@
|
||||
"cmd.config_rule": "כלל תצורה",
|
||||
"cmd.config_rules": "כללי תצורה",
|
||||
"cmd.config_table": "הצגת טבלת תצורות",
|
||||
"cmd.construct.plane.axis_point": "מישור דרך ציר ונקודה",
|
||||
"cmd.construct_axis_edge": "ציר דרך מקצוע",
|
||||
"cmd.construct_axis_normal_to_face": "ציר ניצב לפאה",
|
||||
"cmd.construct_axis_through_cylinder": "ציר דרך גליל/חרוט/טורוס",
|
||||
@@ -925,6 +938,11 @@
|
||||
"cmd.explode_show": "הצגת תצוגה מפוצצת",
|
||||
"cmd.expr_evaluate": "חישוב ביטוי",
|
||||
"cmd.face_delete": "מחיקת פאה",
|
||||
"cmd.file.export_3mf": "ייצוא 3MF",
|
||||
"cmd.file.export_obj": "ייצוא OBJ",
|
||||
"cmd.file.export_step": "ייצוא STEP",
|
||||
"cmd.file.export_stl": "ייצוא STL",
|
||||
"cmd.file.export_urdf": "ייצוא URDF",
|
||||
"cmd.file_export": "ייצוא",
|
||||
"cmd.file_insert_mesh": "הוספת רשת משולשים",
|
||||
"cmd.file_insert_step": "הוספת STEP",
|
||||
@@ -942,6 +960,7 @@
|
||||
"cmd.inspect_colored_components": "הפעלת צבעי רכיבים",
|
||||
"cmd.inspect_colored_components_disable": "ביטול צבעי רכיבים",
|
||||
"cmd.inspect_compare": "השוואת גופים",
|
||||
"cmd.inspect_compare_picture": "השוואה לתמונת ייחוס",
|
||||
"cmd.inspect_continuity": "ניתוח רציפות",
|
||||
"cmd.inspect_curvature_comb": "ניתוח מסרק עקמומיות",
|
||||
"cmd.inspect_curvature_map": "ניתוח מפת עקמומיות",
|
||||
@@ -1032,6 +1051,9 @@
|
||||
"cmd.pointcloud_edit": "עריכת ענן נקודות",
|
||||
"cmd.pointcloud_insert": "הוספת ענן נקודות",
|
||||
"cmd.pointcloud_list": "ענני נקודות",
|
||||
"cmd.render.compare.divider": "הזז את מחיצת ההשוואה של הרינדור",
|
||||
"cmd.render.result.discard": "השלך תוצאת רינדור",
|
||||
"cmd.render.result.select": "בחר תוצאת רינדור",
|
||||
"cmd.render_aging_describe": "הזדקנות משטח בשכבות",
|
||||
"cmd.render_backends": "מנועי הדמיה",
|
||||
"cmd.render_batch": "רינדור כל הצילומים",
|
||||
@@ -1195,6 +1217,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "ספליין נקודות בקרה (מעלה 5)",
|
||||
"cmd.sketch_spline_fit_point": "ספליין נקודות התאמה",
|
||||
"cmd.sketch_spline_insert_point": "הוספת נקודה לספליין",
|
||||
"cmd.sketch_spline_remove_point": "הסרת נקודה מהספליין",
|
||||
"cmd.sketch_spline_reverse": "היפוך כיוון הספליין",
|
||||
"cmd.sketch_spun_profile": "פרופיל סיבובי",
|
||||
"cmd.sketch_toggle_diameter": "מעבר בין רדיוס לקוטר",
|
||||
@@ -1245,6 +1268,7 @@
|
||||
"cmd.timeline_rollback": "הזזת סמן ציר הזמן",
|
||||
"cmd.timeline_stop": "עצירת ציר הזמן",
|
||||
"cmd.timeline_suppress": "השבתת פיצ'ר",
|
||||
"cmd.ui.assistant": "הצגה או הסתרה של העוזר",
|
||||
"cmd.view.render_browser": "דפדפן רינדור",
|
||||
"cmd.view.render_dock": "הזז את עגינת הרינדור",
|
||||
"cmd.view.render_focus": "תצוגת מיקוד רינדור",
|
||||
@@ -1279,9 +1303,11 @@
|
||||
"cmd.view_tool": "כלי ניווט",
|
||||
"cmd.view_top": "תצוגה עליונה",
|
||||
"cmd.view_visual_style": "סגנון תצוגה",
|
||||
"color.blue": "כחול",
|
||||
"color.copy": "לחיצה מעתיקה את ערכי הצבע",
|
||||
"color.edit": "לחיצה לעריכת הצבע",
|
||||
"color.gamma": "ערכי הצבע מוצגים במרחב גמא 0-255",
|
||||
"color.green": "ירוק",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "Enter #rrggbb or #rrggbbaa; six digits preserve opacity",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1289,6 +1315,7 @@
|
||||
"color.linear": "ערכי הצבע מוצגים במרחב לינארי 0-1",
|
||||
"color.recent": "צבעים אחרונים",
|
||||
"color.recent_help": "צבעים שבוחרים בכל שדה צבע מופיעים כאן",
|
||||
"color.red": "אדום",
|
||||
"color.saturation": "רוויה (אחוזים)",
|
||||
"color.saturation_value": "רוויה ובהירות",
|
||||
"color.selected": "הצבע שנבחר",
|
||||
@@ -1296,6 +1323,8 @@
|
||||
"color.step_up": "צעד למעלה",
|
||||
"color.value": "ערך, הבהירות (אחוזים)",
|
||||
"config.column_filter_empty": "אין עמודות תואמות",
|
||||
"config.rule_parameter_columns": "מוצגים כאן רק פרמטרים שיש להם עמודה בטבלה. הוסיפו עמודה דרך \"הוספת עמודה\" (+) כדי להשתמש בפרמטר כאן.",
|
||||
"config.rule_suppression_columns": "מוצגים כאן רק פיצ'רים שיש להם עמודת \"השבתה\" בטבלה. הוסיפו עמודה דרך \"הוספת עמודה\" (+) כדי לשלוט בפיצ'ר כאן.",
|
||||
"contact.components_count": "{count} רכיבים",
|
||||
"contact.enabled_label": "מופעל",
|
||||
"coupling.gear_1": "גלגל שיניים 1",
|
||||
@@ -1344,7 +1373,11 @@
|
||||
"dialog.pattern_sketch_not_object": "בחרו גופים או פיצ'רים: סקיצה אינה אף אחד מהם. חזרו על העקומות שלה בעזרת המערך או השיקוף של הסקיצה עצמה, או בצעו לה שליפה קודם.",
|
||||
"dialog.select": "↖ בחירה",
|
||||
"dialog.selected_count": "↖ {count} נבחרו",
|
||||
"dialog.selection_constraint": "אילוץ {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · עקומה {id}",
|
||||
"dialog.selection_guidance": "יש לבחור קלט כדי לשנות אותו. לחיצה על אובייקט שנבחר מסירה אותו; גם Ctrl/Cmd+לחיצה מחליפה את מצבו. לאחר בדיקת התצוגה המקדימה יש ללחוץ על אישור.",
|
||||
"dialog.selection_midpoint": "נקודת אמצע · {id}",
|
||||
"dialog.selection_profile": "{sketch} · פרופיל {number}",
|
||||
"dialog.selection_step": "{step} מתוך {total}: בחירת {input}",
|
||||
"dialog.step": "שלב",
|
||||
"document.changed_on_disk": "{name} השתנה בדיסק",
|
||||
@@ -1427,6 +1460,7 @@
|
||||
"help.construct.axis.two_points": "יצירת ציר עזר דרך שתי נקודות, קודקודים או נקודות סקיצה. מתאים לציר שעובר בין שני מיקומים ידועים.",
|
||||
"help.construct.plane.along_path": "יצירת מישור עזר ניצב למקצוע או לעקומה, במיקום לאורכם. משמש לשרטוט פרופיל לסחיפה או חתך של מסלול.",
|
||||
"help.construct.plane.at_angle": "יצירת מישור עזר מוטה בזווית ממישור או מפאה, סביב ציר או מקצוע. מתאים לסקיצות משופעות, כמו תמוכה בזווית.",
|
||||
"help.construct.plane.axis_point": "יוצר מישור עזר המכיל ציר ונקודה שאינה עליו. יש לבחור ציר ראשי או ציר עזר, קצה ישר או קו סקיצה, ואז קודקוד, נקודת סקיצה או נקודת עזר. נקודה על הציר אינה מגדירה מישור יחיד.",
|
||||
"help.construct.plane.midplane": "יצירת מישור עזר באמצע בין שני מישורים או פאות שטוחות, או חוצה אותם במקום שבו הם נפגשים בזווית. משמש כמישור סימטריה לשיקוף.",
|
||||
"help.construct.plane.offset": "יצירת מישור עזר מקביל למישור או לפאה שטוחה, במרחק נתון או דרך נקודה. מתאים לשרטוט בגובה קבוע מעל פאה.",
|
||||
"help.construct.plane.perpendicular": "יצירת מישור עזר שמכיל קו או מקצוע וניצב למישור או לפאה. מתאים לשרטוט זקוף על המודל לאורך מקצוע.",
|
||||
@@ -1549,7 +1583,7 @@
|
||||
"help.sketch.constrainer": "מחיל את האילוץ שעקומה אחת או שתיים שנבחרו כמעט מקיימות, כמו אופקי, מקביל, משיק או שווה. מתאים לסידור סקיצה שנוצרה ביד, יחס אחד בכל פעם.",
|
||||
"help.sketch.constraint.coincident": "גורם לנקודה להתלכד עם נקודה אחרת, עם עקומה או עם ציר הסקיצה. מתאים לסגירת פערים בפרופיל או להנחת נקודה על ישר.",
|
||||
"help.sketch.constraint.collinear": "גורם לשני ישרים לשכב על ישר אחד. מתאים ליישור קצוות מקוטעים, כמו שני צדי מגרעת.",
|
||||
"help.sketch.constraint.concentric": "נותן לשני מעגלים או קשתות אותו מרכז. מתאים לטבעות, לשקעי ברגים ולצירים עם מדרגות.",
|
||||
"help.sketch.constraint.concentric": "מעניק לשני מעגלים או קשתות מרכז משותף, או ממקם את מרכזו של מעגל או קשת בנקודה בסקיצה, כולל הראשית. אפשר לבחור את שני הפריטים בכל סדר.",
|
||||
"help.sketch.constraint.curvature": "גורם לשתי עקומות שנפגשות בנקודת קצה להמשיך באותה עקמומיות (G2). מתאים כשחיבור משיק אינו חלק מספיק, למשל על משטחים גלויים.",
|
||||
"help.sketch.constraint.equal": "גורם לישרים להיות באותו אורך, ולמעגלים ולקשתות באותו רדיוס. מתאים כדי שמידה אחת תניע כמה מאפיינים תואמים.",
|
||||
"help.sketch.constraint.fix": "מקבע נקודות או עקומות במקומן, או משחרר אותן שוב. מתאים לגאומטריה שאסור שתזוז בגרירה או בפתרון הסקיצה.",
|
||||
@@ -1803,6 +1837,8 @@
|
||||
"joint_move.blocked_hint": "בדקו את סוג המפרק (למשל מפרק קשיח), את קיבוע החלק, את מגבלות המפרק ואת הערכים הנשלטים על ידי פרמטרים.",
|
||||
"joint_move.blocked_title": "אי אפשר להזיז את החלק הזה",
|
||||
"joint_move.coarse": "צעד סיבוב קרוב לציר (°)",
|
||||
"joint_move.conflict": "אחד המפרקים של החלק הזה מתנגש עם האחרים: {joint}.",
|
||||
"joint_move.conflict_hint": "ערכו, השביתו או מחקו את המפרק המתנגש כדי שהמפרקים האחרים יוכלו למקם את החלק.",
|
||||
"joint_move.distance": "מרחק מנקודת ההתחלה",
|
||||
"joint_move.done": "סיום",
|
||||
"joint_move.fine": "צעד סיבוב רחוק מהציר (°)",
|
||||
@@ -1902,6 +1938,8 @@
|
||||
"motion.track_name": "רצועה {number}",
|
||||
"nav.alias.help": "Shift+Alt וגרירה שמאלית מסובבים, Shift+Alt וגרירה אמצעית מזיזים, Shift+Alt וגרירה ימנית מבצעים זום; גם גרירה אמצעית מזיזה וגרירה ימנית מסובבת",
|
||||
"nav.blender.help": "גרירה אמצעית מסובבת, Shift וגרירה אמצעית מזיזים, Ctrl וגרירה אמצעית מבצעים זום; גרירה ימנית לא מזיזה את המצלמה",
|
||||
"nav.creo.help": "גרירה בלחצן האמצעי מסובבת את התצוגה, Shift והלחצן האמצעי מזיזים אותה, Ctrl וגרירה אנכית בלחצן האמצעי משנים את הזום (גרירה מטה להגדלה); גלילת הגלגלת משנה גם היא את הזום",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "העכבר והעכבר התלת־ממדי מסובבים דרך שני הקטבים ושומרים על הגלגול. הצמדת הצירים עם Shift מושבתת.",
|
||||
"nav.free_orbit.label": "סיבוב חופשי",
|
||||
"nav.free_orbit.snap_help": "העכבר והעכבר התלת־ממדי מסובבים דרך שני הקטבים ושומרים על הגלגול. יש להחזיק את Shift בזמן גרירת סיבוב כדי להצמיד את כיוון התצוגה ואת הגלגול.",
|
||||
@@ -2054,12 +2092,19 @@
|
||||
"parts.family.button_head_cap_screw": "בורג ראש כפתור עם שקע משושה",
|
||||
"parts.family.dowel_pin": "פין גלילי",
|
||||
"parts.family.hex_bolt": "בורג ראש משושה",
|
||||
"parts.family.hex_cap_screw_inch": "בורג ראש משושה (אינץ')",
|
||||
"parts.family.hex_nut": "אום משושה",
|
||||
"parts.family.hex_nut_inch": "אום משושה (אינץ')",
|
||||
"parts.family.set_screw": "בורג אלן ללא ראש, קצה שטוח",
|
||||
"parts.family.socket_head_cap_screw": "בורג ראש גלילי עם משושה פנימי",
|
||||
"parts.family.washer": "דסקית שטוחה",
|
||||
"parts.family.washer_sae": "דסקית שטוחה, SAE (צרה)",
|
||||
"parts.family.washer_uss": "דסקית שטוחה, USS (רחבה)",
|
||||
"parts.holes": "חורים (אופציונלי)",
|
||||
"parts.placement_hint": "בחרו חור אחד או יותר כדי להושיב חלק בכל אחד, או לחצו אישור כדי למקם אותו בראשית",
|
||||
"parts.system": "מערכת יחידות",
|
||||
"parts.system.inch": "אינץ'",
|
||||
"parts.system.metric": "מטרי",
|
||||
"pattern.alignment_2": "יישור 2",
|
||||
"pattern.alignment_hint": "הספירה כוללת את המקור. מרכוז מאזן את העותקים; היפוך בוחר את הצד שמקבל את העותק הנוסף כשהסך זוגי.",
|
||||
"pattern.direction_1": "כיוון 1",
|
||||
@@ -2121,6 +2166,8 @@
|
||||
"prefs.button_left": "גרירה שמאלית",
|
||||
"prefs.button_middle": "גרירה אמצעית",
|
||||
"prefs.button_right": "גרירה ימנית",
|
||||
"prefs.carousel_next": "הבא",
|
||||
"prefs.carousel_previous": "הקודם",
|
||||
"prefs.choose_font": "בחירת קובץ גופן…",
|
||||
"prefs.click": "לחיצה",
|
||||
"prefs.contact_shading": "הצללת מגע רכה",
|
||||
@@ -2133,6 +2180,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "משטחים מעוקלים",
|
||||
"prefs.curves": "משטחים מעוקלים",
|
||||
"prefs.cut_preview_tint": "הצגת חומר שהוסר באדום",
|
||||
"prefs.cut_preview_tint_help": "תצוגה מקדימה ממלאת את החומר שהפעולה מסירה, ואת הצורה שהיא מחליפה, ברוח אדומה שקופה. כשהאפשרות כבויה המילויים האלה שקופים לחלוטין ומצוירים רק קווי המתאר שלהם.",
|
||||
"prefs.dark_parts_shading": "שמירה על צורת חלקים כהים",
|
||||
"prefs.dark_parts_shading_help": "הצללת התצוגה באור ליניארי עם תאורת שמיים רכה, כך שפאות כהות שומרות על מעבר גוונים במקום להפוך לשחורות",
|
||||
"prefs.dead_zone_note": "הפס המקווקו הוא האזור המת: תנועה בתוכו לא נלקחת בחשבון.",
|
||||
@@ -2157,7 +2206,19 @@
|
||||
"prefs.font_sample": "טקסט לדוגמה בגופן · Ø12.50 mm · 45.0° · 0123456789",
|
||||
"prefs.font_unreadable": "לא ניתן היה לקרוא את הגופן שנבחר.",
|
||||
"prefs.general": "כללי",
|
||||
"prefs.general_note": "שפה, שמירה ועדכונים.",
|
||||
"prefs.general.active": "פעיל",
|
||||
"prefs.general.crash_reports": "דוחות קריסה",
|
||||
"prefs.general.edited": "נערך",
|
||||
"prefs.general.held": "נכלל",
|
||||
"prefs.general.import": "ייבוא...",
|
||||
"prefs.general.last_check": "בדיקה אחרונה: {time}",
|
||||
"prefs.general.minutes": "דקות",
|
||||
"prefs.general.name_setup": "תנו שם להגדרה זו",
|
||||
"prefs.general.profile_name": "שם הפרופיל",
|
||||
"prefs.general.save_new": "שמירה כחדש",
|
||||
"prefs.general.step_import": "STEP וייבוא",
|
||||
"prefs.general.this_computer": "מחשב זה",
|
||||
"prefs.general_note": "פרופילים, שפה, קבצים ועדכונים.",
|
||||
"prefs.gesture_orbit": "סיבוב מבט",
|
||||
"prefs.gesture_pan": "הזזת מבט",
|
||||
"prefs.gesture_zoom": "זום",
|
||||
@@ -2191,6 +2252,8 @@
|
||||
"prefs.interface_language": "שפת הממשק",
|
||||
"prefs.interface_note": "איך SolidCraft נראה והיכן חלקיו ממוקמים.",
|
||||
"prefs.interval_minutes": "{n} דק׳",
|
||||
"prefs.invert_curved_roll_arrows": "היפוך חצי הגלילה המעוגלים",
|
||||
"prefs.invert_curved_roll_arrows_note": "שני החצים המעוגלים בפינת הקובייה מסובבים את התצוגה במישור המסך. פועל: החץ עם כיוון השעון מסובב נגד כיוון השעון, והחץ נגד כיוון השעון מסובב עם כיוון השעון. כבוי: כל חץ מסובב את התצוגה לפי העקום שלו.",
|
||||
"prefs.invert_navigator_arrows": "היפוך חצי סיבוב קוביית התצוגה",
|
||||
"prefs.invert_navigator_arrows_note": "החצים סביב הקובייה מחוויים את המצלמה לכיוון שהחץ מצביע, כמו ב-Fusion ו-Inventor: החץ שמשמאל מצביע ימימה ומניע את המצלמה ימימה. כבוי: החץ מסובב את הקובייה אל הצד שבו הוא יושב, כך שחץ שמאל מניע את המצלמה שמאלה. חצי הגלילה המעוגלים אינם מושפעים.",
|
||||
"prefs.keep_work_on_tab_switch": "השארת תיבות דו־שיח וכלים פתוחים במעבר בין עיצובים",
|
||||
@@ -2212,10 +2275,10 @@
|
||||
"prefs.logo_accent": "צבע הדגשה",
|
||||
"prefs.logo_theme": "צבעי ערכת הנושא",
|
||||
"prefs.max_file_size": "גודל קובץ מרבי לפתיחה",
|
||||
"prefs.max_file_size_design": "קובצי תכן",
|
||||
"prefs.max_file_size_design": "הקובץ הגדול ביותר לפתיחה: תכנון",
|
||||
"prefs.max_file_size_flag": "ההפעלה הנוכחית החלה עם --max-file-size {size}, שחל עד סגירת SolidCraft. הגדרות אלה חלות בהפעלה ללא אפשרות זו.",
|
||||
"prefs.max_file_size_note": "ברירות מחדל: 512 MB ו־256 MB; לכל היותר 16 GB. פתיחת קובץ דורשת זיכרון בנפח גדול פי כמה מגודלו. תכן ששומר ייבוא גדול עשוי לדרוש מגבלה גבוהה יותר לקובצי תכן כדי לשמור ולפתוח אותו מחדש.",
|
||||
"prefs.max_file_size_step": "קובצי STEP וייבוא",
|
||||
"prefs.max_file_size_step": "הקובץ הגדול ביותר לפתיחה: STEP וייבוא",
|
||||
"prefs.max_file_size_web": "יישום הדפדפן פותח קובצי STEP וייבוא עד 512 MB וקובצי תכן עד 256 MB: לשונית דפדפן אינה יכולה להכיל קבצים גדולים יותר. ביישום למחשב ניתן להעלות מגבלות אלה.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "סמלים גדולים",
|
||||
@@ -2232,7 +2295,7 @@
|
||||
"prefs.navigator_fit_note": "לחיצה על פאה, פינה או חץ בקובייה, או על ציר בגיזמו, גם מביטה אל מרכז המודל מהמרחק האחד שמכיל אותו מכל צד (הכדור החוסם שלו). תצוגת בית משתמשת באותו זום ובאותו מרכז כל עוד זה פעיל, כך שתצוגות שנבחרות אחריה רק מסתובבות. כבוי: התצוגה מסתובבת במקומה ושומרת על היעד ועל הזום.",
|
||||
"prefs.network_host_remove": "הסרה",
|
||||
"prefs.network_locations": "התרת מיקומי רשת בתכנים",
|
||||
"prefs.network_locations_note": "כבוי: קבצים מקושרים של תכן במחשב אחר (\\\\server\\share) נפתחים רק מהשרתים הרשומים כאן, או אחרי שמתירים שרת כשנשאלים.",
|
||||
"prefs.network_locations_note": "לארגונים השומרים תכנונים בשרתי קבצים. כשהאפשרות כבויה: רק שרתים שאישרתם.",
|
||||
"prefs.network_locations_section": "מיקומי רשת",
|
||||
"prefs.not_on_gpu": "לא זמין במעבד הגרפי הזה",
|
||||
"prefs.on": "פועל",
|
||||
@@ -2250,15 +2313,17 @@
|
||||
"prefs.placement": "מיקום",
|
||||
"prefs.prefer_free_sketch_points": "העדפת נקודות סקיצה ניתנות להזזה כשנקודות חופפות",
|
||||
"prefs.preview_choices": "תצוגה מקדימה של הבחירות שלך",
|
||||
"prefs.preview_tint_new_faces": "צביעת משטח חדש בתצוגה מקדימה",
|
||||
"prefs.preview_tint_new_faces_help": "תצוגה מקדימה של פעולה שמשנה גוף יכולה לצבוע את המשטח שנוסף בצבע התצוגה המקדימה. כשהאפשרות כבויה הגוף מצויר בצבעו שלו.",
|
||||
"prefs.print_face_highlight": "הדגשת פאת ההדפסה",
|
||||
"prefs.print_face_highlight_tip": "עד כמה חזק צובעים ומקיפים את פאת ההדפסה של גוף בתצוגה. 0 % אינו מציג הדגשה.",
|
||||
"prefs.profile_active": "פרופיל פעיל",
|
||||
"prefs.profile_applied": "הפרופיל הוחל.",
|
||||
"prefs.profile_changed": "ההגדרות השתנו מאז שהפרופיל נשמר. „שמירה“ מכניסה אותן לפרופיל.",
|
||||
"prefs.profile_deleted": "הפרופיל נמחק.",
|
||||
"prefs.profile_export": "ייצוא…",
|
||||
"prefs.profile_export": "ייצוא",
|
||||
"prefs.profile_exported": "הפרופיל יוצא.",
|
||||
"prefs.profile_imported": "הפרופיל יובא. בחרו בו תחת „פרופיל פעיל“ כדי לעבור אליו.",
|
||||
"prefs.profile_imported": "הפרופיל יובא.",
|
||||
"prefs.profile_name_hint": "שם הפרופיל, למשל מחשב נייד",
|
||||
"prefs.profile_rename": "שינוי שם",
|
||||
"prefs.profile_renamed": "שם הפרופיל שונה.",
|
||||
@@ -2278,12 +2343,13 @@
|
||||
"prefs.render_previous_geometry_budget": "שימוש בתקציב הגאומטריה הקודם לתצוגה מקדימה של רינדור",
|
||||
"prefs.render_previous_geometry_budget_help": "שחזור מגבלת הגאומטריה הקבועה הקודמת (512 MiB במחשב שולחני של 64 סיביות, 128 MiB בפלטפורמות אחרות) במקום התקציב המבוסס על ההתקן.",
|
||||
"prefs.reports_saved": "{n} שמורים",
|
||||
"prefs.restore_page_defaults": "שחזור ברירות המחדל של עמוד זה",
|
||||
"prefs.ribbon_click_ends_tool": "לחיצה על הלחצן של הכלי הפעיל מסיימת אותו",
|
||||
"prefs.ribbon_click_ends_tool_note": "לחיצה על לחצן הסרט המודגש של כלי השרטוט שבשימוש מסיימת את הכלי, כך שלחיצות בתצוגה בוחרות שוב. כבוי: הלחיצה מפעילה את הכלי מחדש.",
|
||||
"prefs.ribbon_reveal": "הצגת תוצאות חיפוש בסרט",
|
||||
"prefs.ribbon_reveal_note": "מעבר עם הסמן מעל תוצאה בחיפוש פקודות מראה היכן הפקודה נמצאת בסרט: הלשונית, הכפתור או השורה שלה בתפריט הנפתח של לוח. פקודה שהופעלה מהחיפוש מהבהבת שם, ופקודה פעילה מתפריט נפתח מסמנת את הלוח שלה.",
|
||||
"prefs.right_click": "לחיצה ימנית",
|
||||
"prefs.saving": "שמירה אוטומטית ושחזור",
|
||||
"prefs.saving": "הפעלה ושמירה",
|
||||
"prefs.scale_note": "גם Ctrl + / Ctrl − בחלון. הכפתורים בגודל 28 px ב־100%.",
|
||||
"prefs.scale_text": "קנה מידה וטקסט",
|
||||
"prefs.search": "חיפוש הגדרות",
|
||||
@@ -2547,6 +2613,10 @@
|
||||
"render.base_color": "צבע בסיס · sRGB",
|
||||
"render.base_metallic": "שיעור מתכתיות",
|
||||
"render.base_opaque_only": "עורך זה שומר על משמעות של גוף אטום. להעברה אופטית יש להשתמש בפקדי הזכוכית המפורשים.",
|
||||
"render.batch_all_shots": "כל הצילומים ({count})",
|
||||
"render.batch_empty": "שמור צילום לפני רינדור כל הצילומים.",
|
||||
"render.batch_limit": "רינדור כל הצילומים מוגבל ל-32 צילומים.",
|
||||
"render.batch_status": "{running} רצים · {waiting} ממתינים",
|
||||
"render.bloom_enable": "זוהר בתצוגה",
|
||||
"render.bloom_help": "זוהר יצירתי לפלט בלבד מהבהרות ליניאריות חיוביות, המתפשט בשלושה מעברי תיבה מנורמלים. הרדיוס בפיקסלים של הפלט. נוסף באור תצוגה ליניארי לפני sRGB; אינו עדשה, אינו אפקט נפחי ואינו שיפור התכנסות. ה־EXR הגולמי אינו משתנה.",
|
||||
"render.bloom_opaque": "זוהר דורש פלט אטום. PNG שקוף נדחה, ואינו משוטח בשקט.",
|
||||
@@ -2657,6 +2727,13 @@
|
||||
"render.choose_look": "בחר קודם מראה במראות",
|
||||
"render.chromatic_offset": "היסט כרומטי בקצוות",
|
||||
"render.clear_stage": "הסרת הבמה השמורה",
|
||||
"render.compare_divider": "מחיצת השוואה",
|
||||
"render.compare_live": "חי",
|
||||
"render.compare_live_preview": "תצוגה חיה",
|
||||
"render.compare_render": "רינדור {number}",
|
||||
"render.compare_side": "רינדור {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} שנ׳ · {renderer} · נקלט בגרסה {revision}",
|
||||
"render.compare_swap": "החלף",
|
||||
"render.copy_request": "העתקת JSON",
|
||||
"render.cpu_aa_help": "דגימות מרחביות: יותר דגימות משפרות קצוות ומאריכות את זמן ההדמיה.",
|
||||
"render.cpu_area_add": "הוספת אור מלבני ב־CPU",
|
||||
@@ -3406,6 +3483,10 @@
|
||||
"render.result_retained": "מוצגת התמונה הגמורה האחרונה; הדמיה חדשה יותר לא החליפה אותה.",
|
||||
"render.result_sequence": "נקודת ביקורת",
|
||||
"render.result_title": "תוצאת הדמיה",
|
||||
"render.results_choose": "תוצאות שנשמרו",
|
||||
"render.results_label": "רינדור {number} · {shot} · {seconds} שנ׳",
|
||||
"render.results_stale_label": "{result} [ישן]",
|
||||
"render.results_unnamed": "תמונה",
|
||||
"render.return_design": "חזרה לתכן",
|
||||
"render.review_stale": "סקירה זו אינה עדכנית. יש לפתור או לבדוק שוב לפני ההחלה.",
|
||||
"render.ribbon_aging": "הזדקנות",
|
||||
@@ -4066,6 +4147,7 @@
|
||||
"ui.agent_chat_auth_terminal": "יש להריץ את זה במסוף ואז ללחוץ שוב על התחברות:",
|
||||
"ui.agent_chat_auth_title": "התחברות לסוכן",
|
||||
"ui.agent_chat_cancel": "ביטול",
|
||||
"ui.agent_chat_cancel_timeout": "הסוכן לא הגיב לבקשת העצירה ונותק. יש להתחבר מחדש כדי להמשיך.",
|
||||
"ui.agent_chat_cannot_start": "מותקן, אך אינו מופעל: {error}",
|
||||
"ui.agent_chat_changes": "שינויים: {n}, צעד ביטול אחד",
|
||||
"ui.agent_chat_changes_n": "{n} שינויים",
|
||||
@@ -4075,11 +4157,13 @@
|
||||
"ui.agent_chat_command": "פקודת הסוכן",
|
||||
"ui.agent_chat_connect": "התחברות",
|
||||
"ui.agent_chat_copy": "העתקה",
|
||||
"ui.agent_chat_copy_message": "העתק הודעה",
|
||||
"ui.agent_chat_ctx_bodies": "גופים",
|
||||
"ui.agent_chat_ctx_elements": "אלמנטים",
|
||||
"ui.agent_chat_ctx_features": "פיצ'רים",
|
||||
"ui.agent_chat_ctx_more": "+{n} נוספים",
|
||||
"ui.agent_chat_ctx_other": "אחר",
|
||||
"ui.agent_chat_custom_heading": "מודל מותאם אישית",
|
||||
"ui.agent_chat_custom_hint": "כל תוכנה שמדברת Agent Client Protocol דרך stdin ו־stdout. היא לא מורצת דרך מעטפת (shell).",
|
||||
"ui.agent_chat_denied": "נדחה",
|
||||
"ui.agent_chat_deny": "דחייה",
|
||||
@@ -4123,8 +4207,16 @@
|
||||
"ui.agent_chat_mode_all": "הכול",
|
||||
"ui.agent_chat_mode_ask": "שאלה",
|
||||
"ui.agent_chat_mode_edits": "עריכות",
|
||||
"ui.agent_chat_model_custom": "שימוש במותאם אישית: {model}",
|
||||
"ui.agent_chat_model_no_tools": "{model} לא יכול להשתמש בכלים של SolidCraft, ולכן הוא יכול רק לשוחח. יש לבחור מודל שתומך בכלים.",
|
||||
"ui.agent_chat_model_rejected": "הסוכן לא קיבל את {model}: {error}",
|
||||
"ui.agent_chat_model_search": "חיפוש מודלים…",
|
||||
"ui.agent_chat_model_tip": "המודל שבו הסוכן משתמש. הבחירה נשמרת עבור הסוכן הזה.",
|
||||
"ui.agent_chat_models_failed": "לא ניתן היה לטעון את רשימת המודלים: {error}",
|
||||
"ui.agent_chat_models_heading": "מודלים זמינים",
|
||||
"ui.agent_chat_models_listing": "טוען רשימת מודלים…",
|
||||
"ui.agent_chat_models_none": "נקודת הקצה אינה מציגה מודלים",
|
||||
"ui.agent_chat_models_not_a_list": "התשובה לא הייתה רשימת מודלים",
|
||||
"ui.agent_chat_new": "צ'אט חדש",
|
||||
"ui.agent_chat_new_confirm": "להתחיל צ'אט חדש? הצ'אט הנוכחי יימחק.",
|
||||
"ui.agent_chat_next_picture": "התמונה הבאה",
|
||||
@@ -4386,6 +4478,8 @@
|
||||
"ui.coming_later": "בקרוב",
|
||||
"ui.command_done": "{command} הושלם",
|
||||
"ui.command_palette_hint": "יש להקליד פקודה, למשל {example} — או ללחוץ S לחיפוש",
|
||||
"ui.components_from_bodies_hint": "יצירת רכיב לכל גוף שנבחר, בשם הגוף, תחת הרכיב הפעיל.",
|
||||
"ui.components_from_bodies_required": "יש לבחור גוף אחד לפחות.",
|
||||
"ui.concentric": "משותף מרכז",
|
||||
"ui.cone": "חרוט",
|
||||
"ui.conic_curve": "עקום חרוטי",
|
||||
@@ -4398,9 +4492,11 @@
|
||||
"ui.construction.contact_hint": "משיק: בחירת מעגל/קשת או ספליין/חתך חרוט פתוח ואז נקודת השקה (לעקומות חופשיות: קצוות בלבד)",
|
||||
"ui.construction.external_hint": "משיק: בחירת מעגל או קשת, נקודה חיצונית ואז הפתרון הרצוי",
|
||||
"ui.construction.free_hint": "ישר עזר: לחיצה על שתי נקודות; לחיצה ימנית להתחלה מחדש",
|
||||
"ui.construction.normal_needs_length": "קו אינסופי הוא תמיד קו עזר: הקלדת אורך משרטטת קו רגיל",
|
||||
"ui.construction.point_hint": "ישר עזר: לחיצה על נקודת המעבר",
|
||||
"ui.construction.reference_hint": "ישר עזר: בחירת ישר ואז נקודה; לחיצה ימנית להחלפת הייחוס",
|
||||
"ui.construction.ribbon_label": "קווי עזר",
|
||||
"ui.construction.side_hint": "ישר עזר: לחיצה על הצד שאליו הקו נמשך; לחיצה ימנית להתחלה מחדש",
|
||||
"ui.context_menu": "תפריט הקשר",
|
||||
"ui.continuity_g0": "G0 שבר",
|
||||
"ui.continuity_g1": "G1 משיק",
|
||||
@@ -4666,6 +4762,7 @@
|
||||
"ui.hideOrigin": "הסתרת ראשית",
|
||||
"ui.hidePlanes": "הסתרת מישורים",
|
||||
"ui.hideProfile": "הסתרת פרופיל",
|
||||
"ui.hide_component_history": "הסתר את ההיסטוריה שלו",
|
||||
"ui.hide_origin_by_default": "הסתרת הראשית כברירת מחדל",
|
||||
"ui.hide_origin_note": "תכנים חדשים מתחילים כשתיקיית הראשית מוסתרת. כל תכן שומר על הגדרת הראשית שלו אחרי שמציגים או מסתירים אותה.",
|
||||
"ui.history_back": "צעד אחד אחורה",
|
||||
@@ -4673,6 +4770,7 @@
|
||||
"ui.history_edge_hint": "לחיצה כפולה על הקצה",
|
||||
"ui.history_first": "מעבר להתחלה",
|
||||
"ui.history_forward": "צעד אחד קדימה",
|
||||
"ui.history_hide_hidden": "הסתר רכיבים מוסתרים",
|
||||
"ui.history_hide_names": "הסתרת שמות",
|
||||
"ui.history_last": "מעבר לסוף",
|
||||
"ui.history_marker_tip": "סמן ציר הזמן: יש לגרור כדי להחזיר את המודל לאחור; פיצ'רים חדשים נוספים כאן",
|
||||
@@ -4682,10 +4780,12 @@
|
||||
"ui.history_play_button_show": "הצגת כפתור ההפעלה",
|
||||
"ui.history_play_finish_action": "יש לסיים את הפעולה הנוכחית לפני הפעלת ציר הזמן",
|
||||
"ui.history_play_roll_back": "יש להזיז את סמן ציר הזמן לאחור לפני ההפעלה",
|
||||
"ui.history_show_hidden": "הצג רכיבים מוסתרים",
|
||||
"ui.history_show_names": "הצגת שמות",
|
||||
"ui.history_sketch_listed_under": "מופיעה תחת {feature}",
|
||||
"ui.history_title": "ציר הזמן",
|
||||
"ui.history_toggle_names": "הצגה או הסתרה של שמות הפיצ'רים",
|
||||
"ui.history_waits_for_editor": "ביטול, ביצוע מחדש וציר הזמן ממתינים עד שעורך הפעולה ייפתח",
|
||||
"ui.hole": "חור",
|
||||
"ui.hole_tap_type": "סוג הברגת חור",
|
||||
"ui.hole_type": "סוג חור",
|
||||
@@ -4701,6 +4801,7 @@
|
||||
"ui.insert_design.saved_file": "ב-{name} יש שינויים שלא נשמרו, ולכן נוסף הקובץ השמור שלו. שמרו אותו ואז בחרו עדכון קישורים כדי להכניס את השינויים.",
|
||||
"ui.insert_design.unsaved": "שינויים שלא נשמרו",
|
||||
"ui.inspect": "בדיקה",
|
||||
"ui.instances": "מופעים",
|
||||
"ui.intersect": "חיתוך משותף",
|
||||
"ui.isolate": "בידוד",
|
||||
"ui.join": "איחוד",
|
||||
@@ -4851,9 +4952,9 @@
|
||||
"ui.polygon": "מצולע",
|
||||
"ui.preferences": "העדפות",
|
||||
"ui.press_a_key": "יש ללחוץ על מקש…",
|
||||
"ui.print_face_body_not_sent": "הגוף של פאה זו אינו נשלח: הוסף אותו קודם לגופים",
|
||||
"ui.print_face_cleared": "{body}: פאת ההדפסה נוקתה",
|
||||
"ui.print_face_input": "פאת הדפסה",
|
||||
"ui.print_face_one_body": "פאת הדפסה נבחרת לגוף אחד: שלח את הגוף הזה לבדו כדי לשנות אותה",
|
||||
"ui.print_face_pick": "בחר פאה שטוחה אחת של גוף מוצק כדי להפוך אותה לפאת ההדפסה",
|
||||
"ui.print_face_set": "{body}: פאת ההדפסה הוגדרה; שליחה לסלייסר מניחה את הגוף עליה",
|
||||
"ui.profile": "פרופיל",
|
||||
@@ -4965,6 +5066,7 @@
|
||||
"ui.showDimension": "הצגת מידה",
|
||||
"ui.showOrigin": "הצגת ראשית",
|
||||
"ui.showPlanes": "הצגת מישורים",
|
||||
"ui.show_component_history": "הצג את ההיסטוריה שלו",
|
||||
"ui.show_constraints": "אילוצים",
|
||||
"ui.show_construction_geometries": "גאומטריית עזר",
|
||||
"ui.show_dimensions": "מידות",
|
||||
@@ -4996,7 +5098,7 @@
|
||||
"ui.slicer_no_response": "{slicer} לא הגיב בזמן. הקובץ כבר שמור ב־{path}: יש לשלוח אותו שוב אחרי שהסלייסר יופעל.",
|
||||
"ui.slicer_resend_hint": "שליחה חוזרת מחליפה את הקובץ שפתוח בסלייסר.",
|
||||
"ui.slicer_single_instance": "פתיחת קבצים בסלייסר שכבר פועל",
|
||||
"ui.slicer_single_instance_tip": "PrusaSlicer, SuperSlicer ו־Cura טוענים את הקובץ בחלון הפתוח במקום להפעיל חלון נוסף. סלייסרים אחרים מופעלים כרגיל. PrusaSlicer 3.0 עושה זאת תמיד: המפעיל שלו לא יכול לקבל קובץ בדרך אחרת.",
|
||||
"ui.slicer_single_instance_tip": "לסלייסרים שטוענים קובץ לחלון שכבר פועל.",
|
||||
"ui.slicer_starting": "מפעיל את {slicer}…",
|
||||
"ui.slicer_still_starting": "עדיין ממתין להפעלת {slicer}. שום דבר נוסף לא נשלח.",
|
||||
"ui.slicer_stl_fallback": "לא ניתן היה לכתוב את קובץ ה־3MF, ולכן זהו קובץ STL.",
|
||||
@@ -5081,12 +5183,15 @@
|
||||
"ui.view_angles": "זוויות תצוגה",
|
||||
"ui.view_angles_convention": "מעלות סביב הציר העליון. סבסוב 0° הוא התצוגה הקדמית, 90° שמאלית, −90° ימנית, 180° אחורית; עלרוד 90° הוא התצוגה העליונה; גלגול חיובי מסובב את המודל על המסך עם כיוון השעון.",
|
||||
"ui.view_angles_help": "מציג מתחת לגיזמו הכיוון את זוויות העלרוד, הסבסוב והגלגול של התצוגה במעלות ואת סוג ההטלה. לחצו על התצוגה כדי להקליד זוויות מדויקות.",
|
||||
"ui.view_angles_lock": "נעל את {axis}",
|
||||
"ui.view_angles_ortho": "אורתוגרפי",
|
||||
"ui.view_angles_persp": "פרספקטיבה",
|
||||
"ui.view_angles_pitch": "עלרוד",
|
||||
"ui.view_angles_readout": "עלרוד {pitch} · סבסוב {yaw} · גלגול {roll}",
|
||||
"ui.view_angles_roll": "גלגול",
|
||||
"ui.view_angles_unlock": "בטל נעילת {axis}",
|
||||
"ui.view_angles_yaw": "סבסוב",
|
||||
"ui.view_angles_zero": "הגדר את {axis} ל-0°",
|
||||
"ui.view_centre": "מרכז המבט",
|
||||
"ui.viewport_status_clear": "כיבוי {name}",
|
||||
"ui.viewport_status_clear_hint": "כיבוי {name} · Esc",
|
||||
@@ -5138,7 +5243,7 @@
|
||||
"update.available": "SolidCraft {version} זמין",
|
||||
"update.button": "עדכון",
|
||||
"update.button_version": "עדכון ל־{version}",
|
||||
"update.check_automatically": "בדיקת עדכונים אוטומטית",
|
||||
"update.check_automatically": "בדיקה בהפעלה ופעם ביום",
|
||||
"update.check_for_updates": "בדיקת עדכונים",
|
||||
"update.check_now": "בדיקה עכשיו",
|
||||
"update.check_unavailable": "בדיקת עדכונים אינה זמינה עבור התקנה זו.",
|
||||
@@ -5188,6 +5293,7 @@
|
||||
"web.open_link": "לפתוח את התכן המקושר?",
|
||||
"web.open_link_confirm": "יש שינויים שלא נשמרו. לפתוח את התכן המקושר בלשונית חדשה?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ בחירה · Enter / Tab השלמה · Esc סגירה",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ בחירה · Enter מחיל את הערך הזה",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ בחירה · Enter מחיל את מה שהוקלד · Esc סגירה",
|
||||
"⌘Enter continues on GitHub.": "⌘Enter ממשיך ב־GitHub."
|
||||
}
|
||||
|
||||
+71
-1
@@ -793,6 +793,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (pričvršćenje izgubljeno)",
|
||||
"browser.sketch_editing": "{name} (uređuje se)",
|
||||
"browser.sketch_name": "Skica{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "već u svojem dijelu",
|
||||
"browser.sketches_to_parts_design_body": "na tijelu samog dizajna",
|
||||
"browser.sketches_to_parts_moved": "Skice premještene u dio na kojem se nalaze: {moved}",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "na licu bez točnog pričvršćenja",
|
||||
"browser.sketches_to_parts_not_on_face": "ne na licu dijela",
|
||||
"browser.sketches_to_parts_part_missing": "njihov dio više ne postoji",
|
||||
"browser.sketches_to_parts_result": "Skice premještene u dio na kojem se nalaze: {moved}; ostale na mjestu: {stayed} ({why})",
|
||||
"browser.units": "Jedinice: {units}",
|
||||
"cam.flat": "Ravno lice",
|
||||
"cam.follower": "Pratitelj",
|
||||
@@ -860,6 +867,7 @@
|
||||
"cmd.component_move_bodies": "Premjesti tijela u komponentu",
|
||||
"cmd.component_move_features": "Premjesti značajke u komponentu",
|
||||
"cmd.component_move_sketches": "Premjesti skice u komponentu",
|
||||
"cmd.component_move_sketches_to_parts": "Premjesti skice u njihove dijelove",
|
||||
"cmd.component_paste_from_design": "Zalijepi iz drugog dizajna",
|
||||
"cmd.component_paste_new": "Zalijepi kao novo",
|
||||
"cmd.component_properties": "Fizička svojstva komponente",
|
||||
@@ -1195,6 +1203,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Spline kontrolnih točaka (stupanj 5)",
|
||||
"cmd.sketch_spline_fit_point": "Spline kroz točke",
|
||||
"cmd.sketch_spline_insert_point": "Dodaj točku splinea",
|
||||
"cmd.sketch_spline_remove_point": "Ukloni točku splinea",
|
||||
"cmd.sketch_spline_reverse": "Obrni smjer splinea",
|
||||
"cmd.sketch_spun_profile": "Rotirani profil",
|
||||
"cmd.sketch_toggle_diameter": "Prebaci polumjer / promjer",
|
||||
@@ -1287,8 +1296,14 @@
|
||||
"color.hex_hint": "#rrggbb",
|
||||
"color.hue": "Nijansa",
|
||||
"color.linear": "Prikaz vrijednosti boje u linearnom prostoru 0-1",
|
||||
"color.recent": "Nedavne boje",
|
||||
"color.recent_help": "Ovdje se pojavljuju boje koje odaberete u bilo kojem polju za boju",
|
||||
"color.saturation": "Zasićenost (postotak)",
|
||||
"color.saturation_value": "Zasićenost i vrijednost",
|
||||
"color.selected": "Odabrana boja",
|
||||
"color.step_down": "Smanji za korak",
|
||||
"color.step_up": "Povećaj za korak",
|
||||
"color.value": "Vrijednost, svjetlina (postotak)",
|
||||
"config.column_filter_empty": "Nema odgovarajućih stupaca",
|
||||
"contact.components_count": "Komponente: {count}",
|
||||
"contact.enabled_label": "omogućeno",
|
||||
@@ -1335,6 +1350,7 @@
|
||||
"dialog.dimension_name": "Kota: {name}",
|
||||
"dialog.edit_title": "UREDI {title}",
|
||||
"dialog.no_shown_body": "Nema prikazanog tijela za promjenu: prikažite jedno ili odaberite Novo tijelo",
|
||||
"dialog.pattern_sketch_not_object": "Odaberite tijela ili značajke: skica nije ni jedno ni drugo. Ponovite njezine krivulje pomoću alata Niz ili Zrcali u samoj skici ili je najprije ekstrudirajte.",
|
||||
"dialog.select": "↖ Odaberi",
|
||||
"dialog.selected_count": "↖ Odabrano: {count}",
|
||||
"dialog.selection_guidance": "Odaberite ulaz kako biste ga promijenili. Kliknite odabrane objekte kako biste ih uklonili; Ctrl/Cmd-klik ih također uključuje i isključuje. Pogledajte pretpregled, a zatim pritisnite U redu.",
|
||||
@@ -1904,6 +1920,7 @@
|
||||
"nav.hfss.help": "Alt+povlačenje lijevom tipkom pomiče, Shift+Alt+lijeva tipka zumira, Ctrl+povlačenje lijevom tipkom na praznom prostoru orbitira",
|
||||
"nav.inventor.help": "Povlačenje srednjom tipkom pomiče, Shift+srednja orbitira, Ctrl+srednja zumira, povlačenje desnom orbitira",
|
||||
"nav.maya.help": "Alt+povlačenje lijevom tipkom orbitira, Alt+srednja tipka pomiče, Alt+desna tipka zumira; povlačenje srednjom tipkom također pomiče",
|
||||
"nav.nx.help": "Povlačenje srednjom tipkom orbitira, Shift+srednja pomiče (u NX-u srednja + desna), Ctrl+srednja zumira (u NX-u srednja + lijeva); povlačenje desnom tipkom ne dira kameru",
|
||||
"nav.openscad.help": "Povlačenje lijevom tipkom po praznom prostoru orbitira, Shift+povlačenje lijevom odabire okvirom, povlačenje geometrije skice uređuje je; povlačenje desnom ili srednjom tipkom pomiče, Shift+desna ili Shift+srednja zumira",
|
||||
"nav.perspective.sketch": "Uređivanje skice koristi ortografski prikaz. Završite skicu kako biste prešli na perspektivu.",
|
||||
"nav.right_drag_orbits.help": "Isključeno: povlačenje desnom tipkom ne dira kameru, a desna tipka samo otvara kontekstni izbornik. Odabir mapiranja miša SolidWorks isključuje ovu opciju.",
|
||||
@@ -2125,6 +2142,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Zakrivljene plohe",
|
||||
"prefs.curves": "Zakrivljene plohe",
|
||||
"prefs.dark_parts_shading": "Zadrži oblik tamnih dijelova",
|
||||
"prefs.dark_parts_shading_help": "Sjenča prikaz u linearnom svjetlu uz meko dopunsko svjetlo neba, tako da tamna lica zadrže gradijent umjesto da pocrne",
|
||||
"prefs.dead_zone_note": "Šrafirana traka je mrtva zona: kretanje unutar nje se zanemaruje.",
|
||||
"prefs.default": "zadano",
|
||||
"prefs.design": "Zadane postavke dizajna",
|
||||
@@ -2139,6 +2158,8 @@
|
||||
"prefs.edges": "Bridovi i sjenčanje",
|
||||
"prefs.extend": "Produži",
|
||||
"prefs.extra_navigation": "Ctrl + povlačenje lijevom tipkom po praznom prostoru orbitira; držite razmaknicu za pomicanje.",
|
||||
"prefs.extrude_view_isometric": "Okreni prikaz u izometrijski kad se otvori Ekstrudiraj",
|
||||
"prefs.extrude_view_return": "Vrati se na prethodni prikaz kad se Ekstrudiraj zatvori",
|
||||
"prefs.follows_os": "Prati operacijski sustav",
|
||||
"prefs.font": "Font",
|
||||
"prefs.font_fallback": "Znakovi koji nedostaju odabranom fontu prikazuju se ugrađenim fontom, tako da nijedna oznaka ne prikazuje prazan kvadrat.",
|
||||
@@ -2154,6 +2175,7 @@
|
||||
"prefs.graphics_card_auto": "Automatski",
|
||||
"prefs.graphics_card_missing": "Spremljena grafička kartica nije dostupna; koristi se Automatski.",
|
||||
"prefs.graphics_card_restart": "Stupa na snagu nakon ponovnog pokretanja SolidCrafta. WGPU_ADAPTER_NAME ima prednost nad ovim odabirom.",
|
||||
"prefs.graphics_card_unused": "Spremljena grafička kartica nije korištena za ovaj prozor; kartica koja se koristi prikazana je u Pomoć › O programu i u statusnoj traci. Pokušajte s opcijom Automatski ili postavite WGPU_ADAPTER_NAME na jedinstveni dio naziva kartice.",
|
||||
"prefs.graphics_note": "Žrtvujte brzinu za glatkoću. Svaki odabir pokazuje svoj učinak na primjeru.",
|
||||
"prefs.graphics_presets_note": "Unaprijed zadane vrijednosti postavljaju sve odabire ispod odjednom.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Sakrij simbole ograničenja kada je prikaz umanjen",
|
||||
@@ -2168,6 +2190,8 @@
|
||||
"prefs.history_sketches_note": "Kad je Skice postavljeno na Ispod svoje značajke, vremenska traka lijevo prikazuje svaku skicu jednu razinu uvučeno, ispod prve značajke izrađene iz nje, kad god je dovoljno široka za nazive. Vremenska traka na dnu uvijek zadržava redoslijed nastanka.",
|
||||
"prefs.history_sketches_under_feature": "Ispod svoje značajke",
|
||||
"prefs.hold_stop_snapping": "Držite kako biste isključili privlačenje",
|
||||
"prefs.home_fit_size": "Veličina za Početni prikaz i Uklopi",
|
||||
"prefs.home_fit_size_note": "Koliki dio prikaza model ispunjava kad ga uokviri Početni prikaz, Uklopi, otvaranje dizajna ili promjena prikaza u navigatoru, bez obzira na to je li postavka ispod uključena. 100 %: ispunjava prikaz između panela, kompasa i navigacijske trake.",
|
||||
"prefs.icons_only": "Samo ikone",
|
||||
"prefs.imported_file": "Uvezena datoteka",
|
||||
"prefs.in_browser": "U stablu modela",
|
||||
@@ -2176,13 +2200,20 @@
|
||||
"prefs.interface_language": "Jezik sučelja",
|
||||
"prefs.interface_note": "Kako SolidCraft izgleda i gdje su njegovi dijelovi.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_navigator_arrows": "Obrni strelice za rotaciju kocke prikaza",
|
||||
"prefs.invert_navigator_arrows_note": "Strelice oko kocke orbitiraju kameru u smjeru u kojem pokazuju, kao u programima Fusion i Inventor: strelica s lijeve strane pokazuje udesno i zakreće kameru udesno. Isključeno: strelica okreće kocku prema strani na kojoj se nalazi, pa lijeva strelica zakreće kameru ulijevo. Postavka ne utječe na zakrivljene strelice za valjanje.",
|
||||
"prefs.keep_work_on_tab_switch": "Zadrži dijaloge i alate otvorenima pri prebacivanju između dizajna",
|
||||
"prefs.keep_work_on_tab_switch_note": "Svaka kartica dizajna zadržava svoj otvoreni dijalog ili alat, s onim što ste u njemu odabrali i upisali, te svoju karticu alatne trake. Kad se vratite na dizajn, ponovno su tu. Isključeno: prebacivanje između dizajna poništava ih i prikazuje karticu Model.",
|
||||
"prefs.labels": "Oznake",
|
||||
"prefs.language_system": "Sustav",
|
||||
"prefs.layout": "Raspored",
|
||||
"prefs.layout_note": "Kompaktno pričvršćuje panele uz rubove prozora. Prozirnost stabla modela dostupna je u opciji Plutajuće. Stranu Asistenta promijenite na stranici Asistent.",
|
||||
"prefs.left": "Lijevo",
|
||||
"prefs.left_click": "Lijevi klik",
|
||||
"prefs.line_drag_draws_one": "Pritisak i povlačenje crta jednu liniju",
|
||||
"prefs.line_drag_draws_one_note": "U alatu Linija pritisak, povlačenje i otpuštanje crta jednu liniju od mjesta pritiska do mjesta otpuštanja i ondje završava. Klikovi i dalje crtaju lanac. Isključeno: povlačenje postavlja točku ondje gdje se otpusti, kao i klik.",
|
||||
"prefs.line_hover_tangent_arc": "Zadržite pokazivač na posljednjoj točki linije kako biste prešli na tangentni luk",
|
||||
"prefs.line_hover_tangent_arc_note": "U alatu Linija, nakon što je nacrtan segment, odmaknite pokazivač od posljednje točke lanca, vratite ga na nju i držite ga mirno: sljedeći klik završava tangentni luk, a isti pokret vraća na linije. Isključeno: zadržavanje na toj točki ne radi ništa.",
|
||||
"prefs.linked_designs": "Povezani dizajni",
|
||||
"prefs.live_input": "Ulaz uživo",
|
||||
"prefs.live_preview": "Pregled uživo",
|
||||
@@ -2206,6 +2237,8 @@
|
||||
"prefs.mouse_mapping": "Mapiranje miša",
|
||||
"prefs.navigation": "Navigacija",
|
||||
"prefs.navigation_note": "Kako miš, dodirna pločica i 3D miš pomiču prikaz.",
|
||||
"prefs.navigator_fit": "Uklopi model nakon promjene prikaza u navigatoru",
|
||||
"prefs.navigator_fit_note": "Lice, kut ili strelica kocke prikaza, odnosno os orijentacijskog gizma, također usmjerava pogled na sredinu modela s jedne udaljenosti na kojoj se uklapa iz svakog smjera (njegova granična sfera). Početni prikaz koristi isti zum i središte dok je ova opcija uključena, pa se prikazi koje odaberete poslije njega samo okreću. Isključeno: prikaz se okreće na mjestu i zadržava svoj cilj i zum.",
|
||||
"prefs.network_host_remove": "Ukloni",
|
||||
"prefs.network_locations": "Dopusti mrežne lokacije u dizajnima",
|
||||
"prefs.network_locations_note": "Isključeno: povezane datoteke dizajna na drugom računalu (\\\\server\\share) otvaraju se samo s poslužitelja navedenih ovdje ili nakon što ga dopustite kad se to zatraži.",
|
||||
@@ -2254,6 +2287,8 @@
|
||||
"prefs.render_previous_geometry_budget": "Koristi prethodni proračun geometrije za pregled renderiranja",
|
||||
"prefs.render_previous_geometry_budget_help": "Vrati prethodnu fiksnu granicu geometrije (512 MiB na 64-bitnom desktopu, 128 MiB drugdje) umjesto proračuna prema uređaju.",
|
||||
"prefs.reports_saved": "Spremljeno: {n}",
|
||||
"prefs.ribbon_click_ends_tool": "Klik na gumb aktivnog alata završava ga",
|
||||
"prefs.ribbon_click_ends_tool_note": "Klik na istaknuti gumb na vrpci alata za skiciranje koji je u uporabi završava taj alat, pa klikovi u prikazu ponovno služe za odabir. Isključeno: klik ponovno pokreće alat.",
|
||||
"prefs.ribbon_reveal": "Prikaži rezultate pretraživanja na vrpci",
|
||||
"prefs.ribbon_reveal_note": "Zadržavanje pokazivača iznad rezultata u Pretraži naredbe pokazuje gdje se naredba nalazi na vrpci: njezinu karticu, gumb ili redak u padajućem izborniku panela. Naredba pokrenuta iz pretraživanja ondje zatreperi, a pokrenuta naredba iz padajućeg izbornika označava svoj panel.",
|
||||
"prefs.right_click": "Desni klik",
|
||||
@@ -2283,6 +2318,8 @@
|
||||
"prefs.sketch_exit_view.previous": "Vrati se na prethodni prikaz",
|
||||
"prefs.sketch_exit_view.unless_rotated": "Vrati se osim ako je rotirano",
|
||||
"prefs.sketch_exit_view_note": "Kamo prikaz ide nakon Završi skicu, Odustani ili dvostrukog Esc.",
|
||||
"prefs.sketch_face_activates_part": "Skiciranje na licu dijela čini taj dio aktivnim",
|
||||
"prefs.sketch_face_activates_part_note": "Skica započeta na licu tijela druge komponente najprije aktivira tu komponentu, pa skica pripada njoj i u stablu modela ostaje uz taj dio. Taj dio nakon toga ostaje aktivan. Isključeno: skica pripada aktivnoj komponenti.",
|
||||
"prefs.sketch_open_look_at": "Okreni kameru prema skici pri otvaranju",
|
||||
"prefs.sketch_open_look_at_note": "Isključeno: kad izradite ili uredite skicu, prikaz ostaje onakav kakav jest. Pogledaj okreće prikaz prema skici kad to želite.",
|
||||
"prefs.sketch_recent_snap": "Automatski privlači na nedavna središta i središnje točke",
|
||||
@@ -2314,6 +2351,24 @@
|
||||
"prefs.status_error": "Pogreška",
|
||||
"prefs.status_selected": "Odabrano",
|
||||
"prefs.status_warning": "Upozorenje",
|
||||
"prefs.structure.appearance": "Izgled",
|
||||
"prefs.structure.appearance_note": "Boje, tekst i pristupačnost.",
|
||||
"prefs.structure.in_profile": "U profilu",
|
||||
"prefs.structure.layout": "Raspored",
|
||||
"prefs.structure.layout_note": "Gdje se nalaze dijelovi prozora.",
|
||||
"prefs.structure.look": "Vizualno",
|
||||
"prefs.structure.measure_pointer": "Mjerenje i pokazivač",
|
||||
"prefs.structure.mouse3d": "3D miš",
|
||||
"prefs.structure.mouse3d_note": "SpaceMouse i SpaceNavigator.",
|
||||
"prefs.structure.navigation_note": "Miš i dodirna pločica. 3D miš ima vlastitu stranicu.",
|
||||
"prefs.structure.profile": "Profil",
|
||||
"prefs.structure.profile_edited": "Promijenjeno otkako je spremljeno",
|
||||
"prefs.structure.profile_hover": "Spremljeno u profilima postavki",
|
||||
"prefs.structure.scale_percent": "{percent}%",
|
||||
"prefs.structure.sketch": "Skica",
|
||||
"prefs.structure.tools": "Alati",
|
||||
"prefs.structure.tools_note": "Kako se ponašaju alati za modeliranje.",
|
||||
"prefs.structure.work": "Rad",
|
||||
"prefs.supersampling_brief": "Iscrtava prikaz većim i smanjuje ga filtriranjem: vrlo oštro i zahtjevnije.",
|
||||
"prefs.tab_accent_note": "Naglasna boja prati aktivnu karticu",
|
||||
"prefs.theme_file": "datoteka teme",
|
||||
@@ -3811,6 +3866,7 @@
|
||||
"sketch_text.formula": "Počnite znakom = kako biste riječi izgradili iz parametara: ='W' + number(Width)",
|
||||
"sketch_text.italic": "Kurziv",
|
||||
"sketch_text.loading_fonts": "Traženje instaliranih fontova…",
|
||||
"sketch_text.no_bold": "Ugrađeni font nema podebljanu varijantu: odaberite instalirani font",
|
||||
"sketch_text.on_path": "Tekst prati svoju putanju: krivulja ga postavlja",
|
||||
"sketch_text.path_clear": "Skini tekst s njegove krivulje",
|
||||
"sketch_text.path_hint": "Krivulja skice koju tekst prati: kliknite ovdje, a zatim krivulju",
|
||||
@@ -4213,6 +4269,9 @@
|
||||
"ui.ai_permissions": "Dopuštenja",
|
||||
"ui.ai_preset": "Unaprijed zadano",
|
||||
"ui.ai_protocol": "Protokol",
|
||||
"ui.ai_reasoning_effort": "Razina zaključivanja",
|
||||
"ui.ai_reasoning_effort_field_hint": "prazno = zadana vrijednost pružatelja",
|
||||
"ui.ai_reasoning_effort_hint": "Neobavezno. Šalje se krajnjoj točki kao `reasoning_effort`; prihvaćene vrijednosti razlikuju se od pružatelja do pružatelja (na primjer `low`, `high` ili `max`). SolidCraft nikad ne mijenja vrijednost koju ovdje postavite. Ostavite prazno da se ništa ne šalje i da se zadrži zadana vrijednost pružatelja.",
|
||||
"ui.ai_remove": "Ukloni",
|
||||
"ui.ai_request_timed_out": "Model je trajao dulje od {seconds} s. Vremensko ograničenje zahtjeva za ovu krajnju točku možete povećati u Postavke › Asistent.",
|
||||
"ui.ai_request_timeout": "Vremensko ograničenje zahtjeva",
|
||||
@@ -4378,6 +4437,7 @@
|
||||
"ui.dark": "Tamna",
|
||||
"ui.default_modelling_orientation": "Zadana orijentacija modeliranja",
|
||||
"ui.delete": "Izbriši",
|
||||
"ui.delete_select_first": "Najprije odaberite što želite izbrisati: značajke, tijela, komponente ili geometriju skice",
|
||||
"ui.depth": "Dubina",
|
||||
"ui.design": "Dizajn",
|
||||
"ui.design_properties.author": "Autor",
|
||||
@@ -4470,6 +4530,7 @@
|
||||
"ui.edit_in_place_saved_elsewhere": "Dio je spremljen u drugu datoteku; sklop i dalje povezuje {name}",
|
||||
"ui.edit_in_place_wait": "Pričekajte da završi ono što se izvodi u dizajnu",
|
||||
"ui.edit_section_analysis": "Uredi analizu presjeka",
|
||||
"ui.edit_sketch_empty": "Nema skica za uređivanje",
|
||||
"ui.ellipse": "Elipsa",
|
||||
"ui.emboss_wrap_note": "Ortogonalni linijski profili tangentni na jedan podržani valjak automatski se omataju. Odaberite stijenku kako biste odabrali cilj.",
|
||||
"ui.end_radius": "Krajnji polumjer",
|
||||
@@ -4480,7 +4541,9 @@
|
||||
"ui.equation_explicit": "Eksplicitni",
|
||||
"ui.equation_form": "Oblik",
|
||||
"ui.equation_note": "Varijabla je običan broj: sin(t) je uzima u radijanima. Rezultat bez jedinice izražen je u jedinici dizajna.",
|
||||
"ui.equation_note_range": "Varijabla je duljina, kao i njezin raspon: kombinirajte je s parametrima duljine, a za sin() je podijelite duljinom. Rezultat bez jedinice izražen je u jedinici dizajna.",
|
||||
"ui.equation_parametric": "Parametarski",
|
||||
"ui.equation_variable_unit": "Varijabla ima jedinicu raspona",
|
||||
"ui.equation_x_of_t": "x(t)",
|
||||
"ui.equation_y_of_t": "y(t)",
|
||||
"ui.equation_y_of_x": "y(x)",
|
||||
@@ -4647,6 +4710,7 @@
|
||||
"ui.insert_design.saved_file": "{name} ima nespremljene promjene, pa je umetnuta njegova spremljena datoteka. Spremite ga, a zatim pokrenite Ažuriraj veze kako biste unijeli promjene.",
|
||||
"ui.insert_design.unsaved": "Nespremljene promjene",
|
||||
"ui.inspect": "Pregledaj",
|
||||
"ui.instances": "Instance",
|
||||
"ui.intersect": "Presjek",
|
||||
"ui.isolate": "Izoliraj",
|
||||
"ui.join": "Spoji",
|
||||
@@ -4655,12 +4719,14 @@
|
||||
"ui.joints": "Spojevi",
|
||||
"ui.keyboard_shortcuts": "Tipkovnički prečaci",
|
||||
"ui.language": "Jezik",
|
||||
"ui.lasso_select": "Laso (povucite petlju slobodnom rukom: obuhvaća sve što je u cijelosti unutra)",
|
||||
"ui.layout_compact": "Kompaktno",
|
||||
"ui.layout_floating": "Plutajuće",
|
||||
"ui.length": "Duljina",
|
||||
"ui.length_decimals": "Decimale duljine",
|
||||
"ui.light": "Svijetla",
|
||||
"ui.line": "Linija",
|
||||
"ui.line_arc_hint": "Tangentni luk: kliknite kraj luka. Ponovno se zadržite na posljednjoj točki za linije; desni klik ili Esc za završetak",
|
||||
"ui.link_copies": "Poveži identične kopije",
|
||||
"ui.link_copies_keep": "Zadrži",
|
||||
"ui.link_copies_link": "Poveži",
|
||||
@@ -4749,6 +4815,7 @@
|
||||
"ui.part_size_shared": "Ponovno gradi dio, zamjenjujući sve dodane značajke. Svi primjerci ove komponente ažuriraju se.",
|
||||
"ui.paste": "Zalijepi",
|
||||
"ui.path": "Putanja",
|
||||
"ui.pattern_object_type_tip": "Tijela: puni oblici, odabiru se u prikazu ili u stablu modela; svako se kopira u cijelosti.\nZnačajke: koraci vremenske trake, poput ekstruzije ili provrta, odabiru se na vremenskoj traci ili na licu koje su izradili; korak se ponavlja.\nSkica nije ni jedno ni drugo: ponovite njezine krivulje pomoću alata Niz ili Zrcali u samoj skici ili je najprije ekstrudirajte.",
|
||||
"ui.pattern_on_path": "Niz duž putanje",
|
||||
"ui.perforate_margin_default": "iz promjera",
|
||||
"ui.perforate_pitch": "Korak",
|
||||
@@ -4794,9 +4861,9 @@
|
||||
"ui.polygon": "Mnogokut",
|
||||
"ui.preferences": "Postavke",
|
||||
"ui.press_a_key": "pritisnite tipku…",
|
||||
"ui.print_face_body_not_sent": "Tijelo te plohe ne šalje se: najprije ga dodajte u Tijela",
|
||||
"ui.print_face_cleared": "{body}: ploha ispisa uklonjena",
|
||||
"ui.print_face_input": "Ploha ispisa",
|
||||
"ui.print_face_one_body": "Ploha ispisa pripada jednom tijelu: pošaljite to tijelo samo da biste je promijenili",
|
||||
"ui.print_face_pick": "Odaberite ravnu plohu punog tijela da postane ploha ispisa",
|
||||
"ui.print_face_set": "{body}: ploha ispisa postavljena; Pošalji u slicer polaže tijelo na nju",
|
||||
"ui.profile": "Profil",
|
||||
@@ -5024,12 +5091,15 @@
|
||||
"ui.view_angles": "Kutovi prikaza",
|
||||
"ui.view_angles_convention": "Stupnjevi oko osi prema gore. Skretanje 0° je Pogled sprijeda, 90° Pogled slijeva, −90° Pogled zdesna, 180° Pogled straga; nagib 90° je Pogled odozgo; pozitivno valjanje okreće model u smjeru kazaljke na satu.",
|
||||
"ui.view_angles_help": "Prikazuje nagib, skretanje i valjanje prikaza u stupnjevima te njegovu projekciju ispod orijentacijskog gizma. Kliknite očitanje kako biste upisali točne kutove.",
|
||||
"ui.view_angles_lock": "Zaključaj {axis}",
|
||||
"ui.view_angles_ortho": "Ortogr.",
|
||||
"ui.view_angles_persp": "Persp.",
|
||||
"ui.view_angles_pitch": "Nagib",
|
||||
"ui.view_angles_readout": "Nagib {pitch} · Skretanje {yaw} · Valjanje {roll}",
|
||||
"ui.view_angles_roll": "Valjanje",
|
||||
"ui.view_angles_unlock": "Otključaj {axis}",
|
||||
"ui.view_angles_yaw": "Skretanje",
|
||||
"ui.view_angles_zero": "Postavi {axis} na 0°",
|
||||
"ui.view_centre": "Središte prikaza",
|
||||
"ui.viewport_status_clear": "Isključi {name}",
|
||||
"ui.viewport_status_clear_hint": "Isključi {name} · Esc",
|
||||
|
||||
+70
-1
@@ -793,6 +793,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (rögzítés elveszett)",
|
||||
"browser.sketch_editing": "{name} (szerkesztés alatt)",
|
||||
"browser.sketch_name": "Vázlat{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "már az alkatrészükben vannak",
|
||||
"browser.sketches_to_parts_design_body": "magának a tervnek egy testén vannak",
|
||||
"browser.sketches_to_parts_moved": "{moved} vázlat áthelyezve abba az alkatrészbe, amelyen van",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "pontos rögzítés nélküli lapon vannak",
|
||||
"browser.sketches_to_parts_not_on_face": "nem egy alkatrész lapján vannak",
|
||||
"browser.sketches_to_parts_part_missing": "az alkatrészük már nincs meg",
|
||||
"browser.sketches_to_parts_result": "{moved} vázlat áthelyezve abba az alkatrészbe, amelyen van; {stayed} a helyén maradt: {why}",
|
||||
"browser.units": "Egységek: {units}",
|
||||
"cam.flat": "Sík lap",
|
||||
"cam.follower": "Követő",
|
||||
@@ -860,6 +867,7 @@
|
||||
"cmd.component_move_bodies": "Testek áthelyezése komponensbe",
|
||||
"cmd.component_move_features": "Elemek áthelyezése komponensbe",
|
||||
"cmd.component_move_sketches": "Vázlatok áthelyezése komponensbe",
|
||||
"cmd.component_move_sketches_to_parts": "Vázlatok áthelyezése az alkatrészükbe",
|
||||
"cmd.component_paste_from_design": "Beillesztés másik tervből",
|
||||
"cmd.component_paste_new": "Beillesztés újként",
|
||||
"cmd.component_properties": "Komponens fizikai tulajdonságai",
|
||||
@@ -1195,6 +1203,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Vezérlőpontos spline (5. fokú)",
|
||||
"cmd.sketch_spline_fit_point": "Illesztőpontos spline",
|
||||
"cmd.sketch_spline_insert_point": "Splinepont hozzáadása",
|
||||
"cmd.sketch_spline_remove_point": "Splinepont eltávolítása",
|
||||
"cmd.sketch_spline_reverse": "Spline irányának megfordítása",
|
||||
"cmd.sketch_spun_profile": "Forgatott profil",
|
||||
"cmd.sketch_toggle_diameter": "Sugár / átmérő váltása",
|
||||
@@ -1287,8 +1296,14 @@
|
||||
"color.hex_hint": "#rrggbb",
|
||||
"color.hue": "Árnyalat",
|
||||
"color.linear": "Színértékek megjelenítése 0–1 lineáris térben",
|
||||
"color.recent": "Legutóbbi színek",
|
||||
"color.recent_help": "A bármelyik színmezőben kiválasztott színek itt jelennek meg",
|
||||
"color.saturation": "Telítettség (százalék)",
|
||||
"color.saturation_value": "Telítettség és érték",
|
||||
"color.selected": "Kijelölt szín",
|
||||
"color.step_down": "Lépés lefelé",
|
||||
"color.step_up": "Lépés felfelé",
|
||||
"color.value": "Érték, a fényerő (százalék)",
|
||||
"config.column_filter_empty": "Nincs egyező oszlop",
|
||||
"contact.components_count": "{count} komponens",
|
||||
"contact.enabled_label": "engedélyezve",
|
||||
@@ -1335,6 +1350,7 @@
|
||||
"dialog.dimension_name": "Méret: {name}",
|
||||
"dialog.edit_title": "{title} SZERKESZTÉSE",
|
||||
"dialog.no_shown_body": "Nincs módosítható megjelenített test: jelenítsen meg egyet, vagy válassza az Új test lehetőséget",
|
||||
"dialog.pattern_sketch_not_object": "Válasszon testeket vagy elemeket: a vázlat egyik sem. Ismételje a görbéit a vázlat saját Minta vagy Tükrözés parancsával, vagy húzza ki előbb.",
|
||||
"dialog.select": "↖ Kijelölés",
|
||||
"dialog.selected_count": "↖ {count} kijelölve",
|
||||
"dialog.selection_guidance": "Válasszon ki egy bemenetet a módosításhoz. A kijelölt objektumokra kattintva eltávolíthatja őket; a Ctrl/Cmd-kattintás is váltja a kijelölést. Tekintse át az előnézetet, majd nyomja meg az OK gombot.",
|
||||
@@ -1904,6 +1920,7 @@
|
||||
"nav.hfss.help": "Alt+bal húzás: pásztázás, Shift+Alt+bal húzás: nagyítás, Ctrl+bal húzás üres területen: keringés",
|
||||
"nav.inventor.help": "Középső húzás: pásztázás, Shift+középső húzás: keringés, Ctrl+középső húzás: nagyítás, jobb húzás: keringés",
|
||||
"nav.maya.help": "Alt+bal húzás: keringés, Alt+középső húzás: pásztázás, Alt+jobb húzás: nagyítás; a középső húzás is pásztáz",
|
||||
"nav.nx.help": "Középső húzás: keringés, Shift+középső húzás: pásztázás (az NX-ben középső + jobb gomb), Ctrl+középső húzás: nagyítás (az NX-ben középső + bal gomb); a jobb húzás nem mozgatja a kamerát",
|
||||
"nav.openscad.help": "Bal húzás üres helyen: keringés, Shift+bal húzás: kijelölés kerettel, a vázlatgeometria húzása szerkeszt; jobb vagy középső húzás: pásztázás, Shift+jobb vagy Shift+középső húzás: nagyítás",
|
||||
"nav.perspective.sketch": "A vázlatszerkesztés ortografikus nézetet használ. A perspektívára váltáshoz fejezze be a vázlatot.",
|
||||
"nav.right_drag_orbits.help": "Ki: a jobb gombos húzás nem mozgatja a kamerát, és a jobb gomb csak a helyi menüt nyitja meg. A SolidWorks egérkiosztás kiválasztása kikapcsolja.",
|
||||
@@ -2125,6 +2142,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Görbült felületek",
|
||||
"prefs.curves": "Görbült felületek",
|
||||
"prefs.dark_parts_shading": "A sötét alkatrészek formájának megőrzése",
|
||||
"prefs.dark_parts_shading_help": "A nézet árnyalása lineáris fényben, lágy égbolt-kitöltéssel, hogy a sötét lapokon megmaradjon az átmenet, és ne váljanak feketévé",
|
||||
"prefs.dead_zone_note": "A sraffozott sáv a holtsáv: az azon belüli mozgást figyelmen kívül hagyja.",
|
||||
"prefs.default": "alapértelmezett",
|
||||
"prefs.design": "Terv alapbeállításai",
|
||||
@@ -2139,6 +2158,8 @@
|
||||
"prefs.edges": "Élek és árnyalás",
|
||||
"prefs.extend": "Meghosszabbítás",
|
||||
"prefs.extra_navigation": "A Ctrl+bal egérgombos húzás az üres területen keringést végez; a Szóköz nyomva tartásával pásztázhat.",
|
||||
"prefs.extrude_view_isometric": "A nézet izometrikusra fordítása a Kihúzás megnyitásakor",
|
||||
"prefs.extrude_view_return": "Visszatérés a korábbi nézethez a Kihúzás bezárásakor",
|
||||
"prefs.follows_os": "Az operációs rendszert követi",
|
||||
"prefs.font": "Betűtípus",
|
||||
"prefs.font_fallback": "A kiválasztott betűtípusból hiányzó karakterek a beépített betűtípusból pótlódnak, így egyetlen felirat sem mutat üres négyzetet.",
|
||||
@@ -2154,6 +2175,7 @@
|
||||
"prefs.graphics_card_auto": "Automatikus",
|
||||
"prefs.graphics_card_missing": "A mentett videokártya nem érhető el; az Automatikus beállítás van használatban.",
|
||||
"prefs.graphics_card_restart": "A SolidCraft újraindítása után lép életbe. A WGPU_ADAPTER_NAME felülbírálja ezt a választást.",
|
||||
"prefs.graphics_card_unused": "A mentett videokártyát nem használta ez az ablak; a ténylegesen használt kártya a Súgó › A SolidCraftról ablakban és az állapotsorban látható. Próbálja az Automatikus beállítást, vagy a WGPU_ADAPTER_NAME értékét állítsa a kártya nevének egy egyedi részletére.",
|
||||
"prefs.graphics_note": "Cserélje a sebességet simaságra. Minden választás hatását egy minta mutatja.",
|
||||
"prefs.graphics_presets_note": "Az előbeállítások az alábbi beállításokat együtt állítják be.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Kényszerjelek elrejtése kicsinyítéskor",
|
||||
@@ -2168,6 +2190,8 @@
|
||||
"prefs.history_sketches_note": "Ha a Vázlatok beállítása „Elemük alatt”, a bal oldali Előzmények minden vázlatot egy szinttel beljebb, az első belőle készült elem alatt sorol fel, ha elég széles a nevekhez. Az alul lévő idővonal mindig a létrehozás sorrendjét tartja meg.",
|
||||
"prefs.history_sketches_under_feature": "Elemük alatt",
|
||||
"prefs.hold_stop_snapping": "Nyomva tartva leállítja az illesztést",
|
||||
"prefs.home_fit_size": "A Kezdőnézet és az Illesztés mérete",
|
||||
"prefs.home_fit_size_note": "Mekkora részét tölti ki a modell a nézetnek, amikor a Kezdőnézet, az Illesztés, egy megnyitott terv vagy egy navigátornézet-váltás keretezi, függetlenül attól, hogy az alábbi beállítás be van-e kapcsolva. 100 %: kitölti a nézetet a panelek, az iránytű és a navigációs sáv között.",
|
||||
"prefs.icons_only": "Csak ikonok",
|
||||
"prefs.imported_file": "Importált fájl",
|
||||
"prefs.in_browser": "A böngészőben",
|
||||
@@ -2176,13 +2200,20 @@
|
||||
"prefs.interface_language": "Felület nyelve",
|
||||
"prefs.interface_note": "A SolidCraft megjelenése és elemeinek elhelyezése.",
|
||||
"prefs.interval_minutes": "{n} perc",
|
||||
"prefs.invert_navigator_arrows": "A nézetkocka forgatónyilainak megfordítása",
|
||||
"prefs.invert_navigator_arrows_note": "A kocka körüli nyilak úgy keringetik a kamerát, ahogy mutatnak, mint a Fusion és az Inventor esetében: a bal oldali nyíl jobbra mutat, és a kamerát jobbra lendíti. Ki: a nyíl a kockát afelé az oldal felé forgatja, amelyiken van, így a bal nyíl a kamerát balra lendíti. Az íves gördülőnyilakat ez nem érinti.",
|
||||
"prefs.keep_work_on_tab_switch": "A párbeszédablakok és eszközök nyitva tartása tervek közötti váltáskor",
|
||||
"prefs.keep_work_on_tab_switch_note": "Minden tervfül megtartja a nyitott párbeszédablakát vagy eszközét azzal együtt, amit kijelölt és beírt benne, valamint a menüszalag-fülét. A tervhez visszatérve újra ott vannak. Ki: a tervek közötti váltás megszakítja őket, és a Modell fület mutatja.",
|
||||
"prefs.labels": "Feliratok",
|
||||
"prefs.language_system": "Rendszer",
|
||||
"prefs.layout": "Elrendezés",
|
||||
"prefs.layout_note": "A Kompakt a paneleket az ablak széleihez rögzíti. A Böngésző áttetszősége Lebegő módban érhető el. Az Asszisztens oldalát az Asszisztens oldalon módosíthatja.",
|
||||
"prefs.left": "Bal",
|
||||
"prefs.left_click": "Bal kattintás",
|
||||
"prefs.line_drag_draws_one": "Lenyomás és húzás egy vonalat rajzol",
|
||||
"prefs.line_drag_draws_one_note": "A Vonal eszközben a lenyomás, húzás és felengedés egyetlen vonalat rajzol a lenyomás helyétől a felengedés helyéig, és ott véget ér. A kattintások továbbra is láncot rajzolnak. Ki: a húzás a felengedés helyén pontot helyez el, ahogy a kattintás.",
|
||||
"prefs.line_hover_tangent_arc": "Egy vonal utolsó pontján megállva váltás érintő ívre",
|
||||
"prefs.line_hover_tangent_arc_note": "A Vonal eszközben, miután megrajzolt egy szakaszt, mozdítsa el a mutatót a lánc utolsó pontjától, térjen vissza, és tartsa mozdulatlanul rajta: a következő kattintás egy érintő ívet fejez be, ugyanez a mozdulat pedig visszavált vonalakra. Ki: az ott megállás nem csinál semmit.",
|
||||
"prefs.linked_designs": "Kapcsolt tervek",
|
||||
"prefs.live_input": "Élő bevitel",
|
||||
"prefs.live_preview": "Élő előnézet",
|
||||
@@ -2206,6 +2237,8 @@
|
||||
"prefs.mouse_mapping": "Egérkiosztás",
|
||||
"prefs.navigation": "Navigáció",
|
||||
"prefs.navigation_note": "Hogyan mozgatja a nézetet az egér, az érintőpad és a 3D-egér.",
|
||||
"prefs.navigator_fit": "A modell illesztése navigátornézet-váltás után",
|
||||
"prefs.navigator_fit_note": "A nézetkocka egy lapja, sarka vagy nyila, illetve a gizmo egy tengelye a modell közepére is ránéz abból az egyetlen távolságból, amelyből minden oldalról belefér (a határoló gömbje). Amíg ez be van kapcsolva, a Kezdőnézet ugyanazt a nagyítást és középpontot használja, így az utána választott nézetek csak elfordulnak. Ki: a nézet a helyén fordul el, megtartva a célpontját és a nagyítását.",
|
||||
"prefs.network_host_remove": "Eltávolítás",
|
||||
"prefs.network_locations": "Hálózati helyek engedélyezése a tervekben",
|
||||
"prefs.network_locations_note": "Ki: egy terv másik számítógépen lévő hivatkozott fájljai (\\\\server\\share) csak az itt felsorolt kiszolgálókról nyílnak meg, vagy miután kérdésre engedélyez egyet.",
|
||||
@@ -2254,6 +2287,8 @@
|
||||
"prefs.render_previous_geometry_budget": "Korábbi geometriakeret használata a renderelési előnézethez",
|
||||
"prefs.render_previous_geometry_budget_help": "A korábbi rögzített geometriakorlát visszaállítása (64 bites asztali gépen 512 MiB, máshol 128 MiB) az eszközalapú keret helyett.",
|
||||
"prefs.reports_saved": "{n} mentve",
|
||||
"prefs.ribbon_click_ends_tool": "A futó eszköz gombjára kattintva az eszköz befejeződik",
|
||||
"prefs.ribbon_click_ends_tool_note": "A használatban lévő vázlateszköz kivilágított menüszalag-gombjára kattintva az eszköz befejeződik, így a nézetben a kattintások újra kijelölnek. Ki: a kattintás újra elindítja az eszközt.",
|
||||
"prefs.ribbon_reveal": "Keresési találatok megmutatása a menüszalagon",
|
||||
"prefs.ribbon_reveal_note": "Ha a Parancsok keresése egy találata fölé viszi a mutatót, megjelenik, hol található a parancs a menüszalagon: a füle, a gombja vagy a sora egy panel legördülő listájában. A keresésből indított parancs ott felvillan, a legördülő listából futó parancs pedig megjelöli a panelját.",
|
||||
"prefs.right_click": "Jobb kattintás",
|
||||
@@ -2283,6 +2318,8 @@
|
||||
"prefs.sketch_exit_view.previous": "Visszatérés az előző nézethez",
|
||||
"prefs.sketch_exit_view.unless_rotated": "Visszatérés, ha nem lett forgatva",
|
||||
"prefs.sketch_exit_view_note": "Hová kerül a nézet a Vázlat befejezése, a Mégse vagy a dupla Esc után.",
|
||||
"prefs.sketch_face_activates_part": "Ha egy alkatrész lapjára rajzol vázlatot, az az alkatrész aktív lesz",
|
||||
"prefs.sketch_face_activates_part_note": "Egy másik komponens testének lapján indított vázlat előbb aktiválja azt a komponenst, így a vázlat ahhoz tartozik, és a böngészőben ennél az alkatrésznél marad. Az alkatrész utána is aktív marad. Ki: a vázlat az aktív komponenshez tartozik.",
|
||||
"prefs.sketch_open_look_at": "Vázlat megnyitásakor a nézet a vázlatra fordul",
|
||||
"prefs.sketch_open_look_at_note": "Ki: vázlat létrehozása vagy szerkesztése nem változtatja a nézetet. A Ránézés a vázlatra fordítja a nézetet, amikor szeretné.",
|
||||
"prefs.sketch_recent_snap": "Automatikus illesztés a legutóbbi középpontokhoz és felezőpontokhoz",
|
||||
@@ -2314,6 +2351,24 @@
|
||||
"prefs.status_error": "Hiba",
|
||||
"prefs.status_selected": "Kijelölve",
|
||||
"prefs.status_warning": "Figyelmeztetés",
|
||||
"prefs.structure.appearance": "Megjelenés",
|
||||
"prefs.structure.appearance_note": "Színek, szöveg és kisegítő lehetőségek.",
|
||||
"prefs.structure.in_profile": "Profilban",
|
||||
"prefs.structure.layout": "Elrendezés",
|
||||
"prefs.structure.layout_note": "Az ablak részeinek elhelyezkedése.",
|
||||
"prefs.structure.look": "Kinézet",
|
||||
"prefs.structure.measure_pointer": "Mérés és mutató",
|
||||
"prefs.structure.mouse3d": "3D-egér",
|
||||
"prefs.structure.mouse3d_note": "SpaceMouse és SpaceNavigator.",
|
||||
"prefs.structure.navigation_note": "Egér és érintőpad. A 3D-egérnek saját oldala van.",
|
||||
"prefs.structure.profile": "Profil",
|
||||
"prefs.structure.profile_edited": "A mentés óta módosítva",
|
||||
"prefs.structure.profile_hover": "Mentve a beállításprofilokban",
|
||||
"prefs.structure.scale_percent": "{percent}%",
|
||||
"prefs.structure.sketch": "Vázlat",
|
||||
"prefs.structure.tools": "Szerszámok",
|
||||
"prefs.structure.tools_note": "Hogyan viselkednek a modellezőszerszámok.",
|
||||
"prefs.structure.work": "Munka",
|
||||
"prefs.supersampling_brief": "A nézetet nagyobbra rajzolja, majd leszűri: nagyon éles, de erőforrásigényesebb.",
|
||||
"prefs.tab_accent_note": "A hangsúlyszín az aktív fület követi",
|
||||
"prefs.theme_file": "témafájl",
|
||||
@@ -3811,6 +3866,7 @@
|
||||
"sketch_text.formula": "Kezdje egyenlőségjellel (=), hogy a szavakat paraméterekből építse fel: ='W' + number(Width)",
|
||||
"sketch_text.italic": "Dőlt",
|
||||
"sketch_text.loading_fonts": "Telepített betűtípusok keresése…",
|
||||
"sketch_text.no_bold": "A beépített betűtípusnak nincs félkövér változata: válasszon telepített betűtípust",
|
||||
"sketch_text.on_path": "A szöveg követi az útvonalát: a görbe helyezi el",
|
||||
"sketch_text.path_clear": "Szöveg levétele a görbéről",
|
||||
"sketch_text.path_hint": "A vázlat egy görbéje, amelyet a szöveg követ: kattintson ide, majd a görbére",
|
||||
@@ -4213,6 +4269,9 @@
|
||||
"ui.ai_permissions": "Jogosultságok",
|
||||
"ui.ai_preset": "Előbeállítás",
|
||||
"ui.ai_protocol": "Protokoll",
|
||||
"ui.ai_reasoning_effort": "Gondolkodási szint",
|
||||
"ui.ai_reasoning_effort_field_hint": "üres = a szolgáltató alapértelmezése",
|
||||
"ui.ai_reasoning_effort_hint": "Nem kötelező. `reasoning_effort` néven kerül elküldésre a végpontnak; az elfogadott értékek szolgáltatónként eltérnek (például `low`, `high` vagy `max`). Az itt megadott értéket a SolidCraft soha nem cseréli le. Hagyja üresen, ha nem szeretne semmit küldeni, és meg szeretné tartani a szolgáltató alapértelmezését.",
|
||||
"ui.ai_remove": "Eltávolítás",
|
||||
"ui.ai_request_timed_out": "A modell nem válaszolt {seconds} s alatt. A kérés időkorlátját ehhez a végponthoz a Beállítások › Asszisztens alatt emelheti meg.",
|
||||
"ui.ai_request_timeout": "Kérés időkorlátja",
|
||||
@@ -4378,6 +4437,7 @@
|
||||
"ui.dark": "Sötét",
|
||||
"ui.default_modelling_orientation": "Alapértelmezett modellezési tájolás",
|
||||
"ui.delete": "Törlés",
|
||||
"ui.delete_select_first": "Először jelölje ki a törlendőt: elemeket, testeket, komponenseket vagy vázlatgeometriát",
|
||||
"ui.depth": "Mélység",
|
||||
"ui.design": "Tervezés",
|
||||
"ui.design_properties.author": "Szerző",
|
||||
@@ -4470,6 +4530,7 @@
|
||||
"ui.edit_in_place_saved_elsewhere": "Az alkatrész másik fájlba lett mentve; az összeállítás továbbra is ide hivatkozik: {name}",
|
||||
"ui.edit_in_place_wait": "Várja meg, amíg a tervben futó művelet befejeződik",
|
||||
"ui.edit_section_analysis": "Metszetelemzés szerkesztése",
|
||||
"ui.edit_sketch_empty": "Nincs szerkeszthető vázlat",
|
||||
"ui.ellipse": "Ellipszis",
|
||||
"ui.emboss_wrap_note": "Az egyetlen támogatott hengerhez érintő, merőleges vonalprofilok automatikusan a henger köré tekerednek. Válasszon ki egy falat a cél kiválasztásához.",
|
||||
"ui.end_radius": "Végi sugár",
|
||||
@@ -4480,7 +4541,9 @@
|
||||
"ui.equation_explicit": "Explicit",
|
||||
"ui.equation_form": "Alak",
|
||||
"ui.equation_note": "A változó egyszerű szám: a sin(t) radiánban értelmezi. A mértékegység nélküli eredmény a terv mértékegységében értendő.",
|
||||
"ui.equation_note_range": "A változó hossz, ahogy a tartománya is: keverje hosszparaméterekkel, és a sin() előtt ossza el egy hosszal. A mértékegység nélküli eredmény a terv mértékegységében értendő.",
|
||||
"ui.equation_parametric": "Parametrikus",
|
||||
"ui.equation_variable_unit": "A változó a tartomány mértékegységét használja",
|
||||
"ui.equation_x_of_t": "x(t)",
|
||||
"ui.equation_y_of_t": "y(t)",
|
||||
"ui.equation_y_of_x": "y(x)",
|
||||
@@ -4655,12 +4718,14 @@
|
||||
"ui.joints": "Csuklók",
|
||||
"ui.keyboard_shortcuts": "Billentyűparancsok",
|
||||
"ui.language": "Nyelv",
|
||||
"ui.lasso_select": "Lasszó (húzzon szabadkézi hurkot: azt jelöli ki, ami teljesen belül van)",
|
||||
"ui.layout_compact": "Kompakt",
|
||||
"ui.layout_floating": "Lebegő",
|
||||
"ui.length": "Hossz",
|
||||
"ui.length_decimals": "Hossz tizedesjegyei",
|
||||
"ui.light": "Világos",
|
||||
"ui.line": "Vonal",
|
||||
"ui.line_arc_hint": "Érintő ív: kattintson az ív végpontjára. Vonalakhoz álljon meg újra az utolsó ponton; jobb kattintás vagy Esc a befejezéshez",
|
||||
"ui.link_copies": "Azonos másolatok összekapcsolása",
|
||||
"ui.link_copies_keep": "Megtartás",
|
||||
"ui.link_copies_link": "Összekapcsolás",
|
||||
@@ -4749,6 +4814,7 @@
|
||||
"ui.part_size_shared": "Újraépíti az alkatrészt, a hozzáadott elemeket lecserélve. A komponens minden előfordulása frissül.",
|
||||
"ui.paste": "Beillesztés",
|
||||
"ui.path": "Útvonal",
|
||||
"ui.pattern_object_type_tip": "Testek: tömör alakzatok, a nézetben vagy a Böngészőben kijelölve; mindegyik egészében másolódik.\nElemek: az Előzmények lépései, például egy kihúzás vagy egy furat, az Előzményekben vagy az általuk létrehozott lapon kijelölve; a lépés megismétlődik.\nA vázlat egyik sem: a görbéit a vázlat saját Minta vagy Tükrözés parancsával ismételje, vagy húzza ki előbb.",
|
||||
"ui.pattern_on_path": "Minta útvonalon",
|
||||
"ui.perforate_margin_default": "az átmérőből",
|
||||
"ui.perforate_pitch": "Osztás",
|
||||
@@ -4794,9 +4860,9 @@
|
||||
"ui.polygon": "Sokszög",
|
||||
"ui.preferences": "Beállítások",
|
||||
"ui.press_a_key": "nyomjon meg egy billentyűt…",
|
||||
"ui.print_face_body_not_sent": "Ennek a lapnak a teste nincs elküldve: előbb add hozzá a Testekhez",
|
||||
"ui.print_face_cleared": "{body}: nyomtatási lap törölve",
|
||||
"ui.print_face_input": "Nyomtatási lap",
|
||||
"ui.print_face_one_body": "A nyomtatási lap egy testhez tartozik: a módosításhoz küldd el azt a testet egyedül",
|
||||
"ui.print_face_pick": "Válassz ki egy tömör test egy sík lapját, hogy az legyen a nyomtatási lap",
|
||||
"ui.print_face_set": "{body}: nyomtatási lap beállítva; a Küldés szeletelőnek erre a lapra fekteti a testet",
|
||||
"ui.profile": "Profil",
|
||||
@@ -5024,12 +5090,15 @@
|
||||
"ui.view_angles": "Nézetszögek",
|
||||
"ui.view_angles_convention": "Fok a felfelé mutató tengely körül. Az elfordulás 0° az elölnézet, 90° a bal oldali, −90° a jobb oldali, 180° a hátulnézet; a 90°-os billentés a felülnézet; a pozitív gördülés az óramutató járásával megegyező irányba forgatja a modellt.",
|
||||
"ui.view_angles_help": "Megjeleníti a nézet billentését, elfordulását és gördülését fokban, valamint a vetítési módját a tájolásjelző alatt. Kattintson a kijelzőre a pontos szögek beírásához.",
|
||||
"ui.view_angles_lock": "{axis} zárolása",
|
||||
"ui.view_angles_ortho": "Ortog.",
|
||||
"ui.view_angles_persp": "Persp.",
|
||||
"ui.view_angles_pitch": "Billentés",
|
||||
"ui.view_angles_readout": "Billentés {pitch} · Elfordulás {yaw} · Gördülés {roll}",
|
||||
"ui.view_angles_roll": "Gördülés",
|
||||
"ui.view_angles_unlock": "{axis} feloldása",
|
||||
"ui.view_angles_yaw": "Elfordulás",
|
||||
"ui.view_angles_zero": "{axis} beállítása 0°-ra",
|
||||
"ui.view_centre": "Nézet középpontja",
|
||||
"ui.viewport_status_clear": "Kikapcsolás: {name}",
|
||||
"ui.viewport_status_clear_hint": "Kikapcsolás: {name} · Esc",
|
||||
|
||||
+70
-1
@@ -793,6 +793,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (lampiran hilang)",
|
||||
"browser.sketch_editing": "{name} (sedang diedit)",
|
||||
"browser.sketch_name": "Sketsa{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "sudah berada di part-nya",
|
||||
"browser.sketches_to_parts_design_body": "pada bodi desain itu sendiri",
|
||||
"browser.sketches_to_parts_moved": "{moved} sketsa dipindahkan ke part tempat sketsa itu berada",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "pada muka tanpa lampiran yang tepat",
|
||||
"browser.sketches_to_parts_not_on_face": "tidak berada di muka sebuah part",
|
||||
"browser.sketches_to_parts_part_missing": "part-nya sudah tidak ada",
|
||||
"browser.sketches_to_parts_result": "{moved} sketsa dipindahkan ke part tempat sketsa itu berada; {stayed} tetap di tempatnya: {why}",
|
||||
"browser.units": "Satuan: {units}",
|
||||
"cam.flat": "Muka datar",
|
||||
"cam.follower": "Pengikut",
|
||||
@@ -860,6 +867,7 @@
|
||||
"cmd.component_move_bodies": "Pindahkan Bodi ke Komponen",
|
||||
"cmd.component_move_features": "Pindahkan Fitur ke Komponen",
|
||||
"cmd.component_move_sketches": "Pindahkan Sketsa ke Komponen",
|
||||
"cmd.component_move_sketches_to_parts": "Pindahkan Sketsa ke Part Masing-masing",
|
||||
"cmd.component_paste_from_design": "Tempel dari Desain Lain",
|
||||
"cmd.component_paste_new": "Tempel Baru",
|
||||
"cmd.component_properties": "Properti Fisik Komponen",
|
||||
@@ -1195,6 +1203,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Spline Titik Kontrol (Derajat 5)",
|
||||
"cmd.sketch_spline_fit_point": "Spline Titik Fit",
|
||||
"cmd.sketch_spline_insert_point": "Tambah Titik Spline",
|
||||
"cmd.sketch_spline_remove_point": "Hapus Titik Spline",
|
||||
"cmd.sketch_spline_reverse": "Balik Arah Spline",
|
||||
"cmd.sketch_spun_profile": "Profil Putar",
|
||||
"cmd.sketch_toggle_diameter": "Beralih Radius / Diameter",
|
||||
@@ -1287,8 +1296,14 @@
|
||||
"color.hex_hint": "#rrggbb",
|
||||
"color.hue": "Rona",
|
||||
"color.linear": "Menampilkan nilai warna dalam ruang linear 0-1",
|
||||
"color.recent": "Warna terbaru",
|
||||
"color.recent_help": "Warna yang Anda pilih di kolom warna mana pun muncul di sini",
|
||||
"color.saturation": "Saturasi (persen)",
|
||||
"color.saturation_value": "Saturasi dan nilai",
|
||||
"color.selected": "Warna terpilih",
|
||||
"color.step_down": "Turunkan",
|
||||
"color.step_up": "Naikkan",
|
||||
"color.value": "Nilai, yaitu kecerahan (persen)",
|
||||
"config.column_filter_empty": "Tidak ada kolom yang cocok",
|
||||
"contact.components_count": "{count} komponen",
|
||||
"contact.enabled_label": "aktif",
|
||||
@@ -1335,6 +1350,7 @@
|
||||
"dialog.dimension_name": "Dimensi: {name}",
|
||||
"dialog.edit_title": "EDIT {title}",
|
||||
"dialog.no_shown_body": "Tidak ada bodi yang ditampilkan untuk diubah: tampilkan satu, atau pilih Bodi Baru",
|
||||
"dialog.pattern_sketch_not_object": "Pilih bodi atau fitur: sketsa bukan keduanya. Ulangi kurvanya dengan Pola atau Cermin milik sketsa itu sendiri, atau ekstrusi dulu.",
|
||||
"dialog.select": "↖ Pilih",
|
||||
"dialog.selected_count": "↖ {count} terpilih",
|
||||
"dialog.selection_guidance": "Pilih input untuk mengubahnya. Klik objek terpilih untuk menghapusnya; Ctrl/Cmd-klik juga mengalihkannya. Tinjau pratinjau, lalu tekan OK.",
|
||||
@@ -1904,6 +1920,7 @@
|
||||
"nav.hfss.help": "Alt+seret kiri menggeser, Shift+Alt+kiri memperbesar/memperkecil, Ctrl+seret kiri di area kosong mengorbit",
|
||||
"nav.inventor.help": "Seret tombol tengah menggeser, Shift+tombol tengah memutar orbit, Ctrl+tombol tengah memperbesar/memperkecil, seret kanan memutar orbit",
|
||||
"nav.maya.help": "Alt+seret kiri mengorbit, Alt+tengah menggeser, Alt+kanan memperbesar/memperkecil; seret tengah juga menggeser",
|
||||
"nav.nx.help": "Seret tombol tengah memutar orbit, Shift+tombol tengah menggeser (tombol tengah + kanan di NX), Ctrl+tombol tengah memperbesar/memperkecil (tombol tengah + kiri di NX); seret kanan tidak menggerakkan kamera",
|
||||
"nav.openscad.help": "Seret kiri di ruang kosong memutar orbit, Shift+seret kiri memilih dengan kotak, seret geometri sketsa mengeditnya; seret kanan atau tombol tengah menggeser, Shift+seret kanan atau Shift+seret tombol tengah memperbesar/memperkecil",
|
||||
"nav.perspective.sketch": "Pengeditan sketsa memakai tampilan ortografis. Selesaikan sketsa untuk beralih ke perspektif.",
|
||||
"nav.right_drag_orbits.help": "Mati: seret kanan tidak menggerakkan kamera dan tombol kanan hanya membuka menu konteks. Memilih pemetaan mouse SolidWorks akan mematikannya.",
|
||||
@@ -2125,6 +2142,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Permukaan melengkung",
|
||||
"prefs.curves": "Permukaan melengkung",
|
||||
"prefs.dark_parts_shading": "Pertahankan bentuk part yang gelap",
|
||||
"prefs.dark_parts_shading_help": "Melakukan pewarnaan tampilan dalam cahaya linear dengan cahaya pengisi langit yang lembut, sehingga muka yang gelap tetap memiliki gradasi dan tidak menjadi hitam",
|
||||
"prefs.dead_zone_note": "Pita berarsir adalah zona mati: gerakan di dalamnya diabaikan.",
|
||||
"prefs.default": "default",
|
||||
"prefs.design": "Default desain",
|
||||
@@ -2139,6 +2158,8 @@
|
||||
"prefs.edges": "Tepi dan pewarnaan",
|
||||
"prefs.extend": "Perpanjang",
|
||||
"prefs.extra_navigation": "Ctrl+seret kiri pada ruang kosong untuk orbit; tahan Space untuk geser.",
|
||||
"prefs.extrude_view_isometric": "Putar tampilan ke isometrik saat Ekstrusi dibuka",
|
||||
"prefs.extrude_view_return": "Kembali ke tampilan sebelumnya saat Ekstrusi ditutup",
|
||||
"prefs.follows_os": "Mengikuti OS",
|
||||
"prefs.font": "Font",
|
||||
"prefs.font_fallback": "Glif yang tidak dimiliki font pilihan akan memakai font bawaan, sehingga tidak ada label yang menampilkan kotak kosong.",
|
||||
@@ -2154,6 +2175,7 @@
|
||||
"prefs.graphics_card_auto": "Otomatis",
|
||||
"prefs.graphics_card_missing": "Kartu grafis yang disimpan tidak tersedia; Otomatis dipakai.",
|
||||
"prefs.graphics_card_restart": "Berlaku setelah SolidCraft dimulai ulang. WGPU_ADAPTER_NAME menggantikan pilihan ini.",
|
||||
"prefs.graphics_card_unused": "Kartu grafis yang tersimpan tidak dipakai untuk jendela ini; kartu yang sedang dipakai ditampilkan di Bantuan › Tentang dan di bilah status. Coba Otomatis, atau atur WGPU_ADAPTER_NAME ke bagian unik dari nama kartu.",
|
||||
"prefs.graphics_note": "Tukar kecepatan dengan kehalusan. Setiap pilihan menampilkan efeknya pada sebuah contoh.",
|
||||
"prefs.graphics_presets_note": "Preset mengatur pilihan di bawah secara bersamaan.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Sembunyikan glif batasan saat zoom out",
|
||||
@@ -2168,6 +2190,8 @@
|
||||
"prefs.history_sketches_note": "Jika Sketsa diatur ke Di bawah fiturnya, Riwayat di sisi kiri mencantumkan setiap sketsa satu tingkat ke dalam, di bawah fitur pertama yang dibuat darinya, selama cukup lebar untuk menampilkan nama. Timeline di bagian bawah selalu mempertahankan urutan pembuatan.",
|
||||
"prefs.history_sketches_under_feature": "Di bawah fiturnya",
|
||||
"prefs.hold_stop_snapping": "Tahan untuk menghentikan snap",
|
||||
"prefs.home_fit_size": "Ukuran Awal dan Pas",
|
||||
"prefs.home_fit_size_note": "Seberapa besar bagian tampilan yang diisi model saat Awal, Pas, desain yang baru dibuka, atau perubahan tampilan lewat navigator tampilan membingkainya, baik pengaturan di bawah ini aktif maupun tidak. 100 %: mengisi tampilan di antara panel, kompas, dan bilah navigasi.",
|
||||
"prefs.icons_only": "Hanya ikon",
|
||||
"prefs.imported_file": "File yang diimpor",
|
||||
"prefs.in_browser": "Di dalam penjelajah",
|
||||
@@ -2176,13 +2200,20 @@
|
||||
"prefs.interface_language": "Bahasa antarmuka",
|
||||
"prefs.interface_note": "Tampilan SolidCraft dan letak bagian-bagiannya.",
|
||||
"prefs.interval_minutes": "{n} mnt",
|
||||
"prefs.invert_navigator_arrows": "Balikkan panah rotasi kubus tampilan",
|
||||
"prefs.invert_navigator_arrows_note": "Panah di sekeliling kubus memutar orbit kamera ke arah yang ditunjuknya, seperti di Fusion dan Inventor: panah di kiri menunjuk ke kanan dan mengayunkan kamera ke kanan. Mati: panah memutar kubus ke sisi tempatnya berada, sehingga panah kiri mengayunkan kamera ke kiri. Panah roll yang melengkung tidak terpengaruh.",
|
||||
"prefs.keep_work_on_tab_switch": "Biarkan dialog dan alat tetap terbuka saat berpindah desain",
|
||||
"prefs.keep_work_on_tab_switch_note": "Setiap tab desain mempertahankan dialog atau alat yang terbuka, beserta apa yang Anda pilih dan ketik di dalamnya, dan tab toolbar-nya. Semuanya kembali saat Anda kembali ke desain itu. Mati: berpindah desain membatalkannya dan menampilkan tab Model.",
|
||||
"prefs.labels": "Label",
|
||||
"prefs.language_system": "Sistem",
|
||||
"prefs.layout": "Tata letak",
|
||||
"prefs.layout_note": "Ringkas menempelkan panel ke tepi jendela. Translusensi penjelajah tersedia di Mengambang. Ubah sisi Asisten di halaman Asisten.",
|
||||
"prefs.left": "Kiri",
|
||||
"prefs.left_click": "Klik kiri",
|
||||
"prefs.line_drag_draws_one": "Tekan dan seret menggambar satu garis",
|
||||
"prefs.line_drag_draws_one_note": "Di alat Garis, menekan, menyeret, lalu melepas menggambar satu garis dari titik tekan hingga titik lepas, dan garis berakhir di sana. Klik tetap menggambar rantai. Mati: seretan menaruh titik di tempat dilepas, seperti klik.",
|
||||
"prefs.line_hover_tangent_arc": "Diamkan penunjuk di titik terakhir garis untuk beralih ke busur tangen",
|
||||
"prefs.line_hover_tangent_arc_note": "Di alat Garis, setelah satu segmen digambar, jauhkan penunjuk dari titik terakhir rantai, kembali, lalu diamkan penunjuk di titik itu: klik berikutnya menentukan ujung busur tangen, dan gerakan yang sama kembali ke garis. Mati: berdiam di sana tidak berpengaruh.",
|
||||
"prefs.linked_designs": "Desain tertaut",
|
||||
"prefs.live_input": "Input langsung",
|
||||
"prefs.live_preview": "Pratinjau langsung",
|
||||
@@ -2206,6 +2237,8 @@
|
||||
"prefs.mouse_mapping": "Pemetaan mouse",
|
||||
"prefs.navigation": "Navigasi",
|
||||
"prefs.navigation_note": "Cara mouse, trackpad, dan mouse 3D menggerakkan tampilan.",
|
||||
"prefs.navigator_fit": "Buat model pas di tampilan setelah perubahan tampilan lewat navigator",
|
||||
"prefs.navigator_fit_note": "Sebuah muka, sudut, atau panah pada kubus tampilan, atau sumbu pada gizmo, juga menghadap ke tengah model dari satu jarak yang membuatnya pas dari segala sisi (bola pembatasnya). Awal memakai zoom dan pusat yang sama selama opsi ini aktif, sehingga tampilan yang dipilih setelahnya hanya berputar. Mati: tampilan berputar di tempat, dengan target dan zoom tetap.",
|
||||
"prefs.network_host_remove": "Hapus",
|
||||
"prefs.network_locations": "Izinkan lokasi jaringan di desain",
|
||||
"prefs.network_locations_note": "Nonaktif: file tertaut sebuah desain di komputer lain (\\\\server\\share) hanya dibuka dari server yang tercantum di sini, atau setelah Anda mengizinkan salah satunya saat ditanya.",
|
||||
@@ -2254,6 +2287,8 @@
|
||||
"prefs.render_previous_geometry_budget": "Gunakan anggaran geometri pratinjau Render sebelumnya",
|
||||
"prefs.render_previous_geometry_budget_help": "Pulihkan batas geometri tetap sebelumnya (512 MiB di desktop 64-bit, 128 MiB di tempat lain) alih-alih anggaran berbasis perangkat.",
|
||||
"prefs.reports_saved": "{n} tersimpan",
|
||||
"prefs.ribbon_click_ends_tool": "Mengklik tombol alat yang sedang berjalan mengakhirinya",
|
||||
"prefs.ribbon_click_ends_tool_note": "Klik pada tombol ribbon yang menyala dari alat sketsa yang sedang dipakai mengakhiri alat itu, sehingga klik di tampilan kembali memilih. Mati: klik memulai alat itu lagi.",
|
||||
"prefs.ribbon_reveal": "Tampilkan hasil pencarian di ribbon",
|
||||
"prefs.ribbon_reveal_note": "Mengarahkan kursor ke hasil di Cari perintah menunjukkan letak perintah itu di ribbon: tab-nya, tombolnya, atau barisnya di dropdown panel. Perintah yang dimulai dari pencarian berkedip di sana, dan perintah yang berjalan dari dropdown menandai panelnya.",
|
||||
"prefs.right_click": "Klik kanan",
|
||||
@@ -2283,6 +2318,8 @@
|
||||
"prefs.sketch_exit_view.previous": "Kembali ke tampilan sebelumnya",
|
||||
"prefs.sketch_exit_view.unless_rotated": "Kembali kecuali diputar",
|
||||
"prefs.sketch_exit_view_note": "Ke mana tampilan beralih setelah Selesai Sketsa, Batal, atau Esc dua kali.",
|
||||
"prefs.sketch_face_activates_part": "Menggambar sketsa di muka part membuat part itu aktif",
|
||||
"prefs.sketch_face_activates_part_note": "Sketsa yang dimulai di muka bodi milik komponen lain mengaktifkan komponen itu lebih dulu, sehingga sketsa menjadi milik komponen tersebut dan tetap bersama part itu di penjelajah. Part itu tetap aktif setelahnya. Mati: sketsa menjadi milik komponen aktif.",
|
||||
"prefs.sketch_open_look_at": "Putar untuk menghadap sketsa saat dibuka",
|
||||
"prefs.sketch_open_look_at_note": "Mati: membuat atau mengedit sketsa membiarkan tampilan apa adanya. Look At menghadapkan tampilan ke sketsa saat Anda menginginkannya.",
|
||||
"prefs.sketch_recent_snap": "Snap otomatis ke pusat dan titik tengah terbaru",
|
||||
@@ -2314,6 +2351,24 @@
|
||||
"prefs.status_error": "Error",
|
||||
"prefs.status_selected": "Terpilih",
|
||||
"prefs.status_warning": "Peringatan",
|
||||
"prefs.structure.appearance": "Tampilan",
|
||||
"prefs.structure.appearance_note": "Warna, teks, dan aksesibilitas.",
|
||||
"prefs.structure.in_profile": "Dalam profil",
|
||||
"prefs.structure.layout": "Tata letak",
|
||||
"prefs.structure.layout_note": "Letak bagian-bagian jendela.",
|
||||
"prefs.structure.look": "Rupa",
|
||||
"prefs.structure.measure_pointer": "Ukur dan penunjuk",
|
||||
"prefs.structure.mouse3d": "Mouse 3D",
|
||||
"prefs.structure.mouse3d_note": "SpaceMouse dan SpaceNavigator.",
|
||||
"prefs.structure.navigation_note": "Mouse dan trackpad. Mouse 3D punya halamannya sendiri.",
|
||||
"prefs.structure.profile": "Profil",
|
||||
"prefs.structure.profile_edited": "Berubah sejak disimpan",
|
||||
"prefs.structure.profile_hover": "Disimpan di profil pengaturan",
|
||||
"prefs.structure.scale_percent": "{percent}%",
|
||||
"prefs.structure.sketch": "Sketsa",
|
||||
"prefs.structure.tools": "Alat",
|
||||
"prefs.structure.tools_note": "Cara kerja alat pemodelan.",
|
||||
"prefs.structure.work": "Kerja",
|
||||
"prefs.supersampling_brief": "Menggambar tampilan lebih besar lalu menyaringnya turun: sangat tajam, tetapi lebih berat.",
|
||||
"prefs.tab_accent_note": "Aksen mengikuti tab aktif",
|
||||
"prefs.theme_file": "file tema",
|
||||
@@ -3811,6 +3866,7 @@
|
||||
"sketch_text.formula": "Mulai dengan = untuk menyusun kata dari parameter: ='W' + number(Width)",
|
||||
"sketch_text.italic": "Miring",
|
||||
"sketch_text.loading_fonts": "Mencari font terpasang…",
|
||||
"sketch_text.no_bold": "Font bawaan tidak memiliki varian tebal: pilih font yang terpasang",
|
||||
"sketch_text.on_path": "Teks mengikuti jalurnya: kurva yang menempatkannya",
|
||||
"sketch_text.path_clear": "Lepaskan teks dari kurvanya",
|
||||
"sketch_text.path_hint": "Kurva sketsa yang diikuti teks: klik di sini, lalu kurvanya",
|
||||
@@ -4213,6 +4269,9 @@
|
||||
"ui.ai_permissions": "Izin",
|
||||
"ui.ai_preset": "Preset",
|
||||
"ui.ai_protocol": "Protokol",
|
||||
"ui.ai_reasoning_effort": "Upaya penalaran",
|
||||
"ui.ai_reasoning_effort_field_hint": "kosong = default penyedia",
|
||||
"ui.ai_reasoning_effort_hint": "Opsional. Dikirim ke endpoint sebagai `reasoning_effort`; nilai yang diterima berbeda untuk setiap penyedia (misalnya `low`, `high`, atau `max`). Nilai yang Anda atur di sini tidak pernah diganti oleh SolidCraft. Biarkan kosong agar tidak ada yang dikirim dan default penyedia tetap berlaku.",
|
||||
"ui.ai_remove": "Hapus",
|
||||
"ui.ai_request_timed_out": "Model membutuhkan waktu lebih dari {seconds} s. Anda dapat menaikkan batas waktu permintaan untuk endpoint ini di Preferensi › Asisten.",
|
||||
"ui.ai_request_timeout": "Batas waktu permintaan",
|
||||
@@ -4378,6 +4437,7 @@
|
||||
"ui.dark": "Gelap",
|
||||
"ui.default_modelling_orientation": "Orientasi pemodelan default",
|
||||
"ui.delete": "Hapus",
|
||||
"ui.delete_select_first": "Pilih dulu yang akan dihapus: fitur, bodi, komponen, atau geometri sketsa",
|
||||
"ui.depth": "Kedalaman",
|
||||
"ui.design": "Desain",
|
||||
"ui.design_properties.author": "Pembuat",
|
||||
@@ -4470,6 +4530,7 @@
|
||||
"ui.edit_in_place_saved_elsewhere": "Part disimpan ke file lain; rakitan masih menautkan {name}",
|
||||
"ui.edit_in_place_wait": "Tunggu sampai proses yang berjalan di desain selesai",
|
||||
"ui.edit_section_analysis": "Edit Analisis Penampang",
|
||||
"ui.edit_sketch_empty": "Tidak ada sketsa untuk diedit",
|
||||
"ui.ellipse": "Elips",
|
||||
"ui.emboss_wrap_note": "Profil garis ortogonal yang tangen pada satu silinder yang didukung dibungkus otomatis. Pilih dinding untuk menentukan target.",
|
||||
"ui.end_radius": "Radius Akhir",
|
||||
@@ -4480,7 +4541,9 @@
|
||||
"ui.equation_explicit": "Eksplisit",
|
||||
"ui.equation_form": "Bentuk",
|
||||
"ui.equation_note": "Variabelnya berupa angka biasa: sin(t) menganggapnya dalam radian. Hasil tanpa satuan memakai satuan desain.",
|
||||
"ui.equation_note_range": "Variabelnya berupa panjang, seperti rentangnya: campurkan dengan parameter panjang, dan bagi dengan sebuah panjang untuk sin(). Hasil tanpa satuan memakai satuan desain.",
|
||||
"ui.equation_parametric": "Parametrik",
|
||||
"ui.equation_variable_unit": "Variabel memakai satuan rentang",
|
||||
"ui.equation_x_of_t": "x(t)",
|
||||
"ui.equation_y_of_t": "y(t)",
|
||||
"ui.equation_y_of_x": "y(x)",
|
||||
@@ -4655,12 +4718,14 @@
|
||||
"ui.joints": "Sambungan",
|
||||
"ui.keyboard_shortcuts": "Pintasan Keyboard",
|
||||
"ui.language": "Bahasa",
|
||||
"ui.lasso_select": "Lasso (seret lengkung tertutup bebas: memilih yang seluruhnya berada di dalam)",
|
||||
"ui.layout_compact": "Ringkas",
|
||||
"ui.layout_floating": "Mengambang",
|
||||
"ui.length": "Panjang",
|
||||
"ui.length_decimals": "Desimal panjang",
|
||||
"ui.light": "Terang",
|
||||
"ui.line": "Garis",
|
||||
"ui.line_arc_hint": "Busur tangen: klik ujung busur. Diamkan lagi di titik terakhir untuk garis; klik kanan atau Esc untuk selesai",
|
||||
"ui.link_copies": "Tautkan Salinan Identik",
|
||||
"ui.link_copies_keep": "Pertahankan",
|
||||
"ui.link_copies_link": "Tautkan",
|
||||
@@ -4749,6 +4814,7 @@
|
||||
"ui.part_size_shared": "Membangun ulang part, menggantikan fitur tambahan apa pun. Semua kemunculan komponen ini diperbarui.",
|
||||
"ui.paste": "Tempel",
|
||||
"ui.path": "Jalur",
|
||||
"ui.pattern_object_type_tip": "Bodi: bentuk solid, dipilih di tampilan atau Penjelajah; masing-masing disalin utuh.\nFitur: langkah-langkah Riwayat, seperti ekstrusi atau lubang, dipilih di Riwayat atau pada muka yang dibuatnya; langkah itu diulang.\nSketsa bukan keduanya: ulangi kurvanya dengan Pola atau Cermin milik sketsa itu sendiri, atau ekstrusi dulu.",
|
||||
"ui.pattern_on_path": "Pola pada Jalur",
|
||||
"ui.perforate_margin_default": "dari diameter",
|
||||
"ui.perforate_pitch": "Pitch",
|
||||
@@ -4794,9 +4860,9 @@
|
||||
"ui.polygon": "Poligon",
|
||||
"ui.preferences": "Preferensi",
|
||||
"ui.press_a_key": "tekan tombol…",
|
||||
"ui.print_face_body_not_sent": "Bodi permukaan itu tidak dikirim: tambahkan dulu ke Bodi",
|
||||
"ui.print_face_cleared": "{body}: permukaan cetak dihapus",
|
||||
"ui.print_face_input": "Permukaan cetak",
|
||||
"ui.print_face_one_body": "Permukaan cetak milik satu bodi: kirim bodi itu sendiri untuk mengubahnya",
|
||||
"ui.print_face_pick": "Pilih satu permukaan datar dari bodi padat untuk dijadikan permukaan cetak",
|
||||
"ui.print_face_set": "{body}: permukaan cetak diatur; Kirim ke slicer meletakkan bodi di atasnya",
|
||||
"ui.profile": "Profil",
|
||||
@@ -5024,12 +5090,15 @@
|
||||
"ui.view_angles": "Sudut tampilan",
|
||||
"ui.view_angles_convention": "Derajat terhadap sumbu atas. Yaw 0° adalah tampilan Depan, 90° Kiri, −90° Kanan, 180° Belakang; pitch 90° adalah tampilan Atas; roll positif memutar model searah jarum jam.",
|
||||
"ui.view_angles_help": "Menampilkan pitch, yaw, dan roll tampilan dalam derajat serta proyeksinya di bawah gizmo orientasi. Klik pembacaan untuk mengetik sudut yang tepat.",
|
||||
"ui.view_angles_lock": "Kunci {axis}",
|
||||
"ui.view_angles_ortho": "Ortho",
|
||||
"ui.view_angles_persp": "Persp",
|
||||
"ui.view_angles_pitch": "Pitch",
|
||||
"ui.view_angles_readout": "Pitch {pitch} · Yaw {yaw} · Roll {roll}",
|
||||
"ui.view_angles_roll": "Roll",
|
||||
"ui.view_angles_unlock": "Buka kunci {axis}",
|
||||
"ui.view_angles_yaw": "Yaw",
|
||||
"ui.view_angles_zero": "Atur {axis} ke 0°",
|
||||
"ui.view_centre": "Pusat tampilan",
|
||||
"ui.viewport_status_clear": "Matikan {name}",
|
||||
"ui.viewport_status_clear_hint": "Matikan {name} · Esc",
|
||||
|
||||
+65
-1
@@ -793,6 +793,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (aggancio perso)",
|
||||
"browser.sketch_editing": "{name} (in modifica)",
|
||||
"browser.sketch_name": "Schizzo{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "già nella loro parte",
|
||||
"browser.sketches_to_parts_design_body": "su un corpo del progetto stesso",
|
||||
"browser.sketches_to_parts_moved": "Spostati {moved} schizzi nella parte su cui si trovano",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "su una faccia senza un collegamento esatto",
|
||||
"browser.sketches_to_parts_not_on_face": "non sulla faccia di una parte",
|
||||
"browser.sketches_to_parts_part_missing": "la loro parte non esiste più",
|
||||
"browser.sketches_to_parts_result": "Spostati {moved} schizzi nella parte su cui si trovano; {stayed} sono rimasti dove erano: {why}",
|
||||
"browser.units": "Unità: {units}",
|
||||
"cam.flat": "Faccia piana",
|
||||
"cam.follower": "Cedente",
|
||||
@@ -860,6 +867,7 @@
|
||||
"cmd.component_move_bodies": "Sposta corpi nel componente",
|
||||
"cmd.component_move_features": "Sposta lavorazioni nel componente",
|
||||
"cmd.component_move_sketches": "Sposta schizzi nel componente",
|
||||
"cmd.component_move_sketches_to_parts": "Sposta schizzi nelle rispettive parti",
|
||||
"cmd.component_paste_from_design": "Incolla da un altro progetto",
|
||||
"cmd.component_paste_new": "Incolla come nuovo",
|
||||
"cmd.component_properties": "Proprietà fisiche del componente",
|
||||
@@ -1195,6 +1203,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Spline per punti di controllo (grado 5)",
|
||||
"cmd.sketch_spline_fit_point": "Spline interpolante",
|
||||
"cmd.sketch_spline_insert_point": "Aggiungi punto alla spline",
|
||||
"cmd.sketch_spline_remove_point": "Rimuovi punto dalla spline",
|
||||
"cmd.sketch_spline_reverse": "Inverti direzione della spline",
|
||||
"cmd.sketch_spun_profile": "Profilo di tornitura",
|
||||
"cmd.sketch_toggle_diameter": "Alterna raggio / diametro",
|
||||
@@ -1341,6 +1350,7 @@
|
||||
"dialog.dimension_name": "Quota: {name}",
|
||||
"dialog.edit_title": "MODIFICA {title}",
|
||||
"dialog.no_shown_body": "Nessun corpo visibile da modificare: mostrane uno o scegli Nuovo corpo",
|
||||
"dialog.pattern_sketch_not_object": "Seleziona corpi o lavorazioni: uno schizzo non è nessuno dei due. Ripeti le sue curve con Ripetizione o Specchia dello schizzo stesso, oppure estrudilo prima.",
|
||||
"dialog.select": "↖ Seleziona",
|
||||
"dialog.selected_count": "↖ Selezionati: {count}",
|
||||
"dialog.selection_guidance": "Seleziona un dato di ingresso per modificarlo. Fai clic sugli oggetti selezionati per rimuoverli; anche Ctrl/Cmd-clic ne alterna la selezione. Controlla l'anteprima, poi premi OK.",
|
||||
@@ -1910,6 +1920,7 @@
|
||||
"nav.hfss.help": "Alt+trascinamento con il pulsante sinistro esegue la panoramica, Shift+Alt+pulsante sinistro lo zoom, Ctrl+trascinamento con il pulsante sinistro su uno spazio vuoto orbita",
|
||||
"nav.inventor.help": "Il trascinamento centrale fa la panoramica, Shift+centrale orbita, Ctrl+centrale fa lo zoom, il trascinamento destro orbita",
|
||||
"nav.maya.help": "Alt+trascinamento con il pulsante sinistro orbita, Alt+pulsante centrale esegue la panoramica, Alt+pulsante destro lo zoom; anche il trascinamento con il pulsante centrale esegue la panoramica",
|
||||
"nav.nx.help": "Il trascinamento centrale orbita, Shift+centrale fa la panoramica (centrale + destro di NX), Ctrl+centrale fa lo zoom (centrale + sinistro di NX); il trascinamento destro lascia ferma la vista",
|
||||
"nav.openscad.help": "Il trascinamento sinistro nello spazio vuoto orbita, Shift+trascinamento sinistro seleziona con un riquadro, il trascinamento sulla geometria dello schizzo la modifica; il trascinamento destro o centrale fa la panoramica, Shift+destro o Shift+centrale fa lo zoom",
|
||||
"nav.perspective.sketch": "La modifica dello schizzo usa la vista ortogonale. Termina lo schizzo per passare alla prospettiva.",
|
||||
"nav.right_drag_orbits.help": "Disattivato: il trascinamento con il pulsante destro non muove la camera e il pulsante destro apre solo il menu contestuale. Scegliendo l'assegnazione del mouse SolidWorks l'opzione si disattiva.",
|
||||
@@ -2131,6 +2142,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Superfici curve",
|
||||
"prefs.curves": "Superfici curve",
|
||||
"prefs.dark_parts_shading": "Mantieni la forma delle parti scure",
|
||||
"prefs.dark_parts_shading_help": "Ombreggia la vista in luce lineare con un morbido riempimento di luce dal cielo, così le facce scure mantengono una sfumatura invece di diventare nere",
|
||||
"prefs.dead_zone_note": "La fascia tratteggiata è la zona morta: il movimento al suo interno viene ignorato.",
|
||||
"prefs.default": "predefinito",
|
||||
"prefs.design": "Impostazioni predefinite del progetto",
|
||||
@@ -2145,6 +2158,8 @@
|
||||
"prefs.edges": "Spigoli e ombreggiatura",
|
||||
"prefs.extend": "Estendi",
|
||||
"prefs.extra_navigation": "Ctrl+trascina con il sinistro sullo spazio vuoto per orbitare; tieni premuto Space per la panoramica.",
|
||||
"prefs.extrude_view_isometric": "Porta la vista in isometrica all'apertura di Estrusione",
|
||||
"prefs.extrude_view_return": "Torna alla vista precedente alla chiusura di Estrusione",
|
||||
"prefs.follows_os": "Segue il sistema operativo",
|
||||
"prefs.font": "Carattere",
|
||||
"prefs.font_fallback": "I simboli che mancano nel carattere scelto passano a quello integrato, così nessuna etichetta mostra un riquadro vuoto.",
|
||||
@@ -2160,6 +2175,7 @@
|
||||
"prefs.graphics_card_auto": "Automatica",
|
||||
"prefs.graphics_card_missing": "La scheda grafica salvata non è disponibile; viene usata Automatica.",
|
||||
"prefs.graphics_card_restart": "Ha effetto dopo il riavvio di SolidCraft. WGPU_ADAPTER_NAME prevale su questa scelta.",
|
||||
"prefs.graphics_card_unused": "La scheda grafica salvata non è stata usata per questa finestra; la scheda in uso è indicata in Aiuto › Informazioni su e nella barra di stato. Prova Automatica, oppure imposta WGPU_ADAPTER_NAME su una parte univoca del nome della scheda.",
|
||||
"prefs.graphics_note": "Scegli tra velocità e fluidità dell'immagine. Ogni scelta mostra il suo effetto su un esempio.",
|
||||
"prefs.graphics_presets_note": "Una preimpostazione regola insieme le scelte qui sotto.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Nascondi i simboli dei vincoli quando lo zoom è ridotto",
|
||||
@@ -2174,6 +2190,8 @@
|
||||
"prefs.history_sketches_note": "Con Schizzi impostato su Sotto la lavorazione, la Cronologia a sinistra elenca ogni schizzo rientrato di un livello, sotto la prima lavorazione creata a partire da esso, quando è abbastanza larga da mostrare i nomi. La cronologia in basso mantiene sempre l'ordine di creazione.",
|
||||
"prefs.history_sketches_under_feature": "Sotto la lavorazione",
|
||||
"prefs.hold_stop_snapping": "Tieni premuto per disattivare l'aggancio",
|
||||
"prefs.home_fit_size": "Dimensione di Inizio e Adatta",
|
||||
"prefs.home_fit_size_note": "Quanta parte della vista occupa il modello quando Inizio, Adatta, un progetto aperto o un cambio di vista dal navigatore della vista ne impostano l'inquadratura, sia o no attiva l'impostazione qui sotto. 100 %: riempie la vista tra i pannelli, la bussola e la barra di navigazione.",
|
||||
"prefs.icons_only": "Solo icone",
|
||||
"prefs.imported_file": "File importato",
|
||||
"prefs.in_browser": "Nella Struttura",
|
||||
@@ -2182,13 +2200,20 @@
|
||||
"prefs.interface_language": "Lingua dell'interfaccia",
|
||||
"prefs.interface_note": "Aspetto di SolidCraft e disposizione dei suoi elementi.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_navigator_arrows": "Inverti le frecce di rotazione del cubo della vista",
|
||||
"prefs.invert_navigator_arrows_note": "Le frecce attorno al cubo fanno orbitare la camera nella direzione in cui puntano, come in Fusion e Inventor: la freccia a sinistra punta a destra e sposta la camera a destra. Disattivato: una freccia ruota il cubo verso il lato in cui si trova, quindi la freccia sinistra sposta la camera a sinistra. Le frecce curve di rollio non sono interessate.",
|
||||
"prefs.keep_work_on_tab_switch": "Mantieni aperti finestre di dialogo e strumenti quando cambi progetto",
|
||||
"prefs.keep_work_on_tab_switch_note": "Ogni scheda di progetto conserva la finestra di dialogo o lo strumento aperto, con ciò che hai scelto e digitato, e la propria scheda della barra degli strumenti. Li ritrovi quando torni al progetto. Disattivato: cambiando progetto vengono annullati e compare la scheda Modella.",
|
||||
"prefs.labels": "Etichette",
|
||||
"prefs.language_system": "Sistema",
|
||||
"prefs.layout": "Disposizione",
|
||||
"prefs.layout_note": "Compatta: pannelli ancorati ai bordi della finestra. La trasparenza della Struttura è disponibile in Flottante. Cambia il lato dell'Assistente nella pagina Assistente.",
|
||||
"prefs.left": "Sinistra",
|
||||
"prefs.left_click": "Clic sinistro",
|
||||
"prefs.line_drag_draws_one": "Premi e trascina per disegnare una linea",
|
||||
"prefs.line_drag_draws_one_note": "Nello strumento Linea, premere, trascinare e rilasciare disegna una linea dal punto di pressione a quello di rilascio, e il disegno termina lì. I clic disegnano comunque una catena. Disattivato: un trascinamento posiziona un punto dove viene rilasciato, come un clic.",
|
||||
"prefs.line_hover_tangent_arc": "Fermati sull'ultimo punto di una linea per passare a un arco tangente",
|
||||
"prefs.line_hover_tangent_arc_note": "Nello strumento Linea, dopo aver disegnato un segmento, allontanati dall'ultimo punto della catena, torna indietro e tieni fermo il puntatore su di esso: il clic successivo termina un arco tangente, e lo stesso gesto riporta alle linee. Disattivato: fermarsi lì non fa nulla.",
|
||||
"prefs.linked_designs": "Progetti collegati",
|
||||
"prefs.live_input": "Input in tempo reale",
|
||||
"prefs.live_preview": "Anteprima dal vivo",
|
||||
@@ -2212,6 +2237,8 @@
|
||||
"prefs.mouse_mapping": "Assegnazione del mouse",
|
||||
"prefs.navigation": "Navigazione",
|
||||
"prefs.navigation_note": "Come mouse, trackpad e mouse 3D spostano la vista.",
|
||||
"prefs.navigator_fit": "Adatta il modello dopo un cambio di vista dal navigatore",
|
||||
"prefs.navigator_fit_note": "Una faccia, un angolo o una freccia del cubo della vista, o un asse del gizmo, guarda anche il centro del modello dall'unica distanza che lo inquadra da ogni lato (la sua sfera di ingombro). Inizio usa lo stesso zoom e lo stesso centro finché questa opzione è attiva, quindi le viste scelte dopo si limitano a ruotare. Disattivato: la vista ruota sul posto, mantenendo punto di mira e zoom.",
|
||||
"prefs.network_host_remove": "Rimuovi",
|
||||
"prefs.network_locations": "Consenti posizioni di rete nei progetti",
|
||||
"prefs.network_locations_note": "Disattivato: i file collegati di un progetto su un altro computer (\\\\server\\share) vengono aperti solo dai server elencati qui, o dopo che ne consenti uno quando richiesto.",
|
||||
@@ -2260,6 +2287,8 @@
|
||||
"prefs.render_previous_geometry_budget": "Usa il budget di geometria precedente per l'anteprima di Rendering",
|
||||
"prefs.render_previous_geometry_budget_help": "Ripristina il limite fisso di geometria precedente (512 MiB su desktop a 64 bit, 128 MiB altrove) invece del budget basato sul dispositivo.",
|
||||
"prefs.reports_saved": "{n} salvati",
|
||||
"prefs.ribbon_click_ends_tool": "Un clic sul pulsante dello strumento in uso lo termina",
|
||||
"prefs.ribbon_click_ends_tool_note": "Un clic sul pulsante evidenziato nella barra multifunzione dello strumento di schizzo in uso termina lo strumento, così i clic nella vista tornano a selezionare. Disattivato: il clic avvia di nuovo lo strumento.",
|
||||
"prefs.ribbon_reveal": "Mostra i risultati della ricerca nella barra multifunzione",
|
||||
"prefs.ribbon_reveal_note": "Passando il puntatore su un risultato in Cerca comandi si vede dove si trova il comando nella barra multifunzione: la sua scheda, il suo pulsante o la sua riga nel menu a discesa di un pannello. Un comando avviato dalla ricerca lampeggia lì, e un comando in esecuzione da un menu a discesa ne evidenzia il pannello.",
|
||||
"prefs.right_click": "Clic destro",
|
||||
@@ -2289,6 +2318,8 @@
|
||||
"prefs.sketch_exit_view.previous": "Torna alla vista precedente",
|
||||
"prefs.sketch_exit_view.unless_rotated": "Torna, se non ruotata",
|
||||
"prefs.sketch_exit_view_note": "Dove va la vista dopo Termina schizzo, Annulla o un doppio Esc.",
|
||||
"prefs.sketch_face_activates_part": "Creare uno schizzo sulla faccia di una parte rende attiva quella parte",
|
||||
"prefs.sketch_face_activates_part_note": "Uno schizzo avviato sulla faccia di un corpo di un altro componente attiva prima quel componente, così lo schizzo gli appartiene e resta con quella parte nella Struttura. In seguito la parte resta attiva. Disattivato: lo schizzo appartiene al componente attivo.",
|
||||
"prefs.sketch_open_look_at": "Ruota la vista verso lo schizzo quando si apre",
|
||||
"prefs.sketch_open_look_at_note": "Disattivato: creando o modificando uno schizzo la vista resta com'è. Vista normale orienta la vista sullo schizzo quando vuoi.",
|
||||
"prefs.sketch_recent_snap": "Aggancia automaticamente ai centri e punti medi recenti",
|
||||
@@ -2320,6 +2351,24 @@
|
||||
"prefs.status_error": "Errore",
|
||||
"prefs.status_selected": "Selezionato",
|
||||
"prefs.status_warning": "Avviso",
|
||||
"prefs.structure.appearance": "Aspetto",
|
||||
"prefs.structure.appearance_note": "Colori, testo e accessibilità.",
|
||||
"prefs.structure.in_profile": "Nel profilo",
|
||||
"prefs.structure.layout": "Disposizione",
|
||||
"prefs.structure.layout_note": "Dove si trovano le parti della finestra.",
|
||||
"prefs.structure.look": "Aspetto visivo",
|
||||
"prefs.structure.measure_pointer": "Misura e puntatore",
|
||||
"prefs.structure.mouse3d": "Mouse 3D",
|
||||
"prefs.structure.mouse3d_note": "SpaceMouse e SpaceNavigator.",
|
||||
"prefs.structure.navigation_note": "Mouse e trackpad. Il mouse 3D ha una pagina propria.",
|
||||
"prefs.structure.profile": "Profilo",
|
||||
"prefs.structure.profile_edited": "Modificato dopo il salvataggio",
|
||||
"prefs.structure.profile_hover": "Salvato nei profili delle impostazioni",
|
||||
"prefs.structure.scale_percent": "{percent}%",
|
||||
"prefs.structure.sketch": "Schizzo",
|
||||
"prefs.structure.tools": "Strumenti",
|
||||
"prefs.structure.tools_note": "Come si comportano gli strumenti di modellazione.",
|
||||
"prefs.structure.work": "Lavoro",
|
||||
"prefs.supersampling_brief": "Disegna la vista più grande e poi la riduce con un filtro: molto nitida, ma più pesante.",
|
||||
"prefs.tab_accent_note": "L'accento segue la scheda attiva",
|
||||
"prefs.theme_file": "file del tema",
|
||||
@@ -3817,6 +3866,7 @@
|
||||
"sketch_text.formula": "Inizia con = per costruire le parole dai parametri: ='W' + number(Width)",
|
||||
"sketch_text.italic": "Corsivo",
|
||||
"sketch_text.loading_fonts": "Ricerca dei caratteri installati…",
|
||||
"sketch_text.no_bold": "Il carattere integrato non ha il grassetto: scegli un carattere installato",
|
||||
"sketch_text.on_path": "Il testo segue il percorso: la curva ne determina la posizione",
|
||||
"sketch_text.path_clear": "Scollega il testo dalla curva",
|
||||
"sketch_text.path_hint": "Una curva dello schizzo seguita dal testo: fai clic qui, poi sulla curva",
|
||||
@@ -4219,6 +4269,9 @@
|
||||
"ui.ai_permissions": "Autorizzazioni",
|
||||
"ui.ai_preset": "Preimpostazione",
|
||||
"ui.ai_protocol": "Protocollo",
|
||||
"ui.ai_reasoning_effort": "Livello di ragionamento",
|
||||
"ui.ai_reasoning_effort_field_hint": "vuoto = predefinito del provider",
|
||||
"ui.ai_reasoning_effort_hint": "Facoltativo. Inviato all'endpoint come `reasoning_effort`; i valori accettati differiscono da provider a provider (ad esempio `low`, `high` o `max`). Un valore impostato qui non viene mai sostituito da SolidCraft. Lascia vuoto per non inviare nulla e mantenere il valore predefinito del provider.",
|
||||
"ui.ai_remove": "Rimuovi",
|
||||
"ui.ai_request_timed_out": "Il modello ha impiegato più di {seconds} s. Puoi aumentare il timeout della richiesta per questo endpoint in Preferenze › Assistente.",
|
||||
"ui.ai_request_timeout": "Timeout della richiesta",
|
||||
@@ -4384,6 +4437,7 @@
|
||||
"ui.dark": "Scuro",
|
||||
"ui.default_modelling_orientation": "Orientamento di modellazione predefinito",
|
||||
"ui.delete": "Elimina",
|
||||
"ui.delete_select_first": "Seleziona prima cosa eliminare: lavorazioni, corpi, componenti o geometria dello schizzo",
|
||||
"ui.depth": "Profondità",
|
||||
"ui.design": "Progettazione",
|
||||
"ui.design_properties.author": "Autore",
|
||||
@@ -4476,6 +4530,7 @@
|
||||
"ui.edit_in_place_saved_elsewhere": "La parte è stata salvata in un altro file; l'assieme è ancora collegato a {name}",
|
||||
"ui.edit_in_place_wait": "Attendi il termine di ciò che è in esecuzione nel progetto",
|
||||
"ui.edit_section_analysis": "Modifica analisi in sezione",
|
||||
"ui.edit_sketch_empty": "Nessuno schizzo da modificare",
|
||||
"ui.ellipse": "Ellisse",
|
||||
"ui.emboss_wrap_note": "I profili di linee ortogonali tangenti a un cilindro supportato si avvolgono automaticamente. Scegliere una parete per definire la destinazione.",
|
||||
"ui.end_radius": "Raggio finale",
|
||||
@@ -4486,7 +4541,9 @@
|
||||
"ui.equation_explicit": "Esplicita",
|
||||
"ui.equation_form": "Forma",
|
||||
"ui.equation_note": "La variabile è un numero puro: sin(t) la interpreta in radianti. Un risultato senza unità è nell'unità del progetto.",
|
||||
"ui.equation_note_range": "La variabile è una lunghezza, come il suo intervallo: combinala con parametri di lunghezza e dividila per una lunghezza per sin(). Un risultato senza unità è nell'unità del progetto.",
|
||||
"ui.equation_parametric": "Parametrica",
|
||||
"ui.equation_variable_unit": "La variabile ha l'unità dell'intervallo",
|
||||
"ui.equation_x_of_t": "x(t)",
|
||||
"ui.equation_y_of_t": "y(t)",
|
||||
"ui.equation_y_of_x": "y(x)",
|
||||
@@ -4653,6 +4710,7 @@
|
||||
"ui.insert_design.saved_file": "{name} contiene modifiche non salvate, quindi è stato inserito il file salvato. Salvalo, poi usa Aggiorna collegamenti per includere le modifiche.",
|
||||
"ui.insert_design.unsaved": "Modifiche non salvate",
|
||||
"ui.inspect": "Esamina",
|
||||
"ui.instances": "Istanze",
|
||||
"ui.intersect": "Interseca",
|
||||
"ui.isolate": "Isola",
|
||||
"ui.join": "Unisci",
|
||||
@@ -4661,12 +4719,14 @@
|
||||
"ui.joints": "Giunti",
|
||||
"ui.keyboard_shortcuts": "Scorciatoie da tastiera",
|
||||
"ui.language": "Lingua",
|
||||
"ui.lasso_select": "Lazo (trascina un anello a mano libera: prende ciò che sta interamente all'interno)",
|
||||
"ui.layout_compact": "Compatta",
|
||||
"ui.layout_floating": "Flottante",
|
||||
"ui.length": "Lunghezza",
|
||||
"ui.length_decimals": "Decimali delle lunghezze",
|
||||
"ui.light": "Chiaro",
|
||||
"ui.line": "Linea",
|
||||
"ui.line_arc_hint": "Arco tangente: fai clic sulla fine dell'arco. Fermati di nuovo sull'ultimo punto per tornare alle linee; clic destro o Esc per terminare",
|
||||
"ui.link_copies": "Collega copie identiche",
|
||||
"ui.link_copies_keep": "Mantieni",
|
||||
"ui.link_copies_link": "Collega",
|
||||
@@ -4755,6 +4815,7 @@
|
||||
"ui.part_size_shared": "Ricostruisce la parte, sostituendo le eventuali lavorazioni aggiunte. Si aggiornano tutte le occorrenze del componente.",
|
||||
"ui.paste": "Incolla",
|
||||
"ui.path": "Percorso",
|
||||
"ui.pattern_object_type_tip": "Corpi: forme solide, selezionate nella vista o nella Struttura; ciascuno viene copiato per intero.\nLavorazioni: passaggi della Cronologia, come un'estrusione o un foro, selezionati nella Cronologia o su una faccia da essi creata; il passaggio viene ripetuto.\nUno schizzo non è nessuno dei due: ripeti le sue curve con Ripetizione o Specchia dello schizzo stesso, oppure estrudilo prima.",
|
||||
"ui.pattern_on_path": "Ripetizione su percorso",
|
||||
"ui.perforate_margin_default": "dal diametro",
|
||||
"ui.perforate_pitch": "Passo",
|
||||
@@ -4800,9 +4861,9 @@
|
||||
"ui.polygon": "Poligono",
|
||||
"ui.preferences": "Preferenze",
|
||||
"ui.press_a_key": "premi un tasto…",
|
||||
"ui.print_face_body_not_sent": "Il corpo di quella faccia non viene inviato: aggiungilo prima a Corpi",
|
||||
"ui.print_face_cleared": "{body}: faccia di stampa rimossa",
|
||||
"ui.print_face_input": "Faccia di stampa",
|
||||
"ui.print_face_one_body": "Una faccia di stampa appartiene a un solo corpo: invia quel corpo da solo per cambiarla",
|
||||
"ui.print_face_pick": "Seleziona una faccia piana di un corpo solido per renderla la faccia di stampa",
|
||||
"ui.print_face_set": "{body}: faccia di stampa impostata; Invia allo slicer appoggia il corpo su di essa",
|
||||
"ui.profile": "Profilo",
|
||||
@@ -5030,12 +5091,15 @@
|
||||
"ui.view_angles": "Angoli della vista",
|
||||
"ui.view_angles_convention": "Gradi attorno all'asse verticale. Imbardata 0° è la vista frontale, 90° sinistra, −90° destra, 180° posteriore; beccheggio 90° è la vista dall'alto; un rollio positivo ruota il modello in senso orario.",
|
||||
"ui.view_angles_help": "Mostra il beccheggio, l'imbardata e il rollio della vista in gradi e la sua proiezione sotto il gizmo di orientamento. Fai clic sull'indicatore per digitare angoli esatti.",
|
||||
"ui.view_angles_lock": "Blocca {axis}",
|
||||
"ui.view_angles_ortho": "Ortogonale",
|
||||
"ui.view_angles_persp": "Prospettica",
|
||||
"ui.view_angles_pitch": "Beccheggio",
|
||||
"ui.view_angles_readout": "Beccheggio {pitch} · Imbardata {yaw} · Rollio {roll}",
|
||||
"ui.view_angles_roll": "Rollio",
|
||||
"ui.view_angles_unlock": "Sblocca {axis}",
|
||||
"ui.view_angles_yaw": "Imbardata",
|
||||
"ui.view_angles_zero": "Imposta {axis} a 0°",
|
||||
"ui.view_centre": "Centro della vista",
|
||||
"ui.viewport_status_clear": "Disattiva {name}",
|
||||
"ui.viewport_status_clear_hint": "Disattiva {name} · Esc",
|
||||
|
||||
+118
-12
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "長方形",
|
||||
"Rectangular Tube": "角形鋼管",
|
||||
"Reference face": "参照面",
|
||||
"Reference plane": "参照平面",
|
||||
"Relief Depth": "リリーフ深さ",
|
||||
"Relief Width": "リリーフ幅",
|
||||
"Remove Tools": "ツールボディを削除",
|
||||
@@ -494,7 +495,7 @@
|
||||
"Rename Folder": "フォルダーの名前を変更",
|
||||
"Render Stage": "レンダリング段階",
|
||||
"Reopen designs": "デザインを再度開く",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "前回 SolidCraft を閉じたときに開いていたデザインを再度開く",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "前回開いていたデザインを再び開く",
|
||||
"Replace existing library file": "既存のライブラリファイルを置き換える",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "バグを報告 — スクリーンショットを確認してGitHub下書きを作成",
|
||||
"Report copied. Keep this text to submit on GitHub when you are online.": "レポートをコピーしました。このテキストを保存し、オンラインになったらGitHubで送信してください。",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "断面",
|
||||
"Section (optional)": "断面(任意)",
|
||||
"Section position": "断面の位置",
|
||||
"Sections": "セクション",
|
||||
"Sections (one sketch each)": "セクション (各1スケッチ)",
|
||||
"Select Other": "その他を選択",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "その他を選択: モデル上で左ボタンを長押し(または右クリック › その他を選択…)するとカーソル下の要素が一覧表示されます。同じ場所を再度クリックすると次の要素が選択されます。中ボタンをダブルクリックするとホームビューに戻ります(環境設定 › ナビゲーション)。",
|
||||
@@ -793,6 +795,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (アタッチメント消失)",
|
||||
"browser.sketch_editing": "{name} (編集中)",
|
||||
"browser.sketch_name": "スケッチ{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "すでにその部品にある",
|
||||
"browser.sketches_to_parts_design_body": "デザイン自体のボディ上にある",
|
||||
"browser.sketches_to_parts_moved": "{moved} 個のスケッチを描かれている部品に移動しました",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "正確なアタッチメントのない面上にある",
|
||||
"browser.sketches_to_parts_not_on_face": "部品の面上にない",
|
||||
"browser.sketches_to_parts_part_missing": "部品がもうない",
|
||||
"browser.sketches_to_parts_result": "{moved} 個のスケッチを描かれている部品に移動しました。{stayed} 個はそのまま:{why}",
|
||||
"browser.units": "単位: {units}",
|
||||
"cam.flat": "平面",
|
||||
"cam.follower": "従動節",
|
||||
@@ -852,6 +861,7 @@
|
||||
"cmd.component_extract": "ファイルに抽出",
|
||||
"cmd.component_finish_edit": "編集を終了",
|
||||
"cmd.component_from_bodies": "ボディからコンポーネントを作成",
|
||||
"cmd.component_hide_history": "コンポーネントの履歴を非表示",
|
||||
"cmd.component_insert_design": "デザインを挿入",
|
||||
"cmd.component_links": "リンクを一覧表示",
|
||||
"cmd.component_list": "コンポーネントを一覧表示",
|
||||
@@ -860,6 +870,7 @@
|
||||
"cmd.component_move_bodies": "ボディをコンポーネントに移動",
|
||||
"cmd.component_move_features": "フィーチャーをコンポーネントに移動",
|
||||
"cmd.component_move_sketches": "スケッチをコンポーネントに移動",
|
||||
"cmd.component_move_sketches_to_parts": "スケッチをそれぞれの部品に移動",
|
||||
"cmd.component_paste_from_design": "別のデザインから貼り付け",
|
||||
"cmd.component_paste_new": "新規として貼り付け",
|
||||
"cmd.component_properties": "コンポーネントの物理的プロパティ",
|
||||
@@ -867,6 +878,7 @@
|
||||
"cmd.component_rename": "コンポーネントの名前を変更",
|
||||
"cmd.component_replace_with_link": "リンクに置き換え",
|
||||
"cmd.component_revert_position": "位置を元に戻す",
|
||||
"cmd.component_show_history": "コンポーネントの履歴を表示",
|
||||
"cmd.component_unlock": "内容のロックを解除",
|
||||
"cmd.component_update_links": "リンクを更新",
|
||||
"cmd.config_activate": "構成をアクティブ化",
|
||||
@@ -877,6 +889,7 @@
|
||||
"cmd.config_rule": "構成ルール",
|
||||
"cmd.config_rules": "構成ルール一覧",
|
||||
"cmd.config_table": "構成テーブルを表示",
|
||||
"cmd.construct.plane.axis_point": "軸と点を通る平面",
|
||||
"cmd.construct_axis_edge": "エッジを通る軸",
|
||||
"cmd.construct_axis_normal_to_face": "面に垂直な軸",
|
||||
"cmd.construct_axis_through_cylinder": "円柱/円錐/トーラスを通る軸",
|
||||
@@ -925,6 +938,11 @@
|
||||
"cmd.explode_show": "分解図を表示",
|
||||
"cmd.expr_evaluate": "式を評価",
|
||||
"cmd.face_delete": "面を削除",
|
||||
"cmd.file.export_3mf": "3MF をエクスポート",
|
||||
"cmd.file.export_obj": "OBJ をエクスポート",
|
||||
"cmd.file.export_step": "STEP をエクスポート",
|
||||
"cmd.file.export_stl": "STL をエクスポート",
|
||||
"cmd.file.export_urdf": "URDF をエクスポート",
|
||||
"cmd.file_export": "エクスポート",
|
||||
"cmd.file_insert_mesh": "メッシュを挿入",
|
||||
"cmd.file_insert_step": "STEP を挿入",
|
||||
@@ -942,6 +960,7 @@
|
||||
"cmd.inspect_colored_components": "コンポーネントの色分けを有効化",
|
||||
"cmd.inspect_colored_components_disable": "コンポーネントの色分けを無効化",
|
||||
"cmd.inspect_compare": "ボディを比較",
|
||||
"cmd.inspect_compare_picture": "参考画像と比較",
|
||||
"cmd.inspect_continuity": "連続性解析",
|
||||
"cmd.inspect_curvature_comb": "曲率コーム解析",
|
||||
"cmd.inspect_curvature_map": "曲率マップ解析",
|
||||
@@ -1032,6 +1051,9 @@
|
||||
"cmd.pointcloud_edit": "点群を編集",
|
||||
"cmd.pointcloud_insert": "点群を挿入",
|
||||
"cmd.pointcloud_list": "点群",
|
||||
"cmd.render.compare.divider": "レンダー比較の境界を移動",
|
||||
"cmd.render.result.discard": "レンダリング結果を破棄",
|
||||
"cmd.render.result.select": "レンダリング結果を選択",
|
||||
"cmd.render_aging_describe": "レイヤー式表面エイジング",
|
||||
"cmd.render_backends": "レンダリング バックエンド",
|
||||
"cmd.render_batch": "すべてのショットをレンダリング",
|
||||
@@ -1195,6 +1217,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "制御点スプライン (次数 5)",
|
||||
"cmd.sketch_spline_fit_point": "通過点スプライン",
|
||||
"cmd.sketch_spline_insert_point": "スプライン点を追加",
|
||||
"cmd.sketch_spline_remove_point": "スプライン点を削除",
|
||||
"cmd.sketch_spline_reverse": "スプラインの方向を反転",
|
||||
"cmd.sketch_spun_profile": "回転プロファイル",
|
||||
"cmd.sketch_toggle_diameter": "半径 / 直径の切り替え",
|
||||
@@ -1245,6 +1268,7 @@
|
||||
"cmd.timeline_rollback": "タイムライン マーカーを移動",
|
||||
"cmd.timeline_stop": "履歴を停止",
|
||||
"cmd.timeline_suppress": "フィーチャーを抑制",
|
||||
"cmd.ui.assistant": "アシスタントの表示切り替え",
|
||||
"cmd.view.render_browser": "Renderブラウザー",
|
||||
"cmd.view.render_dock": "レンダードックを移動",
|
||||
"cmd.view.render_focus": "Renderフォーカス表示",
|
||||
@@ -1279,9 +1303,11 @@
|
||||
"cmd.view_tool": "ナビゲーションツール",
|
||||
"cmd.view_top": "上面ビュー",
|
||||
"cmd.view_visual_style": "表示スタイル",
|
||||
"color.blue": "青",
|
||||
"color.copy": "クリックして色の値をコピー",
|
||||
"color.edit": "クリックして色を編集",
|
||||
"color.gamma": "0〜255のガンマ空間で色の値を表示",
|
||||
"color.green": "緑",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "#rrggbb または #rrggbbaa を入力。6 桁の場合は不透明度を維持",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1289,6 +1315,7 @@
|
||||
"color.linear": "0〜1の線形空間で色の値を表示",
|
||||
"color.recent": "最近使った色",
|
||||
"color.recent_help": "どの色フィールドで選んだ色もここに表示されます",
|
||||
"color.red": "赤",
|
||||
"color.saturation": "彩度(パーセント)",
|
||||
"color.saturation_value": "彩度と明度",
|
||||
"color.selected": "選択した色",
|
||||
@@ -1296,6 +1323,8 @@
|
||||
"color.step_up": "1 ステップ上げる",
|
||||
"color.value": "明度(パーセント)",
|
||||
"config.column_filter_empty": "一致する列がありません",
|
||||
"config.rule_parameter_columns": "表に列があるパラメータのみが表示されます。ここで使うには「列を追加」(+) で列を追加してください。",
|
||||
"config.rule_suppression_columns": "表に「抑制」列があるフィーチャーのみが表示されます。ここで制御するには「列を追加」(+) で列を追加してください。",
|
||||
"contact.components_count": "{count}個のコンポーネント",
|
||||
"contact.enabled_label": "有効",
|
||||
"coupling.gear_1": "ギア 1",
|
||||
@@ -1344,7 +1373,11 @@
|
||||
"dialog.pattern_sketch_not_object": "ボディかフィーチャーを選んでください。スケッチはどちらでもありません。曲線はスケッチ内のパターンかミラーで繰り返すか、先に押し出してください。",
|
||||
"dialog.select": "↖ 選択",
|
||||
"dialog.selected_count": "↖ {count} 個選択中",
|
||||
"dialog.selection_constraint": "拘束 {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · 曲線 {id}",
|
||||
"dialog.selection_guidance": "入力欄を選んで変更します。選択済みのオブジェクトをクリックすると解除できます。Ctrl/Cmd を押しながらクリックしても切り替えられます。プレビューを確認してから OK を押してください。",
|
||||
"dialog.selection_midpoint": "中点 · {id}",
|
||||
"dialog.selection_profile": "{sketch} · プロファイル {number}",
|
||||
"dialog.selection_step": "{total} ステップ中 {step}: {input} を選択",
|
||||
"dialog.step": "ステップ",
|
||||
"document.changed_on_disk": "{name} がディスク上で変更されました",
|
||||
@@ -1427,6 +1460,7 @@
|
||||
"help.construct.axis.two_points": "2つの点、頂点、スケッチ点を通る構築軸を作ります。位置が分かっている2か所の間を通る軸に使います。",
|
||||
"help.construct.plane.along_path": "エッジまたは曲線に垂直な構築平面を、その上の指定位置に作ります。スイープ用のプロファイルや、パスの断面をスケッチするときに使います。",
|
||||
"help.construct.plane.at_angle": "平面または面を、軸またはエッジを中心に指定した角度だけ傾けた構築平面を作ります。斜めのブラケットなど、傾いたスケッチに使います。",
|
||||
"help.construct.plane.axis_point": "軸と、その軸上にない点を含む構築平面を作成します。原点軸、構築軸、直線エッジまたはスケッチの直線を選択し、次に頂点、スケッチ点または構築点を選択します。軸上の点では平面が一意に定まりません。",
|
||||
"help.construct.plane.midplane": "2つの平面または平らな面の中間、または角度をなして交わる場合はその二等分に構築平面を作ります。ミラーの対称面として使います。",
|
||||
"help.construct.plane.offset": "平面または平らな面に平行な構築平面を、指定した距離または点を通る位置に作ります。面から一定の高さの位置にスケッチするときに使います。",
|
||||
"help.construct.plane.perpendicular": "線またはエッジを含み、平面または面に垂直な構築平面を作ります。エッジに沿ってモデル上に垂直にスケッチするときに使います。",
|
||||
@@ -1549,7 +1583,7 @@
|
||||
"help.sketch.constrainer": "選んだ1〜2本の曲線がほぼ満たしている拘束(水平、平行、正接、等長など)を適用します。手描きのスケッチを1つずつ整えるときに使います。",
|
||||
"help.sketch.constraint.coincident": "点を、別の点、曲線、スケッチ軸の上に載せます。輪郭の隙間を閉じたり、点を直線上に置いたりするときに使います。",
|
||||
"help.sketch.constraint.collinear": "2本の直線を同一直線上に揃えます。切り欠きの両側など、途切れたエッジを揃えるときに使います。",
|
||||
"help.sketch.constraint.concentric": "2つの円または円弧の中心を揃えます。リング、座ぐり、段付きシャフトに使います。",
|
||||
"help.sketch.constraint.concentric": "2つの円または円弧の中心を揃えるか、円または円弧の中心をスケッチ点(原点を含む)に一致させます。選択する順序は問いません。",
|
||||
"help.sketch.constraint.curvature": "端点で接する2本の曲線を、同じ曲率で続けます (G2)。正接では滑らかさが足りない、見える面などに使います。",
|
||||
"help.sketch.constraint.equal": "直線を同じ長さに、円や円弧を同じ半径にします。1つの寸法で複数の同じ形状を制御するときに使います。",
|
||||
"help.sketch.constraint.fix": "点や曲線を現在の位置に固定、または固定を解除します。ドラッグやスケッチの解決で動いてはいけないジオメトリに使います。",
|
||||
@@ -1803,6 +1837,8 @@
|
||||
"joint_move.blocked_hint": "Rigid合致、部品の固定状態、ジョイントの制限値、パラメーターでの拘束を確認してください。",
|
||||
"joint_move.blocked_title": "この部品は移動できません",
|
||||
"joint_move.coarse": "軸に近い位置の回転刻み(°)",
|
||||
"joint_move.conflict": "この部品のジョイントが他のジョイントと競合しています: {joint}。",
|
||||
"joint_move.conflict_hint": "競合しているジョイントを編集、抑制、または削除して、他のジョイントで部品を配置できるようにしてください。",
|
||||
"joint_move.distance": "開始位置からの移動距離",
|
||||
"joint_move.done": "完了",
|
||||
"joint_move.fine": "軸から遠い位置の回転刻み(°)",
|
||||
@@ -1902,6 +1938,8 @@
|
||||
"motion.track_name": "トラック {number}",
|
||||
"nav.alias.help": "Shift+Alt+左ドラッグで回転、Shift+Alt+中ドラッグでパン、Shift+Alt+右ドラッグでズーム。中ドラッグでもパン、右ドラッグで回転。",
|
||||
"nav.blender.help": "中ドラッグで回転、Shift+中ドラッグでパン、Ctrl+中ドラッグでズーム。右ドラッグではカメラは動きません。",
|
||||
"nav.creo.help": "マウスの中ボタンドラッグで回転、Shift+中ボタンで平行移動、Ctrl+中ボタンの上下ドラッグでズーム(下方向で拡大)。ホイールの回転でもズーム",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "マウスと3Dマウスで、極点を越えて回転し、ロールを保ちます。\nShiftによる軸スナップは無効になります。",
|
||||
"nav.free_orbit.label": "自由回転",
|
||||
"nav.free_orbit.snap_help": "マウスと3Dマウスで両極を越えて回転でき、ロールも保持されます。オービットのドラッグ中にShiftを押すと、視線方向とロールがスナップします。",
|
||||
@@ -2054,12 +2092,19 @@
|
||||
"parts.family.button_head_cap_screw": "ボタンヘッド六角穴付きボルト",
|
||||
"parts.family.dowel_pin": "平行ピン",
|
||||
"parts.family.hex_bolt": "六角ボルト",
|
||||
"parts.family.hex_cap_screw_inch": "六角ボルト(インチ)",
|
||||
"parts.family.hex_nut": "六角ナット",
|
||||
"parts.family.hex_nut_inch": "六角ナット(インチ)",
|
||||
"parts.family.set_screw": "六角穴付き止めねじ 平先",
|
||||
"parts.family.socket_head_cap_screw": "六角穴付きボルト",
|
||||
"parts.family.washer": "平座金",
|
||||
"parts.family.washer_sae": "平座金、SAE(小形)",
|
||||
"parts.family.washer_uss": "平座金、USS(大形)",
|
||||
"parts.holes": "穴 (省略可)",
|
||||
"parts.placement_hint": "1 つ以上の穴を選ぶとそれぞれに部品を取り付けます。選ばずに OK を押すと原点に配置します",
|
||||
"parts.system": "単位系",
|
||||
"parts.system.inch": "インチ",
|
||||
"parts.system.metric": "メートル",
|
||||
"pattern.alignment_2": "配置 2",
|
||||
"pattern.alignment_hint": "個数には元の図形が含まれます。中央配置ではコピーを両側に配置し、反転で偶数個の場合に多く配置する側を切り替えます。",
|
||||
"pattern.direction_1": "方向 1",
|
||||
@@ -2121,6 +2166,8 @@
|
||||
"prefs.button_left": "左ドラッグ",
|
||||
"prefs.button_middle": "中央ドラッグ",
|
||||
"prefs.button_right": "右ドラッグ",
|
||||
"prefs.carousel_next": "次へ",
|
||||
"prefs.carousel_previous": "前へ",
|
||||
"prefs.choose_font": "フォントファイルを選択…",
|
||||
"prefs.click": "クリック",
|
||||
"prefs.contact_shading": "柔らかな接触陰影",
|
||||
@@ -2133,6 +2180,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "曲面",
|
||||
"prefs.curves": "曲面",
|
||||
"prefs.cut_preview_tint": "除去される材料を赤で表示",
|
||||
"prefs.cut_preview_tint_help": "プレビューでは、フィーチャーが除去する材料と置き換えられる形状を、赤い透過ゴーストで塗りつぶします。オフにすると、この塗りつぶしは完全に透明になり、輪郭のみが描画されます。",
|
||||
"prefs.dark_parts_shading": "暗い部品の形状を保つ",
|
||||
"prefs.dark_parts_shading_help": "リニア光と柔らかいスカイライトでビューをシェーディングし、暗い面が黒くつぶれずにグラデーションを保つようにします",
|
||||
"prefs.dead_zone_note": "斜線の帯はデッドゾーンです。この範囲内の動きは無視されます。",
|
||||
@@ -2157,7 +2206,19 @@
|
||||
"prefs.font_sample": "いろはにほへと · Ø12.50 mm · 45.0° · 0123456789",
|
||||
"prefs.font_unreadable": "選択したフォントを読み込めませんでした。",
|
||||
"prefs.general": "一般",
|
||||
"prefs.general_note": "言語、保存、更新。",
|
||||
"prefs.general.active": "使用中",
|
||||
"prefs.general.crash_reports": "クラッシュレポート",
|
||||
"prefs.general.edited": "変更あり",
|
||||
"prefs.general.held": "保存対象",
|
||||
"prefs.general.import": "インポート...",
|
||||
"prefs.general.last_check": "最終確認: {time}",
|
||||
"prefs.general.minutes": "分",
|
||||
"prefs.general.name_setup": "この設定に名前を付ける",
|
||||
"prefs.general.profile_name": "プロファイル名",
|
||||
"prefs.general.save_new": "新規保存",
|
||||
"prefs.general.step_import": "STEP とインポート",
|
||||
"prefs.general.this_computer": "このコンピューター",
|
||||
"prefs.general_note": "プロファイル、言語、ファイル、更新。",
|
||||
"prefs.gesture_orbit": "回転",
|
||||
"prefs.gesture_pan": "移動",
|
||||
"prefs.gesture_zoom": "ズーム",
|
||||
@@ -2191,6 +2252,8 @@
|
||||
"prefs.interface_language": "表示言語",
|
||||
"prefs.interface_note": "SolidCraft の外観と各パネルの配置。",
|
||||
"prefs.interval_minutes": "{n} 分",
|
||||
"prefs.invert_curved_roll_arrows": "カーブしたロール矢印を反転",
|
||||
"prefs.invert_curved_roll_arrows_note": "キューブの隅にある2つのカーブした矢印は、画面内でビューをロールします。オン: 時計回りの矢印が反時計回りに、反時計回りの矢印が時計回りにロールします。オフ: 各矢印は、そのカーブの向きにビューをロールします。",
|
||||
"prefs.invert_navigator_arrows": "ビューキューブの回転矢印を反転",
|
||||
"prefs.invert_navigator_arrows_note": "キューブの周りの矢印が、矢印の指す方向にカメラを回します(Fusion や Inventor と同じ動き):左の矢印は右を指し、カメラを右に振ります。オフ: 矢印は、そのある面の方へキューブを回すため、左の矢印はカメラを左に振ります。カーブしたロール矢印は影響を受けません。",
|
||||
"prefs.keep_work_on_tab_switch": "デザインを切り替えてもダイアログとツールを開いたままにする",
|
||||
@@ -2212,10 +2275,10 @@
|
||||
"prefs.logo_accent": "アクセント",
|
||||
"prefs.logo_theme": "テーマの色",
|
||||
"prefs.max_file_size": "開けるファイルの最大サイズ",
|
||||
"prefs.max_file_size_design": "デザインファイル",
|
||||
"prefs.max_file_size_design": "開くファイルの上限: デザイン",
|
||||
"prefs.max_file_size_flag": "今回は --max-file-size {size} を指定して起動したため、SolidCraft を閉じるまでその値が適用されます。この設定は、そのオプションなしで起動したときに適用されます。",
|
||||
"prefs.max_file_size_note": "既定値は 512 MB と 256 MB、上限は 16 GB です。ファイルを開くには、サイズの数倍のメモリが必要です。大きなインポートを保持するデザインの保存と再読み込みには、デザインファイルの上限を上げる必要がある場合があります。",
|
||||
"prefs.max_file_size_step": "STEP・インポートファイル",
|
||||
"prefs.max_file_size_step": "開くファイルの上限: STEP・インポート",
|
||||
"prefs.max_file_size_web": "ウェブ版で開けるのは STEP・インポートファイルが 512 MB、デザインが 256 MB までです。ブラウザーのタブはそれ以上のファイルを保持できません。デスクトップ版では上限を上げられます。",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "大きなアイコン",
|
||||
@@ -2232,7 +2295,7 @@
|
||||
"prefs.navigator_fit_note": "キューブの面・角・矢印やギズモの軸をクリックすると、どの方向からでも全体が収まる一定の距離(外接球)からモデルの中心を見ます。オンの間はホームビューも同じズームと中心を使うため、その後に選んだ視点は向きだけが変わります。オフ: 視点はその場で回転し、注視点とズームを保ちます。",
|
||||
"prefs.network_host_remove": "削除",
|
||||
"prefs.network_locations": "デザイン内のネットワーク上の場所を許可",
|
||||
"prefs.network_locations_note": "オフ: 別のコンピューター (\\\\server\\share) にあるデザインのリンク先ファイルは、ここに表示されたサーバー、または確認時に許可したサーバーからのみ開きます。",
|
||||
"prefs.network_locations_note": "ファイルサーバーでデザインを管理する組織向け。オフの場合は許可したサーバーのみ。",
|
||||
"prefs.network_locations_section": "ネットワーク上の場所",
|
||||
"prefs.not_on_gpu": "この GPU は非対応",
|
||||
"prefs.on": "オン",
|
||||
@@ -2250,15 +2313,17 @@
|
||||
"prefs.placement": "配置",
|
||||
"prefs.prefer_free_sketch_points": "スケッチ点が重なったときは移動可能な点を優先する",
|
||||
"prefs.preview_choices": "選択した設定のプレビュー",
|
||||
"prefs.preview_tint_new_faces": "プレビューで新しい面に色を付ける",
|
||||
"prefs.preview_tint_new_faces_help": "ボディを変更するフィーチャーのプレビューで、追加される面をプレビュー色で描画できます。オフにすると、ボディはそのままの色で描画されます。",
|
||||
"prefs.print_face_highlight": "造形面の強調",
|
||||
"prefs.print_face_highlight_tip": "ビューでボディの造形面を色付けし輪郭を描く強さ。0 % で強調しません。",
|
||||
"prefs.profile_active": "使用中のプロファイル",
|
||||
"prefs.profile_applied": "プロファイルを適用しました。",
|
||||
"prefs.profile_changed": "プロファイルの保存後に設定が変更されています。「保存」でプロファイルに反映されます。",
|
||||
"prefs.profile_deleted": "プロファイルを削除しました。",
|
||||
"prefs.profile_export": "エクスポート…",
|
||||
"prefs.profile_export": "エクスポート",
|
||||
"prefs.profile_exported": "プロファイルをエクスポートしました。",
|
||||
"prefs.profile_imported": "プロファイルをインポートしました。切り替えるには「使用中のプロファイル」で選択してください。",
|
||||
"prefs.profile_imported": "プロファイルをインポートしました。",
|
||||
"prefs.profile_name_hint": "プロファイル名(例: ノートPC)",
|
||||
"prefs.profile_rename": "名前を変更",
|
||||
"prefs.profile_renamed": "プロファイルの名前を変更しました。",
|
||||
@@ -2278,12 +2343,13 @@
|
||||
"prefs.render_previous_geometry_budget": "レンダープレビューで以前のジオメトリ予算を使用",
|
||||
"prefs.render_previous_geometry_budget_help": "デバイスに基づく予算の代わりに、以前の固定ジオメトリ上限(64ビットデスクトップでは512 MiB、その他では128 MiB)に戻します。",
|
||||
"prefs.reports_saved": "{n} 件保存済み",
|
||||
"prefs.restore_page_defaults": "このページを既定値に戻す",
|
||||
"prefs.ribbon_click_ends_tool": "実行中のツールのボタンをクリックすると終了する",
|
||||
"prefs.ribbon_click_ends_tool_note": "使用中のスケッチツールの点灯したリボンボタンをクリックするとツールが終了し、ビューのクリックで再び選択できます。オフ: クリックするとツールが最初から開始されます。",
|
||||
"prefs.ribbon_reveal": "検索結果をリボンに表示",
|
||||
"prefs.ribbon_reveal_note": "コマンド検索で結果にポインターを合わせると、そのコマンドがリボンのどこにあるか(タブ、ボタン、またはパネルのドロップダウン内の行)を表示します。検索から開始したコマンドはそこで点滅し、ドロップダウンから実行中のコマンドはそのパネルに印が付きます。",
|
||||
"prefs.right_click": "右クリック",
|
||||
"prefs.saving": "自動保存と復元",
|
||||
"prefs.saving": "起動と保存",
|
||||
"prefs.scale_note": "ウィンドウ内では Ctrl + / Ctrl − でも変更できます。100% のボタンは 28 px です。",
|
||||
"prefs.scale_text": "拡大率と文字",
|
||||
"prefs.search": "設定を検索",
|
||||
@@ -2547,6 +2613,10 @@
|
||||
"render.base_color": "ベースカラー・sRGB",
|
||||
"render.base_metallic": "金属成分の割合",
|
||||
"render.base_opaque_only": "この編集では不透明ボディの意味を保持します。透過には明示的なガラス設定を使用してください。",
|
||||
"render.batch_all_shots": "すべてのショット ({count})",
|
||||
"render.batch_empty": "すべてのショットをレンダリングする前にショットを保存してください。",
|
||||
"render.batch_limit": "一度にレンダリングできるショットは32個までです。",
|
||||
"render.batch_status": "{running} 実行中 · {waiting} 待機中",
|
||||
"render.bloom_enable": "表示ブルーム",
|
||||
"render.bloom_help": "正の線形ハイライトを3回の正規化フィルターで広げる表示専用の効果です。半径は出力ピクセル単位。sRGB変換前に加算され、レンズ・体積・収束の改善ではありません。生EXRは変わりません。",
|
||||
"render.bloom_opaque": "ブルームには不透明な出力が必要です。透明PNGを暗黙に統合せず拒否します。",
|
||||
@@ -2657,6 +2727,13 @@
|
||||
"render.choose_look": "まず外観ライブラリで外観を選択",
|
||||
"render.chromatic_offset": "色収差のずれ",
|
||||
"render.clear_stage": "保存済みステージを削除",
|
||||
"render.compare_divider": "比較の境界",
|
||||
"render.compare_live": "ライブ",
|
||||
"render.compare_live_preview": "ライブプレビュー",
|
||||
"render.compare_render": "レンダー {number}",
|
||||
"render.compare_side": "レンダー {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} 秒 · {renderer} · リビジョン {revision} で取得",
|
||||
"render.compare_swap": "入れ替え",
|
||||
"render.copy_request": "JSONをコピー",
|
||||
"render.cpu_aa_help": "空間サンプル数を増やすと輪郭が滑らかになりますが、描画時間も増えます。",
|
||||
"render.cpu_area_add": "CPU矩形ライトを追加",
|
||||
@@ -3406,6 +3483,10 @@
|
||||
"render.result_retained": "最後に完成した画像を表示しています。新しいレンダリングにはまだ置き換わっていません。",
|
||||
"render.result_sequence": "中間結果",
|
||||
"render.result_title": "レンダリング結果",
|
||||
"render.results_choose": "保持した結果",
|
||||
"render.results_label": "レンダー {number} · {shot} · {seconds} 秒",
|
||||
"render.results_stale_label": "{result} [更新前]",
|
||||
"render.results_unnamed": "画像",
|
||||
"render.return_design": "設計に戻る",
|
||||
"render.review_stale": "この確認結果は古くなっています。適用前に再解決または再確認してください。",
|
||||
"render.ribbon_aging": "エイジング",
|
||||
@@ -4066,6 +4147,7 @@
|
||||
"ui.agent_chat_auth_terminal": "ターミナルでこれを実行してから、もう一度「接続」を押してください:",
|
||||
"ui.agent_chat_auth_title": "エージェントにサインイン",
|
||||
"ui.agent_chat_cancel": "キャンセル",
|
||||
"ui.agent_chat_cancel_timeout": "エージェントが停止要求に応答しなかったため、接続を切断しました。続行するには再接続してください。",
|
||||
"ui.agent_chat_cannot_start": "インストールされていますが、起動しません: {error}",
|
||||
"ui.agent_chat_changes": "{n} 件の変更(元に戻すは1回)",
|
||||
"ui.agent_chat_changes_n": "{n} 件の変更",
|
||||
@@ -4075,11 +4157,13 @@
|
||||
"ui.agent_chat_command": "エージェントのコマンド",
|
||||
"ui.agent_chat_connect": "接続",
|
||||
"ui.agent_chat_copy": "コピー",
|
||||
"ui.agent_chat_copy_message": "メッセージをコピー",
|
||||
"ui.agent_chat_ctx_bodies": "ボディ",
|
||||
"ui.agent_chat_ctx_elements": "要素",
|
||||
"ui.agent_chat_ctx_features": "フィーチャ",
|
||||
"ui.agent_chat_ctx_more": "ほか{n}件",
|
||||
"ui.agent_chat_ctx_other": "その他",
|
||||
"ui.agent_chat_custom_heading": "カスタムモデル",
|
||||
"ui.agent_chat_custom_hint": "標準入出力で Agent Client Protocol を話す任意のプログラム。シェルは使いません。",
|
||||
"ui.agent_chat_denied": "拒否しました",
|
||||
"ui.agent_chat_deny": "拒否",
|
||||
@@ -4123,8 +4207,16 @@
|
||||
"ui.agent_chat_mode_all": "すべて",
|
||||
"ui.agent_chat_mode_ask": "確認",
|
||||
"ui.agent_chat_mode_edits": "編集",
|
||||
"ui.agent_chat_model_custom": "カスタムを使用: {model}",
|
||||
"ui.agent_chat_model_no_tools": "{model} は SolidCraft のツールを使えず、チャットのみ可能です。ツール対応のモデルを選んでください。",
|
||||
"ui.agent_chat_model_rejected": "エージェントは {model} を受け付けませんでした: {error}",
|
||||
"ui.agent_chat_model_search": "モデルを検索…",
|
||||
"ui.agent_chat_model_tip": "エージェントが使うモデル。選択はこのエージェントに対して記憶されます。",
|
||||
"ui.agent_chat_models_failed": "モデルを一覧表示できませんでした: {error}",
|
||||
"ui.agent_chat_models_heading": "利用可能なモデル",
|
||||
"ui.agent_chat_models_listing": "モデルを一覧表示中…",
|
||||
"ui.agent_chat_models_none": "エンドポイントにモデルがありません",
|
||||
"ui.agent_chat_models_not_a_list": "応答はモデル一覧ではありませんでした",
|
||||
"ui.agent_chat_new": "新しいチャット",
|
||||
"ui.agent_chat_new_confirm": "新しいチャットを始めますか? 現在のチャットは消去されます。",
|
||||
"ui.agent_chat_next_picture": "次の画像",
|
||||
@@ -4386,6 +4478,8 @@
|
||||
"ui.coming_later": "準備中",
|
||||
"ui.command_done": "{command} 完了",
|
||||
"ui.command_palette_hint": "コマンドを入力(例: {example})— または S キーで検索",
|
||||
"ui.components_from_bodies_hint": "選択した各ボディと同じ名前のコンポーネントを、アクティブなコンポーネントの下に作成します。",
|
||||
"ui.components_from_bodies_required": "ボディを1つ以上選択してください。",
|
||||
"ui.concentric": "同心",
|
||||
"ui.cone": "円錐",
|
||||
"ui.conic_curve": "円錐曲線",
|
||||
@@ -4398,9 +4492,11 @@
|
||||
"ui.construction.contact_hint": "接線: 円/円弧または開いたスプライン/円錐曲線を選び、接点を選択 (自由曲線は端点のみ)",
|
||||
"ui.construction.external_hint": "接線: 円または円弧、外部点、必要な解の順に選択",
|
||||
"ui.construction.free_hint": "補助直線: 2点をクリック。右クリックで最初から",
|
||||
"ui.construction.normal_needs_length": "無限直線は常に補助線です。通常の線を描くには長さを入力",
|
||||
"ui.construction.point_hint": "補助直線: 通過点をクリック",
|
||||
"ui.construction.reference_hint": "補助直線: 直線、点の順に選択。右クリックで参照を変更",
|
||||
"ui.construction.ribbon_label": "補助線",
|
||||
"ui.construction.side_hint": "補助直線: 線を伸ばす側をクリック。右クリックで最初から",
|
||||
"ui.context_menu": "コンテキスト メニュー",
|
||||
"ui.continuity_g0": "G0 折れ",
|
||||
"ui.continuity_g1": "G1 接線",
|
||||
@@ -4666,6 +4762,7 @@
|
||||
"ui.hideOrigin": "原点を非表示",
|
||||
"ui.hidePlanes": "平面を非表示",
|
||||
"ui.hideProfile": "プロファイルを非表示",
|
||||
"ui.hide_component_history": "このコンポーネントの履歴を非表示",
|
||||
"ui.hide_origin_by_default": "原点を既定で非表示",
|
||||
"ui.hide_origin_note": "新しいデザインは原点フォルダーを非表示にして始まります。原点の表示/非表示を切り替えたデザインは、その設定を保持します。",
|
||||
"ui.history_back": "1ステップ戻る",
|
||||
@@ -4673,6 +4770,7 @@
|
||||
"ui.history_edge_hint": "端をダブルクリック",
|
||||
"ui.history_first": "先頭に移動",
|
||||
"ui.history_forward": "1ステップ進む",
|
||||
"ui.history_hide_hidden": "非表示のコンポーネントを隠す",
|
||||
"ui.history_hide_names": "名前を非表示",
|
||||
"ui.history_last": "末尾に移動",
|
||||
"ui.history_marker_tip": "履歴マーカー:ドラッグしてモデルをロールバックします。新しいフィーチャーはここに追加されます",
|
||||
@@ -4682,10 +4780,12 @@
|
||||
"ui.history_play_button_show": "再生ボタンを表示",
|
||||
"ui.history_play_finish_action": "現在の操作を完了してから履歴を再生してください",
|
||||
"ui.history_play_roll_back": "再生する前に履歴マーカーを戻してください",
|
||||
"ui.history_show_hidden": "非表示のコンポーネントを表示",
|
||||
"ui.history_show_names": "名前を表示",
|
||||
"ui.history_sketch_listed_under": "{feature} の下に表示",
|
||||
"ui.history_title": "履歴",
|
||||
"ui.history_toggle_names": "フィーチャー名を表示・非表示",
|
||||
"ui.history_waits_for_editor": "元に戻す、やり直す、履歴は、フィーチャーエディターが開くまで待機します",
|
||||
"ui.hole": "穴",
|
||||
"ui.hole_tap_type": "ねじ穴タイプ",
|
||||
"ui.hole_type": "穴タイプ",
|
||||
@@ -4701,6 +4801,7 @@
|
||||
"ui.insert_design.saved_file": "{name} に保存されていない変更があるため、保存済みのファイルを挿入しました。保存してから、リンクを更新で変更を取り込んでください。",
|
||||
"ui.insert_design.unsaved": "未保存の変更",
|
||||
"ui.inspect": "検査",
|
||||
"ui.instances": "インスタンス",
|
||||
"ui.intersect": "交差",
|
||||
"ui.isolate": "分離表示",
|
||||
"ui.join": "結合",
|
||||
@@ -4851,9 +4952,9 @@
|
||||
"ui.polygon": "ポリゴン",
|
||||
"ui.preferences": "環境設定",
|
||||
"ui.press_a_key": "キーを押してください…",
|
||||
"ui.print_face_body_not_sent": "この面のボディは送信されません。先にボディに追加してください",
|
||||
"ui.print_face_cleared": "{body}: 造形面を解除しました",
|
||||
"ui.print_face_input": "造形面",
|
||||
"ui.print_face_one_body": "造形面はボディごとに選びます。変更するにはそのボディだけを送信してください",
|
||||
"ui.print_face_pick": "ソリッドボディの平面を1つ選択すると造形面になります",
|
||||
"ui.print_face_set": "{body}: 造形面を設定しました。スライサーへ送るとこの面を下にして置きます",
|
||||
"ui.profile": "プロファイル",
|
||||
@@ -4965,6 +5066,7 @@
|
||||
"ui.showDimension": "寸法を表示",
|
||||
"ui.showOrigin": "原点を表示",
|
||||
"ui.showPlanes": "平面を表示",
|
||||
"ui.show_component_history": "このコンポーネントの履歴を表示",
|
||||
"ui.show_constraints": "拘束",
|
||||
"ui.show_construction_geometries": "構築ジオメトリ",
|
||||
"ui.show_dimensions": "寸法",
|
||||
@@ -4996,7 +5098,7 @@
|
||||
"ui.slicer_no_response": "{slicer} が時間内に応答しませんでした。ファイルは {path} に保存済みです。スライサーが起動したら、もう一度送ってください。",
|
||||
"ui.slicer_resend_hint": "もう一度送ると、スライサーで開いているファイルが置き換わります。",
|
||||
"ui.slicer_single_instance": "すでに起動しているスライサーでファイルを開く",
|
||||
"ui.slicer_single_instance_tip": "PrusaSlicer、SuperSlicer、Cura は、別のウィンドウを起動せず、開いているウィンドウでファイルを読み込みます。ほかのスライサーはそのまま起動します。PrusaSlicer 3.0 は常にこの動作です。ランチャーはほかの方法ではファイルを受け取れません。",
|
||||
"ui.slicer_single_instance_tip": "実行中のウィンドウにファイルを読み込むスライサー用。",
|
||||
"ui.slicer_starting": "{slicer} を起動しています…",
|
||||
"ui.slicer_still_starting": "まだ {slicer} の起動を待っています。ほかには何も送信していません。",
|
||||
"ui.slicer_stl_fallback": "3MF を書き込めなかったため、STL で送信しました。",
|
||||
@@ -5081,12 +5183,15 @@
|
||||
"ui.view_angles": "ビュー角度",
|
||||
"ui.view_angles_convention": "上方向の軸まわりの角度(度)。ヨー 0° が正面ビュー、90° が左、−90° が右、180° が背面です。ピッチ 90° が上面ビューです。ロールが正のとき、モデルは画面上で時計回りに回ります。",
|
||||
"ui.view_angles_help": "方向ギズモの下に、ビューのピッチ、ヨー、ロール(度)と投影法を表示します。表示をクリックすると正確な角度を入力できます。",
|
||||
"ui.view_angles_lock": "{axis}をロック",
|
||||
"ui.view_angles_ortho": "平行投影",
|
||||
"ui.view_angles_persp": "透視投影",
|
||||
"ui.view_angles_pitch": "ピッチ",
|
||||
"ui.view_angles_readout": "ピッチ {pitch} · ヨー {yaw} · ロール {roll}",
|
||||
"ui.view_angles_roll": "ロール",
|
||||
"ui.view_angles_unlock": "{axis}のロックを解除",
|
||||
"ui.view_angles_yaw": "ヨー",
|
||||
"ui.view_angles_zero": "{axis}を0°に設定",
|
||||
"ui.view_centre": "ビュー中心",
|
||||
"ui.viewport_status_clear": "{name}をオフにする",
|
||||
"ui.viewport_status_clear_hint": "{name}をオフにする · Esc",
|
||||
@@ -5138,7 +5243,7 @@
|
||||
"update.available": "SolidCraft {version} が利用できます",
|
||||
"update.button": "アップデート",
|
||||
"update.button_version": "{version} にアップデート",
|
||||
"update.check_automatically": "アップデートを自動的に確認",
|
||||
"update.check_automatically": "起動時と1日1回確認",
|
||||
"update.check_for_updates": "アップデートを確認",
|
||||
"update.check_now": "今すぐ確認",
|
||||
"update.check_unavailable": "このインストールではアップデートを確認できません。",
|
||||
@@ -5188,6 +5293,7 @@
|
||||
"web.open_link": "リンク先のデザインを開きますか?",
|
||||
"web.open_link_confirm": "保存していない変更があります。リンク先のデザインを新しいタブで開きますか?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ 選択 · Enter / Tab 補完 · Esc 閉じる",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ 選択 · Enter この値を適用",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ 選択 · Enter 入力どおりに適用 · Esc 閉じる",
|
||||
"⌘Enter continues on GitHub.": "⌘EnterでGitHubに進みます。"
|
||||
}
|
||||
|
||||
+79
-6
@@ -407,6 +407,7 @@
|
||||
"Recover unsaved design?": "저장되지 않은 디자인을 복구할까요?",
|
||||
"Rectangular": "직사각형",
|
||||
"Reference face": "참조 면",
|
||||
"Reference plane": "참조 평면",
|
||||
"Relief Depth": "릴리프 깊이",
|
||||
"Relief Width": "릴리프 너비",
|
||||
"Remove Tools": "공구 제거",
|
||||
@@ -422,6 +423,7 @@
|
||||
"Rename Feature": "피처 이름 바꾸기",
|
||||
"Rename Folder": "폴더 이름 바꾸기",
|
||||
"Render Stage": "렌더링 단계",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "지난번에 열려 있던 디자인 다시 열기",
|
||||
"Replace existing library file": "기존 라이브러리 파일 바꾸기",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "버그 신고 — 스크린샷을 검토하고 GitHub 초안 열기",
|
||||
"Report text copied. Paste it into GitHub and submit your report there.": "보고서 텍스트를 복사했습니다. GitHub에 붙여넣고 그곳에서 보고서를 제출하세요.",
|
||||
@@ -476,6 +478,7 @@
|
||||
"Second point": "두 번째 점",
|
||||
"Section": "단면",
|
||||
"Section position": "단면 위치",
|
||||
"Sections": "단면",
|
||||
"Sections (one sketch each)": "단면 (각각 스케치 하나)",
|
||||
"Select Other": "다른 항목 선택",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "다른 항목 선택: 모델 위에서 왼쪽 버튼을 가만히 누르고 있거나(또는 오른쪽 클릭 › 다른 항목 선택…) 커서 아래의 모든 항목을 나열합니다. 같은 위치를 다시 클릭하면 다음 항목이 선택됩니다. 가운데 버튼을 더블 클릭하면 홈으로 이동합니다(환경설정 › 탐색).",
|
||||
@@ -758,6 +761,7 @@
|
||||
"cmd.config_rule": "구성 규칙",
|
||||
"cmd.config_rules": "구성 규칙",
|
||||
"cmd.config_table": "구성 표시",
|
||||
"cmd.construct.plane.axis_point": "축과 점을 지나는 평면",
|
||||
"cmd.construct_axis_edge": "모서리를 통과하는 축",
|
||||
"cmd.construct_axis_normal_to_face": "면에 수직인 축",
|
||||
"cmd.construct_axis_through_cylinder": "원기둥/원뿔/토러스를 통과하는 축",
|
||||
@@ -904,6 +908,9 @@
|
||||
"cmd.pointcloud_edit": "포인트 클라우드 편집",
|
||||
"cmd.pointcloud_insert": "포인트 클라우드 삽입",
|
||||
"cmd.pointcloud_list": "포인트 클라우드",
|
||||
"cmd.render.compare.divider": "렌더 비교 구분선 이동",
|
||||
"cmd.render.result.discard": "렌더 결과 버리기",
|
||||
"cmd.render.result.select": "렌더 결과 선택",
|
||||
"cmd.render_backends": "렌더링 백엔드",
|
||||
"cmd.render_batch": "모든 샷 렌더링",
|
||||
"cmd.render_camera_fit": "렌더 대상 화면 맞춤",
|
||||
@@ -1092,6 +1099,7 @@
|
||||
"cmd.timeline_rollback": "타임라인 마커 이동",
|
||||
"cmd.timeline_stop": "기록 재생 중지",
|
||||
"cmd.timeline_suppress": "특성 비활성화",
|
||||
"cmd.ui.assistant": "도우미 표시 전환",
|
||||
"cmd.view.render_browser": "렌더 브라우저",
|
||||
"cmd.view.render_dock": "렌더 도크 이동",
|
||||
"cmd.view.render_focus": "렌더 집중 보기",
|
||||
@@ -1109,14 +1117,17 @@
|
||||
"cmd.view_render_frame": "렌더 프레임 가이드",
|
||||
"cmd.view_save": "새 이름 있는 보기",
|
||||
"cmd.view_shots_strip": "샷 스트립",
|
||||
"color.blue": "파랑",
|
||||
"color.copy": "클릭하여 색상 값 복사",
|
||||
"color.edit": "클릭하여 색상 편집",
|
||||
"color.gamma": "색상 값을 0-255 감마 공간으로 표시 중",
|
||||
"color.green": "초록",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "#rrggbb 또는 #rrggbbaa 입력. 6자리 값은 불투명도 유지",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
"color.hue": "색조",
|
||||
"color.linear": "색상 값을 0-1 선형 공간으로 표시 중",
|
||||
"color.red": "빨강",
|
||||
"color.saturation_value": "채도 및 명도",
|
||||
"color.selected": "선택한 색상",
|
||||
"config.column_filter_empty": "일치하는 열이 없습니다",
|
||||
@@ -1166,7 +1177,11 @@
|
||||
"dialog.pattern_sketch_not_object": "바디나 피처를 선택하세요. 스케치는 둘 다 아닙니다. 곡선은 스케치 안의 패턴이나 대칭 복사로 반복하거나, 먼저 돌출하세요.",
|
||||
"dialog.select": "↖ 선택",
|
||||
"dialog.selected_count": "↖ {count}개 선택됨",
|
||||
"dialog.selection_constraint": "구속 조건 {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · 곡선 {id}",
|
||||
"dialog.selection_guidance": "변경할 입력을 선택하세요. 선택한 객체를 클릭하면 선택이 해제되며, Ctrl/Cmd를 누른 채 클릭해도 전환됩니다. 미리 보기를 확인한 다음 확인을 누르세요.",
|
||||
"dialog.selection_midpoint": "중점 · {id}",
|
||||
"dialog.selection_profile": "{sketch} · 프로파일 {number}",
|
||||
"dialog.selection_step": "{step}/{total} 단계: {input} 선택",
|
||||
"dialog.step": "단계",
|
||||
"document.save_changes": "{name}의 변경 사항을 저장하시겠습니까?",
|
||||
@@ -1207,6 +1222,7 @@
|
||||
"help.attributions": "저작자 표시 (ATTRIBUTION.md)",
|
||||
"help.build": "버전 {version} · 커밋 {commit} · 빌드 날짜 {date}",
|
||||
"help.component.finish_edit": "제자리에서 편집 중인 부품을 저장하고 어셈블리로 돌아가 그 부품으로 어셈블리를 업데이트합니다. 연결된 부품의 변경을 마쳤을 때 사용하세요.",
|
||||
"help.construct.plane.axis_point": "축과 축 위에 있지 않은 점을 포함하는 구성 평면을 만듭니다. 원점 축, 구성 축, 직선 모서리 또는 스케치 직선을 선택한 다음 꼭짓점, 스케치 점 또는 구성점을 선택합니다. 축 위의 점으로는 평면을 하나로 결정할 수 없습니다.",
|
||||
"help.font_details": "인터페이스는 egui의 내장 글꼴을 사용합니다(Ubuntu Font Licence 및 MIT/Bitstream Vera 라이선스의 Ubuntu Light와 Hack, OFL 및 MIT의 Noto Emoji와 이모지 아이콘 글꼴). 스케치 텍스트는 Liberation Sans(SIL OFL 1.1)를 사용합니다. The Crafting Apps의 선택적 글꼴 팩 craft-fonts(BIZ UDPGothic, Shippori Mincho, BIZ UDMincho, Noto Sans CJK SC, Noto Sans Arabic; SIL OFL 1.1)는 SolidCraft 빌드에 포함되지 않습니다.",
|
||||
"help.fonts": "글꼴",
|
||||
"help.licences": "라이선스",
|
||||
@@ -1309,6 +1325,8 @@
|
||||
"joint.from_bodies_hint": "선택한 각 바디가 제자리에 그대로 있으면서 자체의 새 구성 요소로 옮겨집니다.",
|
||||
"joint.one_body": "두 선택이 모두 {body}에 있습니다. 두 번째는 다른 부품에서 선택하세요.",
|
||||
"joint.one_component": "이 부품들은 한 구성 요소의 바디입니다: {bodies}. 관절은 한 구성 요소를 다른 구성 요소에 대해 움직이므로 부품마다 자체 구성 요소가 필요합니다.",
|
||||
"joint_move.conflict": "이 부품의 조인트가 다른 조인트와 충돌합니다: {joint}.",
|
||||
"joint_move.conflict_hint": "충돌하는 조인트를 편집, 비활성화 또는 삭제하여 다른 조인트가 부품을 배치할 수 있게 하세요.",
|
||||
"keymap.command_id": "명령 ID",
|
||||
"keymap.empty": "일치하는 명령이 없습니다",
|
||||
"keymap.use_anyway": "그래도 여기서 {key} 사용",
|
||||
@@ -1349,6 +1367,8 @@
|
||||
"motion.track_name": "트랙 {number}",
|
||||
"nav.alias.help": "Shift+Alt+왼쪽 드래그는 회전, Shift+Alt+가운데 드래그는 이동, Shift+Alt+오른쪽 드래그는 확대/축소입니다. 가운데 드래그로도 이동하고 오른쪽 드래그로도 회전합니다",
|
||||
"nav.blender.help": "가운데 드래그는 회전, Shift+가운데 드래그는 이동, Ctrl+가운데 드래그는 확대/축소입니다. 오른쪽 드래그는 카메라에 영향을 주지 않습니다",
|
||||
"nav.creo.help": "가운데 버튼을 끌면 회전, Shift+가운데 버튼은 이동, Ctrl+가운데 버튼을 위아래로 끌면 확대/축소합니다(아래로 끌면 확대). 휠을 굴려도 확대/축소합니다",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "마우스와 3D 마우스로 롤을 유지하며 양 극점을 넘어 회전합니다. Shift 축 스냅은 비활성화됩니다.",
|
||||
"nav.free_orbit.label": "자유 회전",
|
||||
"nav.free_orbit.snap_help": "마우스와 3D 마우스로 양쪽 극점을 지나 회전할 수 있으며 롤 각도가 유지됩니다. 궤도 회전 드래그 중 Shift를 누르면 보기 방향과 롤 각도가 스냅됩니다.",
|
||||
@@ -1535,6 +1555,8 @@
|
||||
"prefs.button_left": "왼쪽 드래그",
|
||||
"prefs.button_middle": "가운데 드래그",
|
||||
"prefs.button_right": "오른쪽 드래그",
|
||||
"prefs.carousel_next": "다음",
|
||||
"prefs.carousel_previous": "이전",
|
||||
"prefs.choose_font": "글꼴 파일 선택…",
|
||||
"prefs.click": "클릭",
|
||||
"prefs.contact_shading": "부드러운 접촉 음영",
|
||||
@@ -1547,6 +1569,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "곡면",
|
||||
"prefs.curves": "곡면",
|
||||
"prefs.cut_preview_tint": "제거되는 재료를 빨간색으로 표시",
|
||||
"prefs.cut_preview_tint_help": "미리 보기는 피처가 제거하는 재료와 대체되는 형상을 빨간색 반투명 고스트로 채웁니다. 끄면 이 채움이 완전히 투명해지고 윤곽선만 그려집니다.",
|
||||
"prefs.dark_parts_shading": "어두운 부품의 형태 유지",
|
||||
"prefs.dark_parts_shading_help": "선형 조명과 부드러운 하늘광으로 뷰를 음영 처리하여 어두운 면이 검게 뭉개지지 않고 그라데이션을 유지합니다",
|
||||
"prefs.dead_zone_note": "빗금 친 영역이 데드존입니다. 그 안의 움직임은 무시됩니다.",
|
||||
@@ -1569,7 +1593,19 @@
|
||||
"prefs.font_sample": "The quick brown fox · Ø12.50 mm · 45.0° · 0123456789",
|
||||
"prefs.font_unreadable": "선택한 글꼴을 읽을 수 없습니다.",
|
||||
"prefs.general": "일반",
|
||||
"prefs.general_note": "언어, 저장 및 업데이트.",
|
||||
"prefs.general.active": "사용 중",
|
||||
"prefs.general.crash_reports": "충돌 보고서",
|
||||
"prefs.general.edited": "수정됨",
|
||||
"prefs.general.held": "포함 항목",
|
||||
"prefs.general.import": "가져오기...",
|
||||
"prefs.general.last_check": "마지막 확인: {time}",
|
||||
"prefs.general.minutes": "분",
|
||||
"prefs.general.name_setup": "이 설정의 이름",
|
||||
"prefs.general.profile_name": "프로필 이름",
|
||||
"prefs.general.save_new": "새로 저장",
|
||||
"prefs.general.step_import": "STEP 및 가져오기",
|
||||
"prefs.general.this_computer": "이 컴퓨터",
|
||||
"prefs.general_note": "프로필, 언어, 파일 및 업데이트.",
|
||||
"prefs.gesture_orbit": "회전",
|
||||
"prefs.gesture_pan": "이동",
|
||||
"prefs.gesture_zoom": "확대/축소",
|
||||
@@ -1606,10 +1642,10 @@
|
||||
"prefs.live_input": "실시간 입력",
|
||||
"prefs.live_preview": "실시간 미리보기",
|
||||
"prefs.max_file_size": "열 수 있는 최대 파일 크기",
|
||||
"prefs.max_file_size_design": "디자인 파일",
|
||||
"prefs.max_file_size_design": "열 파일의 최대 크기: 디자인",
|
||||
"prefs.max_file_size_flag": "이번 실행은 --max-file-size {size}로 시작했으며 SolidCraft를 닫을 때까지 적용됩니다. 이 설정은 해당 옵션 없이 시작할 때 적용됩니다.",
|
||||
"prefs.max_file_size_note": "기본값: 512 MB 및 256 MB, 최대 16 GB. 파일을 열려면 파일 크기의 몇 배에 달하는 메모리가 필요합니다. 큰 가져오기 데이터를 보관하는 디자인은 저장하고 다시 열기 위해 디자인 파일 한도를 높여야 할 수 있습니다.",
|
||||
"prefs.max_file_size_step": "STEP 및 가져오기 파일",
|
||||
"prefs.max_file_size_step": "열 파일의 최대 크기: STEP 및 가져오기",
|
||||
"prefs.max_file_size_web": "웹 앱은 STEP 및 가져오기 파일을 512 MB까지, 디자인을 256 MB까지 엽니다. 브라우저 탭은 더 큰 파일을 담을 수 없습니다. 데스크톱 앱에서는 이 한도를 높일 수 있습니다.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.minutes": "분",
|
||||
@@ -1621,6 +1657,7 @@
|
||||
"prefs.navigation_note": "마우스, 트랙패드, 3D 마우스로 뷰를 움직이는 방식입니다.",
|
||||
"prefs.navigator_fit": "뷰 탐색기로 뷰를 바꾼 뒤 모델 맞춤",
|
||||
"prefs.navigator_fit_note": "큐브의 면, 모서리, 화살표나 기즈모의 축을 클릭하면 어느 방향에서든 전체가 들어가는 한 거리(경계 구)에서 모델의 중심을 바라봅니다. 켜져 있는 동안 홈 뷰도 같은 확대/축소와 중심을 쓰므로 그 뒤에 고른 뷰는 방향만 바뀝니다. 끄면 뷰가 제자리에서 회전하며 대상과 확대/축소를 유지합니다.",
|
||||
"prefs.network_locations_note": "파일 서버에 디자인을 보관하는 조직용입니다. 끄면 허용한 서버만 사용합니다.",
|
||||
"prefs.not_on_gpu": "이 GPU에서는 사용할 수 없음",
|
||||
"prefs.on": "켜짐",
|
||||
"prefs.opaque": "불투명",
|
||||
@@ -1633,8 +1670,12 @@
|
||||
"prefs.persistent_ribbon_pressed_help": "효과가 켜져 있는 동안 리본 명령을 강조 표시합니다. 끄면 이전의 중립적인 모양과 명령 도움말을 유지합니다.",
|
||||
"prefs.perspective_fov": "시야각",
|
||||
"prefs.preview_choices": "선택 항목 미리보기",
|
||||
"prefs.preview_tint_new_faces": "미리 보기에서 새 면에 색 입히기",
|
||||
"prefs.preview_tint_new_faces_help": "바디를 변경하는 피처의 미리 보기에서 추가되는 면을 미리 보기 색으로 그릴 수 있습니다. 끄면 바디가 원래 색으로 그려집니다.",
|
||||
"prefs.print_face_highlight": "출력 바닥면 강조",
|
||||
"prefs.print_face_highlight_tip": "뷰에서 바디의 출력 바닥면을 색칠하고 윤곽을 그리는 강도입니다. 0 %이면 강조하지 않습니다.",
|
||||
"prefs.profile_export": "내보내기",
|
||||
"prefs.profile_imported": "프로필을 가져왔습니다.",
|
||||
"prefs.project_options_dialog": "Project 옵션 대화상자",
|
||||
"prefs.project_options_dialog_note": "켜기: 투영 전에 지오메트리 또는 바디와 투영 링크를 선택합니다. 끄기: 기존 직접 선택 작업 흐름을 유지합니다.",
|
||||
"prefs.quality_default": "기본값",
|
||||
@@ -1642,10 +1683,11 @@
|
||||
"prefs.quality_slowest": "가장 느림",
|
||||
"prefs.quality_smoother": "더 부드럽게",
|
||||
"prefs.reports_saved": "{n}개 저장됨",
|
||||
"prefs.restore_page_defaults": "페이지 기본값 복원",
|
||||
"prefs.ribbon_click_ends_tool": "실행 중인 도구의 버튼을 클릭하면 도구 종료",
|
||||
"prefs.ribbon_click_ends_tool_note": "사용 중인 스케치 도구의 강조된 리본 버튼을 클릭하면 도구가 종료되어 뷰에서 클릭하면 다시 선택됩니다. 끄기: 클릭하면 도구가 다시 시작됩니다.",
|
||||
"prefs.right_click": "오른쪽 클릭",
|
||||
"prefs.saving": "자동 저장 및 복구",
|
||||
"prefs.saving": "시작 및 저장",
|
||||
"prefs.scale_note": "창에서 Ctrl + / Ctrl −로도 조절합니다. 버튼은 100%에서 28 px입니다.",
|
||||
"prefs.scale_text": "배율 및 텍스트",
|
||||
"prefs.search": "설정 검색",
|
||||
@@ -1823,6 +1865,10 @@
|
||||
"render.appearances": "외관",
|
||||
"render.assign_help": "클릭하여 선택 항목에 적용하거나 드래그하여 미리 보고 놓으세요. 선택이 없으면 칠하기를 시작합니다.",
|
||||
"render.background": "장면 배경",
|
||||
"render.batch_all_shots": "모든 샷 ({count})",
|
||||
"render.batch_empty": "모든 샷을 렌더링하기 전에 샷을 저장하세요.",
|
||||
"render.batch_limit": "한 번에 최대 32개의 샷을 렌더링할 수 있습니다.",
|
||||
"render.batch_status": "{running}개 실행 중 · {waiting}개 대기 중",
|
||||
"render.bloom_enable": "표시 블룸",
|
||||
"render.bloom_help": "양의 선형 하이라이트를 세 번의 정규화 필터로 확산하는 출력 전용 효과입니다. 반경은 출력 픽셀 단위이며 sRGB 전에 더합니다. 렌즈·볼륨·수렴 개선이 아니며 원본 EXR은 변하지 않습니다.",
|
||||
"render.bloom_opaque": "블룸에는 불투명 출력이 필요합니다. 투명 PNG를 몰래 합성하지 않고 거부합니다.",
|
||||
@@ -1914,6 +1960,13 @@
|
||||
"render.children.workstation": "워크스테이션",
|
||||
"render.choose_look": "먼저 외관 라이브러리에서 외관을 선택하세요",
|
||||
"render.clear_stage": "Remove saved stage",
|
||||
"render.compare_divider": "비교 구분선",
|
||||
"render.compare_live": "라이브",
|
||||
"render.compare_live_preview": "라이브 미리보기",
|
||||
"render.compare_render": "렌더 {number}",
|
||||
"render.compare_side": "렌더 {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds}초 · {renderer} · 리비전 {revision}에서 캡처",
|
||||
"render.compare_swap": "맞바꾸기",
|
||||
"render.cpu_budget": "CPU 리소스 프로필",
|
||||
"render.cpu_budget_bodies": "바디 수 상한",
|
||||
"render.cpu_budget_compact": "컴팩트 (기존 제한)",
|
||||
@@ -2382,6 +2435,10 @@
|
||||
"render.result_actual": "100%",
|
||||
"render.result_actual_size": "100%",
|
||||
"render.result_reduced_display": "표시 미리보기를 뷰포트 장치 또는 캐시 제한에 맞춰 축소했습니다. 보관된 PNG와 내보내기는 전체 해상도를 유지합니다. 이 축소 보기에서는 원본 100% 검사가 불가능합니다.",
|
||||
"render.results_choose": "보관된 결과",
|
||||
"render.results_label": "렌더 {number} · {shot} · {seconds}초",
|
||||
"render.results_stale_label": "{result} [이전 버전]",
|
||||
"render.results_unnamed": "이미지",
|
||||
"render.return_design": "설계로 돌아가기",
|
||||
"render.ribbon_backgrounds": "배경",
|
||||
"render.ribbon_check": "검사",
|
||||
@@ -2922,6 +2979,7 @@
|
||||
"ui.agent_chat_auth_terminal": "터미널에서 이것을 실행한 뒤 다시 연결을 누르세요:",
|
||||
"ui.agent_chat_auth_title": "에이전트에 로그인",
|
||||
"ui.agent_chat_cancel": "취소",
|
||||
"ui.agent_chat_cancel_timeout": "에이전트가 중지 요청에 응답하지 않아 연결이 끊어졌습니다. 계속하려면 다시 연결하세요.",
|
||||
"ui.agent_chat_cannot_start": "설치되어 있지만 시작되지 않습니다: {error}",
|
||||
"ui.agent_chat_changes": "변경 {n}개, 실행 취소 한 번",
|
||||
"ui.agent_chat_changes_of": "변경 {n}/{limit}",
|
||||
@@ -2929,6 +2987,7 @@
|
||||
"ui.agent_chat_command": "에이전트 명령",
|
||||
"ui.agent_chat_connect": "연결",
|
||||
"ui.agent_chat_copy": "복사",
|
||||
"ui.agent_chat_custom_heading": "사용자 지정 모델",
|
||||
"ui.agent_chat_custom_hint": "표준 입출력으로 Agent Client Protocol을 사용하는 모든 프로그램. 셸 없이 실행됩니다.",
|
||||
"ui.agent_chat_denied": "거부함",
|
||||
"ui.agent_chat_deny": "거부",
|
||||
@@ -2970,7 +3029,15 @@
|
||||
"ui.agent_chat_mode_all": "모두",
|
||||
"ui.agent_chat_mode_ask": "확인",
|
||||
"ui.agent_chat_mode_edits": "편집",
|
||||
"ui.agent_chat_model_custom": "직접 입력 사용: {model}",
|
||||
"ui.agent_chat_model_rejected": "에이전트가 {model}을(를) 받아들이지 않았습니다: {error}",
|
||||
"ui.agent_chat_model_search": "모델 검색…",
|
||||
"ui.agent_chat_model_tip": "에이전트가 사용하는 모델입니다. 선택은 이 에이전트에 기억됩니다.",
|
||||
"ui.agent_chat_models_failed": "모델 목록을 가져올 수 없습니다: {error}",
|
||||
"ui.agent_chat_models_heading": "사용 가능한 모델",
|
||||
"ui.agent_chat_models_listing": "모델 목록을 가져오는 중…",
|
||||
"ui.agent_chat_models_none": "엔드포인트에 나열된 모델이 없습니다",
|
||||
"ui.agent_chat_models_not_a_list": "응답이 모델 목록이 아닙니다",
|
||||
"ui.agent_chat_new": "새 채팅",
|
||||
"ui.agent_chat_new_confirm": "새 채팅을 시작할까요? 현재 채팅은 지워집니다.",
|
||||
"ui.agent_chat_next_picture": "다음 이미지",
|
||||
@@ -3188,6 +3255,8 @@
|
||||
"ui.coming_later": "추후 지원 예정",
|
||||
"ui.command_done": "{command} 완료",
|
||||
"ui.command_palette_hint": "명령 입력(예: {example}) — 또는 S 키를 눌러 검색",
|
||||
"ui.components_from_bodies_hint": "활성 구성요소 아래에 선택한 각 바디와 같은 이름의 구성요소를 만듭니다.",
|
||||
"ui.components_from_bodies_required": "바디를 하나 이상 선택하세요.",
|
||||
"ui.concentric": "동심",
|
||||
"ui.cone": "원뿔",
|
||||
"ui.conic_curve": "원뿔 곡선",
|
||||
@@ -3200,9 +3269,11 @@
|
||||
"ui.construction.contact_hint": "접선: 원/원호 또는 열린 스플라인/원뿔 곡선 선택 후 접점 선택 (자유 곡선은 끝점만)",
|
||||
"ui.construction.external_hint": "접선: 원 또는 원호, 외부 점, 원하는 해를 차례로 선택",
|
||||
"ui.construction.free_hint": "보조 직선: 두 점 클릭; 마우스 오른쪽 버튼으로 다시 시작",
|
||||
"ui.construction.normal_needs_length": "무한 직선은 항상 보조선입니다. 일반 선을 그리려면 길이를 입력하세요",
|
||||
"ui.construction.point_hint": "보조 직선: 통과할 점 클릭",
|
||||
"ui.construction.reference_hint": "보조 직선: 직선 선택 후 점 선택; 마우스 오른쪽 버튼으로 참조 변경",
|
||||
"ui.construction.ribbon_label": "보조선",
|
||||
"ui.construction.side_hint": "보조 직선: 선이 뻗어 나갈 쪽 클릭; 마우스 오른쪽 버튼으로 다시 시작",
|
||||
"ui.context_menu": "컨텍스트 메뉴",
|
||||
"ui.continuity_g0": "G0 꺾임",
|
||||
"ui.continuity_g1": "G1 접선",
|
||||
@@ -3489,6 +3560,7 @@
|
||||
"ui.insert_design.saved_file": "{name}에 저장되지 않은 변경 사항이 있어 저장된 파일을 삽입했습니다. 저장한 다음 링크 업데이트로 변경 사항을 가져오세요.",
|
||||
"ui.insert_design.unsaved": "저장되지 않은 변경 사항",
|
||||
"ui.inspect": "검사",
|
||||
"ui.instances": "인스턴스",
|
||||
"ui.intersect": "교차",
|
||||
"ui.isolate": "분리 표시",
|
||||
"ui.join": "접합",
|
||||
@@ -3616,9 +3688,9 @@
|
||||
"ui.polygon": "다각형",
|
||||
"ui.preferences": "환경 설정",
|
||||
"ui.press_a_key": "키를 누르세요…",
|
||||
"ui.print_face_body_not_sent": "이 면의 바디는 보내지지 않습니다. 먼저 바디에 추가하십시오",
|
||||
"ui.print_face_cleared": "{body}: 출력 바닥면을 해제했습니다",
|
||||
"ui.print_face_input": "출력 바닥면",
|
||||
"ui.print_face_one_body": "출력 바닥면은 바디마다 하나입니다. 변경하려면 그 바디만 보내십시오",
|
||||
"ui.print_face_pick": "솔리드 바디의 평면 하나를 선택하면 출력 바닥면이 됩니다",
|
||||
"ui.print_face_set": "{body}: 출력 바닥면을 설정했습니다. 슬라이서로 보내면 이 면이 아래로 놓입니다",
|
||||
"ui.profile": "프로파일",
|
||||
@@ -3741,6 +3813,7 @@
|
||||
"ui.slice": "슬라이스",
|
||||
"ui.slicer": "슬라이서",
|
||||
"ui.slicer_delete_tip": "{program}\nDelete 키로 목록에서 제거",
|
||||
"ui.slicer_single_instance_tip": "실행 중인 창에 파일을 불러오는 슬라이서용입니다.",
|
||||
"ui.slot": "슬롯",
|
||||
"ui.smooth": "부드럽게",
|
||||
"ui.smooth_wheel_zoom": "부드러운 휠 확대/축소",
|
||||
@@ -3870,7 +3943,7 @@
|
||||
"update.available": "SolidCraft {version} 사용 가능",
|
||||
"update.button": "업데이트",
|
||||
"update.button_version": "{version}(으)로 업데이트",
|
||||
"update.check_automatically": "업데이트 자동 확인",
|
||||
"update.check_automatically": "시작 시 및 하루에 한 번 확인",
|
||||
"update.check_for_updates": "업데이트 확인",
|
||||
"update.check_now": "지금 확인",
|
||||
"update.check_unavailable": "이 설치에서는 업데이트를 확인할 수 없습니다.",
|
||||
|
||||
+70
-1
@@ -793,6 +793,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (pautan hilang)",
|
||||
"browser.sketch_editing": "{name} (sedang disunting)",
|
||||
"browser.sketch_name": "Lakaran{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "sudah berada dalam bahagiannya",
|
||||
"browser.sketches_to_parts_design_body": "pada jasad reka bentuk itu sendiri",
|
||||
"browser.sketches_to_parts_moved": "{moved} lakaran dialihkan ke bahagian tempat lakaran itu berada",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "pada muka tanpa pautan yang tepat",
|
||||
"browser.sketches_to_parts_not_on_face": "bukan pada muka sesuatu bahagian",
|
||||
"browser.sketches_to_parts_part_missing": "bahagiannya sudah tiada",
|
||||
"browser.sketches_to_parts_result": "{moved} lakaran dialihkan ke bahagian tempat lakaran itu berada; {stayed} kekal di tempatnya: {why}",
|
||||
"browser.units": "Unit: {units}",
|
||||
"cam.flat": "Muka rata",
|
||||
"cam.follower": "Pengikut",
|
||||
@@ -860,6 +867,7 @@
|
||||
"cmd.component_move_bodies": "Alih Jasad ke Komponen",
|
||||
"cmd.component_move_features": "Alih Ciri ke Komponen",
|
||||
"cmd.component_move_sketches": "Alih Lakaran ke Komponen",
|
||||
"cmd.component_move_sketches_to_parts": "Alihkan Lakaran ke Bahagian Masing-masing",
|
||||
"cmd.component_paste_from_design": "Tampal daripada Reka Bentuk Lain",
|
||||
"cmd.component_paste_new": "Tampal Baharu",
|
||||
"cmd.component_properties": "Sifat Fizikal Komponen",
|
||||
@@ -1195,6 +1203,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Splin Titik Kawalan (Darjah 5)",
|
||||
"cmd.sketch_spline_fit_point": "Splin Titik Padanan",
|
||||
"cmd.sketch_spline_insert_point": "Tambah Titik Splin",
|
||||
"cmd.sketch_spline_remove_point": "Buang Titik Splin",
|
||||
"cmd.sketch_spline_reverse": "Songsangkan Arah Splin",
|
||||
"cmd.sketch_spun_profile": "Profil Putaran",
|
||||
"cmd.sketch_toggle_diameter": "Togol Jejari / Diameter",
|
||||
@@ -1287,8 +1296,14 @@
|
||||
"color.hex_hint": "#rrggbb",
|
||||
"color.hue": "Rona",
|
||||
"color.linear": "Memaparkan nilai warna dalam ruang linear 0-1",
|
||||
"color.recent": "Warna terkini",
|
||||
"color.recent_help": "Warna yang anda pilih dalam mana-mana medan warna dipaparkan di sini",
|
||||
"color.saturation": "Ketepuan (peratus)",
|
||||
"color.saturation_value": "Ketepuan dan nilai",
|
||||
"color.selected": "Warna dipilih",
|
||||
"color.step_down": "Turunkan",
|
||||
"color.step_up": "Naikkan",
|
||||
"color.value": "Nilai, iaitu kecerahan (peratus)",
|
||||
"config.column_filter_empty": "Tiada lajur yang sepadan",
|
||||
"contact.components_count": "{count} komponen",
|
||||
"contact.enabled_label": "diaktifkan",
|
||||
@@ -1335,6 +1350,7 @@
|
||||
"dialog.dimension_name": "Dimensi: {name}",
|
||||
"dialog.edit_title": "SUNTING {title}",
|
||||
"dialog.no_shown_body": "Tiada jasad dipaparkan untuk diubah: paparkan satu, atau pilih Jasad Baharu",
|
||||
"dialog.pattern_sketch_not_object": "Pilih jasad atau ciri: lakaran bukan kedua-duanya. Ulang lengkungnya dengan Corak atau Cermin milik lakaran itu sendiri, atau semperit dahulu.",
|
||||
"dialog.select": "↖ Pilih",
|
||||
"dialog.selected_count": "↖ {count} dipilih",
|
||||
"dialog.selection_guidance": "Pilih input untuk diubah. Klik objek dipilih untuk nyahpilih; Ctrl/Cmd+klik juga menogol pilihan. Semak pratonton, kemudian tekan OK.",
|
||||
@@ -1904,6 +1920,7 @@
|
||||
"nav.hfss.help": "Alt+seret kiri menyorot, Shift+Alt+seret kiri menzum, Ctrl+seret kiri pada ruang kosong mengorbit",
|
||||
"nav.inventor.help": "Seret tengah menyorot, Shift+tengah mengorbit, Ctrl+tengah menzum, seret kanan mengorbit",
|
||||
"nav.maya.help": "Alt+seret kiri mengorbit, Alt+seret tengah menyorot, Alt+seret kanan menzum; seret tengah juga menyorot",
|
||||
"nav.nx.help": "Seret tengah mengorbit, Shift+tengah menyorot (tengah + kanan dalam NX), Ctrl+tengah menzum (tengah + kiri dalam NX); seret kanan tidak menggerakkan kamera",
|
||||
"nav.openscad.help": "Seret kiri pada ruang kosong mengorbit, Shift+seret kiri memilih dengan kotak, seretan geometri lakaran menyunting; seret kanan atau tengah menyorot, Shift+seret kanan atau Shift+seret tengah menzum",
|
||||
"nav.perspective.sketch": "Penyuntingan lakaran menggunakan pandangan ortografik. Siapkan lakaran untuk bertukar ke perspektif.",
|
||||
"nav.right_drag_orbits.help": "Mati: seret kanan tidak menggerakkan kamera dan butang kanan hanya membuka menu konteks. Memilih pemetaan tetikus SolidWorks akan mematikannya.",
|
||||
@@ -2125,6 +2142,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Permukaan melengkung",
|
||||
"prefs.curves": "Permukaan melengkung",
|
||||
"prefs.dark_parts_shading": "Kekalkan bentuk bahagian yang gelap",
|
||||
"prefs.dark_parts_shading_help": "Lorekkan pandangan dalam cahaya linear dengan cahaya isian langit yang lembut, supaya muka yang gelap mengekalkan kecerunan dan tidak menjadi hitam",
|
||||
"prefs.dead_zone_note": "Jalur berlorek ialah zon mati: gerakan di dalamnya diabaikan.",
|
||||
"prefs.default": "lalai",
|
||||
"prefs.design": "Lalai reka bentuk",
|
||||
@@ -2139,6 +2158,8 @@
|
||||
"prefs.edges": "Tepi dan teduhan",
|
||||
"prefs.extend": "Panjangkan",
|
||||
"prefs.extra_navigation": "Seret kiri dengan Ctrl pada ruang kosong untuk mengorbit; tahan Space untuk menyorot.",
|
||||
"prefs.extrude_view_isometric": "Putar pandangan ke isometrik apabila Semperit dibuka",
|
||||
"prefs.extrude_view_return": "Kembali ke pandangan terdahulu apabila Semperit ditutup",
|
||||
"prefs.follows_os": "Ikut OS",
|
||||
"prefs.font": "Fon",
|
||||
"prefs.font_fallback": "Glif yang tiada dalam fon pilihan akan beralih kepada fon terbina dalam, jadi tiada label yang memaparkan kotak kosong.",
|
||||
@@ -2154,6 +2175,7 @@
|
||||
"prefs.graphics_card_auto": "Automatik",
|
||||
"prefs.graphics_card_missing": "Kad grafik yang disimpan tidak tersedia; Automatik digunakan.",
|
||||
"prefs.graphics_card_restart": "Berkuat kuasa selepas SolidCraft dimulakan semula. WGPU_ADAPTER_NAME mengatasi pilihan ini.",
|
||||
"prefs.graphics_card_unused": "Kad grafik yang disimpan tidak digunakan untuk tetingkap ini; kad yang sedang digunakan dipaparkan dalam Bantuan › Tentang dan dalam bar status. Cuba Automatik, atau tetapkan WGPU_ADAPTER_NAME kepada bahagian unik pada nama kad itu.",
|
||||
"prefs.graphics_note": "Tukar kelajuan dengan kelicinan. Setiap pilihan menunjukkan kesannya pada contoh.",
|
||||
"prefs.graphics_presets_note": "Pratetap menetapkan pilihan di bawah sekali gus.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Sembunyikan glif kekangan apabila dizum keluar",
|
||||
@@ -2168,6 +2190,8 @@
|
||||
"prefs.history_sketches_note": "Apabila Lakaran ditetapkan kepada Di bawah cirinya, Sejarah di sebelah kiri menyenaraikan setiap lakaran satu tahap ke dalam, di bawah ciri pertama yang dibina daripadanya, apabila ruangnya cukup lebar untuk nama. Garis masa di bahagian bawah sentiasa mengekalkan urutan penciptaan.",
|
||||
"prefs.history_sketches_under_feature": "Di bawah cirinya",
|
||||
"prefs.hold_stop_snapping": "Tahan untuk menghentikan pelekapan",
|
||||
"prefs.home_fit_size": "Saiz Utama dan Muatkan",
|
||||
"prefs.home_fit_size_note": "Seberapa banyak pandangan yang diisi oleh model apabila Utama, Muatkan, reka bentuk yang baru dibuka atau perubahan pandangan melalui kawalan pandangan membingkainya, sama ada tetapan di bawah dihidupkan atau tidak. 100 %: memenuhi pandangan di antara panel, kompas dan bar navigasi.",
|
||||
"prefs.icons_only": "Ikon sahaja",
|
||||
"prefs.imported_file": "Fail yang diimport",
|
||||
"prefs.in_browser": "Dalam pelayar",
|
||||
@@ -2176,13 +2200,20 @@
|
||||
"prefs.interface_language": "Bahasa antara muka",
|
||||
"prefs.interface_note": "Rupa SolidCraft dan kedudukan bahagiannya.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_navigator_arrows": "Songsangkan anak panah putaran kubus pandangan",
|
||||
"prefs.invert_navigator_arrows_note": "Anak panah di sekeliling kubus mengorbit kamera mengikut arah yang ditunjuknya, seperti dalam Fusion dan Inventor: anak panah di kiri menunjuk ke kanan dan mengayunkan kamera ke kanan. Mati: anak panah memutarkan kubus ke arah sisi tempat ia berada, jadi anak panah kiri mengayunkan kamera ke kiri. Anak panah gelek yang melengkung tidak terjejas.",
|
||||
"prefs.keep_work_on_tab_switch": "Kekalkan dialog dan alat terbuka apabila menukar reka bentuk",
|
||||
"prefs.keep_work_on_tab_switch_note": "Setiap tab reka bentuk mengekalkan dialog atau alat yang dibukanya, bersama apa yang anda pilih dan taip di dalamnya, serta tab bar alatnya. Semuanya ada semula apabila anda kembali ke reka bentuk itu. Mati: menukar reka bentuk membatalkannya dan menunjukkan tab Model.",
|
||||
"prefs.labels": "Label",
|
||||
"prefs.language_system": "Sistem",
|
||||
"prefs.layout": "Susun atur",
|
||||
"prefs.layout_note": "Padat melabuhkan panel pada tepi tetingkap. Kelutsinaran pelayar tersedia dalam Terapung. Tukar sisi Pembantu pada halaman Pembantu.",
|
||||
"prefs.left": "Kiri",
|
||||
"prefs.left_click": "Klik kiri",
|
||||
"prefs.line_drag_draws_one": "Tekan dan seret melukis satu garis",
|
||||
"prefs.line_drag_draws_one_note": "Dalam alat Garis, menekan, menyeret dan melepaskan melukis satu garis dari titik tekan hingga titik lepas, dan ia tamat di situ. Klik masih melukis rantaian. Mati: seretan meletakkan titik di tempat ia dilepaskan, seperti klik.",
|
||||
"prefs.line_hover_tangent_arc": "Berhenti pada titik terakhir garis untuk bertukar kepada lengkok tangen",
|
||||
"prefs.line_hover_tangent_arc_note": "Dalam alat Garis, selepas satu segmen dilukis, alihkan penuding menjauhi titik terakhir rantaian, kembali dan tahan penuding diam di situ: klik seterusnya menentukan hujung lengkok tangen, dan gerak isyarat yang sama kembali kepada garis. Mati: berhenti di situ tidak berbuat apa-apa.",
|
||||
"prefs.linked_designs": "Reka bentuk terpaut",
|
||||
"prefs.live_input": "Input langsung",
|
||||
"prefs.live_preview": "Pratonton langsung",
|
||||
@@ -2206,6 +2237,8 @@
|
||||
"prefs.mouse_mapping": "Pemetaan tetikus",
|
||||
"prefs.navigation": "Navigasi",
|
||||
"prefs.navigation_note": "Cara tetikus, pad jejak dan tetikus 3D menggerakkan pandangan.",
|
||||
"prefs.navigator_fit": "Muatkan model selepas perubahan pandangan navigator",
|
||||
"prefs.navigator_fit_note": "Muka, sudut atau anak panah pada kubus pandangan, atau paksi pada gizmo, turut menghadap ke tengah model dari satu jarak yang memuatkannya dari semua sisi (sfera pembatasnya). Utama menggunakan zum dan pusat yang sama selagi ini dihidupkan, jadi pandangan yang dipilih selepasnya hanya berputar. Mati: pandangan berputar di tempat, dengan sasaran dan zumnya kekal.",
|
||||
"prefs.network_host_remove": "Buang",
|
||||
"prefs.network_locations": "Benarkan lokasi rangkaian dalam reka bentuk",
|
||||
"prefs.network_locations_note": "Dimatikan: fail terpaut bagi reka bentuk pada komputer lain (\\\\server\\share) hanya dibuka daripada pelayan yang disenaraikan di sini, atau selepas anda membenarkannya apabila ditanya.",
|
||||
@@ -2254,6 +2287,8 @@
|
||||
"prefs.render_previous_geometry_budget": "Gunakan bajet geometri pratonton Render sebelumnya",
|
||||
"prefs.render_previous_geometry_budget_help": "Pulihkan had geometri tetap sebelumnya (512 MiB pada desktop 64-bit, 128 MiB di tempat lain) dan bukannya bajet berdasarkan peranti.",
|
||||
"prefs.reports_saved": "{n} disimpan",
|
||||
"prefs.ribbon_click_ends_tool": "Mengklik butang alat yang sedang berjalan menamatkannya",
|
||||
"prefs.ribbon_click_ends_tool_note": "Satu klik pada butang reben yang menyala bagi alat lakaran yang sedang digunakan menamatkan alat itu, jadi klik dalam pandangan memilih semula. Mati: klik memulakan alat itu semula.",
|
||||
"prefs.ribbon_reveal": "Tunjukkan hasil carian dalam reben",
|
||||
"prefs.ribbon_reveal_note": "Melalukan kursor pada hasil dalam Cari arahan menunjukkan kedudukan arahan itu dalam reben: tabnya, butangnya atau barisnya dalam senarai juntai sesuatu panel. Arahan yang dimulakan daripada carian berkelip di situ, dan arahan yang sedang berjalan daripada senarai juntai menandakan panelnya.",
|
||||
"prefs.right_click": "Klik kanan",
|
||||
@@ -2283,6 +2318,8 @@
|
||||
"prefs.sketch_exit_view.previous": "Kembali ke pandangan sebelumnya",
|
||||
"prefs.sketch_exit_view.unless_rotated": "Kembali melainkan diputar",
|
||||
"prefs.sketch_exit_view_note": "Ke mana pandangan beralih selepas Siap Lakaran, Batal atau Esc dua kali.",
|
||||
"prefs.sketch_face_activates_part": "Melakar pada muka sesuatu bahagian menjadikan bahagian itu aktif",
|
||||
"prefs.sketch_face_activates_part_note": "Lakaran yang dimulakan pada muka jasad milik komponen lain mengaktifkan komponen itu terlebih dahulu, jadi lakaran itu menjadi milik komponen tersebut dan kekal bersama bahagian itu dalam pelayar. Bahagian itu kekal aktif selepas itu. Mati: lakaran menjadi milik komponen aktif.",
|
||||
"prefs.sketch_open_look_at": "Putar menghadap lakaran apabila dibuka",
|
||||
"prefs.sketch_open_look_at_note": "Mati: mencipta atau menyunting lakaran membiarkan pandangan seperti sedia ada. Pandang Tepat menghadapkan pandangan ke lakaran apabila anda mahu.",
|
||||
"prefs.sketch_recent_snap": "Lekap auto pada pusat dan titik tengah terkini",
|
||||
@@ -2314,6 +2351,24 @@
|
||||
"prefs.status_error": "Ralat",
|
||||
"prefs.status_selected": "Dipilih",
|
||||
"prefs.status_warning": "Amaran",
|
||||
"prefs.structure.appearance": "Penampilan",
|
||||
"prefs.structure.appearance_note": "Warna, teks dan kebolehcapaian.",
|
||||
"prefs.structure.in_profile": "Dalam profil",
|
||||
"prefs.structure.layout": "Susun atur",
|
||||
"prefs.structure.layout_note": "Kedudukan bahagian-bahagian tetingkap.",
|
||||
"prefs.structure.look": "Gaya",
|
||||
"prefs.structure.measure_pointer": "Ukur dan penuding",
|
||||
"prefs.structure.mouse3d": "Tetikus 3D",
|
||||
"prefs.structure.mouse3d_note": "SpaceMouse dan SpaceNavigator.",
|
||||
"prefs.structure.navigation_note": "Tetikus dan pad jejak. Tetikus 3D mempunyai halamannya sendiri.",
|
||||
"prefs.structure.profile": "Profil",
|
||||
"prefs.structure.profile_edited": "Berubah sejak disimpan",
|
||||
"prefs.structure.profile_hover": "Disimpan dalam profil tetapan",
|
||||
"prefs.structure.scale_percent": "{percent}%",
|
||||
"prefs.structure.sketch": "Lakaran",
|
||||
"prefs.structure.tools": "Alat",
|
||||
"prefs.structure.tools_note": "Cara alat pemodelan berfungsi.",
|
||||
"prefs.structure.work": "Kerja",
|
||||
"prefs.supersampling_brief": "Melukis pandangan pada saiz lebih besar lalu menapisnya ke bawah: sangat tajam, tetapi lebih berat.",
|
||||
"prefs.tab_accent_note": "Aksen mengikut tab aktif",
|
||||
"prefs.theme_file": "fail tema",
|
||||
@@ -3811,6 +3866,7 @@
|
||||
"sketch_text.formula": "Mulakan dengan = untuk membina perkataan daripada parameter: ='W' + number(Width)",
|
||||
"sketch_text.italic": "Condong",
|
||||
"sketch_text.loading_fonts": "Mencari fon yang dipasang…",
|
||||
"sketch_text.no_bold": "Fon terbina dalam tiada versi tebal: pilih fon yang dipasang",
|
||||
"sketch_text.on_path": "Teks mengikut laluan lengkung, yang menentukan kedudukannya",
|
||||
"sketch_text.path_clear": "Pisahkan teks daripada lengkung",
|
||||
"sketch_text.path_hint": "Lengkung lakaran yang akan diikuti teks: klik di sini, kemudian klik lengkung",
|
||||
@@ -4213,6 +4269,9 @@
|
||||
"ui.ai_permissions": "Kebenaran",
|
||||
"ui.ai_preset": "Pratetap",
|
||||
"ui.ai_protocol": "Protokol",
|
||||
"ui.ai_reasoning_effort": "Usaha penaakulan",
|
||||
"ui.ai_reasoning_effort_field_hint": "kosong = lalai pembekal",
|
||||
"ui.ai_reasoning_effort_hint": "Pilihan. Dihantar kepada titik akhir sebagai `reasoning_effort`; nilai yang diterima berbeza bagi setiap pembekal (contohnya `low`, `high` atau `max`). Nilai yang anda tetapkan di sini tidak sekali-kali diganti oleh SolidCraft. Biarkan kosong untuk tidak menghantar apa-apa dan mengekalkan lalai pembekal.",
|
||||
"ui.ai_remove": "Buang",
|
||||
"ui.ai_request_timed_out": "Model mengambil masa lebih daripada {seconds} s. Anda boleh menaikkan had masa permintaan untuk titik akhir ini dalam Tetapan › Pembantu.",
|
||||
"ui.ai_request_timeout": "Had masa permintaan",
|
||||
@@ -4378,6 +4437,7 @@
|
||||
"ui.dark": "Gelap",
|
||||
"ui.default_modelling_orientation": "Orientasi pemodelan lalai",
|
||||
"ui.delete": "Padam",
|
||||
"ui.delete_select_first": "Pilih dahulu apa yang hendak dipadam: ciri, jasad, komponen atau geometri lakaran",
|
||||
"ui.depth": "Kedalaman",
|
||||
"ui.design": "Reka",
|
||||
"ui.design_properties.author": "Pengarang",
|
||||
@@ -4470,6 +4530,7 @@
|
||||
"ui.edit_in_place_saved_elsewhere": "Bahagian ini disimpan ke fail lain; pemasangan masih memaut {name}",
|
||||
"ui.edit_in_place_wait": "Tunggu sehingga apa yang sedang berjalan dalam reka bentuk selesai",
|
||||
"ui.edit_section_analysis": "Sunting Analisis Keratan",
|
||||
"ui.edit_sketch_empty": "Tiada lakaran untuk disunting",
|
||||
"ui.ellipse": "Elips",
|
||||
"ui.emboss_wrap_note": "Profil garis ortogon yang tangen pada satu silinder yang disokong dibalut secara automatik. Pilih dinding untuk memilih sasaran.",
|
||||
"ui.end_radius": "Jejari Akhir",
|
||||
@@ -4480,7 +4541,9 @@
|
||||
"ui.equation_explicit": "Eksplisit",
|
||||
"ui.equation_form": "Bentuk",
|
||||
"ui.equation_note": "Pemboleh ubah ialah nombor biasa: sin(t) mengambilnya dalam radian. Hasil tanpa unit adalah dalam unit reka bentuk.",
|
||||
"ui.equation_note_range": "Pemboleh ubah ialah panjang, begitu juga julatnya: campurkannya dengan parameter panjang, dan bahagikannya dengan satu panjang untuk sin(). Hasil tanpa unit adalah dalam unit reka bentuk.",
|
||||
"ui.equation_parametric": "Parametrik",
|
||||
"ui.equation_variable_unit": "Pemboleh ubah menggunakan unit julat",
|
||||
"ui.equation_x_of_t": "x(t)",
|
||||
"ui.equation_y_of_t": "y(t)",
|
||||
"ui.equation_y_of_x": "y(x)",
|
||||
@@ -4655,12 +4718,14 @@
|
||||
"ui.joints": "Sendi",
|
||||
"ui.keyboard_shortcuts": "Pintasan Kekunci",
|
||||
"ui.language": "Bahasa",
|
||||
"ui.lasso_select": "Lasso (seret gelung bebas: memilih yang sepenuhnya berada di dalam)",
|
||||
"ui.layout_compact": "Padat",
|
||||
"ui.layout_floating": "Terapung",
|
||||
"ui.length": "Panjang",
|
||||
"ui.length_decimals": "Perpuluhan panjang",
|
||||
"ui.light": "Cerah",
|
||||
"ui.line": "Garis",
|
||||
"ui.line_arc_hint": "Lengkok tangen: klik hujung lengkok. Berhenti lagi pada titik terakhir untuk garis; klik kanan atau Esc untuk selesai",
|
||||
"ui.link_copies": "Pautkan Salinan Serupa",
|
||||
"ui.link_copies_keep": "Kekalkan",
|
||||
"ui.link_copies_link": "Pautkan",
|
||||
@@ -4749,6 +4814,7 @@
|
||||
"ui.part_size_shared": "Membina semula bahagian, menggantikan sebarang ciri yang ditambah. Semua kejadian komponen ini dikemas kini.",
|
||||
"ui.paste": "Tampal",
|
||||
"ui.path": "Laluan",
|
||||
"ui.pattern_object_type_tip": "Jasad: bentuk pepejal, dipilih dalam pandangan atau Pelayar; setiap satu disalin keseluruhannya.\nCiri: langkah Sejarah, seperti semperit atau lubang, dipilih dalam Sejarah atau pada muka yang dihasilkannya; langkah itu diulang.\nLakaran bukan kedua-duanya: ulang lengkungnya dengan Corak atau Cermin milik lakaran itu sendiri, atau semperit dahulu.",
|
||||
"ui.pattern_on_path": "Corak Sepanjang Laluan",
|
||||
"ui.perforate_margin_default": "daripada diameter",
|
||||
"ui.perforate_pitch": "Pic",
|
||||
@@ -4794,9 +4860,9 @@
|
||||
"ui.polygon": "Poligon",
|
||||
"ui.preferences": "Tetapan",
|
||||
"ui.press_a_key": "tekan kekunci…",
|
||||
"ui.print_face_body_not_sent": "Jasad muka itu tidak dihantar: tambahkannya pada Jasad dahulu",
|
||||
"ui.print_face_cleared": "{body}: muka cetakan dikosongkan",
|
||||
"ui.print_face_input": "Muka cetakan",
|
||||
"ui.print_face_one_body": "Muka cetakan dimiliki oleh satu jasad: hantar jasad itu sahaja untuk menukarnya",
|
||||
"ui.print_face_pick": "Pilih satu muka rata pada jasad pepejal untuk menjadikannya muka cetakan",
|
||||
"ui.print_face_set": "{body}: muka cetakan ditetapkan; Hantar ke penghiris meletakkan jasad di atasnya",
|
||||
"ui.profile": "Profil",
|
||||
@@ -5024,12 +5090,15 @@
|
||||
"ui.view_angles": "Sudut pandangan",
|
||||
"ui.view_angles_convention": "Darjah pada paksi ke atas. Yaw 0° ialah pandangan Hadapan, 90° Kiri, −90° Kanan, 180° Belakang; pic 90° ialah pandangan Atas; gelek positif memutar model mengikut arah jam.",
|
||||
"ui.view_angles_help": "Memaparkan pic, yaw dan gelek pandangan dalam darjah serta unjurannya di bawah gizmo orientasi. Klik bacaan untuk menaip sudut yang tepat.",
|
||||
"ui.view_angles_lock": "Kunci {axis}",
|
||||
"ui.view_angles_ortho": "Orto",
|
||||
"ui.view_angles_persp": "Persp",
|
||||
"ui.view_angles_pitch": "Pic",
|
||||
"ui.view_angles_readout": "Pic {pitch} · Yaw {yaw} · Gelek {roll}",
|
||||
"ui.view_angles_roll": "Gelek",
|
||||
"ui.view_angles_unlock": "Buka kunci {axis}",
|
||||
"ui.view_angles_yaw": "Yaw",
|
||||
"ui.view_angles_zero": "Tetapkan {axis} kepada 0°",
|
||||
"ui.view_centre": "Pusat pandangan",
|
||||
"ui.viewport_status_clear": "Matikan {name}",
|
||||
"ui.viewport_status_clear_hint": "Matikan {name} · Esc",
|
||||
|
||||
+70
-1
@@ -793,6 +793,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (tilknytning mistet)",
|
||||
"browser.sketch_editing": "{name} (redigeres)",
|
||||
"browser.sketch_name": "Skisse{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "allerede i delen sin",
|
||||
"browser.sketches_to_parts_design_body": "på en kropp i selve designet",
|
||||
"browser.sketches_to_parts_moved": "Flyttet {moved} skisser til delen de ligger på",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "på en flate uten nøyaktig tilknytning",
|
||||
"browser.sketches_to_parts_not_on_face": "ikke på flaten til en del",
|
||||
"browser.sketches_to_parts_part_missing": "delen finnes ikke lenger",
|
||||
"browser.sketches_to_parts_result": "Flyttet {moved} skisser til delen de ligger på; {stayed} ble liggende: {why}",
|
||||
"browser.units": "Enheter: {units}",
|
||||
"cam.flat": "Plan flate",
|
||||
"cam.follower": "Følger",
|
||||
@@ -860,6 +867,7 @@
|
||||
"cmd.component_move_bodies": "Flytt kropper til komponent",
|
||||
"cmd.component_move_features": "Flytt funksjoner til komponent",
|
||||
"cmd.component_move_sketches": "Flytt skisser til komponent",
|
||||
"cmd.component_move_sketches_to_parts": "Flytt skisser til delene deres",
|
||||
"cmd.component_paste_from_design": "Lim inn fra et annet design",
|
||||
"cmd.component_paste_new": "Lim inn ny",
|
||||
"cmd.component_properties": "Fysiske egenskaper for komponent",
|
||||
@@ -1195,6 +1203,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Spline med kontrollpunkter (grad 5)",
|
||||
"cmd.sketch_spline_fit_point": "Spline gjennom punkter",
|
||||
"cmd.sketch_spline_insert_point": "Legg til splinepunkt",
|
||||
"cmd.sketch_spline_remove_point": "Fjern splinepunkt",
|
||||
"cmd.sketch_spline_reverse": "Reverser splineretning",
|
||||
"cmd.sketch_spun_profile": "Dreid profil",
|
||||
"cmd.sketch_toggle_diameter": "Veksle mellom radius / diameter",
|
||||
@@ -1287,8 +1296,14 @@
|
||||
"color.hex_hint": "#rrggbb",
|
||||
"color.hue": "Kulør",
|
||||
"color.linear": "Viser lineære fargeverdier i området 0–1",
|
||||
"color.recent": "Nylige farger",
|
||||
"color.recent_help": "Fargene du velger i et fargefelt, vises her",
|
||||
"color.saturation": "Metning (prosent)",
|
||||
"color.saturation_value": "Metning og lysverdi",
|
||||
"color.selected": "Valgt farge",
|
||||
"color.step_down": "Ett trinn ned",
|
||||
"color.step_up": "Ett trinn opp",
|
||||
"color.value": "Lysverdi, lysstyrken (prosent)",
|
||||
"config.column_filter_empty": "Ingen samsvarende kolonner",
|
||||
"contact.components_count": "{count} komponenter",
|
||||
"contact.enabled_label": "aktivert",
|
||||
@@ -1335,6 +1350,7 @@
|
||||
"dialog.dimension_name": "Mål: {name}",
|
||||
"dialog.edit_title": "REDIGER {title}",
|
||||
"dialog.no_shown_body": "Ingen synlig kropp å endre: vis en, eller velg Ny kropp",
|
||||
"dialog.pattern_sketch_not_object": "Velg kropper eller funksjoner: en skisse er ingen av delene. Gjenta kurvene med skissens eget Mønster eller Speil, eller ekstruder den først.",
|
||||
"dialog.select": "↖ Velg",
|
||||
"dialog.selected_count": "↖ {count} valgt",
|
||||
"dialog.selection_guidance": "Velg et felt for å endre det. Klikk på valgte objekter for å fjerne dem fra utvalget; Ctrl/Cmd+klikk veksler også mellom valgt og ikke valgt. Se over forhåndsvisningen, og trykk deretter OK.",
|
||||
@@ -1904,6 +1920,7 @@
|
||||
"nav.hfss.help": "Alt+venstredrag panorerer, Shift+Alt+venstredrag zoomer, Ctrl+venstredrag fra tomt område roterer",
|
||||
"nav.inventor.help": "Midtdrag panorerer, Shift+midtdrag roterer, Ctrl+midtdrag zoomer, høyredrag roterer",
|
||||
"nav.maya.help": "Alt+venstredrag roterer, Alt+midtdrag panorerer, Alt+høyredrag zoomer; midtdrag panorerer også",
|
||||
"nav.nx.help": "Midtdrag roterer, Shift+midtdrag panorerer (i NX: midt + høyre), Ctrl+midtdrag zoomer (i NX: midt + venstre); høyredrag lar kameraet stå i ro",
|
||||
"nav.openscad.help": "Venstredrag i tomt område roterer, Shift+venstredrag velger med ramme, dra i skissegeometri for å redigere; høyre- eller midtdrag panorerer, Shift+høyredrag eller Shift+midtdrag zoomer",
|
||||
"nav.perspective.sketch": "Skisseredigering bruker ortografisk visning. Fullfør skissen for å bytte til perspektiv.",
|
||||
"nav.right_drag_orbits.help": "Av: et høyredrag lar kameraet stå i ro, og høyre museknapp åpner bare kontekstmenyen. Når du velger SolidWorks-museoppsettet, slås dette av.",
|
||||
@@ -2125,6 +2142,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Buede flater",
|
||||
"prefs.curves": "Buede flater",
|
||||
"prefs.dark_parts_shading": "Bevar formen på mørke deler",
|
||||
"prefs.dark_parts_shading_help": "Skygger visningen i lineært lys med en myk himmelfylling, slik at mørke flater beholder en gradient i stedet for å bli svarte",
|
||||
"prefs.dead_zone_note": "Det skraverte feltet er dødsonen: bevegelser innenfor den ignoreres.",
|
||||
"prefs.default": "standard",
|
||||
"prefs.design": "Standarder for design",
|
||||
@@ -2139,6 +2158,8 @@
|
||||
"prefs.edges": "Kanter og skyggelegging",
|
||||
"prefs.extend": "Utvid",
|
||||
"prefs.extra_navigation": "Ctrl+venstredrag i tomt område roterer; hold mellomromstasten inne for å panorere.",
|
||||
"prefs.extrude_view_isometric": "Drei visningen til isometrisk når Ekstrudering åpnes",
|
||||
"prefs.extrude_view_return": "Gå tilbake til forrige visning når Ekstrudering lukkes",
|
||||
"prefs.follows_os": "Følger operativsystemet",
|
||||
"prefs.font": "Skrift",
|
||||
"prefs.font_fallback": "Tegn som mangler i valgt skrift, hentes fra den innebygde skriften, slik at ingen etiketter viser tomme ruter.",
|
||||
@@ -2154,6 +2175,7 @@
|
||||
"prefs.graphics_card_auto": "Automatisk",
|
||||
"prefs.graphics_card_missing": "Det lagrede grafikkortet er utilgjengelig. Automatisk brukes i stedet.",
|
||||
"prefs.graphics_card_restart": "Trer i kraft etter at SolidCraft er startet på nytt. WGPU_ADAPTER_NAME overstyrer dette valget.",
|
||||
"prefs.graphics_card_unused": "Det lagrede grafikkortet ble ikke brukt for dette vinduet. Kortet som er i bruk, vises i Hjelp › Om og i statuslinjen. Prøv Automatisk, eller sett WGPU_ADAPTER_NAME til en unik del av kortets navn.",
|
||||
"prefs.graphics_note": "Velg mellom hastighet og jevnhet. Hvert valg viser effekten på et eksempel.",
|
||||
"prefs.graphics_presets_note": "Forhåndsinnstillinger angir valgene nedenfor samlet.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Skjul bindingssymboler når det er zoomet ut",
|
||||
@@ -2168,6 +2190,8 @@
|
||||
"prefs.history_sketches_note": "Når Skisser er satt til Under funksjonen sin, viser tidslinjen til venstre hver skisse ett nivå inn, under den første funksjonen som er bygget fra den, så lenge den er bred nok til å vise navn. Tidslinjen nederst beholder alltid opprettelsesrekkefølgen.",
|
||||
"prefs.history_sketches_under_feature": "Under funksjonen sin",
|
||||
"prefs.hold_stop_snapping": "Hold inne for å slå av punktfangst",
|
||||
"prefs.home_fit_size": "Størrelse for Hjem og Tilpass",
|
||||
"prefs.home_fit_size_note": "Hvor mye av visningen modellen fyller når Hjem, Tilpass, et åpnet design eller en visningsendring fra navigatoren rammer den inn, enten innstillingen under er på eller ikke. 100 %: fyller visningen mellom panelene, kompasset og navigasjonslinjen.",
|
||||
"prefs.icons_only": "Bare ikoner",
|
||||
"prefs.imported_file": "Importert fil",
|
||||
"prefs.in_browser": "I modelltreet",
|
||||
@@ -2176,13 +2200,20 @@
|
||||
"prefs.interface_language": "Grensesnittspråk",
|
||||
"prefs.interface_note": "Hvordan SolidCraft ser ut, og hvor delene er plassert.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_navigator_arrows": "Inverter rotasjonspilene på visningskuben",
|
||||
"prefs.invert_navigator_arrows_note": "Pilene rundt kuben roterer kameraet i den retningen de peker, som i Fusion og Inventor: pilen til venstre peker mot høyre og svinger kameraet mot høyre. Av: en pil roterer kuben mot siden den står på, så venstre pil svinger kameraet mot venstre. De krumme rullepilene påvirkes ikke.",
|
||||
"prefs.keep_work_on_tab_switch": "Behold dialoger og verktøy åpne når du bytter design",
|
||||
"prefs.keep_work_on_tab_switch_note": "Hver designfane beholder sin åpne dialog eller sitt åpne verktøy, med det du har valgt og skrevet i den, og sin fane på båndet. De er der igjen når du kommer tilbake til designet. Av: å bytte design avbryter dem og viser fanen Modell.",
|
||||
"prefs.labels": "Etiketter",
|
||||
"prefs.language_system": "System",
|
||||
"prefs.layout": "Oppsett",
|
||||
"prefs.layout_note": "Kompakt fester panelene til vinduskantene. Gjennomsiktig modelltre er tilgjengelig i Flytende. Endre siden for assistenten på assistentsiden.",
|
||||
"prefs.left": "Venstre",
|
||||
"prefs.left_click": "Venstreklikk",
|
||||
"prefs.line_drag_draws_one": "Trykk og dra tegner én linje",
|
||||
"prefs.line_drag_draws_one_note": "I Linje-verktøyet tegner trykk, dra og slipp én linje fra der du trykket til der du slapp, og linjen avsluttes der. Klikk tegner fortsatt en kjede. Av: et drag plasserer et punkt der det slippes, slik et klikk gjør.",
|
||||
"prefs.line_hover_tangent_arc": "Hvil på det siste punktet på en linje for å bytte til en tangentbue",
|
||||
"prefs.line_hover_tangent_arc_note": "I Linje-verktøyet: når et segment er tegnet, beveg pekeren bort fra det siste punktet i kjeden, kom tilbake og hold pekeren i ro på det: neste klikk avslutter en tangentbue, og samme bevegelse går tilbake til linjer. Av: å hvile der gjør ingenting.",
|
||||
"prefs.linked_designs": "Koblede design",
|
||||
"prefs.live_input": "Direkte inndata",
|
||||
"prefs.live_preview": "Direkte forhåndsvisning",
|
||||
@@ -2206,6 +2237,8 @@
|
||||
"prefs.mouse_mapping": "Museoppsett",
|
||||
"prefs.navigation": "Navigasjon",
|
||||
"prefs.navigation_note": "Hvordan mus, styreflate og 3D-mus beveger visningen.",
|
||||
"prefs.navigator_fit": "Tilpass modellen etter en visningsendring fra navigatoren",
|
||||
"prefs.navigator_fit_note": "En flate, et hjørne eller en pil på visningskuben, eller en akse på orienteringsindikatoren, ser også mot midten av modellen fra den ene avstanden som får den til å passe fra alle sider (den omsluttende kulen). Hjem bruker samme zoom og senter mens dette er på, så visninger som velges etterpå bare dreier. Av: visningen dreier på stedet og beholder målpunktet og zoomen sin.",
|
||||
"prefs.network_host_remove": "Fjern",
|
||||
"prefs.network_locations": "Tillat nettverksplasseringer i design",
|
||||
"prefs.network_locations_note": "Av: lenkede filer i et design på en annen datamaskin (\\\\server\\share) åpnes bare fra serverne som er oppført her, eller etter at du har tillatt en når du blir spurt.",
|
||||
@@ -2254,6 +2287,8 @@
|
||||
"prefs.render_previous_geometry_budget": "Bruk forrige geometribudsjett for rendringsforhåndsvisning",
|
||||
"prefs.render_previous_geometry_budget_help": "Gjenopprett den forrige faste geometrigrensen (512 MiB på 64-biters skrivebordsmaskiner, 128 MiB ellers) i stedet for budsjettet som er basert på enheten.",
|
||||
"prefs.reports_saved": "{n} lagret",
|
||||
"prefs.ribbon_click_ends_tool": "Å klikke på knappen til verktøyet som kjører, avslutter det",
|
||||
"prefs.ribbon_click_ends_tool_note": "Et klikk på den lysende båndknappen til skisseverktøyet som er i bruk, avslutter verktøyet, så klikk i visningen velger igjen. Av: klikket starter verktøyet på nytt.",
|
||||
"prefs.ribbon_reveal": "Vis søkeresultater på båndet",
|
||||
"prefs.ribbon_reveal_note": "Når pekeren holdes over et resultat i Søk i kommandoer, vises hvor kommandoen ligger på båndet: fanen, knappen eller raden i et panels nedtrekksmeny. En kommando som startes fra søket, blinker der, og en kjørende kommando fra en nedtrekksmeny markerer panelet sitt.",
|
||||
"prefs.right_click": "Høyreklikk",
|
||||
@@ -2283,6 +2318,8 @@
|
||||
"prefs.sketch_exit_view.previous": "Gå tilbake til forrige visning",
|
||||
"prefs.sketch_exit_view.unless_rotated": "Gå tilbake med mindre visningen er rotert",
|
||||
"prefs.sketch_exit_view_note": "Hvor visningen går etter Fullfør skisse, Avbryt eller dobbelt Esc.",
|
||||
"prefs.sketch_face_activates_part": "Skissering på flaten til en del gjør delen aktiv",
|
||||
"prefs.sketch_face_activates_part_note": "En skisse som startes på en flate av kroppen til en annen komponent, aktiverer først den komponenten, så skissen tilhører den og blir hos den delen i modelltreet. Delen forblir aktiv etterpå. Av: skissen tilhører den aktive komponenten.",
|
||||
"prefs.sketch_open_look_at": "Orienter mot skissen når den åpnes",
|
||||
"prefs.sketch_open_look_at_note": "Av: å opprette eller redigere en skisse lar visningen stå som den er. Orienter mot vender visningen mot skissen når du vil.",
|
||||
"prefs.sketch_recent_snap": "Fang automatisk til nylige sentre og midtpunkter",
|
||||
@@ -2314,6 +2351,24 @@
|
||||
"prefs.status_error": "Feil",
|
||||
"prefs.status_selected": "Valgt",
|
||||
"prefs.status_warning": "Advarsel",
|
||||
"prefs.structure.appearance": "Utseende",
|
||||
"prefs.structure.appearance_note": "Farger, tekst og tilgjengelighet.",
|
||||
"prefs.structure.in_profile": "I profilen",
|
||||
"prefs.structure.layout": "Oppsett",
|
||||
"prefs.structure.layout_note": "Hvor delene av vinduet plasseres.",
|
||||
"prefs.structure.look": "Stil",
|
||||
"prefs.structure.measure_pointer": "Mål og peker",
|
||||
"prefs.structure.mouse3d": "3D-mus",
|
||||
"prefs.structure.mouse3d_note": "SpaceMouse og SpaceNavigator.",
|
||||
"prefs.structure.navigation_note": "Mus og styreflate. 3D-musen har sin egen side.",
|
||||
"prefs.structure.profile": "Profil",
|
||||
"prefs.structure.profile_edited": "Endret siden den ble lagret",
|
||||
"prefs.structure.profile_hover": "Lagret i innstillingsprofiler",
|
||||
"prefs.structure.scale_percent": "{percent} %",
|
||||
"prefs.structure.sketch": "Skisse",
|
||||
"prefs.structure.tools": "Verktøy",
|
||||
"prefs.structure.tools_note": "Hvordan modelleringsverktøyene oppfører seg.",
|
||||
"prefs.structure.work": "Arbeid",
|
||||
"prefs.supersampling_brief": "Tegner visningen større og skalerer den ned: svært skarpt, og mer krevende.",
|
||||
"prefs.tab_accent_note": "Aksentfargen følger den aktive fanen",
|
||||
"prefs.theme_file": "temafil",
|
||||
@@ -3811,6 +3866,7 @@
|
||||
"sketch_text.formula": "Start med = for å bygge ordene fra parametere: ='W' + number(Width)",
|
||||
"sketch_text.italic": "Kursiv",
|
||||
"sketch_text.loading_fonts": "Ser etter installerte skrifter…",
|
||||
"sketch_text.no_bold": "Den innebygde skriften har ingen fet utgave: velg en installert skrift",
|
||||
"sketch_text.on_path": "Teksten følger banen og plasseres langs kurven",
|
||||
"sketch_text.path_clear": "Ta teksten av kurven",
|
||||
"sketch_text.path_hint": "Velg en kurve i skissen som teksten skal følge: klikk her og deretter på kurven",
|
||||
@@ -4213,6 +4269,9 @@
|
||||
"ui.ai_permissions": "Tillatelser",
|
||||
"ui.ai_preset": "Forhåndsinnstilling",
|
||||
"ui.ai_protocol": "Protokoll",
|
||||
"ui.ai_reasoning_effort": "Resonneringsinnsats",
|
||||
"ui.ai_reasoning_effort_field_hint": "tom = standard fra leverandøren",
|
||||
"ui.ai_reasoning_effort_hint": "Valgfritt. Sendes til endepunktet som `reasoning_effort`; godkjente verdier varierer fra leverandør til leverandør (for eksempel `low`, `high` eller `max`). En verdi du angir her, blir aldri erstattet av SolidCraft. La feltet stå tomt for å ikke sende noe og beholde leverandørens standard.",
|
||||
"ui.ai_remove": "Fjern",
|
||||
"ui.ai_request_timed_out": "Modellen brukte lenger tid enn {seconds} s. Du kan øke tidsavbruddet for forespørsler til dette endepunktet i Innstillinger › Assistent.",
|
||||
"ui.ai_request_timeout": "Tidsavbrudd for forespørsel",
|
||||
@@ -4378,6 +4437,7 @@
|
||||
"ui.dark": "Mørk",
|
||||
"ui.default_modelling_orientation": "Standard modelleringsorientering",
|
||||
"ui.delete": "Slett",
|
||||
"ui.delete_select_first": "Velg først hva som skal slettes: funksjoner, kropper, komponenter eller skissegeometri",
|
||||
"ui.depth": "Dybde",
|
||||
"ui.design": "Design",
|
||||
"ui.design_properties.author": "Forfatter",
|
||||
@@ -4470,6 +4530,7 @@
|
||||
"ui.edit_in_place_saved_elsewhere": "Delen ble lagret i en annen fil; sammenstillingen er fortsatt koblet til {name}",
|
||||
"ui.edit_in_place_wait": "Vent til det som kjører i designet er ferdig",
|
||||
"ui.edit_section_analysis": "Rediger snittanalyse",
|
||||
"ui.edit_sketch_empty": "Ingen skisser å redigere",
|
||||
"ui.ellipse": "Ellipse",
|
||||
"ui.emboss_wrap_note": "Ortogonale linjeprofiler som tangerer én støttet sylinder, legges automatisk rundt den. Velg en vegg for å velge et mål.",
|
||||
"ui.end_radius": "Sluttradius",
|
||||
@@ -4480,7 +4541,9 @@
|
||||
"ui.equation_explicit": "Eksplisitt",
|
||||
"ui.equation_form": "Form",
|
||||
"ui.equation_note": "Variabelen er et rent tall: sin(t) tolker det i radianer. Et resultat uten enhet er i designets enhet.",
|
||||
"ui.equation_note_range": "Variabelen er en lengde, slik intervallet er: kombiner den med lengdeparametere, og del den på en lengde for sin(). Et resultat uten enhet er i designets enhet.",
|
||||
"ui.equation_parametric": "Parametrisk",
|
||||
"ui.equation_variable_unit": "Variabelen har intervallets enhet",
|
||||
"ui.equation_x_of_t": "x(t)",
|
||||
"ui.equation_y_of_t": "y(t)",
|
||||
"ui.equation_y_of_x": "y(x)",
|
||||
@@ -4655,12 +4718,14 @@
|
||||
"ui.joints": "Ledd",
|
||||
"ui.keyboard_shortcuts": "Tastatursnarveier",
|
||||
"ui.language": "Språk",
|
||||
"ui.lasso_select": "Lasso (dra en frihåndssløyfe: velger alt som ligger helt innenfor)",
|
||||
"ui.layout_compact": "Kompakt",
|
||||
"ui.layout_floating": "Flytende",
|
||||
"ui.length": "Lengde",
|
||||
"ui.length_decimals": "Lengdedesimaler",
|
||||
"ui.light": "Lys",
|
||||
"ui.line": "Linje",
|
||||
"ui.line_arc_hint": "Tangentbue: klikk på enden av buen. Hvil på det siste punktet igjen for linjer; høyreklikk eller Esc for å avslutte",
|
||||
"ui.link_copies": "Lenk identiske kopier",
|
||||
"ui.link_copies_keep": "Behold",
|
||||
"ui.link_copies_link": "Lenk",
|
||||
@@ -4749,6 +4814,7 @@
|
||||
"ui.part_size_shared": "Bygger delen på nytt og erstatter eventuelle funksjoner som er lagt til. Alle forekomster av denne komponenten oppdateres.",
|
||||
"ui.paste": "Lim inn",
|
||||
"ui.path": "Bane",
|
||||
"ui.pattern_object_type_tip": "Kropper: volumformer, valgt i visningen eller modelltreet; hver kopieres hel.\nFunksjoner: trinn i tidslinjen, som en ekstrudering eller et hull, valgt i tidslinjen eller på en flate de har laget; trinnet gjentas.\nEn skisse er ingen av delene: gjenta kurvene med skissens eget Mønster eller Speil, eller ekstruder den først.",
|
||||
"ui.pattern_on_path": "Mønster på bane",
|
||||
"ui.perforate_margin_default": "fra diameteren",
|
||||
"ui.perforate_pitch": "Senteravstand",
|
||||
@@ -4794,9 +4860,9 @@
|
||||
"ui.polygon": "Mangekant",
|
||||
"ui.preferences": "Innstillinger",
|
||||
"ui.press_a_key": "trykk på en tast…",
|
||||
"ui.print_face_body_not_sent": "Legemet til den flaten sendes ikke: legg det til i Kropper først",
|
||||
"ui.print_face_cleared": "{body}: utskriftsflate fjernet",
|
||||
"ui.print_face_input": "Utskriftsflate",
|
||||
"ui.print_face_one_body": "En utskriftsflate hører til ett legeme: send det legemet alene for å endre den",
|
||||
"ui.print_face_pick": "Velg en plan flate på et massivt legeme for å gjøre den til utskriftsflaten",
|
||||
"ui.print_face_set": "{body}: utskriftsflate satt; Send til slicer legger legemet på den",
|
||||
"ui.profile": "Profil",
|
||||
@@ -5024,12 +5090,15 @@
|
||||
"ui.view_angles": "Visningsvinkler",
|
||||
"ui.view_angles_convention": "Grader om oppaksen. Giring 0° er frontvisningen, 90° venstre, −90° høyre, 180° bak; helning 90° er toppvisningen; positiv rull dreier modellen med klokken.",
|
||||
"ui.view_angles_help": "Viser visningens helning, giring og rull i grader og projeksjonen under orienteringsindikatoren. Klikk på avlesningen for å skrive inn nøyaktige vinkler.",
|
||||
"ui.view_angles_lock": "Lås {axis}",
|
||||
"ui.view_angles_ortho": "Orto",
|
||||
"ui.view_angles_persp": "Persp.",
|
||||
"ui.view_angles_pitch": "Helning",
|
||||
"ui.view_angles_readout": "Helning {pitch} · Giring {yaw} · Rull {roll}",
|
||||
"ui.view_angles_roll": "Rull",
|
||||
"ui.view_angles_unlock": "Lås opp {axis}",
|
||||
"ui.view_angles_yaw": "Giring",
|
||||
"ui.view_angles_zero": "Sett {axis} til 0°",
|
||||
"ui.view_centre": "Visningssenter",
|
||||
"ui.viewport_status_clear": "Slå av {name}",
|
||||
"ui.viewport_status_clear_hint": "Slå av {name} · Esc",
|
||||
|
||||
+71
-1
@@ -793,6 +793,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (bevestiging kwijt)",
|
||||
"browser.sketch_editing": "{name} (wordt bewerkt)",
|
||||
"browser.sketch_name": "Schets{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "al in hun onderdeel",
|
||||
"browser.sketches_to_parts_design_body": "op een lichaam van het ontwerp zelf",
|
||||
"browser.sketches_to_parts_moved": "{moved} schetsen verplaatst naar het onderdeel waarop ze staan",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "op een vlak zonder exacte bevestiging",
|
||||
"browser.sketches_to_parts_not_on_face": "niet op het vlak van een onderdeel",
|
||||
"browser.sketches_to_parts_part_missing": "hun onderdeel bestaat niet meer",
|
||||
"browser.sketches_to_parts_result": "{moved} schetsen verplaatst naar het onderdeel waarop ze staan; {stayed} bleven staan: {why}",
|
||||
"browser.units": "Eenheden: {units}",
|
||||
"cam.flat": "Plat vlak",
|
||||
"cam.follower": "Volger",
|
||||
@@ -860,6 +867,7 @@
|
||||
"cmd.component_move_bodies": "Lichamen naar component verplaatsen",
|
||||
"cmd.component_move_features": "Kenmerken naar component verplaatsen",
|
||||
"cmd.component_move_sketches": "Schetsen naar component verplaatsen",
|
||||
"cmd.component_move_sketches_to_parts": "Schetsen naar hun onderdeel verplaatsen",
|
||||
"cmd.component_paste_from_design": "Plakken uit een ander ontwerp",
|
||||
"cmd.component_paste_new": "Nieuw plakken",
|
||||
"cmd.component_properties": "Fysieke eigenschappen component",
|
||||
@@ -1195,6 +1203,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Spline met controlepunten (graad 5)",
|
||||
"cmd.sketch_spline_fit_point": "Spline door punten",
|
||||
"cmd.sketch_spline_insert_point": "Splinepunt toevoegen",
|
||||
"cmd.sketch_spline_remove_point": "Splinepunt verwijderen",
|
||||
"cmd.sketch_spline_reverse": "Splinerichting omkeren",
|
||||
"cmd.sketch_spun_profile": "Gedraaid profiel",
|
||||
"cmd.sketch_toggle_diameter": "Straal/diameter omschakelen",
|
||||
@@ -1287,8 +1296,14 @@
|
||||
"color.hex_hint": "#rrggbb",
|
||||
"color.hue": "Tint",
|
||||
"color.linear": "Kleurwaarden worden getoond in lineaire ruimte 0-1",
|
||||
"color.recent": "Recente kleuren",
|
||||
"color.recent_help": "Kleuren die je in een kleurveld kiest, verschijnen hier",
|
||||
"color.saturation": "Verzadiging (procent)",
|
||||
"color.saturation_value": "Verzadiging en waarde",
|
||||
"color.selected": "Geselecteerde kleur",
|
||||
"color.step_down": "Stap omlaag",
|
||||
"color.step_up": "Stap omhoog",
|
||||
"color.value": "Waarde, de helderheid (procent)",
|
||||
"config.column_filter_empty": "Geen overeenkomende kolommen",
|
||||
"contact.components_count": "{count} componenten",
|
||||
"contact.enabled_label": "ingeschakeld",
|
||||
@@ -1335,6 +1350,7 @@
|
||||
"dialog.dimension_name": "Maat: {name}",
|
||||
"dialog.edit_title": "{title} BEWERKEN",
|
||||
"dialog.no_shown_body": "Geen zichtbaar lichaam om te wijzigen: toon er een of kies Nieuw lichaam",
|
||||
"dialog.pattern_sketch_not_object": "Kies lichamen of kenmerken: een schets is geen van beide. Herhaal de curven ervan met het eigen Patroon of Spiegelen van de schets, of extrudeer de schets eerst.",
|
||||
"dialog.select": "↖ Selecteren",
|
||||
"dialog.selected_count": "↖ {count} geselecteerd",
|
||||
"dialog.selection_guidance": "Selecteer een invoer om deze te wijzigen. Klik op geselecteerde objecten om ze te verwijderen; Ctrl/Cmd-klik schakelt ze ook om. Controleer het voorbeeld en druk daarna op OK.",
|
||||
@@ -1904,6 +1920,7 @@
|
||||
"nav.hfss.help": "Alt+linkerknop slepen pant, Shift+Alt+linkerknop zoomt, Ctrl+linkerknop slepen op lege ruimte draait",
|
||||
"nav.inventor.help": "Middenknop slepen pant, Shift+middenknop draait, Ctrl+middenknop zoomt, rechterknop slepen draait",
|
||||
"nav.maya.help": "Alt+linkerknop slepen draait, Alt+middenknop pant, Alt+rechterknop zoomt; middenknop slepen pant ook",
|
||||
"nav.nx.help": "Middenknop slepen draait, Shift+middenknop pant (middelste + rechterknop in NX), Ctrl+middenknop zoomt (middelste + linkerknop in NX); rechterknop slepen heeft geen invloed op de camera",
|
||||
"nav.openscad.help": "Linkerknop slepen op lege ruimte draait, Shift+linkerknop slepen selecteert met een kader, schetsgeometrie slepen bewerkt die; rechter- of middenknop slepen pant, Shift+rechter- of Shift+middenknop slepen zoomt",
|
||||
"nav.perspective.sketch": "Schetsbewerking gebruikt een orthografische weergave. Rond de schets af om naar perspectief te schakelen.",
|
||||
"nav.right_drag_orbits.help": "Uit: rechts slepen laat de camera met rust en de rechtermuisknop opent alleen het contextmenu. Het kiezen van de SolidWorks-muistoewijzing schakelt dit uit.",
|
||||
@@ -2125,6 +2142,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Gebogen oppervlakken",
|
||||
"prefs.curves": "Gebogen oppervlakken",
|
||||
"prefs.dark_parts_shading": "Vorm van donkere onderdelen behouden",
|
||||
"prefs.dark_parts_shading_help": "Schaduw de weergave in lineair licht met een zachte hemelvulling, zodat donkere vlakken een verloop houden in plaats van zwart te worden",
|
||||
"prefs.dead_zone_note": "De gearceerde band is de dode zone: beweging daarbinnen wordt genegeerd.",
|
||||
"prefs.default": "standaard",
|
||||
"prefs.design": "Standaardwaarden voor ontwerpen",
|
||||
@@ -2139,6 +2158,8 @@
|
||||
"prefs.edges": "Randen en schaduwing",
|
||||
"prefs.extend": "Verlengen",
|
||||
"prefs.extra_navigation": "Ctrl+linkerknop slepen op lege ruimte roteert; houd Space ingedrukt om te pannen.",
|
||||
"prefs.extrude_view_isometric": "De weergave isometrisch draaien wanneer Extruderen opent",
|
||||
"prefs.extrude_view_return": "Terug naar de eerdere weergave wanneer Extruderen sluit",
|
||||
"prefs.follows_os": "Volgt het besturingssysteem",
|
||||
"prefs.font": "Lettertype",
|
||||
"prefs.font_fallback": "Tekens die het gekozen lettertype mist, vallen terug op het ingebouwde lettertype, zodat geen enkel label een leeg vakje toont.",
|
||||
@@ -2154,6 +2175,7 @@
|
||||
"prefs.graphics_card_auto": "Automatisch",
|
||||
"prefs.graphics_card_missing": "De opgeslagen grafische kaart is niet beschikbaar; Automatisch wordt gebruikt.",
|
||||
"prefs.graphics_card_restart": "Wordt van kracht na het opnieuw starten van SolidCraft. WGPU_ADAPTER_NAME heeft voorrang op deze keuze.",
|
||||
"prefs.graphics_card_unused": "De opgeslagen grafische kaart is niet gebruikt voor dit venster; de kaart die wel in gebruik is, staat in Help › Over SolidCraft en in de statusbalk. Probeer Automatisch, of stel WGPU_ADAPTER_NAME in op een uniek deel van de kaartnaam.",
|
||||
"prefs.graphics_note": "Wissel snelheid in voor een gladder beeld. Elke keuze toont het effect op een voorbeeld.",
|
||||
"prefs.graphics_presets_note": "Voorinstellingen stellen de onderstaande keuzes samen in.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Beperkingssymbolen verbergen bij uitzoomen",
|
||||
@@ -2168,6 +2190,8 @@
|
||||
"prefs.history_sketches_note": "Als Schetsen op Onder hun kenmerk staat, toont de geschiedenis links elke schets een niveau ingesprongen, onder het eerste kenmerk dat erop is gebouwd, zodra die breed genoeg is voor namen. De tijdlijn onderaan houdt altijd de aanmaakvolgorde aan.",
|
||||
"prefs.history_sketches_under_feature": "Onder hun kenmerk",
|
||||
"prefs.hold_stop_snapping": "Ingedrukt houden om snappen te stoppen",
|
||||
"prefs.home_fit_size": "Grootte bij Beginweergave en Passend",
|
||||
"prefs.home_fit_size_note": "Hoeveel van de weergave het model vult wanneer Beginweergave, Passend, een geopend ontwerp of een wijziging van de navigatorweergave het model in beeld brengt, ongeacht of de instelling hieronder aan staat. 100 %: vult de weergave tussen de panelen, het kompas en de navigatiebalk.",
|
||||
"prefs.icons_only": "Alleen pictogrammen",
|
||||
"prefs.imported_file": "Geïmporteerd bestand",
|
||||
"prefs.in_browser": "In de browser",
|
||||
@@ -2176,13 +2200,20 @@
|
||||
"prefs.interface_language": "Interfacetaal",
|
||||
"prefs.interface_note": "Hoe SolidCraft eruitziet en waar de onderdelen staan.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_navigator_arrows": "De rotatiepijlen van de weergavekubus omkeren",
|
||||
"prefs.invert_navigator_arrows_note": "De pijlen rond de kubus laten de camera roteren in de richting waarin ze wijzen, zoals in Fusion en Inventor: de pijl links wijst naar rechts en zwaait de camera naar rechts. Uit: een pijl draait de kubus naar de kant waar hij staat, dus de linkerpijl zwaait de camera naar links. De gebogen rolpijlen worden hierdoor niet beïnvloed.",
|
||||
"prefs.keep_work_on_tab_switch": "Dialoogvensters en gereedschappen open houden bij het wisselen van ontwerp",
|
||||
"prefs.keep_work_on_tab_switch_note": "Elk ontwerptabblad behoudt zijn geopende dialoogvenster of gereedschap, met wat je erin hebt gekozen en getypt, en zijn lint-tabblad. Ze zijn er weer als je terugkeert naar het ontwerp. Uit: wisselen van ontwerp breekt ze af en toont het tabblad Model.",
|
||||
"prefs.labels": "Labels",
|
||||
"prefs.language_system": "Systeem",
|
||||
"prefs.layout": "Indeling",
|
||||
"prefs.layout_note": "Compact plaatst panelen vast aan de vensterranden. Doorschijnendheid van de browser is beschikbaar in Zwevend. Wijzig de kant van de Assistent op de pagina Assistent.",
|
||||
"prefs.left": "Links",
|
||||
"prefs.left_click": "Linksklik",
|
||||
"prefs.line_drag_draws_one": "Indrukken en slepen tekent één lijn",
|
||||
"prefs.line_drag_draws_one_note": "In het lijngereedschap tekent indrukken, slepen en loslaten één lijn van het indrukpunt tot het loslaatpunt, en daar eindigt de lijn. Klikken tekent nog steeds een keten. Uit: slepen zet, net als een klik, een punt waar losgelaten wordt.",
|
||||
"prefs.line_hover_tangent_arc": "Op het laatste punt van een lijn stilstaan om naar een tangentiële boog te wisselen",
|
||||
"prefs.line_hover_tangent_arc_note": "Als je in het lijngereedschap een segment hebt getekend, beweeg je weg van het laatste punt van de keten, kom je terug en houd je de aanwijzer er stil: de volgende klik beëindigt een tangentiële boog, en hetzelfde gebaar schakelt terug naar lijnen. Uit: daar stilstaan doet niets.",
|
||||
"prefs.linked_designs": "Gekoppelde ontwerpen",
|
||||
"prefs.live_input": "Live invoer",
|
||||
"prefs.live_preview": "Live voorbeeld",
|
||||
@@ -2206,6 +2237,8 @@
|
||||
"prefs.mouse_mapping": "Muistoewijzing",
|
||||
"prefs.navigation": "Navigatie",
|
||||
"prefs.navigation_note": "Hoe de muis, het trackpad en de 3D-muis de weergave verplaatsen.",
|
||||
"prefs.navigator_fit": "Het model passend in beeld brengen na een wijziging van de navigatorweergave",
|
||||
"prefs.navigator_fit_note": "Een vlak, hoek of pijl van de weergavekubus, of een as van de gizmo, kijkt ook naar het midden van het model, vanaf de ene afstand waarop het van alle kanten in beeld past (de omsluitende bol). Beginweergave gebruikt dezelfde zoom en hetzelfde middelpunt zolang dit aan staat, zodat weergaven die je daarna kiest alleen draaien. Uit: de weergave draait ter plaatse en behoudt het richtpunt en de zoom.",
|
||||
"prefs.network_host_remove": "Verwijderen",
|
||||
"prefs.network_locations": "Netwerklocaties in ontwerpen toestaan",
|
||||
"prefs.network_locations_note": "Uit: gekoppelde bestanden van een ontwerp op een andere computer (\\\\server\\share) worden alleen geopend vanaf de servers in deze lijst, of nadat je er een toestaat wanneer erom wordt gevraagd.",
|
||||
@@ -2254,6 +2287,8 @@
|
||||
"prefs.render_previous_geometry_budget": "Vorig geometriebudget voor het rendervoorbeeld gebruiken",
|
||||
"prefs.render_previous_geometry_budget_help": "De vorige vaste geometrielimiet herstellen (512 MiB op 64-bits desktops, elders 128 MiB) in plaats van het budget op basis van het apparaat.",
|
||||
"prefs.reports_saved": "{n} opgeslagen",
|
||||
"prefs.ribbon_click_ends_tool": "Klikken op de knop van het actieve gereedschap beëindigt het",
|
||||
"prefs.ribbon_click_ends_tool_note": "Een klik op de verlichte lintknop van het schetsgereedschap dat in gebruik is, beëindigt het gereedschap, zodat klikken in de weergave weer selecteert. Uit: de klik start het gereedschap opnieuw.",
|
||||
"prefs.ribbon_reveal": "Zoekresultaten in het lint tonen",
|
||||
"prefs.ribbon_reveal_note": "Als je de aanwijzer op een resultaat in Opdrachten zoeken houdt, zie je waar de opdracht in het lint staat: het tabblad, de knop of de rij in de vervolgkeuzelijst van een paneel. Een opdracht die vanuit de zoekfunctie is gestart, knippert daar, en een actieve opdracht uit een vervolgkeuzelijst markeert het paneel.",
|
||||
"prefs.right_click": "Rechtsklik",
|
||||
@@ -2283,6 +2318,8 @@
|
||||
"prefs.sketch_exit_view.previous": "Terug naar de vorige weergave",
|
||||
"prefs.sketch_exit_view.unless_rotated": "Terug, tenzij geroteerd",
|
||||
"prefs.sketch_exit_view_note": "Waar de weergave naartoe gaat na Schets afronden, Annuleren of een dubbele Esc.",
|
||||
"prefs.sketch_face_activates_part": "Schetsen op het vlak van een onderdeel maakt dat onderdeel actief",
|
||||
"prefs.sketch_face_activates_part_note": "Een schets die op een vlak van het lichaam van een andere component wordt gestart, activeert eerst die component, zodat de schets erbij hoort en in de browser bij dat onderdeel blijft. Het onderdeel blijft daarna actief. Uit: de schets hoort bij de actieve component.",
|
||||
"prefs.sketch_open_look_at": "Naar een schets toe draaien wanneer die wordt geopend",
|
||||
"prefs.sketch_open_look_at_note": "Uit: een schets maken of bewerken laat de weergave zoals die is. Kijken naar richt de weergave op de schets wanneer je dat wilt.",
|
||||
"prefs.sketch_recent_snap": "Automatisch snappen op recente cirkelmiddens en middelpunten",
|
||||
@@ -2314,6 +2351,24 @@
|
||||
"prefs.status_error": "Fout",
|
||||
"prefs.status_selected": "Geselecteerd",
|
||||
"prefs.status_warning": "Waarschuwing",
|
||||
"prefs.structure.appearance": "Uiterlijk",
|
||||
"prefs.structure.appearance_note": "Kleuren, tekst en toegankelijkheid.",
|
||||
"prefs.structure.in_profile": "In profiel",
|
||||
"prefs.structure.layout": "Indeling",
|
||||
"prefs.structure.layout_note": "Waar de onderdelen van het venster staan.",
|
||||
"prefs.structure.look": "Vormgeving",
|
||||
"prefs.structure.measure_pointer": "Meten en aanwijzer",
|
||||
"prefs.structure.mouse3d": "3D-muis",
|
||||
"prefs.structure.mouse3d_note": "SpaceMouse en SpaceNavigator.",
|
||||
"prefs.structure.navigation_note": "Muis en trackpad. De 3D-muis heeft een eigen pagina.",
|
||||
"prefs.structure.profile": "Profiel",
|
||||
"prefs.structure.profile_edited": "Gewijzigd sinds opslaan",
|
||||
"prefs.structure.profile_hover": "Opgeslagen in instellingenprofielen",
|
||||
"prefs.structure.scale_percent": "{percent}%",
|
||||
"prefs.structure.sketch": "Schets",
|
||||
"prefs.structure.tools": "Gereedschappen",
|
||||
"prefs.structure.tools_note": "Hoe modelleergereedschappen zich gedragen.",
|
||||
"prefs.structure.work": "Werk",
|
||||
"prefs.supersampling_brief": "Tekent de weergave groter en filtert die omlaag: zeer scherp, maar zwaarder.",
|
||||
"prefs.tab_accent_note": "De accentkleur volgt het actieve tabblad",
|
||||
"prefs.theme_file": "themabestand",
|
||||
@@ -3811,6 +3866,7 @@
|
||||
"sketch_text.formula": "Begin met = om de woorden uit parameters op te bouwen: ='W' + number(Width)",
|
||||
"sketch_text.italic": "Cursief",
|
||||
"sketch_text.loading_fonts": "Geïnstalleerde lettertypen zoeken…",
|
||||
"sketch_text.no_bold": "Het ingebouwde lettertype heeft geen vette variant: kies een geïnstalleerd lettertype",
|
||||
"sketch_text.on_path": "De tekst volgt zijn pad: de curve plaatst hem",
|
||||
"sketch_text.path_clear": "De tekst van zijn curve halen",
|
||||
"sketch_text.path_hint": "Een curve van de schets die de tekst volgt: klik hier en daarna op de curve",
|
||||
@@ -4213,6 +4269,9 @@
|
||||
"ui.ai_permissions": "Machtigingen",
|
||||
"ui.ai_preset": "Voorinstelling",
|
||||
"ui.ai_protocol": "Protocol",
|
||||
"ui.ai_reasoning_effort": "Redeneerinspanning",
|
||||
"ui.ai_reasoning_effort_field_hint": "leeg = standaard van de provider",
|
||||
"ui.ai_reasoning_effort_hint": "Optioneel. Wordt als `reasoning_effort` naar het eindpunt gestuurd; toegestane waarden verschillen per provider (bijvoorbeeld `low`, `high` of `max`). Een waarde die je hier instelt, wordt nooit door SolidCraft vervangen. Laat het veld leeg om niets te sturen en de standaard van de provider te behouden.",
|
||||
"ui.ai_remove": "Verwijderen",
|
||||
"ui.ai_request_timed_out": "Het model deed er langer dan {seconds} s over. Je kunt de time-out voor aanvragen van dit eindpunt verhogen in Voorkeuren › Assistent.",
|
||||
"ui.ai_request_timeout": "Time-out voor aanvragen",
|
||||
@@ -4378,6 +4437,7 @@
|
||||
"ui.dark": "Donker",
|
||||
"ui.default_modelling_orientation": "Standaard modelleeroriëntatie",
|
||||
"ui.delete": "Verwijderen",
|
||||
"ui.delete_select_first": "Selecteer eerst wat je wilt verwijderen: kenmerken, lichamen, componenten of schetsgeometrie",
|
||||
"ui.depth": "Diepte",
|
||||
"ui.design": "Ontwerp",
|
||||
"ui.design_properties.author": "Auteur",
|
||||
@@ -4470,6 +4530,7 @@
|
||||
"ui.edit_in_place_saved_elsewhere": "Het onderdeel is in een ander bestand opgeslagen; de samenstelling is nog steeds gekoppeld aan {name}",
|
||||
"ui.edit_in_place_wait": "Wacht tot wat in het ontwerp wordt uitgevoerd klaar is",
|
||||
"ui.edit_section_analysis": "Doorsnedeanalyse bewerken",
|
||||
"ui.edit_sketch_empty": "Geen schetsen om te bewerken",
|
||||
"ui.ellipse": "Ellips",
|
||||
"ui.emboss_wrap_note": "Orthogonale lijnprofielen die raken aan één ondersteunde cilinder worden automatisch omwikkeld. Kies een wand om een doel te kiezen.",
|
||||
"ui.end_radius": "Eindstraal",
|
||||
@@ -4480,7 +4541,9 @@
|
||||
"ui.equation_explicit": "Expliciet",
|
||||
"ui.equation_form": "Vorm",
|
||||
"ui.equation_note": "De variabele is een gewoon getal: sin(t) neemt het in radialen. Een resultaat zonder eenheid is in de eenheid van het ontwerp.",
|
||||
"ui.equation_note_range": "De variabele is een lengte, net als haar bereik: combineer haar met lengteparameters en deel haar voor sin() door een lengte. Een resultaat zonder eenheid is in de eenheid van het ontwerp.",
|
||||
"ui.equation_parametric": "Parametrisch",
|
||||
"ui.equation_variable_unit": "Variabele heeft de eenheid van het bereik",
|
||||
"ui.equation_x_of_t": "x(t)",
|
||||
"ui.equation_y_of_t": "y(t)",
|
||||
"ui.equation_y_of_x": "y(x)",
|
||||
@@ -4647,6 +4710,7 @@
|
||||
"ui.insert_design.saved_file": "{name} heeft niet-opgeslagen wijzigingen, dus is het opgeslagen bestand ingevoegd. Sla het op en gebruik daarna Koppelingen bijwerken om de wijzigingen over te nemen.",
|
||||
"ui.insert_design.unsaved": "Niet-opgeslagen wijzigingen",
|
||||
"ui.inspect": "Inspecteren",
|
||||
"ui.instances": "Instanties",
|
||||
"ui.intersect": "Snijden",
|
||||
"ui.isolate": "Isoleren",
|
||||
"ui.join": "Samenvoegen",
|
||||
@@ -4655,12 +4719,14 @@
|
||||
"ui.joints": "Verbindingen",
|
||||
"ui.keyboard_shortcuts": "Sneltoetsen",
|
||||
"ui.language": "Taal",
|
||||
"ui.lasso_select": "Lasso (sleep een lus uit de vrije hand: neemt alles wat er helemaal binnen valt)",
|
||||
"ui.layout_compact": "Compact",
|
||||
"ui.layout_floating": "Zwevend",
|
||||
"ui.length": "Lengte",
|
||||
"ui.length_decimals": "Lengtedecimalen",
|
||||
"ui.light": "Licht",
|
||||
"ui.line": "Lijn",
|
||||
"ui.line_arc_hint": "Tangentiële boog: klik op het eindpunt van de boog. Blijf opnieuw stilstaan op het laatste punt voor lijnen; klik met rechts of druk op Esc om te voltooien",
|
||||
"ui.link_copies": "Identieke kopieën koppelen",
|
||||
"ui.link_copies_keep": "Behouden",
|
||||
"ui.link_copies_link": "Koppelen",
|
||||
@@ -4749,6 +4815,7 @@
|
||||
"ui.part_size_shared": "Bouwt het onderdeel opnieuw op en vervangt alle toegevoegde kenmerken. Alle voorkomens van dit component worden bijgewerkt.",
|
||||
"ui.paste": "Plakken",
|
||||
"ui.path": "Pad",
|
||||
"ui.pattern_object_type_tip": "Lichamen: massieve vormen, gekozen in de weergave of de Browser; elk wordt in zijn geheel gekopieerd.\nKenmerken: stappen van de Geschiedenis, zoals een extrusie of een gat, gekozen in de Geschiedenis of op een vlak dat ze hebben gemaakt; de stap wordt herhaald.\nEen schets is geen van beide: herhaal de curven ervan met het eigen Patroon of Spiegelen van de schets, of extrudeer de schets eerst.",
|
||||
"ui.pattern_on_path": "Patroon op pad",
|
||||
"ui.perforate_margin_default": "vanaf de diameter",
|
||||
"ui.perforate_pitch": "Steek",
|
||||
@@ -4794,9 +4861,9 @@
|
||||
"ui.polygon": "Veelhoek",
|
||||
"ui.preferences": "Voorkeuren",
|
||||
"ui.press_a_key": "druk op een toets…",
|
||||
"ui.print_face_body_not_sent": "Het lichaam van dat vlak wordt niet verstuurd: voeg het eerst toe aan Lichamen",
|
||||
"ui.print_face_cleared": "{body}: printvlak gewist",
|
||||
"ui.print_face_input": "Printvlak",
|
||||
"ui.print_face_one_body": "Een printvlak hoort bij één lichaam: stuur dat lichaam alleen om het te wijzigen",
|
||||
"ui.print_face_pick": "Selecteer één vlak van een massief lichaam om het het printvlak te maken",
|
||||
"ui.print_face_set": "{body}: printvlak ingesteld; Naar slicer sturen legt het lichaam erop",
|
||||
"ui.profile": "Profiel",
|
||||
@@ -5024,12 +5091,15 @@
|
||||
"ui.view_angles": "Weergavehoeken",
|
||||
"ui.view_angles_convention": "Graden rond de verticale as. Gieren 0° is het vooraanzicht, 90° het linkeraanzicht, −90° het rechteraanzicht, 180° het achteraanzicht; kanteling 90° is het bovenaanzicht; een positieve rol draait het model met de klok mee.",
|
||||
"ui.view_angles_help": "Toont de stamp-, gier- en rolhoek van de weergave in graden en de projectie ervan onder de oriëntatiegizmo. Klik op de uitlezing om exacte hoeken in te typen.",
|
||||
"ui.view_angles_lock": "{axis} vergrendelen",
|
||||
"ui.view_angles_ortho": "Ortho",
|
||||
"ui.view_angles_persp": "Persp.",
|
||||
"ui.view_angles_pitch": "Kanteling",
|
||||
"ui.view_angles_readout": "Kanteling {pitch} · Gieren {yaw} · Rollen {roll}",
|
||||
"ui.view_angles_roll": "Rollen",
|
||||
"ui.view_angles_unlock": "{axis} ontgrendelen",
|
||||
"ui.view_angles_yaw": "Gieren",
|
||||
"ui.view_angles_zero": "{axis} instellen op 0°",
|
||||
"ui.view_centre": "Weergavemidden",
|
||||
"ui.viewport_status_clear": "{name} uitschakelen",
|
||||
"ui.viewport_status_clear_hint": "{name} uitschakelen · Esc",
|
||||
|
||||
@@ -1190,6 +1190,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Splajn z punktów kontrolnych (stopień 5)",
|
||||
"cmd.sketch_spline_fit_point": "Splajn przez punkty",
|
||||
"cmd.sketch_spline_insert_point": "Dodaj punkt splajnu",
|
||||
"cmd.sketch_spline_remove_point": "Usuń punkt splajnu",
|
||||
"cmd.sketch_spline_reverse": "Odwróć kierunek splajnu",
|
||||
"cmd.sketch_spun_profile": "Profil obrotowy",
|
||||
"cmd.sketch_toggle_diameter": "Przełącz promień / średnicę",
|
||||
@@ -2998,6 +2999,7 @@
|
||||
"ui.insert_design.saved_file": "{name} ma niezapisane zmiany, więc wstawiono jego zapisany plik. Zapisz go, a potem użyj Aktualizuj łącza, aby wprowadzić zmiany.",
|
||||
"ui.insert_design.unsaved": "Niezapisane zmiany",
|
||||
"ui.inspect": "Sprawdź",
|
||||
"ui.instances": "Wystąpienia",
|
||||
"ui.intersect": "Przytnij",
|
||||
"ui.isolate": "Izoluj",
|
||||
"ui.join": "Połącz",
|
||||
@@ -3140,9 +3142,9 @@
|
||||
"ui.polygon": "Wielokąt",
|
||||
"ui.preferences": "Preferencje",
|
||||
"ui.press_a_key": "naciśnij klawisz…",
|
||||
"ui.print_face_body_not_sent": "Bryła tej powierzchni nie jest wysyłana: najpierw dodaj ją do Bryły",
|
||||
"ui.print_face_cleared": "{body}: usunięto powierzchnię druku",
|
||||
"ui.print_face_input": "Powierzchnia druku",
|
||||
"ui.print_face_one_body": "Powierzchnia druku należy do jednej bryły: wyślij tę bryłę osobno, aby ją zmienić",
|
||||
"ui.print_face_pick": "Wybierz płaską powierzchnię bryły, aby ustawić ją jako powierzchnię druku",
|
||||
"ui.print_face_set": "{body}: ustawiono powierzchnię druku; Wyślij do slicera kładzie bryłę na niej",
|
||||
"ui.profile": "Profil",
|
||||
|
||||
@@ -2092,6 +2092,7 @@
|
||||
"ui.infinite": "Infinita",
|
||||
"ui.insert": "Inserir",
|
||||
"ui.inspect": "Inspecionar",
|
||||
"ui.instances": "Instâncias",
|
||||
"ui.intersect": "Interseção",
|
||||
"ui.isolate": "Isolar",
|
||||
"ui.join": "Unir",
|
||||
@@ -2205,9 +2206,9 @@
|
||||
"ui.polygon": "Polígono",
|
||||
"ui.preferences": "Preferências",
|
||||
"ui.press_a_key": "pressione uma tecla…",
|
||||
"ui.print_face_body_not_sent": "O corpo dessa face não está sendo enviado: adicione-o primeiro a Corpos",
|
||||
"ui.print_face_cleared": "{body}: face de impressão removida",
|
||||
"ui.print_face_input": "Face de impressão",
|
||||
"ui.print_face_one_body": "Uma face de impressão pertence a um único corpo: envie esse corpo sozinho para alterá-la",
|
||||
"ui.print_face_pick": "Selecione uma face plana de um corpo sólido para torná-la a face de impressão",
|
||||
"ui.print_face_set": "{body}: face de impressão definida; Enviar ao fatiador apoia o corpo sobre ela",
|
||||
"ui.profile": "Perfil",
|
||||
|
||||
+70
-1
@@ -793,6 +793,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (atașare pierdută)",
|
||||
"browser.sketch_editing": "{name} (în editare)",
|
||||
"browser.sketch_name": "Schița{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "deja în piesa lor",
|
||||
"browser.sketches_to_parts_design_body": "pe un corp al proiectului însuși",
|
||||
"browser.sketches_to_parts_moved": "Au fost mutate {moved} schițe în piesa pe care se află",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "pe o față fără atașare exactă",
|
||||
"browser.sketches_to_parts_not_on_face": "nu pe fața unei piese",
|
||||
"browser.sketches_to_parts_part_missing": "piesa lor nu mai există",
|
||||
"browser.sketches_to_parts_result": "Au fost mutate {moved} schițe în piesa pe care se află; {stayed} au rămas pe loc: {why}",
|
||||
"browser.units": "Unități: {units}",
|
||||
"cam.flat": "Față plană",
|
||||
"cam.follower": "Tachet",
|
||||
@@ -860,6 +867,7 @@
|
||||
"cmd.component_move_bodies": "Mută corpurile în componentă",
|
||||
"cmd.component_move_features": "Mută operațiile în componentă",
|
||||
"cmd.component_move_sketches": "Mută schițele în componentă",
|
||||
"cmd.component_move_sketches_to_parts": "Mută schițele în piesele lor",
|
||||
"cmd.component_paste_from_design": "Lipește din alt proiect",
|
||||
"cmd.component_paste_new": "Lipește ca nou",
|
||||
"cmd.component_properties": "Proprietățile fizice ale componentei",
|
||||
@@ -1195,6 +1203,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Spline cu puncte de control (grad 5)",
|
||||
"cmd.sketch_spline_fit_point": "Spline cu puncte de potrivire",
|
||||
"cmd.sketch_spline_insert_point": "Adaugă punct la spline",
|
||||
"cmd.sketch_spline_remove_point": "Elimină punct din spline",
|
||||
"cmd.sketch_spline_reverse": "Inversează direcția spline-ului",
|
||||
"cmd.sketch_spun_profile": "Profil rotit",
|
||||
"cmd.sketch_toggle_diameter": "Comută rază / diametru",
|
||||
@@ -1287,8 +1296,14 @@
|
||||
"color.hex_hint": "#rrggbb",
|
||||
"color.hue": "Nuanță",
|
||||
"color.linear": "Se afișează valorile culorii în spațiul liniar 0-1",
|
||||
"color.recent": "Culori recente",
|
||||
"color.recent_help": "Culorile pe care le alegeți în orice câmp de culoare apar aici",
|
||||
"color.saturation": "Saturație (procente)",
|
||||
"color.saturation_value": "Saturație și valoare",
|
||||
"color.selected": "Culoare selectată",
|
||||
"color.step_down": "Scade cu un pas",
|
||||
"color.step_up": "Crește cu un pas",
|
||||
"color.value": "Valoare, luminozitatea (procente)",
|
||||
"config.column_filter_empty": "Nicio coloană potrivită",
|
||||
"contact.components_count": "{count} componente",
|
||||
"contact.enabled_label": "activat",
|
||||
@@ -1335,6 +1350,7 @@
|
||||
"dialog.dimension_name": "Cotă: {name}",
|
||||
"dialog.edit_title": "EDITARE {title}",
|
||||
"dialog.no_shown_body": "Niciun corp afișat de modificat: afișați unul sau alegeți Corp nou",
|
||||
"dialog.pattern_sketch_not_object": "Alegeți corpuri sau operații: o schiță nu este niciuna dintre ele. Repetați curbele ei cu Matrice sau Oglindire din schiță, ori extrudați-o mai întâi.",
|
||||
"dialog.select": "↖ Selectează",
|
||||
"dialog.selected_count": "↖ {count} selectate",
|
||||
"dialog.selection_guidance": "Selectați o intrare pentru a o modifica. Faceți clic pe obiectele selectate pentru a le elimina; Ctrl/Cmd-clic le comută de asemenea. Verificați previzualizarea, apoi apăsați OK.",
|
||||
@@ -1904,6 +1920,7 @@
|
||||
"nav.hfss.help": "Alt+tragere cu butonul stâng: panoramare, Shift+Alt+tragere cu butonul stâng: zoom, Ctrl+tragere cu butonul stâng pe spațiu gol: orbitare",
|
||||
"nav.inventor.help": "Tragerea cu mijlocul panoramează, Shift+mijloc orbitează, Ctrl+mijloc face zoom, tragerea cu dreapta orbitează",
|
||||
"nav.maya.help": "Alt+tragere cu butonul stâng: orbitare, Alt+butonul din mijloc: panoramare, Alt+butonul drept: zoom; tragerea cu butonul din mijloc face de asemenea panoramare",
|
||||
"nav.nx.help": "Tragerea cu mijlocul orbitează, Shift+mijloc panoramează (mijloc + dreapta în NX), Ctrl+mijloc face zoom (mijloc + stânga în NX); tragerea cu dreapta nu mișcă camera",
|
||||
"nav.openscad.help": "Tragerea cu stânga pe spațiu gol orbitează, Shift+tragere cu stânga selectează cu casetă, tragerea geometriei schiței o editează; tragerea cu dreapta sau cu mijlocul panoramează, Shift+tragere cu dreapta sau Shift+tragere cu mijlocul face zoom",
|
||||
"nav.perspective.sketch": "Editarea schiței folosește vizualizarea ortografică. Finalizați schița pentru a comuta la perspectivă.",
|
||||
"nav.right_drag_orbits.help": "Dezactivat: tragerea cu dreapta nu mișcă camera, iar butonul drept doar deschide meniul contextual. Alegerea mapării de mouse SolidWorks dezactivează această opțiune.",
|
||||
@@ -2125,6 +2142,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Suprafețe curbe",
|
||||
"prefs.curves": "Suprafețe curbe",
|
||||
"prefs.dark_parts_shading": "Păstrează forma pieselor întunecate",
|
||||
"prefs.dark_parts_shading_help": "Umbrește vizualizarea în lumină liniară, cu o lumină de umplere moale de la cer, astfel încât fețele întunecate să păstreze un gradient în loc să devină negre",
|
||||
"prefs.dead_zone_note": "Banda hașurată este zona moartă: mișcarea din interiorul ei este ignorată.",
|
||||
"prefs.default": "implicit",
|
||||
"prefs.design": "Valori implicite ale proiectului",
|
||||
@@ -2139,6 +2158,8 @@
|
||||
"prefs.edges": "Muchii și umbrire",
|
||||
"prefs.extend": "Prelungește",
|
||||
"prefs.extra_navigation": "Ctrl+tragere cu butonul stâng pe un spațiu gol orbitează; țineți apăsat Space pentru panoramare.",
|
||||
"prefs.extrude_view_isometric": "Treci la vizualizarea izometrică la deschiderea Extrudării",
|
||||
"prefs.extrude_view_return": "Revino la vizualizarea anterioară la închiderea Extrudării",
|
||||
"prefs.follows_os": "Urmează sistemul de operare",
|
||||
"prefs.font": "Font",
|
||||
"prefs.font_fallback": "Glifele care lipsesc din fontul ales trec la fontul încorporat, astfel încât nicio etichetă să nu afișeze un pătrat gol.",
|
||||
@@ -2154,6 +2175,7 @@
|
||||
"prefs.graphics_card_auto": "Automat",
|
||||
"prefs.graphics_card_missing": "Placa video salvată nu este disponibilă; se folosește Automat.",
|
||||
"prefs.graphics_card_restart": "Intră în vigoare după repornirea SolidCraft. WGPU_ADAPTER_NAME suprascrie această alegere.",
|
||||
"prefs.graphics_card_unused": "Placa video salvată nu a fost folosită pentru această fereastră; placa în uz este afișată în Ajutor › Despre și în bara de stare. Încercați Automat sau setați WGPU_ADAPTER_NAME la o parte unică din numele plăcii.",
|
||||
"prefs.graphics_note": "Alegeți între viteză și netezime. Fiecare opțiune își arată efectul pe un eșantion.",
|
||||
"prefs.graphics_presets_note": "Presetările stabilesc împreună opțiunile de mai jos.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Ascunde simbolurile de constrângere la micșorare",
|
||||
@@ -2168,6 +2190,8 @@
|
||||
"prefs.history_sketches_note": "Cu Schițe setat la Sub operația lor, Istoricul din stânga listează fiecare schiță cu un nivel mai în interior, sub prima operație construită din ea, ori de câte ori este suficient de lat pentru nume. Cronologia din partea de jos păstrează întotdeauna ordinea creării.",
|
||||
"prefs.history_sketches_under_feature": "Sub operația lor",
|
||||
"prefs.hold_stop_snapping": "Țineți apăsat pentru a opri agățarea",
|
||||
"prefs.home_fit_size": "Dimensiunea pentru Acasă și Potrivire",
|
||||
"prefs.home_fit_size_note": "Cât din vizualizare umple modelul când Acasă, Potrivire, un proiect deschis sau o schimbare de vizualizare din navigatorul de vizualizare îi stabilește încadrarea, indiferent dacă setarea de mai jos este activată sau nu. 100 %: umple vizualizarea dintre panouri, busolă și bara de navigare.",
|
||||
"prefs.icons_only": "Doar pictograme",
|
||||
"prefs.imported_file": "Fișier importat",
|
||||
"prefs.in_browser": "În navigator",
|
||||
@@ -2176,13 +2200,20 @@
|
||||
"prefs.interface_language": "Limba interfeței",
|
||||
"prefs.interface_note": "Cum arată SolidCraft și unde sunt așezate elementele sale.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_navigator_arrows": "Inversează săgețile de rotație ale cubului de vizualizare",
|
||||
"prefs.invert_navigator_arrows_note": "Săgețile din jurul cubului rotesc camera în jurul modelului în direcția în care indică, ca în Fusion și Inventor: săgeata din stânga indică spre dreapta și rotește camera spre dreapta. Dezactivat: o săgeată rotește cubul spre partea pe care se află, deci săgeata din stânga rotește camera spre stânga. Săgețile curbe de ruliu nu sunt afectate.",
|
||||
"prefs.keep_work_on_tab_switch": "Păstrează deschise dialogurile și instrumentele la schimbarea proiectului",
|
||||
"prefs.keep_work_on_tab_switch_note": "Fiecare filă de proiect își păstrează dialogul sau instrumentul deschis, cu ce ați ales și ați tastat în el, și fila ei din bara de instrumente. Le regăsiți când reveniți la proiect. Dezactivat: schimbarea proiectului le anulează și afișează fila Model.",
|
||||
"prefs.labels": "Etichete",
|
||||
"prefs.language_system": "Sistem",
|
||||
"prefs.layout": "Dispunere",
|
||||
"prefs.layout_note": "Compact fixează panourile la marginile ferestrei. Transparența navigatorului este disponibilă în modul Plutitor. Schimbați partea Asistentului în pagina Asistent.",
|
||||
"prefs.left": "Stânga",
|
||||
"prefs.left_click": "Clic stânga",
|
||||
"prefs.line_drag_draws_one": "Apăsarea și tragerea desenează o singură linie",
|
||||
"prefs.line_drag_draws_one_note": "În instrumentul Linie, apăsarea, tragerea și eliberarea desenează o singură linie de la punctul apăsării la cel al eliberării, iar linia se termină acolo. Clicurile desenează în continuare un lanț. Dezactivat: o tragere plasează un punct acolo unde este eliberată, la fel ca un clic.",
|
||||
"prefs.line_hover_tangent_arc": "Oprește indicatorul pe ultimul punct al unei linii pentru a trece la un arc tangent",
|
||||
"prefs.line_hover_tangent_arc_note": "În instrumentul Linie, după ce a fost desenat un segment, îndepărtați-vă de ultimul punct al lanțului, reveniți și țineți indicatorul nemișcat pe el: următorul clic încheie un arc tangent, iar același gest revine la linii. Dezactivat: oprirea acolo nu face nimic.",
|
||||
"prefs.linked_designs": "Proiecte legate",
|
||||
"prefs.live_input": "Intrare în timp real",
|
||||
"prefs.live_preview": "Previzualizare în timp real",
|
||||
@@ -2206,6 +2237,8 @@
|
||||
"prefs.mouse_mapping": "Mapare mouse",
|
||||
"prefs.navigation": "Navigare",
|
||||
"prefs.navigation_note": "Cum mută vizualizarea mouse-ul, trackpadul și mouse-ul 3D.",
|
||||
"prefs.navigator_fit": "Potrivește modelul după o schimbare de vizualizare din navigator",
|
||||
"prefs.navigator_fit_note": "O față, un colț sau o săgeată a cubului de vizualizare ori o axă a reperului de orientare privește și spre mijlocul modelului, de la singura distanță care îl cuprinde din toate părțile (sfera lui de încadrare). Acasă folosește același zoom și același centru cât timp opțiunea este activată, așa că vizualizările alese după aceea doar se rotesc. Dezactivat: vizualizarea se rotește pe loc, păstrându-și ținta și zoomul.",
|
||||
"prefs.network_host_remove": "Elimină",
|
||||
"prefs.network_locations": "Permite locațiile de rețea în proiecte",
|
||||
"prefs.network_locations_note": "Dezactivat: fișierele legate ale unui proiect aflate pe alt computer (\\\\server\\share) se deschid doar de pe serverele listate aici sau după ce permiteți unul, când vi se cere.",
|
||||
@@ -2254,6 +2287,8 @@
|
||||
"prefs.render_previous_geometry_budget": "Folosește bugetul anterior de geometrie pentru previzualizarea din Randare",
|
||||
"prefs.render_previous_geometry_budget_help": "Restabilește limita fixă anterioară de geometrie (512 MiB pe desktop pe 64 de biți, 128 MiB în rest) în locul bugetului bazat pe dispozitiv.",
|
||||
"prefs.reports_saved": "{n} salvate",
|
||||
"prefs.ribbon_click_ends_tool": "Un clic pe butonul instrumentului în uz îl încheie",
|
||||
"prefs.ribbon_click_ends_tool_note": "Un clic pe butonul evidențiat din panglică al instrumentului de schiță în uz încheie instrumentul, astfel încât clicurile din vizualizare selectează din nou. Dezactivat: clicul pornește din nou instrumentul.",
|
||||
"prefs.ribbon_reveal": "Afișează rezultatele căutării în panglică",
|
||||
"prefs.ribbon_reveal_note": "Trecerea cursorului peste un rezultat din Caută comenzi arată unde se află comanda în panglică: fila, butonul sau rândul ei din lista derulantă a unui panou. O comandă pornită din căutare clipește acolo, iar o comandă în curs pornită dintr-o listă derulantă îi marchează panoul.",
|
||||
"prefs.right_click": "Clic dreapta",
|
||||
@@ -2283,6 +2318,8 @@
|
||||
"prefs.sketch_exit_view.previous": "Revino la vizualizarea anterioară",
|
||||
"prefs.sketch_exit_view.unless_rotated": "Revino, dacă nu a fost rotită",
|
||||
"prefs.sketch_exit_view_note": "Unde ajunge vizualizarea după Finalizează schița, Anulează sau un dublu Esc.",
|
||||
"prefs.sketch_face_activates_part": "Schițarea pe fața unei piese activează acea piesă",
|
||||
"prefs.sketch_face_activates_part_note": "O schiță începută pe fața unui corp al altei componente activează mai întâi acea componentă, astfel încât schița îi aparține și rămâne cu acea piesă în Navigator. Piesa rămâne activă după aceea. Dezactivat: schița aparține componentei active.",
|
||||
"prefs.sketch_open_look_at": "Rotește vizualizarea spre schiță la deschiderea ei",
|
||||
"prefs.sketch_open_look_at_note": "Dezactivat: crearea sau editarea unei schițe lasă vizualizarea neschimbată. Comanda Privește spre orientează vizualizarea spre schiță atunci când doriți.",
|
||||
"prefs.sketch_recent_snap": "Agățare automată la centrele și punctele de mijloc recente",
|
||||
@@ -2314,6 +2351,24 @@
|
||||
"prefs.status_error": "Eroare",
|
||||
"prefs.status_selected": "Selectat",
|
||||
"prefs.status_warning": "Avertisment",
|
||||
"prefs.structure.appearance": "Aspect",
|
||||
"prefs.structure.appearance_note": "Culori, text și accesibilitate.",
|
||||
"prefs.structure.in_profile": "În profil",
|
||||
"prefs.structure.layout": "Dispunere",
|
||||
"prefs.structure.layout_note": "Unde se află părțile ferestrei.",
|
||||
"prefs.structure.look": "Stil",
|
||||
"prefs.structure.measure_pointer": "Măsurare și indicator",
|
||||
"prefs.structure.mouse3d": "Mouse 3D",
|
||||
"prefs.structure.mouse3d_note": "SpaceMouse și SpaceNavigator.",
|
||||
"prefs.structure.navigation_note": "Mouse și trackpad. Mouse-ul 3D are propria pagină.",
|
||||
"prefs.structure.profile": "Profil",
|
||||
"prefs.structure.profile_edited": "Modificat de la salvare",
|
||||
"prefs.structure.profile_hover": "Salvat în profilurile de setări",
|
||||
"prefs.structure.scale_percent": "{percent}%",
|
||||
"prefs.structure.sketch": "Schiță",
|
||||
"prefs.structure.tools": "Instrumente",
|
||||
"prefs.structure.tools_note": "Cum se comportă instrumentele de modelare.",
|
||||
"prefs.structure.work": "Lucru",
|
||||
"prefs.supersampling_brief": "Desenează vizualizarea mai mare și o filtrează la dimensiunea finală: foarte clar, dar mai solicitant.",
|
||||
"prefs.tab_accent_note": "Accentul urmează fila activă",
|
||||
"prefs.theme_file": "fișier de temă",
|
||||
@@ -3811,6 +3866,7 @@
|
||||
"sketch_text.formula": "Începeți cu = pentru a construi cuvintele din parametri: ='W' + number(Width)",
|
||||
"sketch_text.italic": "Cursiv",
|
||||
"sketch_text.loading_fonts": "Se caută fonturile instalate…",
|
||||
"sketch_text.no_bold": "Fontul încorporat nu are variantă aldină: alegeți un font instalat",
|
||||
"sketch_text.on_path": "Textul urmează traseul său: curba îl poziționează",
|
||||
"sketch_text.path_clear": "Scoate textul de pe curbă",
|
||||
"sketch_text.path_hint": "O curbă a schiței pe care o urmează textul: faceți clic aici, apoi pe curbă",
|
||||
@@ -4213,6 +4269,9 @@
|
||||
"ui.ai_permissions": "Permisiuni",
|
||||
"ui.ai_preset": "Presetare",
|
||||
"ui.ai_protocol": "Protocol",
|
||||
"ui.ai_reasoning_effort": "Efort de raționament",
|
||||
"ui.ai_reasoning_effort_field_hint": "gol = valoarea implicită a furnizorului",
|
||||
"ui.ai_reasoning_effort_hint": "Opțional. Trimis către endpoint ca `reasoning_effort`; valorile acceptate diferă în funcție de furnizor (de exemplu `low`, `high` sau `max`). O valoare setată aici nu este niciodată înlocuită de SolidCraft. Lăsați gol pentru a nu trimite nimic și a păstra valoarea implicită a furnizorului.",
|
||||
"ui.ai_remove": "Elimină",
|
||||
"ui.ai_request_timed_out": "Modelul a durat mai mult de {seconds} s. Puteți mări timpul de expirare pentru acest endpoint în Preferințe › Asistent.",
|
||||
"ui.ai_request_timeout": "Timp de expirare a cererii",
|
||||
@@ -4378,6 +4437,7 @@
|
||||
"ui.dark": "Întunecat",
|
||||
"ui.default_modelling_orientation": "Orientarea implicită de modelare",
|
||||
"ui.delete": "Șterge",
|
||||
"ui.delete_select_first": "Selectați mai întâi ce doriți să ștergeți: operații, corpuri, componente sau geometrie de schiță",
|
||||
"ui.depth": "Adâncime",
|
||||
"ui.design": "Proiect",
|
||||
"ui.design_properties.author": "Autor",
|
||||
@@ -4470,6 +4530,7 @@
|
||||
"ui.edit_in_place_saved_elsewhere": "Piesa a fost salvată într-un alt fișier; ansamblul rămâne legat de {name}",
|
||||
"ui.edit_in_place_wait": "Așteptați până se termină ce rulează în proiect",
|
||||
"ui.edit_section_analysis": "Editează analiza secțiunii",
|
||||
"ui.edit_sketch_empty": "Nicio schiță de editat",
|
||||
"ui.ellipse": "Elipsă",
|
||||
"ui.emboss_wrap_note": "Profilurile din linii ortogonale, tangente la un cilindru acceptat, se înfășoară automat. Alegeți un perete pentru a alege o țintă.",
|
||||
"ui.end_radius": "Rază finală",
|
||||
@@ -4480,7 +4541,9 @@
|
||||
"ui.equation_explicit": "Explicită",
|
||||
"ui.equation_form": "Formă",
|
||||
"ui.equation_note": "Variabila este un număr simplu: sin(t) o primește în radiani. Un rezultat fără unitate este în unitatea proiectului.",
|
||||
"ui.equation_note_range": "Variabila este o lungime, la fel ca intervalul ei: combinați-o cu parametri de lungime și împărțiți-o la o lungime pentru sin(). Un rezultat fără unitate este în unitatea proiectului.",
|
||||
"ui.equation_parametric": "Parametrică",
|
||||
"ui.equation_variable_unit": "Variabila are unitatea intervalului",
|
||||
"ui.equation_x_of_t": "x(t)",
|
||||
"ui.equation_y_of_t": "y(t)",
|
||||
"ui.equation_y_of_x": "y(x)",
|
||||
@@ -4655,12 +4718,14 @@
|
||||
"ui.joints": "Îmbinări",
|
||||
"ui.keyboard_shortcuts": "Scurtături de tastatură",
|
||||
"ui.language": "Limbă",
|
||||
"ui.lasso_select": "Lasou (trageți o buclă liberă: preia tot ce se află în întregime înăuntru)",
|
||||
"ui.layout_compact": "Compact",
|
||||
"ui.layout_floating": "Plutitor",
|
||||
"ui.length": "Lungime",
|
||||
"ui.length_decimals": "Zecimale lungime",
|
||||
"ui.light": "Luminos",
|
||||
"ui.line": "Linie",
|
||||
"ui.line_arc_hint": "Arc tangent: faceți clic pe capătul arcului. Opriți-vă din nou pe ultimul punct pentru linii; clic dreapta sau Esc pentru a finaliza",
|
||||
"ui.link_copies": "Leagă copiile identice",
|
||||
"ui.link_copies_keep": "Păstrează",
|
||||
"ui.link_copies_link": "Leagă",
|
||||
@@ -4749,6 +4814,7 @@
|
||||
"ui.part_size_shared": "Reconstruiește piesa, înlocuind orice operații adăugate. Toate aparițiile acestei componente se actualizează.",
|
||||
"ui.paste": "Lipește",
|
||||
"ui.path": "Traseu",
|
||||
"ui.pattern_object_type_tip": "Corpuri: forme solide, alese în vizualizare sau în Navigator; fiecare este copiat întreg.\nOperații: pași ai Istoricului, de exemplu o extrudare sau o gaură, alese în Istoric sau pe o față creată de ei; pasul este repetat.\nO schiță nu este niciuna dintre ele: repetați curbele ei cu Matrice sau Oglindire din schiță, ori extrudați-o mai întâi.",
|
||||
"ui.pattern_on_path": "Matrice pe traseu",
|
||||
"ui.perforate_margin_default": "din diametru",
|
||||
"ui.perforate_pitch": "Pas",
|
||||
@@ -4794,9 +4860,9 @@
|
||||
"ui.polygon": "Poligon",
|
||||
"ui.preferences": "Preferințe",
|
||||
"ui.press_a_key": "apăsați o tastă…",
|
||||
"ui.print_face_body_not_sent": "Corpul acelei fețe nu este trimis: adăugați-l mai întâi la Corpuri",
|
||||
"ui.print_face_cleared": "{body}: fața de imprimare a fost ștearsă",
|
||||
"ui.print_face_input": "Față de imprimare",
|
||||
"ui.print_face_one_body": "O față de imprimare aparține unui singur corp: trimiteți acel corp singur pentru a o schimba",
|
||||
"ui.print_face_pick": "Selectați o față plană a unui corp solid pentru a o face fața de imprimare",
|
||||
"ui.print_face_set": "{body}: fața de imprimare a fost setată; Trimite la slicer așază corpul pe ea",
|
||||
"ui.profile": "Profil",
|
||||
@@ -5024,12 +5090,15 @@
|
||||
"ui.view_angles": "Unghiuri de vizualizare",
|
||||
"ui.view_angles_convention": "Grade în jurul axei verticale. Girația 0° este vizualizarea din față, 90° din stânga, −90° din dreapta, 180° din spate; înclinarea 90° este vizualizarea de sus; un ruliu pozitiv rotește modelul în sens orar.",
|
||||
"ui.view_angles_help": "Afișează înclinarea, girația și ruliul vizualizării în grade, plus proiecția ei, sub reperul de orientare. Faceți clic pe valori pentru a introduce unghiuri exacte.",
|
||||
"ui.view_angles_lock": "Blochează {axis}",
|
||||
"ui.view_angles_ortho": "Ortog.",
|
||||
"ui.view_angles_persp": "Persp.",
|
||||
"ui.view_angles_pitch": "Înclinare",
|
||||
"ui.view_angles_readout": "Înclinare {pitch} · Girație {yaw} · Ruliu {roll}",
|
||||
"ui.view_angles_roll": "Ruliu",
|
||||
"ui.view_angles_unlock": "Deblochează {axis}",
|
||||
"ui.view_angles_yaw": "Girație",
|
||||
"ui.view_angles_zero": "Setează {axis} la 0°",
|
||||
"ui.view_centre": "Centrul vizualizării",
|
||||
"ui.viewport_status_clear": "Dezactivează {name}",
|
||||
"ui.viewport_status_clear_hint": "Dezactivează {name} · Esc",
|
||||
|
||||
+91
-2
@@ -115,7 +115,7 @@
|
||||
"Circular": "Круговой",
|
||||
"Circumference": "Окружность",
|
||||
"Clockwise": "По часовой стрелке",
|
||||
"Close draft": "Закрыть анализ уклонов",
|
||||
"Close draft": "Закрыть черновик",
|
||||
"Collapse": "Свернуть",
|
||||
"Collisions": "Столкновения",
|
||||
"Colour": "Цвет",
|
||||
@@ -775,6 +775,8 @@
|
||||
"at the face": "у грани",
|
||||
"bom.group_children": "Вложенные детали в строке своей детали",
|
||||
"browser.body_name": "Тело{number}",
|
||||
"browser.calibrate_click_a": "Щёлкните по изображению, чтобы задать точку A, или введите её координаты.",
|
||||
"browser.calibrate_click_b": "Щёлкните по изображению, чтобы задать точку B, или введите её координаты.",
|
||||
"browser.calibrate_title": "Калибровка: {name}",
|
||||
"browser.capture_many": "Компоненты ({count}) перемещены, но их положения не зафиксированы.",
|
||||
"browser.capture_note": "«Зафиксировать положение» сохраняет новое положение; «Вернуть положение» восстанавливает прежнее.",
|
||||
@@ -787,6 +789,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (привязка потеряна)",
|
||||
"browser.sketch_editing": "{name} (редактируется)",
|
||||
"browser.sketch_name": "Эскиз{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "уже в своей детали",
|
||||
"browser.sketches_to_parts_design_body": "на теле самого дизайна",
|
||||
"browser.sketches_to_parts_moved": "Перемещено эскизов в деталь, на которой они лежат: {moved}",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "на грани без точной привязки",
|
||||
"browser.sketches_to_parts_not_on_face": "не на грани детали",
|
||||
"browser.sketches_to_parts_part_missing": "их детали больше нет",
|
||||
"browser.sketches_to_parts_result": "Перемещено эскизов в деталь, на которой они лежат: {moved}; остались на месте: {stayed} — {why}",
|
||||
"browser.units": "Единицы: {units}",
|
||||
"cam.flat": "Плоский",
|
||||
"cam.follower": "Толкатель",
|
||||
@@ -854,6 +863,7 @@
|
||||
"cmd.component_move_bodies": "Переместить тела в компонент",
|
||||
"cmd.component_move_features": "Переместить операции в компонент",
|
||||
"cmd.component_move_sketches": "Переместить эскизы в компонент",
|
||||
"cmd.component_move_sketches_to_parts": "Переместить эскизы в их детали",
|
||||
"cmd.component_paste_from_design": "Вставить из другого дизайна",
|
||||
"cmd.component_paste_new": "Вставить как новый",
|
||||
"cmd.component_properties": "Физические свойства компонента",
|
||||
@@ -1180,6 +1190,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Сплайн по контрольным точкам (степень 5)",
|
||||
"cmd.sketch_spline_fit_point": "Сплайн по точкам сглаживания",
|
||||
"cmd.sketch_spline_insert_point": "Добавить точку сплайна",
|
||||
"cmd.sketch_spline_remove_point": "Удалить точку сплайна",
|
||||
"cmd.sketch_spline_reverse": "Обратить направление сплайна",
|
||||
"cmd.sketch_spun_profile": "Профиль вращения",
|
||||
"cmd.sketch_toggle_diameter": "Переключить радиус / диаметр",
|
||||
@@ -1272,8 +1283,14 @@
|
||||
"color.hex_hint": "#rrggbb",
|
||||
"color.hue": "Оттенок",
|
||||
"color.linear": "Значения цвета показаны в линейном пространстве 0–1",
|
||||
"color.recent": "Недавние цвета",
|
||||
"color.recent_help": "Цвета, выбранные в любом поле цвета, появляются здесь",
|
||||
"color.saturation": "Насыщенность (в процентах)",
|
||||
"color.saturation_value": "Насыщенность и яркость",
|
||||
"color.selected": "Выбранный цвет",
|
||||
"color.step_down": "Уменьшить на шаг",
|
||||
"color.step_up": "Увеличить на шаг",
|
||||
"color.value": "Значение, то есть яркость (в процентах)",
|
||||
"config.column_filter_empty": "Нет подходящих столбцов",
|
||||
"contact.components_count": "Компонентов: {count}",
|
||||
"contact.enabled_label": "включено",
|
||||
@@ -1319,6 +1336,7 @@
|
||||
"dialog.dimension_insert_hint": "Дважды щёлкните другой управляющий размер, чтобы вставить его имя",
|
||||
"dialog.dimension_name": "Размер: {name}",
|
||||
"dialog.edit_title": "ИЗМЕНИТЬ: {title}",
|
||||
"dialog.pattern_sketch_not_object": "Выберите тела или операции: эскиз — ни то, ни другое. Повторите его кривые собственным инструментом «Массив» или «Зеркало» в эскизе либо сначала выдавите его.",
|
||||
"dialog.select": "↖ Выбрать",
|
||||
"dialog.selected_count": "↖ Выбрано: {count}",
|
||||
"dialog.selection_guidance": "Щёлкните поле ввода, чтобы изменить его. Щелчок по выделенному объекту убирает его; Ctrl/Cmd+щелчок тоже переключает. Проверьте предпросмотр и нажмите OK.",
|
||||
@@ -1537,6 +1555,16 @@
|
||||
"help.sketch.constraint.symmetry": "Делает две точки или кривые зеркальными друг для друга относительно линии или центральной точки. Используйте, чтобы профиль оставался симметричным при изменении одной стороны.",
|
||||
"help.sketch.constraint.tangent": "Заставляет две кривые касаться, например линию и дугу. Используйте для плавных стыков без излома.",
|
||||
"help.sketch.construction": "Переключает кривые между обычной и вспомогательной геометрией, которую профили не учитывают. Используйте вспомогательные кривые как помощники для разметки, ориентиры и размеры.",
|
||||
"help.sketch.construction_line": "Создайте бесконечную вспомогательную прямую через две точки. С её помощью можно разметить эскиз, не разделяя профили.",
|
||||
"help.sketch.construction_line.angle": "Создайте бесконечную вспомогательную прямую под углом к оси X эскиза. Укажите точку и введите угол либо задайте направление.",
|
||||
"help.sketch.construction_line.bisector": "Выберите две пересекающиеся прямые эскиза, затем одну из двух биссектрис. Результат изменяется вместе с исходными прямыми.",
|
||||
"help.sketch.construction_line.horizontal": "Создайте бесконечную горизонтальную вспомогательную прямую через точку. Её горизонтальное ограничение сохраняется при последующих изменениях.",
|
||||
"help.sketch.construction_line.parallel": "Выберите прямую эскиза, затем разместите параллельную бесконечную вспомогательную прямую. Щелчок правой кнопкой — выбрать другую исходную прямую.",
|
||||
"help.sketch.construction_line.perpendicular": "Выберите прямую эскиза, затем разместите перпендикулярную бесконечную вспомогательную прямую. Щелчок правой кнопкой — выбрать другую исходную прямую.",
|
||||
"help.sketch.construction_line.tangent_at": "Создайте касательную в точке окружности или дуги окружности либо в конечной точке открытого сплайна или коники. Выберите кривую, затем точку касания.",
|
||||
"help.sketch.construction_line.tangent_external": "Выберите окружность или дугу, затем внешнюю точку и решение для касательной. В предпросмотре показаны только точки касания на нарисованной дуге.",
|
||||
"help.sketch.construction_line.tangent_two": "Выберите две окружности или дуги, затем в предпросмотре выберите общую касательную. Точки касания должны лежать на нарисованных кривых.",
|
||||
"help.sketch.construction_line.vertical": "Создайте бесконечную вертикальную вспомогательную прямую через точку. Её вертикальное ограничение сохраняется при последующих изменениях.",
|
||||
"help.sketch.copy": "Копирует выбранные кривые и точки эскиза вместе с ограничениями и размерами между ними. Вставьте их в этот эскиз или в другой.",
|
||||
"help.sketch.create": "Начинает новый 2D-эскиз на плоскости или плоской грани модели. Каждое «Выдавливание», «Вращение» и «Сдвиг» начинается с эскиза.",
|
||||
"help.sketch.cut": "Копирует выбранную геометрию эскиза и удаляет её из эскиза одним шагом отмены. Вставьте её в другое место этого эскиза или в другой эскиз.",
|
||||
@@ -1875,6 +1903,7 @@
|
||||
"nav.hfss.help": "Alt+перетаскивание левой кнопкой панорамирует, Shift+Alt+левая кнопка меняет масштаб, Ctrl+перетаскивание левой кнопкой по пустому месту вращает",
|
||||
"nav.inventor.help": "Перетаскивание средней кнопкой панорамирует, Shift+средняя вращает, Ctrl+средняя меняет масштаб, перетаскивание правой кнопкой вращает",
|
||||
"nav.maya.help": "Alt+перетаскивание левой кнопкой вращает, Alt+средняя кнопка панорамирует, Alt+правая кнопка меняет масштаб; перетаскивание средней кнопкой тоже панорамирует",
|
||||
"nav.nx.help": "Перетаскивание средней кнопкой вращает, Shift+средняя панорамирует (в NX — средняя + правая), Ctrl+средняя меняет масштаб (в NX — средняя + левая); перетаскивание правой кнопкой камеру не двигает",
|
||||
"nav.openscad.help": "Перетаскивание левой кнопкой по пустому месту вращает, Shift+левая выделяет рамкой, перетаскивание геометрии эскиза изменяет её; правая или средняя кнопка панорамирует, Shift+правая или Shift+средняя меняет масштаб",
|
||||
"nav.perspective.sketch": "При редактировании эскиза используется ортогональный вид. Завершите эскиз, чтобы включить перспективу.",
|
||||
"nav.right_drag_orbits.help": "Выкл.: перетаскивание правой кнопкой не двигает камеру, а правая кнопка только открывает контекстное меню. Выбор назначения кнопок мыши SolidWorks отключает этот параметр.",
|
||||
@@ -2092,6 +2121,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Криволинейные поверхности",
|
||||
"prefs.curves": "Криволинейные поверхности",
|
||||
"prefs.dark_parts_shading": "Сохранять форму тёмных деталей",
|
||||
"prefs.dark_parts_shading_help": "Затенять вид в линейном свете с мягкой заполняющей подсветкой неба, чтобы тёмные грани сохраняли градиент, а не становились чёрными",
|
||||
"prefs.dead_zone_note": "Заштрихованная полоса — мёртвая зона: движение внутри неё игнорируется.",
|
||||
"prefs.default": "по умолчанию",
|
||||
"prefs.design": "Параметры дизайна по умолчанию",
|
||||
@@ -2106,6 +2137,8 @@
|
||||
"prefs.edges": "Рёбра и затенение",
|
||||
"prefs.extend": "Удлинить",
|
||||
"prefs.extra_navigation": "Ctrl+перетаскивание левой кнопкой по пустому месту вращает вид; удерживайте Space для панорамирования.",
|
||||
"prefs.extrude_view_isometric": "Поворачивать вид в изометрию при открытии «Выдавливания»",
|
||||
"prefs.extrude_view_return": "Возвращать прежний вид при закрытии «Выдавливания»",
|
||||
"prefs.follows_os": "Как в системе",
|
||||
"prefs.font": "Шрифт",
|
||||
"prefs.font_fallback": "Символы, которых нет в выбранном шрифте, берутся из встроенного шрифта, поэтому ни одна подпись не показывает пустой квадрат.",
|
||||
@@ -2121,6 +2154,7 @@
|
||||
"prefs.graphics_card_auto": "Автоматически",
|
||||
"prefs.graphics_card_missing": "Сохранённая видеокарта недоступна; используется «Автоматически».",
|
||||
"prefs.graphics_card_restart": "Вступит в силу после перезапуска SolidCraft. WGPU_ADAPTER_NAME переопределяет этот выбор.",
|
||||
"prefs.graphics_card_unused": "Сохранённая видеокарта для этого окна не использовалась; используемая видеокарта показана в меню Справка › О SolidCraft и в строке состояния. Попробуйте «Автоматически» или задайте для WGPU_ADAPTER_NAME уникальную часть названия видеокарты.",
|
||||
"prefs.graphics_note": "Выбирайте между скоростью и плавностью изображения. Каждый вариант показывает свой эффект на образце.",
|
||||
"prefs.graphics_presets_note": "Пресеты задают сразу все настройки ниже.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Скрывать знаки ограничений при уменьшении масштаба",
|
||||
@@ -2135,6 +2169,8 @@
|
||||
"prefs.history_sketches_note": "Если для эскизов выбрано «Под своей операцией», История слева показывает каждый эскиз на один уровень глубже, под первой операцией, построенной по нему, когда она достаточно широкая для названий. Шкала внизу всегда сохраняет порядок создания.",
|
||||
"prefs.history_sketches_under_feature": "Под своей операцией",
|
||||
"prefs.hold_stop_snapping": "Удерживайте, чтобы отключить привязку",
|
||||
"prefs.home_fit_size": "Размер для «Главная» и «Вписать»",
|
||||
"prefs.home_fit_size_note": "Какую часть вида занимает модель, когда её кадрируют «Главная», «Вписать», открытый дизайн или смена вида в навигаторе, независимо от того, включён ли параметр ниже. 100 %: заполняет вид между панелями, компасом и панелью навигации.",
|
||||
"prefs.icons_only": "Только значки",
|
||||
"prefs.imported_file": "Импортированный файл",
|
||||
"prefs.input": "Ввод",
|
||||
@@ -2142,13 +2178,20 @@
|
||||
"prefs.interface_language": "Язык интерфейса",
|
||||
"prefs.interface_note": "Как выглядит SolidCraft и где расположены его части.",
|
||||
"prefs.interval_minutes": "{n} мин",
|
||||
"prefs.invert_navigator_arrows": "Инвертировать стрелки вращения куба видов",
|
||||
"prefs.invert_navigator_arrows_note": "Стрелки вокруг куба вращают камеру в ту сторону, куда указывают, как в Fusion и Inventor: стрелка слева указывает вправо и поворачивает камеру вправо. Выкл.: стрелка поворачивает куб в сторону, где она расположена, поэтому левая стрелка поворачивает камеру влево. Изогнутые стрелки крена не затрагиваются.",
|
||||
"prefs.keep_work_on_tab_switch": "Сохранять открытые диалоги и инструменты при переключении между дизайнами",
|
||||
"prefs.keep_work_on_tab_switch_note": "Каждая вкладка дизайна сохраняет свой открытый диалог или инструмент вместе с тем, что вы в нём выбрали и ввели, а также свою вкладку панели инструментов. Они снова на месте, когда вы возвращаетесь к дизайну. Выкл.: при переключении дизайнов они отменяются, и показывается вкладка «Модель».",
|
||||
"prefs.labels": "Подписи",
|
||||
"prefs.language_system": "Системный",
|
||||
"prefs.layout": "Расположение",
|
||||
"prefs.layout_note": "«Компактная» прикрепляет панели к краям окна. Прозрачность браузера доступна в режиме «Плавающая». Сторону ассистента можно изменить на странице «Ассистент».",
|
||||
"prefs.left": "Слева",
|
||||
"prefs.left_click": "Щелчок левой кнопкой",
|
||||
"prefs.line_drag_draws_one": "Нажатие и перетаскивание рисует одну линию",
|
||||
"prefs.line_drag_draws_one_note": "В инструменте «Линия» нажатие, перетаскивание и отпускание рисуют одну линию от места нажатия до места отпускания, и на этом она заканчивается. Щелчки по-прежнему рисуют цепочку. Выкл.: перетаскивание ставит точку там, где его отпустили, как и щелчок.",
|
||||
"prefs.line_hover_tangent_arc": "Задержаться на последней точке линии, чтобы перейти к касательной дуге",
|
||||
"prefs.line_hover_tangent_arc_note": "В инструменте «Линия» после того, как сегмент нарисован, отведите указатель от последней точки цепочки, вернитесь и удерживайте его на ней неподвижно: следующий щелчок завершит касательную дугу, а тот же жест вернёт к линиям. Выкл.: задержка там ничего не делает.",
|
||||
"prefs.linked_designs": "Связанные дизайны",
|
||||
"prefs.live_input": "Ввод в реальном времени",
|
||||
"prefs.live_preview": "Предпросмотр в реальном времени",
|
||||
@@ -2172,6 +2215,8 @@
|
||||
"prefs.mouse_mapping": "Назначение кнопок мыши",
|
||||
"prefs.navigation": "Навигация",
|
||||
"prefs.navigation_note": "Как мышь, тачпад и 3D-мышь двигают вид.",
|
||||
"prefs.navigator_fit": "Вписывать модель после смены вида в навигаторе",
|
||||
"prefs.navigator_fit_note": "Грань, угол или стрелка куба видов либо ось гизмо также направляет взгляд на середину модели с единственного расстояния, с которого она целиком видна со всех сторон (её габаритная сфера). «Главная» при включённом параметре использует тот же масштаб и центр, поэтому выбранные после неё виды только поворачиваются. Выкл.: вид поворачивается на месте, сохраняя свою цель и масштаб.",
|
||||
"prefs.network_host_remove": "Убрать",
|
||||
"prefs.network_locations": "Разрешить сетевые расположения в дизайнах",
|
||||
"prefs.network_locations_note": "Выкл.: связанные файлы дизайна на другом компьютере (\\\\server\\share) открываются только с перечисленных здесь серверов или после того, как вы разрешите сервер по запросу.",
|
||||
@@ -2186,6 +2231,8 @@
|
||||
"prefs.overall_speed": "Общая скорость",
|
||||
"prefs.parameter_list_rows": "Строк в списке параметров",
|
||||
"prefs.parameter_list_rows_tip": "Стрелка вниз в пустом поле значения открывает список параметров, сначала избранные, не больше этого числа. Меньшее число делает список лёгким в большом дизайне.",
|
||||
"prefs.persistent_ribbon_pressed": "Подсвечивать активные команды на ленте",
|
||||
"prefs.persistent_ribbon_pressed_help": "Подсвечивает команды на ленте, пока их действие включено. Отключите, чтобы сохранить прежний нейтральный вид и справку по командам.",
|
||||
"prefs.perspective_fov": "Угол обзора",
|
||||
"prefs.placement": "Размещение",
|
||||
"prefs.prefer_free_sketch_points": "Предпочитать подвижные точки эскиза, когда точки перекрываются",
|
||||
@@ -2218,6 +2265,8 @@
|
||||
"prefs.render_previous_geometry_budget": "Использовать прежний бюджет геометрии предпросмотра рендера",
|
||||
"prefs.render_previous_geometry_budget_help": "Вернуть прежний фиксированный предел геометрии (512 МиБ на 64-разрядном настольном компьютере, 128 МиБ на остальных устройствах) вместо бюджета, зависящего от устройства.",
|
||||
"prefs.reports_saved": "Сохранено: {n}",
|
||||
"prefs.ribbon_click_ends_tool": "Щелчок по кнопке работающего инструмента завершает его",
|
||||
"prefs.ribbon_click_ends_tool_note": "Щелчок по подсвеченной кнопке используемого инструмента эскиза на ленте завершает инструмент, и щелчки в виде снова выделяют объекты. Выкл.: щелчок снова запускает инструмент.",
|
||||
"prefs.ribbon_reveal": "Показывать результаты поиска на ленте",
|
||||
"prefs.ribbon_reveal_note": "Наведение на результат в «Поиске команд» показывает, где команда находится на ленте: её вкладку, кнопку или строку в раскрывающемся списке панели. Команда, запущенная из поиска, мигает там, а выполняемая команда из раскрывающегося списка отмечает свою панель.",
|
||||
"prefs.right_click": "Правый щелчок",
|
||||
@@ -2245,6 +2294,10 @@
|
||||
"prefs.sketch_exit_view.previous": "Вернуться к прежнему виду",
|
||||
"prefs.sketch_exit_view.unless_rotated": "Вернуться, если вид не повёрнут",
|
||||
"prefs.sketch_exit_view_note": "Куда переходит вид после «Завершить эскиз», «Отмена» или двойного Esc.",
|
||||
"prefs.sketch_face_activates_part": "Эскиз на грани детали делает эту деталь активной",
|
||||
"prefs.sketch_face_activates_part_note": "Эскиз, начатый на грани тела другого компонента, сначала активирует этот компонент, поэтому эскиз принадлежит ему и в браузере остаётся с этой деталью. После этого деталь остаётся активной. Выкл.: эскиз принадлежит активному компоненту.",
|
||||
"prefs.sketch_open_look_at": "Поворачивать камеру к эскизу при его открытии",
|
||||
"prefs.sketch_open_look_at_note": "Выкл.: при создании или редактировании эскиза вид не меняется. «Взгляд на» поворачивает вид к эскизу, когда это нужно.",
|
||||
"prefs.sketch_recent_snap": "Автопривязка к недавним центрам и серединам",
|
||||
"prefs.sketch_recent_snap_note": "При рисовании курсор привязывается к месту пересечения направляющих двух последних центров окружностей или середин прямых отрезков. Выкл.: эти направляющие остаются только визуальными, а курсор не смещается.",
|
||||
"prefs.sketch_selection": "Эскиз и выделение",
|
||||
@@ -2267,6 +2320,24 @@
|
||||
"prefs.status_error": "Ошибка",
|
||||
"prefs.status_selected": "Выбрано",
|
||||
"prefs.status_warning": "Предупреждение",
|
||||
"prefs.structure.appearance": "Внешний вид",
|
||||
"prefs.structure.appearance_note": "Цвета, текст и специальные возможности.",
|
||||
"prefs.structure.in_profile": "В профиле",
|
||||
"prefs.structure.layout": "Расположение",
|
||||
"prefs.structure.layout_note": "Где находятся части окна.",
|
||||
"prefs.structure.look": "Оформление",
|
||||
"prefs.structure.measure_pointer": "Измерение и указатель",
|
||||
"prefs.structure.mouse3d": "3D-мышь",
|
||||
"prefs.structure.mouse3d_note": "SpaceMouse и SpaceNavigator.",
|
||||
"prefs.structure.navigation_note": "Мышь и тачпад. У 3D-мыши есть своя страница.",
|
||||
"prefs.structure.profile": "Профиль",
|
||||
"prefs.structure.profile_edited": "Изменено после сохранения",
|
||||
"prefs.structure.profile_hover": "Сохранено в профилях настроек",
|
||||
"prefs.structure.scale_percent": "{percent}%",
|
||||
"prefs.structure.sketch": "Эскиз",
|
||||
"prefs.structure.tools": "Инструменты",
|
||||
"prefs.structure.tools_note": "Как ведут себя инструменты моделирования.",
|
||||
"prefs.structure.work": "Работа",
|
||||
"prefs.supersampling_brief": "Рисует вид в большем размере и уменьшает его: очень чётко, но тяжелее.",
|
||||
"prefs.tab_accent_note": "Акцентный цвет следует за активной вкладкой",
|
||||
"prefs.theme_file": "файл темы",
|
||||
@@ -2334,6 +2405,10 @@
|
||||
"properties.title": "Свойства: {name}",
|
||||
"recovery.days_ago": "{count} дн. назад",
|
||||
"recovery.detail": "{file} · операций: {features} · автосохранение {when}",
|
||||
"recovery.discard_all": "Отбросить все",
|
||||
"recovery.discard_all_detail": "Это действие нельзя отменить.",
|
||||
"recovery.discard_all_title": "Отбросить автосохранения ({count})?",
|
||||
"recovery.discard_all_title_one": "Отбросить 1 автосохранение?",
|
||||
"recovery.hours_ago": "{count} ч назад",
|
||||
"recovery.just_now": "только что",
|
||||
"recovery.min_ago": "{count} мин назад",
|
||||
@@ -3717,6 +3792,7 @@
|
||||
"sketch_text.formula": "Начните с =, чтобы составить слова из параметров: ='W' + number(Width)",
|
||||
"sketch_text.italic": "Курсив",
|
||||
"sketch_text.loading_fonts": "Поиск установленных шрифтов…",
|
||||
"sketch_text.no_bold": "У встроенного шрифта нет полужирного начертания: выберите установленный шрифт",
|
||||
"sketch_text.on_path": "Текст следует по траектории: кривая задаёт его положение",
|
||||
"sketch_text.path_clear": "Снять текст с кривой",
|
||||
"sketch_text.path_hint": "Кривая эскиза, вдоль которой идёт текст: щёлкните здесь, затем по кривой",
|
||||
@@ -4117,6 +4193,9 @@
|
||||
"ui.ai_permissions": "Разрешения",
|
||||
"ui.ai_preset": "Пресет",
|
||||
"ui.ai_protocol": "Протокол",
|
||||
"ui.ai_reasoning_effort": "Уровень рассуждений",
|
||||
"ui.ai_reasoning_effort_field_hint": "пусто = значение поставщика по умолчанию",
|
||||
"ui.ai_reasoning_effort_hint": "Необязательно. Отправляется на конечную точку как `reasoning_effort`; допустимые значения зависят от поставщика (например, `low`, `high` или `max`). Заданное здесь значение SolidCraft никогда не заменяет. Оставьте поле пустым, чтобы ничего не отправлять и сохранить значение поставщика по умолчанию.",
|
||||
"ui.ai_remove": "Удалить",
|
||||
"ui.ai_request_timed_out": "Модель отвечала дольше {seconds} с. Тайм-аут запроса для этой конечной точки можно увеличить в Настройки › Ассистент.",
|
||||
"ui.ai_request_timeout": "Тайм-аут запроса",
|
||||
@@ -4280,6 +4359,7 @@
|
||||
"ui.dark": "Тёмная",
|
||||
"ui.default_modelling_orientation": "Ориентация моделирования по умолчанию",
|
||||
"ui.delete": "Удалить",
|
||||
"ui.delete_select_first": "Сначала выберите, что удалить: операции, тела, компоненты или геометрию эскиза",
|
||||
"ui.depth": "Глубина",
|
||||
"ui.design": "Дизайн",
|
||||
"ui.design_properties.author": "Автор",
|
||||
@@ -4372,6 +4452,7 @@
|
||||
"ui.edit_in_place_saved_elsewhere": "Деталь сохранена в другой файл; сборка по-прежнему связана с {name}",
|
||||
"ui.edit_in_place_wait": "Дождитесь завершения того, что выполняется в дизайне",
|
||||
"ui.edit_section_analysis": "Изменить анализ сечения",
|
||||
"ui.edit_sketch_empty": "Нет эскизов для редактирования",
|
||||
"ui.ellipse": "Эллипс",
|
||||
"ui.emboss_wrap_note": "Профили из ортогональных линий, касательные к одному поддерживаемому цилиндру, оборачиваются автоматически. Выберите стенку, чтобы задать цель.",
|
||||
"ui.end_radius": "Конечный радиус",
|
||||
@@ -4382,7 +4463,9 @@
|
||||
"ui.equation_explicit": "Явная",
|
||||
"ui.equation_form": "Форма",
|
||||
"ui.equation_note": "Переменная — обычное число: sin(t) принимает её в радианах. Результат без единицы измерения задаётся в единицах дизайна.",
|
||||
"ui.equation_note_range": "Переменная — длина, как и её диапазон: сочетайте её с параметрами длины, а для sin() делите её на длину. Результат без единицы измерения задаётся в единицах дизайна.",
|
||||
"ui.equation_parametric": "Параметрическая",
|
||||
"ui.equation_variable_unit": "Переменная имеет единицу измерения диапазона",
|
||||
"ui.equation_x_of_t": "x(t)",
|
||||
"ui.equation_y_of_t": "y(t)",
|
||||
"ui.equation_y_of_x": "y(x)",
|
||||
@@ -4557,12 +4640,14 @@
|
||||
"ui.joints": "Сопряжения",
|
||||
"ui.keyboard_shortcuts": "Сочетания клавиш",
|
||||
"ui.language": "Язык",
|
||||
"ui.lasso_select": "Лассо (перетащите произвольный контур: выделяет всё, что целиком внутри)",
|
||||
"ui.layout_compact": "Компактная",
|
||||
"ui.layout_floating": "Плавающая",
|
||||
"ui.length": "Длина",
|
||||
"ui.length_decimals": "Знаков после запятой (длины)",
|
||||
"ui.light": "Светлая",
|
||||
"ui.line": "Линия",
|
||||
"ui.line_arc_hint": "Касательная дуга: щёлкните в конце дуги. Снова задержитесь на последней точке, чтобы вернуться к линиям; щёлкните правой кнопкой или нажмите Esc, чтобы завершить",
|
||||
"ui.link_copies": "Связать одинаковые копии",
|
||||
"ui.link_copies_keep": "Оставить",
|
||||
"ui.link_copies_link": "Связать",
|
||||
@@ -4651,6 +4736,7 @@
|
||||
"ui.part_size_shared": "Перестраивает деталь, заменяя все добавленные операции. Обновляются все вхождения этого компонента.",
|
||||
"ui.paste": "Вставить",
|
||||
"ui.path": "Траектория",
|
||||
"ui.pattern_object_type_tip": "Тела: твёрдые формы, выбираемые в виде или в браузере; каждое копируется целиком.\nОперации: шаги Истории, например выдавливание или отверстие, выбираемые в Истории или на созданной ими грани; шаг повторяется.\nЭскиз — ни то, ни другое: повторите его кривые собственным инструментом «Массив» или «Зеркало» в эскизе либо сначала выдавите его.",
|
||||
"ui.pattern_on_path": "Массив по траектории",
|
||||
"ui.perforate_margin_default": "от диаметра",
|
||||
"ui.perforate_pitch": "Шаг",
|
||||
@@ -4687,9 +4773,9 @@
|
||||
"ui.polygon": "Многоугольник",
|
||||
"ui.preferences": "Настройки",
|
||||
"ui.press_a_key": "нажмите клавишу…",
|
||||
"ui.print_face_body_not_sent": "Тело этой грани не отправляется: сначала добавьте его в Тела",
|
||||
"ui.print_face_cleared": "{body}: грань печати сброшена",
|
||||
"ui.print_face_input": "Грань печати",
|
||||
"ui.print_face_one_body": "Грань печати относится к одному телу: отправьте это тело отдельно, чтобы изменить её",
|
||||
"ui.print_face_pick": "Выберите плоскую грань твёрдого тела, чтобы сделать её гранью печати",
|
||||
"ui.print_face_set": "{body}: грань печати задана; «Отправить в слайсер» кладёт тело на неё",
|
||||
"ui.profile": "Профиль",
|
||||
@@ -4915,12 +5001,15 @@
|
||||
"ui.view_angles": "Углы вида",
|
||||
"ui.view_angles_convention": "Градусы вокруг вертикальной оси. Рыскание 0° — вид спереди, 90° — слева, −90° — справа, 180° — сзади; тангаж 90° — вид сверху; положительный крен поворачивает модель по часовой стрелке.",
|
||||
"ui.view_angles_help": "Показывает тангаж, рыскание и крен вида в градусах, а также его проекцию под гизмо ориентации. Щёлкните по показанию, чтобы ввести точные углы.",
|
||||
"ui.view_angles_lock": "Заблокировать {axis}",
|
||||
"ui.view_angles_ortho": "Орто",
|
||||
"ui.view_angles_persp": "Персп.",
|
||||
"ui.view_angles_pitch": "Тангаж",
|
||||
"ui.view_angles_readout": "Тангаж {pitch} · Рыскание {yaw} · Крен {roll}",
|
||||
"ui.view_angles_roll": "Крен",
|
||||
"ui.view_angles_unlock": "Разблокировать {axis}",
|
||||
"ui.view_angles_yaw": "Рыскание",
|
||||
"ui.view_angles_zero": "Установить {axis} в 0°",
|
||||
"ui.view_centre": "Центр вида",
|
||||
"ui.viewport_status_clear": "Выключить {name}",
|
||||
"ui.viewport_status_clear_hint": "Выключить {name} · Esc",
|
||||
|
||||
+71
-1
@@ -793,6 +793,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (причвршћење изгубљено)",
|
||||
"browser.sketch_editing": "{name} (у измени)",
|
||||
"browser.sketch_name": "Скица{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "већ у свом делу",
|
||||
"browser.sketches_to_parts_design_body": "на телу самог дизајна",
|
||||
"browser.sketches_to_parts_moved": "Скице премештене у део на којем се налазе: {moved}",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "на лицу без тачног причвршћења",
|
||||
"browser.sketches_to_parts_not_on_face": "не на лицу дела",
|
||||
"browser.sketches_to_parts_part_missing": "њихов део више не постоји",
|
||||
"browser.sketches_to_parts_result": "Скице премештене у део на којем се налазе: {moved}; остале на месту: {stayed} ({why})",
|
||||
"browser.units": "Јединице: {units}",
|
||||
"cam.flat": "Равно лице",
|
||||
"cam.follower": "Пратилац",
|
||||
@@ -860,6 +867,7 @@
|
||||
"cmd.component_move_bodies": "Премести тела у компоненту",
|
||||
"cmd.component_move_features": "Премести операције у компоненту",
|
||||
"cmd.component_move_sketches": "Премести скице у компоненту",
|
||||
"cmd.component_move_sketches_to_parts": "Премести скице у њихове делове",
|
||||
"cmd.component_paste_from_design": "Налепи из другог дизајна",
|
||||
"cmd.component_paste_new": "Налепи као ново",
|
||||
"cmd.component_properties": "Физичка својства компоненте",
|
||||
@@ -1195,6 +1203,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Сплајн контролних тачака (степен 5)",
|
||||
"cmd.sketch_spline_fit_point": "Сплајн кроз тачке",
|
||||
"cmd.sketch_spline_insert_point": "Додај тачку сплајна",
|
||||
"cmd.sketch_spline_remove_point": "Уклони тачку сплајна",
|
||||
"cmd.sketch_spline_reverse": "Обрни смер сплајна",
|
||||
"cmd.sketch_spun_profile": "Ротирани профил",
|
||||
"cmd.sketch_toggle_diameter": "Пребаци полупречник / пречник",
|
||||
@@ -1287,8 +1296,14 @@
|
||||
"color.hex_hint": "#rrggbb",
|
||||
"color.hue": "Нијанса",
|
||||
"color.linear": "Приказ вредности боје у линеарном простору 0-1",
|
||||
"color.recent": "Недавне боје",
|
||||
"color.recent_help": "Овде се појављују боје које изаберете у било ком пољу боје",
|
||||
"color.saturation": "Засићеност (проценат)",
|
||||
"color.saturation_value": "Засићеност и вредност",
|
||||
"color.selected": "Изабрана боја",
|
||||
"color.step_down": "Смањи за корак",
|
||||
"color.step_up": "Повећај за корак",
|
||||
"color.value": "Вредност, светлина (проценат)",
|
||||
"config.column_filter_empty": "Нема одговарајућих колона",
|
||||
"contact.components_count": "Компоненте: {count}",
|
||||
"contact.enabled_label": "омогућено",
|
||||
@@ -1335,6 +1350,7 @@
|
||||
"dialog.dimension_name": "Кота: {name}",
|
||||
"dialog.edit_title": "ИЗМЕНИ {title}",
|
||||
"dialog.no_shown_body": "Нема приказаног тела за измену: прикажите једно или изаберите Ново тело",
|
||||
"dialog.pattern_sketch_not_object": "Изаберите тела или операције: скица није ни једно ни друго. Поновите њене криве помоћу алата Низ или Пресликај у самој скици или је прво екструдирајте.",
|
||||
"dialog.select": "↖ Изабери",
|
||||
"dialog.selected_count": "↖ Изабрано: {count}",
|
||||
"dialog.selection_guidance": "Изаберите улаз да га промените. Кликните изабране објекте да их уклоните; Ctrl/Cmd-клик их такође укључује и искључује. Погледајте преглед, а затим притисните У реду.",
|
||||
@@ -1904,6 +1920,7 @@
|
||||
"nav.hfss.help": "Alt+превлачење левим тастером помера, Shift+Alt+леви зумира, Ctrl+превлачење левим на празном простору орбитира",
|
||||
"nav.inventor.help": "Превлачење средњим дугметом помера, Shift+средње орбитира, Ctrl+средње зумира, превлачење десним орбитира",
|
||||
"nav.maya.help": "Alt+превлачење левим орбитира, Alt+средњи помера, Alt+десни зумира; превлачење средњим такође помера",
|
||||
"nav.nx.help": "Превлачење средњим дугметом орбитира, Shift+средње помера (у NX-у средње + десно), Ctrl+средње зумира (у NX-у средње + лево); превлачење десним дугметом не дира камеру",
|
||||
"nav.openscad.help": "Превлачење левим дугметом по празном простору орбитира, Shift+превлачење левим бира оквиром, превлачење геометрије скице је мења; превлачење десним или средњим дугметом помера, Shift+десно или Shift+средње зумира",
|
||||
"nav.perspective.sketch": "Измена скице користи ортографски приказ. Завршите скицу да пребаците на перспективу.",
|
||||
"nav.right_drag_orbits.help": "Искључено: превлачење десним дугметом не дира камеру, а десно дугме само отвара контекстни мени. Избор SolidWorks мапирања миша га искључује.",
|
||||
@@ -2125,6 +2142,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Закривљене површи",
|
||||
"prefs.curves": "Закривљене површи",
|
||||
"prefs.dark_parts_shading": "Задржи облик тамних делова",
|
||||
"prefs.dark_parts_shading_help": "Сенчи приказ у линеарном светлу уз меко допунско светло неба, тако да тамна лица задрже градијент уместо да поцрне",
|
||||
"prefs.dead_zone_note": "Шрафирана трака је мртва зона: кретање унутар ње се занемарује.",
|
||||
"prefs.default": "подразумевано",
|
||||
"prefs.design": "Подразумевана подешавања дизајна",
|
||||
@@ -2139,6 +2158,8 @@
|
||||
"prefs.edges": "Ивице и сенчење",
|
||||
"prefs.extend": "Продужи",
|
||||
"prefs.extra_navigation": "Ctrl + превлачење левим дугметом по празном простору орбитира; држите размакницу за померање.",
|
||||
"prefs.extrude_view_isometric": "Окрени приказ у изометријски када се отвори Екструдирај",
|
||||
"prefs.extrude_view_return": "Врати се на претходни приказ када се Екструдирај затвори",
|
||||
"prefs.follows_os": "Прати оперативни систем",
|
||||
"prefs.font": "Фонт",
|
||||
"prefs.font_fallback": "Знакови који недостају изабраном фонту приказују се уграђеним фонтом, тако да ниједна ознака не приказује празан квадрат.",
|
||||
@@ -2154,6 +2175,7 @@
|
||||
"prefs.graphics_card_auto": "Аутоматски",
|
||||
"prefs.graphics_card_missing": "Сачувана графичка картица није доступна; користи се Аутоматски.",
|
||||
"prefs.graphics_card_restart": "Ступа на снагу након поновног покретања SolidCraft-а. WGPU_ADAPTER_NAME има предност над овим избором.",
|
||||
"prefs.graphics_card_unused": "Сачувана графичка картица није коришћена за овај прозор; картица која се користи приказана је у Помоћ › О програму и у статусној траци. Покушајте са опцијом Аутоматски или поставите WGPU_ADAPTER_NAME на јединствени део назива картице.",
|
||||
"prefs.graphics_note": "Жртвујте брзину за глаткоћу. Сваки избор показује свој учинак на примеру.",
|
||||
"prefs.graphics_presets_note": "Унапред подешене вредности подешавају све изборе испод одједном.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Сакриј симболе ограничења када је приказ умањен",
|
||||
@@ -2168,6 +2190,8 @@
|
||||
"prefs.history_sketches_note": "Када је Скице подешено на Испод своје операције, временска трака лево приказује сваку скицу један ниво увучено, испод прве операције направљене из ње, кад год је довољно широка за називе. Временска трака на дну увек задржава редослед настанка.",
|
||||
"prefs.history_sketches_under_feature": "Испод своје операције",
|
||||
"prefs.hold_stop_snapping": "Држите да искључите привлачење",
|
||||
"prefs.home_fit_size": "Величина за Почетни приказ и Уклопи",
|
||||
"prefs.home_fit_size_note": "Колики део приказа модел испуњава када га уоквири Почетни приказ, Уклопи, отварање дизајна или промена приказа у навигатору, без обзира на то да ли је подешавање испод укључено. 100 %: испуњава приказ између панела, компаса и навигационе траке.",
|
||||
"prefs.icons_only": "Само иконе",
|
||||
"prefs.imported_file": "Увезена датотека",
|
||||
"prefs.in_browser": "У стаблу модела",
|
||||
@@ -2176,13 +2200,20 @@
|
||||
"prefs.interface_language": "Језик интерфејса",
|
||||
"prefs.interface_note": "Како SolidCraft изгледа и где су његови делови.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_navigator_arrows": "Обрни стрелице за ротацију коцке приказа",
|
||||
"prefs.invert_navigator_arrows_note": "Стрелице око коцке орбитирају камеру у смеру у којем показују, као у програмима Fusion и Inventor: стрелица са леве стране показује удесно и закреће камеру удесно. Искључено: стрелица окреће коцку ка страни на којој се налази, па лева стрелица закреће камеру улево. Подешавање не утиче на закривљене стрелице за ваљање.",
|
||||
"prefs.keep_work_on_tab_switch": "Задржи дијалоге и алате отвореним при пребацивању између дизајна",
|
||||
"prefs.keep_work_on_tab_switch_note": "Свака картица дизајна задржава свој отворени дијалог или алат, са оним што сте у њему изабрали и уписали, те своју картицу траке са алатима. Када се вратите на дизајн, поново су ту. Искључено: пребацивање између дизајна отказује их и приказује картицу Модел.",
|
||||
"prefs.labels": "Ознаке",
|
||||
"prefs.language_system": "Систем",
|
||||
"prefs.layout": "Распоред",
|
||||
"prefs.layout_note": "Компактно причвршћује панеле уз ивице прозора. Провидност стабла модела доступна је у опцији Плутајуће. Страну Асистента промените на страници Асистент.",
|
||||
"prefs.left": "Лево",
|
||||
"prefs.left_click": "Леви клик",
|
||||
"prefs.line_drag_draws_one": "Притисак и превлачење цртају једну линију",
|
||||
"prefs.line_drag_draws_one_note": "У алату Линија, притисак, превлачење и пуштање цртају једну линију од места притиска до места пуштања и ту се завршава. Кликови и даље цртају ланац. Искључено: превлачење поставља тачку где се пусти, као и клик.",
|
||||
"prefs.line_hover_tangent_arc": "Задржите показивач на последњој тачки линије да пређете на тангентни лук",
|
||||
"prefs.line_hover_tangent_arc_note": "У алату Линија, након што је нацртан сегмент, одмакните показивач од последње тачке ланца, вратите га на њу и држите га мирно: следећи клик завршава тангентни лук, а исти покрет враћа на линије. Искључено: задржавање на тој тачки не ради ништа.",
|
||||
"prefs.linked_designs": "Повезани дизајни",
|
||||
"prefs.live_input": "Улаз уживо",
|
||||
"prefs.live_preview": "Преглед уживо",
|
||||
@@ -2206,6 +2237,8 @@
|
||||
"prefs.mouse_mapping": "Мапирање миша",
|
||||
"prefs.navigation": "Навигација",
|
||||
"prefs.navigation_note": "Како миш, додирна табла и 3D миш померају приказ.",
|
||||
"prefs.navigator_fit": "Уклопи модел након промене приказа у навигатору",
|
||||
"prefs.navigator_fit_note": "Лице, угао или стрелица коцке приказа, односно оса гизма оријентације, такође усмерава поглед на средину модела са једног растојања на којем се уклапа из сваког смера (његова гранична сфера). Почетни приказ користи исти зум и центар док је ова опција укључена, па се прикази које изаберете после њега само окрећу. Искључено: приказ се окреће на месту и задржава свој циљ и зум.",
|
||||
"prefs.network_host_remove": "Уклони",
|
||||
"prefs.network_locations": "Дозволи мрежне локације у дизајнима",
|
||||
"prefs.network_locations_note": "Искључено: повезане датотеке дизајна на другом рачунару (\\\\server\\share) отварају се само са сервера наведених овде или након што га дозволите када се то затражи.",
|
||||
@@ -2254,6 +2287,8 @@
|
||||
"prefs.render_previous_geometry_budget": "Користи претходни буџет геометрије за преглед у Рендеровању",
|
||||
"prefs.render_previous_geometry_budget_help": "Враћа претходно фиксно ограничење геометрије (512 MiB на 64-битним десктоп рачунарима, 128 MiB другде) уместо буџета према уређају.",
|
||||
"prefs.reports_saved": "Сачувано: {n}",
|
||||
"prefs.ribbon_click_ends_tool": "Клик на дугме активног алата завршава га",
|
||||
"prefs.ribbon_click_ends_tool_note": "Клик на истакнуто дугме на траци алата за скицирање који је у употреби завршава тај алат, па кликови у приказу поново служе за избор. Искључено: клик поново покреће алат.",
|
||||
"prefs.ribbon_reveal": "Прикажи резултате претраге на траци",
|
||||
"prefs.ribbon_reveal_note": "Задржавање показивача изнад резултата у Претражи команде показује где се команда налази на траци: њену картицу, њено дугме или њен ред у падајућем менију панела. Команда покренута из претраге тамо трепери, а покренута команда из падајућег менија означава свој панел.",
|
||||
"prefs.right_click": "Десни клик",
|
||||
@@ -2283,6 +2318,8 @@
|
||||
"prefs.sketch_exit_view.previous": "Врати се на претходни приказ",
|
||||
"prefs.sketch_exit_view.unless_rotated": "Врати се осим ако је ротирано",
|
||||
"prefs.sketch_exit_view_note": "Куда приказ иде након Заврши скицу, Откажи или двоструког Esc.",
|
||||
"prefs.sketch_face_activates_part": "Скицирање на лицу дела чини тај део активним",
|
||||
"prefs.sketch_face_activates_part_note": "Скица започета на лицу тела друге компоненте прво активира ту компоненту, па скица припада њој и у стаблу модела остаје уз тај део. Тај део након тога остаје активан. Искључено: скица припада активној компоненти.",
|
||||
"prefs.sketch_open_look_at": "Окрени камеру према скици када се отвори",
|
||||
"prefs.sketch_open_look_at_note": "Искључено: када направите или измените скицу, приказ остаје онакав какав јесте. Погледај окреће приказ према скици када то желите.",
|
||||
"prefs.sketch_recent_snap": "Аутоматски привлачи на недавне центре и средишње тачке",
|
||||
@@ -2314,6 +2351,24 @@
|
||||
"prefs.status_error": "Грешка",
|
||||
"prefs.status_selected": "Изабрано",
|
||||
"prefs.status_warning": "Упозорење",
|
||||
"prefs.structure.appearance": "Изглед",
|
||||
"prefs.structure.appearance_note": "Боје, текст и приступачност.",
|
||||
"prefs.structure.in_profile": "У профилу",
|
||||
"prefs.structure.layout": "Распоред",
|
||||
"prefs.structure.layout_note": "Где се налазе делови прозора.",
|
||||
"prefs.structure.look": "Визуелно",
|
||||
"prefs.structure.measure_pointer": "Мерење и показивач",
|
||||
"prefs.structure.mouse3d": "3D миш",
|
||||
"prefs.structure.mouse3d_note": "SpaceMouse и SpaceNavigator.",
|
||||
"prefs.structure.navigation_note": "Миш и додирна табла. 3D миш има сопствену страницу.",
|
||||
"prefs.structure.profile": "Профил",
|
||||
"prefs.structure.profile_edited": "Промењено откако је сачувано",
|
||||
"prefs.structure.profile_hover": "Сачувано у профилима подешавања",
|
||||
"prefs.structure.scale_percent": "{percent}%",
|
||||
"prefs.structure.sketch": "Скица",
|
||||
"prefs.structure.tools": "Алати",
|
||||
"prefs.structure.tools_note": "Како се понашају алати за моделовање.",
|
||||
"prefs.structure.work": "Рад",
|
||||
"prefs.supersampling_brief": "Исцртава приказ већим и смањује га филтрирањем: веома оштро и захтевније.",
|
||||
"prefs.tab_accent_note": "Нагласна боја прати активну картицу",
|
||||
"prefs.theme_file": "датотека теме",
|
||||
@@ -3811,6 +3866,7 @@
|
||||
"sketch_text.formula": "Почните са = да речи саставите од параметара: ='W' + number(Width)",
|
||||
"sketch_text.italic": "Курзив",
|
||||
"sketch_text.loading_fonts": "Тражење инсталираних фонтова…",
|
||||
"sketch_text.no_bold": "Уграђени фонт нема подебљану варијанту: изаберите инсталирани фонт",
|
||||
"sketch_text.on_path": "Текст прати своју путању: крива га поставља",
|
||||
"sketch_text.path_clear": "Скини текст са његове криве",
|
||||
"sketch_text.path_hint": "Крива скице коју текст прати: кликните овде, а затим криву",
|
||||
@@ -4213,6 +4269,9 @@
|
||||
"ui.ai_permissions": "Дозволе",
|
||||
"ui.ai_preset": "Унапред подешено",
|
||||
"ui.ai_protocol": "Протокол",
|
||||
"ui.ai_reasoning_effort": "Ниво закључивања",
|
||||
"ui.ai_reasoning_effort_field_hint": "празно = подразумевана вредност пружаоца",
|
||||
"ui.ai_reasoning_effort_hint": "Необавезно. Шаље се крајњој тачки као `reasoning_effort`; прихваћене вредности се разликују од пружаоца до пружаоца (на пример `low`, `high` или `max`). SolidCraft никад не мења вредност коју овде поставите. Оставите празно да се ништа не шаље и да се задржи подразумевана вредност пружаоца.",
|
||||
"ui.ai_remove": "Уклони",
|
||||
"ui.ai_request_timed_out": "Модел је трајао дуже од {seconds} s. Временско ограничење захтева за ову крајњу тачку можете повећати у Подешавања › Асистент.",
|
||||
"ui.ai_request_timeout": "Временско ограничење захтева",
|
||||
@@ -4378,6 +4437,7 @@
|
||||
"ui.dark": "Тамна",
|
||||
"ui.default_modelling_orientation": "Подразумевана оријентација моделовања",
|
||||
"ui.delete": "Избриши",
|
||||
"ui.delete_select_first": "Прво изаберите шта желите да избришете: операције, тела, компоненте или геометрију скице",
|
||||
"ui.depth": "Дубина",
|
||||
"ui.design": "Дизајн",
|
||||
"ui.design_properties.author": "Аутор",
|
||||
@@ -4470,6 +4530,7 @@
|
||||
"ui.edit_in_place_saved_elsewhere": "Део је сачуван у другу датотеку; склоп и даље повезује {name}",
|
||||
"ui.edit_in_place_wait": "Сачекајте док се оно што ради у дизајну не заврши",
|
||||
"ui.edit_section_analysis": "Измени анализу пресека",
|
||||
"ui.edit_sketch_empty": "Нема скица за измену",
|
||||
"ui.ellipse": "Елипса",
|
||||
"ui.emboss_wrap_note": "Профили од ортогоналних линија тангентни на један подржани цилиндар омотавају се аутоматски. Изаберите зид да бисте изабрали циљ.",
|
||||
"ui.end_radius": "Крајњи полупречник",
|
||||
@@ -4480,7 +4541,9 @@
|
||||
"ui.equation_explicit": "Експлицитни",
|
||||
"ui.equation_form": "Облик",
|
||||
"ui.equation_note": "Променљива је обичан број: sin(t) је узима у радијанима. Резултат без јединице је у јединици дизајна.",
|
||||
"ui.equation_note_range": "Променљива је дужина, као и њен опсег: комбинујте је са параметрима дужине, а за sin() је поделите дужином. Резултат без јединице је у јединици дизајна.",
|
||||
"ui.equation_parametric": "Параметарски",
|
||||
"ui.equation_variable_unit": "Променљива има јединицу опсега",
|
||||
"ui.equation_x_of_t": "x(t)",
|
||||
"ui.equation_y_of_t": "y(t)",
|
||||
"ui.equation_y_of_x": "y(x)",
|
||||
@@ -4647,6 +4710,7 @@
|
||||
"ui.insert_design.saved_file": "Дизајн {name} има несачуване промене, па је уметнута његова сачувана датотека. Сачувајте га, а затим покрените Ажурирај везе да бисте унели промене.",
|
||||
"ui.insert_design.unsaved": "Несачуване промене",
|
||||
"ui.inspect": "Прегледај",
|
||||
"ui.instances": "Инстанце",
|
||||
"ui.intersect": "Пресек",
|
||||
"ui.isolate": "Изолуј",
|
||||
"ui.join": "Споји",
|
||||
@@ -4655,12 +4719,14 @@
|
||||
"ui.joints": "Спојеви",
|
||||
"ui.keyboard_shortcuts": "Пречице на тастатури",
|
||||
"ui.language": "Језик",
|
||||
"ui.lasso_select": "Ласо (превуците петљу слободном руком: узима све што је у целини унутра)",
|
||||
"ui.layout_compact": "Компактно",
|
||||
"ui.layout_floating": "Плутајуће",
|
||||
"ui.length": "Дужина",
|
||||
"ui.length_decimals": "Децимале дужине",
|
||||
"ui.light": "Светла",
|
||||
"ui.line": "Линија",
|
||||
"ui.line_arc_hint": "Тангентни лук: кликните крај лука. Поново се задржите на последњој тачки за линије; десни клик или Esc за завршетак",
|
||||
"ui.link_copies": "Повежи идентичне копије",
|
||||
"ui.link_copies_keep": "Задржи",
|
||||
"ui.link_copies_link": "Повежи",
|
||||
@@ -4749,6 +4815,7 @@
|
||||
"ui.part_size_shared": "Поново гради део, замењујући све додате операције. Сви примерци ове компоненте се ажурирају.",
|
||||
"ui.paste": "Налепи",
|
||||
"ui.path": "Путања",
|
||||
"ui.pattern_object_type_tip": "Тела: пуни облици, бирају се у приказу или у стаблу модела; свако се копира у целини.\nОперације: кораци временске траке, као што су екструзија или рупа, бирају се на временској траци или на лицу које су направили; корак се понавља.\nСкица није ни једно ни друго: поновите њене криве помоћу алата Низ или Пресликај у самој скици или је прво екструдирајте.",
|
||||
"ui.pattern_on_path": "Низ дуж путање",
|
||||
"ui.perforate_margin_default": "из пречника",
|
||||
"ui.perforate_pitch": "Корак",
|
||||
@@ -4794,9 +4861,9 @@
|
||||
"ui.polygon": "Полигон",
|
||||
"ui.preferences": "Подешавања",
|
||||
"ui.press_a_key": "притисните тастер…",
|
||||
"ui.print_face_body_not_sent": "Тело те површине се не шаље: прво га додајте у Тела",
|
||||
"ui.print_face_cleared": "{body}: површина за штампу уклоњена",
|
||||
"ui.print_face_input": "Површина за штампу",
|
||||
"ui.print_face_one_body": "Површина за штампу припада једном телу: пошаљите то тело само да бисте је променили",
|
||||
"ui.print_face_pick": "Изаберите равну површину пуног тела да постане површина за штампу",
|
||||
"ui.print_face_set": "{body}: површина за штампу постављена; Пошаљи у слајсер полаже тело на њу",
|
||||
"ui.profile": "Профил",
|
||||
@@ -5024,12 +5091,15 @@
|
||||
"ui.view_angles": "Углови приказа",
|
||||
"ui.view_angles_convention": "Степени око осе нагоре. Скретање 0° је Поглед спреда, 90° слева, −90° здесна, 180° отпозади; корак 90° је Поглед одозго; позитивно ваљање окреће модел у смеру казаљке на сату.",
|
||||
"ui.view_angles_help": "Приказује корак, скретање и ваљање приказа у степенима и његову пројекцију испод гизма оријентације. Кликните на очитање да унесете тачне углове.",
|
||||
"ui.view_angles_lock": "Закључај {axis}",
|
||||
"ui.view_angles_ortho": "Орто",
|
||||
"ui.view_angles_persp": "Перс.",
|
||||
"ui.view_angles_pitch": "Нагиб",
|
||||
"ui.view_angles_readout": "Нагиб {pitch} · Скретање {yaw} · Ваљање {roll}",
|
||||
"ui.view_angles_roll": "Ваљање",
|
||||
"ui.view_angles_unlock": "Откључај {axis}",
|
||||
"ui.view_angles_yaw": "Скретање",
|
||||
"ui.view_angles_zero": "Постави {axis} на 0°",
|
||||
"ui.view_centre": "Центар приказа",
|
||||
"ui.viewport_status_clear": "Искључи {name}",
|
||||
"ui.viewport_status_clear_hint": "Искључи {name} · Esc",
|
||||
|
||||
+70
-1
@@ -793,6 +793,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (fäste förlorat)",
|
||||
"browser.sketch_editing": "{name} (redigeras)",
|
||||
"browser.sketch_name": "Skiss{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "redan i sin del",
|
||||
"browser.sketches_to_parts_design_body": "på en kropp i själva konstruktionen",
|
||||
"browser.sketches_to_parts_moved": "Flyttade {moved} skisser till den del de ligger på",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "på en yta utan exakt fäste",
|
||||
"browser.sketches_to_parts_not_on_face": "inte på en dels yta",
|
||||
"browser.sketches_to_parts_part_missing": "deras del finns inte längre",
|
||||
"browser.sketches_to_parts_result": "Flyttade {moved} skisser till den del de ligger på; {stayed} låg kvar: {why}",
|
||||
"browser.units": "Enheter: {units}",
|
||||
"cam.flat": "Plan yta",
|
||||
"cam.follower": "Följare",
|
||||
@@ -860,6 +867,7 @@
|
||||
"cmd.component_move_bodies": "Flytta kroppar till komponent",
|
||||
"cmd.component_move_features": "Flytta funktioner till komponent",
|
||||
"cmd.component_move_sketches": "Flytta skisser till komponent",
|
||||
"cmd.component_move_sketches_to_parts": "Flytta skisser till deras delar",
|
||||
"cmd.component_paste_from_design": "Klistra in från en annan konstruktion",
|
||||
"cmd.component_paste_new": "Klistra in ny",
|
||||
"cmd.component_properties": "Komponentens fysiska egenskaper",
|
||||
@@ -1195,6 +1203,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Spline med kontrollpunkter (grad 5)",
|
||||
"cmd.sketch_spline_fit_point": "Spline med passpunkter",
|
||||
"cmd.sketch_spline_insert_point": "Lägg till splinepunkt",
|
||||
"cmd.sketch_spline_remove_point": "Ta bort splinepunkt",
|
||||
"cmd.sketch_spline_reverse": "Vänd splinens riktning",
|
||||
"cmd.sketch_spun_profile": "Roterad profil",
|
||||
"cmd.sketch_toggle_diameter": "Växla radie / diameter",
|
||||
@@ -1287,8 +1296,14 @@
|
||||
"color.hex_hint": "#rrggbb",
|
||||
"color.hue": "Nyans",
|
||||
"color.linear": "Visar färgvärden i linjär rymd 0–1",
|
||||
"color.recent": "Senaste färger",
|
||||
"color.recent_help": "Färger som du väljer i ett färgfält visas här",
|
||||
"color.saturation": "Mättnad (procent)",
|
||||
"color.saturation_value": "Mättnad och värde",
|
||||
"color.selected": "Vald färg",
|
||||
"color.step_down": "Ett steg ned",
|
||||
"color.step_up": "Ett steg upp",
|
||||
"color.value": "Värde, ljusstyrkan (procent)",
|
||||
"config.column_filter_empty": "Inga matchande kolumner",
|
||||
"contact.components_count": "{count} komponenter",
|
||||
"contact.enabled_label": "aktiverad",
|
||||
@@ -1335,6 +1350,7 @@
|
||||
"dialog.dimension_name": "Mått: {name}",
|
||||
"dialog.edit_title": "REDIGERA {title}",
|
||||
"dialog.no_shown_body": "Ingen synlig kropp att ändra: visa en, eller välj Ny kropp",
|
||||
"dialog.pattern_sketch_not_object": "Välj kroppar eller funktioner: en skiss är ingetdera. Upprepa dess kurvor med skissens egna Mönster eller Spegla, eller extrudera den först.",
|
||||
"dialog.select": "↖ Markera",
|
||||
"dialog.selected_count": "↖ {count} markerade",
|
||||
"dialog.selection_guidance": "Markera en indata för att ändra den. Klicka på markerade objekt för att ta bort dem; Ctrl/Cmd-klick växlar dem också. Granska förhandsvisningen och tryck sedan på OK.",
|
||||
@@ -1904,6 +1920,7 @@
|
||||
"nav.hfss.help": "Alt+vänsterdrag panorerar, Shift+Alt+vänster zoomar, Ctrl+vänsterdrag på tom yta orbiterar",
|
||||
"nav.inventor.help": "Mittdrag panorerar, Shift+mittdrag orbiterar, Ctrl+mittdrag zoomar, högerdrag orbiterar",
|
||||
"nav.maya.help": "Alt+vänsterdrag orbiterar, Alt+mittknapp panorerar, Alt+högerknapp zoomar; mittknappsdrag panorerar också",
|
||||
"nav.nx.help": "Mittdrag orbiterar, Shift+mittdrag panorerar (i NX: mitten + höger), Ctrl+mittdrag zoomar (i NX: mitten + vänster); högerdrag påverkar inte kameran",
|
||||
"nav.openscad.help": "Vänsterdrag på tomt utrymme orbiterar, Shift+vänsterdrag markerar med ruta, drag på skissgeometri redigerar; höger- eller mittdrag panorerar, Shift+höger- eller Shift+mittdrag zoomar",
|
||||
"nav.perspective.sketch": "Skissredigering använder ortografisk vy. Slutför skissen för att växla till perspektiv.",
|
||||
"nav.right_drag_orbits.help": "Av: ett högerdrag påverkar inte kameran och högerknappen öppnar bara snabbmenyn. Om du väljer musmappningen SolidWorks stängs det av.",
|
||||
@@ -2125,6 +2142,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Krökta ytor",
|
||||
"prefs.curves": "Krökta ytor",
|
||||
"prefs.dark_parts_shading": "Behåll formen på mörka delar",
|
||||
"prefs.dark_parts_shading_help": "Skuggar vyn i linjärt ljus med en mjuk himmelsfyllnad, så att mörka ytor behåller en gradient i stället för att bli svarta",
|
||||
"prefs.dead_zone_note": "Det skrafferade bandet är dödzonen: rörelse inom det ignoreras.",
|
||||
"prefs.default": "standard",
|
||||
"prefs.design": "Standardvärden för konstruktioner",
|
||||
@@ -2139,6 +2158,8 @@
|
||||
"prefs.edges": "Kanter och skuggning",
|
||||
"prefs.extend": "Förläng",
|
||||
"prefs.extra_navigation": "Ctrl+vänsterdrag på ett tomt område orbiterar; håll Blanksteg för att panorera.",
|
||||
"prefs.extrude_view_isometric": "Vrid vyn till isometrisk när Extrudera öppnas",
|
||||
"prefs.extrude_view_return": "Gå tillbaka till den tidigare vyn när Extrudera stängs",
|
||||
"prefs.follows_os": "Följer operativsystemet",
|
||||
"prefs.font": "Teckensnitt",
|
||||
"prefs.font_fallback": "Tecken som det valda teckensnittet saknar ersätts av det inbyggda teckensnittet, så ingen etikett visar en tom ruta.",
|
||||
@@ -2154,6 +2175,7 @@
|
||||
"prefs.graphics_card_auto": "Automatisk",
|
||||
"prefs.graphics_card_missing": "Det sparade grafikkortet är inte tillgängligt; Automatisk används.",
|
||||
"prefs.graphics_card_restart": "Träder i kraft efter att SolidCraft startats om. WGPU_ADAPTER_NAME åsidosätter detta val.",
|
||||
"prefs.graphics_card_unused": "Det sparade grafikkortet användes inte för det här fönstret. Kortet som används visas i Hjälp › Om och i statusfältet. Prova Automatisk, eller ange WGPU_ADAPTER_NAME som en unik del av kortets namn.",
|
||||
"prefs.graphics_note": "Avväg hastighet mot jämnhet. Varje val visar sin effekt på ett exempel.",
|
||||
"prefs.graphics_presets_note": "Förinställningar anger valen nedan tillsammans.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Dölj villkorssymboler vid utzoomning",
|
||||
@@ -2168,6 +2190,8 @@
|
||||
"prefs.history_sketches_note": "Med Skisser inställt på Under sin funktion listar historiken till vänster varje skiss en nivå in, under den första funktionen som byggts från den, så snart den är bred nog för namn. Tidslinjen längst ned behåller alltid skapandeordningen.",
|
||||
"prefs.history_sketches_under_feature": "Under sin funktion",
|
||||
"prefs.hold_stop_snapping": "Håll nedtryckt för att stoppa snappning",
|
||||
"prefs.home_fit_size": "Storlek för Hem och Anpassa",
|
||||
"prefs.home_fit_size_note": "Hur mycket av vyn modellen fyller när Hem, Anpassa, en öppnad konstruktion eller en vyändring i navigatorn ramar in den, oavsett om inställningen nedan är på eller inte. 100 %: fyller vyn mellan panelerna, kompassen och navigeringsfältet.",
|
||||
"prefs.icons_only": "Endast ikoner",
|
||||
"prefs.imported_file": "Importerad fil",
|
||||
"prefs.in_browser": "I webbläsaren",
|
||||
@@ -2176,13 +2200,20 @@
|
||||
"prefs.interface_language": "Gränssnittsspråk",
|
||||
"prefs.interface_note": "Hur SolidCraft ser ut och var dess delar placeras.",
|
||||
"prefs.interval_minutes": "{n} min",
|
||||
"prefs.invert_navigator_arrows": "Invertera rotationspilarna på vykuben",
|
||||
"prefs.invert_navigator_arrows_note": "Pilarna runt kuben orbiterar kameran åt det håll de pekar, som i Fusion och Inventor: pilen till vänster pekar åt höger och svänger kameran åt höger. Av: en pil roterar kuben mot den sida den sitter på, så den vänstra pilen svänger kameran åt vänster. De böjda rullningspilarna påverkas inte.",
|
||||
"prefs.keep_work_on_tab_switch": "Behåll dialoger och verktyg öppna när du byter konstruktion",
|
||||
"prefs.keep_work_on_tab_switch_note": "Varje konstruktionsflik behåller sin öppna dialog eller sitt öppna verktyg, med det du har valt och skrivit i den, och sin flik i menyfliksområdet. De finns kvar när du kommer tillbaka till konstruktionen. Av: att byta konstruktion avbryter dem och visar fliken Modell.",
|
||||
"prefs.labels": "Etiketter",
|
||||
"prefs.language_system": "System",
|
||||
"prefs.layout": "Layout",
|
||||
"prefs.layout_note": "Kompakt dockar paneler mot fönsterkanterna. Genomskinlig webbläsare finns i Flytande. Ändra assistentens sida på sidan Assistent.",
|
||||
"prefs.left": "Vänster",
|
||||
"prefs.left_click": "Vänsterklick",
|
||||
"prefs.line_drag_draws_one": "Tryck och dra ritar en linje",
|
||||
"prefs.line_drag_draws_one_note": "I verktyget Linje ritar ett tryck, ett drag och ett släpp en linje från där du tryckte till där du släppte, och där slutar den. Klick ritar fortfarande en kedja. Av: ett drag placerar en punkt där det släpps, som ett klick gör.",
|
||||
"prefs.line_hover_tangent_arc": "Vila på en linjes sista punkt för att växla till en tangentbåge",
|
||||
"prefs.line_hover_tangent_arc_note": "I verktyget Linje: när ett segment har ritats, flytta pekaren bort från kedjans sista punkt, kom tillbaka och håll pekaren stilla på den: nästa klick avslutar en tangentbåge, och samma gest går tillbaka till linjer. Av: att vila där gör ingenting.",
|
||||
"prefs.linked_designs": "Länkade konstruktioner",
|
||||
"prefs.live_input": "Liveinmatning",
|
||||
"prefs.live_preview": "Liveförhandsvisning",
|
||||
@@ -2206,6 +2237,8 @@
|
||||
"prefs.mouse_mapping": "Musmappning",
|
||||
"prefs.navigation": "Navigering",
|
||||
"prefs.navigation_note": "Hur musen, styrplattan och 3D-musen flyttar vyn.",
|
||||
"prefs.navigator_fit": "Anpassa modellen efter en vyändring i navigatorn",
|
||||
"prefs.navigator_fit_note": "En yta, ett hörn eller en pil på vykuben, eller en axel på orienteringsgizmot, tittar också mot modellens mitt från det enda avstånd som får den att rymmas från alla sidor (dess begränsningssfär). Hem använder samma zoom och centrum medan detta är på, så vyer som väljs efteråt bara vrids. Av: vyn vrids på stället och behåller sitt mål och sin zoom.",
|
||||
"prefs.network_host_remove": "Ta bort",
|
||||
"prefs.network_locations": "Tillåt nätverksplatser i konstruktioner",
|
||||
"prefs.network_locations_note": "Av: en konstruktions länkade filer på en annan dator (\\\\server\\share) öppnas bara från de servrar som listas här, eller efter att du tillåtit en när du tillfrågas.",
|
||||
@@ -2254,6 +2287,8 @@
|
||||
"prefs.render_previous_geometry_budget": "Använd tidigare geometribudget för renderingsförhandsvisning",
|
||||
"prefs.render_previous_geometry_budget_help": "Återställ den tidigare fasta geometrigränsen (512 MiB på 64-bitars dator, 128 MiB i övrigt) i stället för den enhetsbaserade budgeten.",
|
||||
"prefs.reports_saved": "{n} sparade",
|
||||
"prefs.ribbon_click_ends_tool": "Ett klick på knappen för det pågående verktyget avslutar det",
|
||||
"prefs.ribbon_click_ends_tool_note": "Ett klick på den tända knappen i menyfliksområdet för skissverktyget som används avslutar verktyget, så att klick i vyn markerar igen. Av: klicket startar verktyget igen.",
|
||||
"prefs.ribbon_reveal": "Visa sökresultat i menyfliksområdet",
|
||||
"prefs.ribbon_reveal_note": "När du håller pekaren över ett resultat i Sök kommandon visas var kommandot finns i menyfliksområdet: dess flik, dess knapp eller dess rad i en panels rullgardinsmeny. Ett kommando som startas från sökningen blinkar där, och ett pågående kommando från en rullgardinsmeny markerar sin panel.",
|
||||
"prefs.right_click": "Högerklick",
|
||||
@@ -2283,6 +2318,8 @@
|
||||
"prefs.sketch_exit_view.previous": "Återgå till föregående vy",
|
||||
"prefs.sketch_exit_view.unless_rotated": "Återgå om inte roterad",
|
||||
"prefs.sketch_exit_view_note": "Dit vyn går efter Slutför skiss, Avbryt eller en dubbel Esc-tryckning.",
|
||||
"prefs.sketch_face_activates_part": "Att skissa på en dels yta gör delen aktiv",
|
||||
"prefs.sketch_face_activates_part_note": "En skiss som påbörjas på en yta på en annan komponents kropp aktiverar först den komponenten, så att skissen tillhör den och stannar hos den delen i webbläsaren. Delen förblir aktiv efteråt. Av: skissen tillhör den aktiva komponenten.",
|
||||
"prefs.sketch_open_look_at": "Vrid vyn mot en skiss när den öppnas",
|
||||
"prefs.sketch_open_look_at_note": "Av: att skapa eller redigera en skiss lämnar vyn som den är. Titta på riktar vyn mot skissen när du vill.",
|
||||
"prefs.sketch_recent_snap": "Snappa automatiskt till senaste centrum och mittpunkter",
|
||||
@@ -2314,6 +2351,24 @@
|
||||
"prefs.status_error": "Fel",
|
||||
"prefs.status_selected": "Markerad",
|
||||
"prefs.status_warning": "Varning",
|
||||
"prefs.structure.appearance": "Utseende",
|
||||
"prefs.structure.appearance_note": "Färger, text och tillgänglighet.",
|
||||
"prefs.structure.in_profile": "I profilen",
|
||||
"prefs.structure.layout": "Layout",
|
||||
"prefs.structure.layout_note": "Var fönstrets delar sitter.",
|
||||
"prefs.structure.look": "Stil",
|
||||
"prefs.structure.measure_pointer": "Mätning och pekare",
|
||||
"prefs.structure.mouse3d": "3D-mus",
|
||||
"prefs.structure.mouse3d_note": "SpaceMouse och SpaceNavigator.",
|
||||
"prefs.structure.navigation_note": "Mus och styrplatta. 3D-musen har en egen sida.",
|
||||
"prefs.structure.profile": "Profil",
|
||||
"prefs.structure.profile_edited": "Ändrad sedan den sparades",
|
||||
"prefs.structure.profile_hover": "Sparad i inställningsprofiler",
|
||||
"prefs.structure.scale_percent": "{percent}%",
|
||||
"prefs.structure.sketch": "Skiss",
|
||||
"prefs.structure.tools": "Verktyg",
|
||||
"prefs.structure.tools_note": "Hur modelleringsverktygen beter sig.",
|
||||
"prefs.structure.work": "Arbete",
|
||||
"prefs.supersampling_brief": "Ritar vyn större och filtrerar ned den: mycket skarp, och tyngre.",
|
||||
"prefs.tab_accent_note": "Accentfärgen följer den aktiva fliken",
|
||||
"prefs.theme_file": "temafil",
|
||||
@@ -3811,6 +3866,7 @@
|
||||
"sketch_text.formula": "Börja med = för att bygga orden av parametrar: ='W' + number(Width)",
|
||||
"sketch_text.italic": "Kursiv",
|
||||
"sketch_text.loading_fonts": "Söker efter installerade teckensnitt…",
|
||||
"sketch_text.no_bold": "Det inbyggda teckensnittet har ingen fet variant: välj ett installerat teckensnitt",
|
||||
"sketch_text.on_path": "Texten följer sin bana: kurvan placerar den",
|
||||
"sketch_text.path_clear": "Ta bort texten från dess kurva",
|
||||
"sketch_text.path_hint": "En kurva i skissen som texten följer: klicka här och sedan på kurvan",
|
||||
@@ -4213,6 +4269,9 @@
|
||||
"ui.ai_permissions": "Behörigheter",
|
||||
"ui.ai_preset": "Förinställning",
|
||||
"ui.ai_protocol": "Protokoll",
|
||||
"ui.ai_reasoning_effort": "Resonemangsnivå",
|
||||
"ui.ai_reasoning_effort_field_hint": "tomt = leverantörens standard",
|
||||
"ui.ai_reasoning_effort_hint": "Valfritt. Skickas till slutpunkten som `reasoning_effort`; vilka värden som accepteras skiljer sig mellan leverantörer (till exempel `low`, `high` eller `max`). Ett värde som du anger här ersätts aldrig av SolidCraft. Lämna fältet tomt för att inte skicka något och behålla leverantörens standard.",
|
||||
"ui.ai_remove": "Ta bort",
|
||||
"ui.ai_request_timed_out": "Modellen tog längre tid än {seconds} s. Du kan öka tidsgränsen för begäran för den här slutpunkten i Inställningar › Assistent.",
|
||||
"ui.ai_request_timeout": "Tidsgräns för begäran",
|
||||
@@ -4378,6 +4437,7 @@
|
||||
"ui.dark": "Mörkt",
|
||||
"ui.default_modelling_orientation": "Standardorientering för modellering",
|
||||
"ui.delete": "Ta bort",
|
||||
"ui.delete_select_first": "Markera först vad som ska tas bort: funktioner, kroppar, komponenter eller skissgeometri",
|
||||
"ui.depth": "Djup",
|
||||
"ui.design": "Konstruktion",
|
||||
"ui.design_properties.author": "Upphovsperson",
|
||||
@@ -4470,6 +4530,7 @@
|
||||
"ui.edit_in_place_saved_elsewhere": "Delen sparades i en annan fil; sammanställningen länkar fortfarande till {name}",
|
||||
"ui.edit_in_place_wait": "Vänta tills det som körs i konstruktionen är klart",
|
||||
"ui.edit_section_analysis": "Redigera sektionsanalys",
|
||||
"ui.edit_sketch_empty": "Inga skisser att redigera",
|
||||
"ui.ellipse": "Ellips",
|
||||
"ui.emboss_wrap_note": "Ortogonala linjeprofiler som tangerar en stödd cylinder lindas automatiskt runt den. Välj en vägg för att välja ett mål.",
|
||||
"ui.end_radius": "Slutradie",
|
||||
@@ -4480,7 +4541,9 @@
|
||||
"ui.equation_explicit": "Explicit",
|
||||
"ui.equation_form": "Form",
|
||||
"ui.equation_note": "Variabeln är ett vanligt tal: sin(t) tolkar den i radianer. Ett resultat utan enhet anges i konstruktionens enhet.",
|
||||
"ui.equation_note_range": "Variabeln är en längd, liksom dess intervall: blanda den med längdparametrar och dividera den med en längd för sin(). Ett resultat utan enhet anges i konstruktionens enhet.",
|
||||
"ui.equation_parametric": "Parametrisk",
|
||||
"ui.equation_variable_unit": "Variabeln har intervallets enhet",
|
||||
"ui.equation_x_of_t": "x(t)",
|
||||
"ui.equation_y_of_t": "y(t)",
|
||||
"ui.equation_y_of_x": "y(x)",
|
||||
@@ -4655,12 +4718,14 @@
|
||||
"ui.joints": "Leder",
|
||||
"ui.keyboard_shortcuts": "Kortkommandon",
|
||||
"ui.language": "Språk",
|
||||
"ui.lasso_select": "Lasso (dra en frihandsslinga: markerar allt som ligger helt innanför)",
|
||||
"ui.layout_compact": "Kompakt",
|
||||
"ui.layout_floating": "Flytande",
|
||||
"ui.length": "Längd",
|
||||
"ui.length_decimals": "Längddecimaler",
|
||||
"ui.light": "Ljust",
|
||||
"ui.line": "Linje",
|
||||
"ui.line_arc_hint": "Tangentbåge: klicka på bågens slut. Vila på den sista punkten igen för linjer; högerklicka eller Esc för att avsluta",
|
||||
"ui.link_copies": "Länka identiska kopior",
|
||||
"ui.link_copies_keep": "Behåll",
|
||||
"ui.link_copies_link": "Länka",
|
||||
@@ -4749,6 +4814,7 @@
|
||||
"ui.part_size_shared": "Återbygger delen och ersätter eventuella tillagda funktioner. Alla förekomster av denna komponent uppdateras.",
|
||||
"ui.paste": "Klistra in",
|
||||
"ui.path": "Bana",
|
||||
"ui.pattern_object_type_tip": "Kroppar: solida former som väljs i vyn eller webbläsaren; var och en kopieras hel.\nFunktioner: steg i historiken, till exempel en extrudering eller ett hål, som väljs i historiken eller på en yta som de har skapat; steget upprepas.\nEn skiss är ingetdera: upprepa dess kurvor med skissens egna Mönster eller Spegla, eller extrudera den först.",
|
||||
"ui.pattern_on_path": "Mönster på bana",
|
||||
"ui.perforate_margin_default": "från diametern",
|
||||
"ui.perforate_pitch": "Stigning",
|
||||
@@ -4794,9 +4860,9 @@
|
||||
"ui.polygon": "Polygon",
|
||||
"ui.preferences": "Inställningar",
|
||||
"ui.press_a_key": "tryck på en tangent…",
|
||||
"ui.print_face_body_not_sent": "Den ytans kropp skickas inte: lägg först till den i Kroppar",
|
||||
"ui.print_face_cleared": "{body}: utskriftsyta rensad",
|
||||
"ui.print_face_input": "Utskriftsyta",
|
||||
"ui.print_face_one_body": "En utskriftsyta hör till en kropp: skicka den kroppen ensam för att ändra den",
|
||||
"ui.print_face_pick": "Välj en plan yta på en solid kropp för att göra den till utskriftsytan",
|
||||
"ui.print_face_set": "{body}: utskriftsyta angiven; Skicka till slicer lägger kroppen på den",
|
||||
"ui.profile": "Profil",
|
||||
@@ -5024,12 +5090,15 @@
|
||||
"ui.view_angles": "Vyvinklar",
|
||||
"ui.view_angles_convention": "Grader runt uppaxeln. Girning 0° är vyn framifrån, 90° från vänster, −90° från höger, 180° bakifrån; lutning 90° är vyn ovanifrån; en positiv rullning vrider modellen medurs.",
|
||||
"ui.view_angles_help": "Visar vyns lutning, girning och rullning i grader samt dess projektion under orienteringsgizmot. Klicka på avläsningen för att skriva in exakta vinklar.",
|
||||
"ui.view_angles_lock": "Lås {axis}",
|
||||
"ui.view_angles_ortho": "Ortog.",
|
||||
"ui.view_angles_persp": "Persp.",
|
||||
"ui.view_angles_pitch": "Lutning",
|
||||
"ui.view_angles_readout": "Lutning {pitch} · Girning {yaw} · Rullning {roll}",
|
||||
"ui.view_angles_roll": "Rullning",
|
||||
"ui.view_angles_unlock": "Lås upp {axis}",
|
||||
"ui.view_angles_yaw": "Girning",
|
||||
"ui.view_angles_zero": "Sätt {axis} till 0°",
|
||||
"ui.view_centre": "Vycentrum",
|
||||
"ui.viewport_status_clear": "Stäng av {name}",
|
||||
"ui.viewport_status_clear_hint": "Stäng av {name} · Esc",
|
||||
|
||||
+69
-1
@@ -793,6 +793,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (bağlama kayboldu)",
|
||||
"browser.sketch_editing": "{name} (düzenleniyor)",
|
||||
"browser.sketch_name": "Eskiz {number}",
|
||||
"browser.sketches_to_parts_already_in_part": "zaten kendi parçalarında",
|
||||
"browser.sketches_to_parts_design_body": "tasarımın kendi bir gövdesinde",
|
||||
"browser.sketches_to_parts_moved": "{moved} eskiz, üzerinde oldukları parçaya taşındı",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "tam bağlaması olmayan bir yüzde",
|
||||
"browser.sketches_to_parts_not_on_face": "bir parçanın yüzünde değil",
|
||||
"browser.sketches_to_parts_part_missing": "parçaları artık yok",
|
||||
"browser.sketches_to_parts_result": "{moved} eskiz, üzerinde oldukları parçaya taşındı; {stayed} yerinde kaldı: {why}",
|
||||
"browser.units": "Birimler: {units}",
|
||||
"cam.flat": "Düz yüz",
|
||||
"cam.follower": "İzleyici",
|
||||
@@ -860,6 +867,7 @@
|
||||
"cmd.component_move_bodies": "Gövdeleri bileşene taşı",
|
||||
"cmd.component_move_features": "Özellikleri bileşene taşı",
|
||||
"cmd.component_move_sketches": "Eskizleri bileşene taşı",
|
||||
"cmd.component_move_sketches_to_parts": "Eskizleri kendi parçalarına taşı",
|
||||
"cmd.component_paste_from_design": "Başka Bir Tasarımdan Yapıştır",
|
||||
"cmd.component_paste_new": "Yeni olarak yapıştır",
|
||||
"cmd.component_properties": "Bileşenin fiziksel özellikleri",
|
||||
@@ -1195,6 +1203,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Kontrol noktalı spline (derece 5)",
|
||||
"cmd.sketch_spline_fit_point": "Uydurma noktalı spline",
|
||||
"cmd.sketch_spline_insert_point": "Spline Noktası Ekle",
|
||||
"cmd.sketch_spline_remove_point": "Spline Noktasını Kaldır",
|
||||
"cmd.sketch_spline_reverse": "Spline yönünü ters çevir",
|
||||
"cmd.sketch_spun_profile": "Döndürülmüş profil",
|
||||
"cmd.sketch_toggle_diameter": "Yarıçap / Çap Arasında Geçiş Yap",
|
||||
@@ -1287,8 +1296,14 @@
|
||||
"color.hex_hint": "#rrggbb",
|
||||
"color.hue": "Ton",
|
||||
"color.linear": "Renk değerleri 0-1 doğrusal uzayda gösteriliyor",
|
||||
"color.recent": "Son kullanılan renkler",
|
||||
"color.recent_help": "Herhangi bir renk alanında seçtiğiniz renkler burada görünür",
|
||||
"color.saturation": "Doygunluk (yüzde)",
|
||||
"color.saturation_value": "Doygunluk ve değer",
|
||||
"color.selected": "Seçili renk",
|
||||
"color.step_down": "Bir adım aşağı",
|
||||
"color.step_up": "Bir adım yukarı",
|
||||
"color.value": "Değer, yani parlaklık (yüzde)",
|
||||
"config.column_filter_empty": "Eşleşen sütun yok",
|
||||
"contact.components_count": "{count} bileşen",
|
||||
"contact.enabled_label": "etkin",
|
||||
@@ -1335,6 +1350,7 @@
|
||||
"dialog.dimension_name": "Ölçü: {name}",
|
||||
"dialog.edit_title": "{title} düzenleniyor",
|
||||
"dialog.no_shown_body": "Değiştirilecek görünür gövde yok: bir gövdeyi gösterin ya da Yeni gövde seçin",
|
||||
"dialog.pattern_sketch_not_object": "Gövdeleri veya özellikleri seçin: eskiz bunlardan hiçbiri değildir. Eğrilerini eskizin kendi Dizi veya Aynala komutuyla tekrarlayın ya da önce ekstrüzyon yapın.",
|
||||
"dialog.select": "↖ Seç",
|
||||
"dialog.selected_count": "↖ {count} seçildi",
|
||||
"dialog.selection_guidance": "Değiştirmek için bir girdi seçin. Kaldırmak için seçili nesnelere tıklayın; Ctrl/Cmd+tıklama ile de seçime ekleyip çıkarabilirsiniz. Önizlemeyi kontrol edin ve ardından OK'e basın.",
|
||||
@@ -1904,6 +1920,7 @@
|
||||
"nav.hfss.help": "Alt+sol sürükleme kaydırır, Shift+Alt+sol yakınlaştırır, boş alanda Ctrl+sol sürükleme döndürür",
|
||||
"nav.inventor.help": "Orta sürükleme kaydırır, Shift+orta döndürür, Ctrl+orta yakınlaştırır, sağ sürükleme döndürür",
|
||||
"nav.maya.help": "Alt+sol sürükleme döndürür, Alt+orta kaydırır, Alt+sağ yakınlaştırır; orta sürükleme ayrıca kaydırır",
|
||||
"nav.nx.help": "Orta sürükleme döndürür, Shift+orta kaydırır (NX'te orta + sağ), Ctrl+orta yakınlaştırır (NX'te orta + sol); sağ sürükleme kameraya dokunmaz",
|
||||
"nav.openscad.help": "Boşlukta sol sürükleme döndürür, Shift+sol sürükleme kutu seçimi yapar, eskiz geometrisi sürükleyerek düzenlenir; sağ veya orta sürükleme kaydırır, Shift+sağ veya Shift+orta sürükleme yakınlaştırır",
|
||||
"nav.perspective.sketch": "Eskiz düzenleme dik görünüm kullanır. Perspektife geçmek için eskizi bitirin.",
|
||||
"nav.right_drag_orbits.help": "Kapalı: sağ sürükleme kameraya dokunmaz ve sağ tuş yalnızca bağlam menüsünü açar. SolidWorks fare eşlemesini seçmek bunu kapatır.",
|
||||
@@ -2125,6 +2142,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Eğri yüzeyler",
|
||||
"prefs.curves": "Eğri yüzeyler",
|
||||
"prefs.dark_parts_shading": "Koyu parçaların biçimini koru",
|
||||
"prefs.dark_parts_shading_help": "Görünümü doğrusal ışıkta, yumuşak bir gökyüzü dolgusuyla gölgele; böylece koyu yüzler siyaha dönmek yerine degradelerini korur",
|
||||
"prefs.dead_zone_note": "Taramalı bant ölü bölgedir: içindeki hareket yok sayılır.",
|
||||
"prefs.default": "varsayılan",
|
||||
"prefs.design": "Tasarım varsayılanları",
|
||||
@@ -2139,6 +2158,8 @@
|
||||
"prefs.edges": "Kenarlar ve gölgeleme",
|
||||
"prefs.extend": "Uzat",
|
||||
"prefs.extra_navigation": "Boşlukta Ctrl+sol sürükleme döndürür; kaydırmak için Space tuşunu basılı tutun.",
|
||||
"prefs.extrude_view_isometric": "Ekstrüzyon açıldığında görünümü izometriğe çevir",
|
||||
"prefs.extrude_view_return": "Ekstrüzyon kapandığında önceki görünüme dön",
|
||||
"prefs.follows_os": "İşletim sistemini izler",
|
||||
"prefs.font": "Yazı tipi",
|
||||
"prefs.font_fallback": "Seçilen yazı tipinde bulunmayan glifler yerleşik yazı tipine düşer, böylece hiçbir etiket boş kutu göstermez.",
|
||||
@@ -2154,6 +2175,7 @@
|
||||
"prefs.graphics_card_auto": "Otomatik",
|
||||
"prefs.graphics_card_missing": "Kaydedilen ekran kartı kullanılamıyor; Otomatik kullanılıyor.",
|
||||
"prefs.graphics_card_restart": "SolidCraft'ı yeniden başlattıktan sonra etkili olur. WGPU_ADAPTER_NAME bu seçimi geçersiz kılar.",
|
||||
"prefs.graphics_card_unused": "Kaydedilen ekran kartı bu pencere için kullanılmadı; kullanılan kart Yardım › SolidCraft hakkında içinde ve durum çubuğunda gösterilir. Otomatik seçeneğini deneyin veya WGPU_ADAPTER_NAME değerini kart adının benzersiz bir bölümüne ayarlayın.",
|
||||
"prefs.graphics_note": "Hızdan ödün verip pürüzsüzlük kazanın. Her seçenek, etkisini bir örnek üzerinde gösterir.",
|
||||
"prefs.graphics_presets_note": "Ön ayarlar aşağıdaki seçimleri birlikte belirler.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Uzaklaşıldığında kısıtlama simgelerini gizle",
|
||||
@@ -2168,6 +2190,8 @@
|
||||
"prefs.history_sketches_note": "Eskizler, Özelliklerinin altında ayarlıyken, soldaki Geçmiş, adlar için yeterince geniş olduğunda her eskizi, kendisinden oluşturulan ilk özelliğin altında bir seviye içeride listeler. Alttaki zaman çizelgesi her zaman oluşturma sırasını korur.",
|
||||
"prefs.history_sketches_under_feature": "Özelliklerinin altında",
|
||||
"prefs.hold_stop_snapping": "Yapışmayı durdurmak için basılı tutun",
|
||||
"prefs.home_fit_size": "Ana görünüm ve Sığdır boyutu",
|
||||
"prefs.home_fit_size_note": "Ana görünüm, Sığdır, açılan bir tasarım veya bir gezgin görünümü değişikliği modeli çerçevelediğinde modelin görünümün ne kadarını doldurduğu; aşağıdaki ayar açık olsa da olmasa da geçerlidir. %100: paneller, pusula ve gezinme çubuğu arasındaki görünümü doldurur.",
|
||||
"prefs.icons_only": "Yalnızca simgeler",
|
||||
"prefs.imported_file": "İçe aktarılan dosya",
|
||||
"prefs.in_browser": "Tarayıcının içinde",
|
||||
@@ -2176,13 +2200,20 @@
|
||||
"prefs.interface_language": "Arayüz dili",
|
||||
"prefs.interface_note": "SolidCraft'ın nasıl göründüğü ve parçalarının nerede durduğu.",
|
||||
"prefs.interval_minutes": "{n} dk",
|
||||
"prefs.invert_navigator_arrows": "Görünüm küpünün döndürme oklarını ters çevir",
|
||||
"prefs.invert_navigator_arrows_note": "Küpün çevresindeki oklar kamerayı gösterdikleri yönde döndürür, tıpkı Fusion ve Inventor'daki gibi: soldaki ok sağı gösterir ve kamerayı sağa savurur. Kapalı: bir ok küpü bulunduğu tarafa doğru döndürür, yani sol ok kamerayı sola savurur. Kıvrık yuvarlanma okları bundan etkilenmez.",
|
||||
"prefs.keep_work_on_tab_switch": "Tasarımlar arasında geçerken diyalogları ve araçları açık tut",
|
||||
"prefs.keep_work_on_tab_switch_note": "Her tasarım sekmesi, açık diyaloğunu veya aracını, içinde seçtiklerinizi ve yazdıklarınızı ve şerit sekmesini korur. Tasarıma döndüğünüzde yeniden oradadırlar. Kapalı: tasarımlar arasında geçmek bunları iptal eder ve Model sekmesini gösterir.",
|
||||
"prefs.labels": "Etiketler",
|
||||
"prefs.language_system": "Sistem",
|
||||
"prefs.layout": "Düzen",
|
||||
"prefs.layout_note": "Sıkışık, panelleri pencere kenarlarına yerleştirir. Tarayıcı yarı saydamlığı Yüzen düzende kullanılabilir. Asistan tarafını Asistan sayfasından değiştirin.",
|
||||
"prefs.left": "Sol",
|
||||
"prefs.left_click": "Sol tıklama",
|
||||
"prefs.line_drag_draws_one": "Basıp sürüklemek tek çizgi çizer",
|
||||
"prefs.line_drag_draws_one_note": "Çizgi aracında basmak, sürüklemek ve bırakmak, basılan yerden bırakılan yere tek bir çizgi çizer ve çizim orada biter. Tıklamalar yine bir zincir çizer. Kapalı: sürükleme, tıklama gibi, bırakıldığı yere bir nokta koyar.",
|
||||
"prefs.line_hover_tangent_arc": "Teğet yaya geçmek için bir çizginin son noktasında bekle",
|
||||
"prefs.line_hover_tangent_arc_note": "Çizgi aracında bir çizgi parçası çizildikten sonra zincirin son noktasından uzaklaşın, geri gelin ve imleci üzerinde sabit tutun: sonraki tıklama bir teğet yayı bitirir, aynı hareket çizgilere geri döner. Kapalı: orada beklemek hiçbir şey yapmaz.",
|
||||
"prefs.linked_designs": "Bağlı tasarımlar",
|
||||
"prefs.live_input": "Canlı girdi",
|
||||
"prefs.live_preview": "Canlı önizleme",
|
||||
@@ -2206,6 +2237,8 @@
|
||||
"prefs.mouse_mapping": "Fare eşlemesi",
|
||||
"prefs.navigation": "Gezinme",
|
||||
"prefs.navigation_note": "Farenin, trackpad'in ve 3B farenin görünümü nasıl hareket ettirdiği.",
|
||||
"prefs.navigator_fit": "Gezgin görünümü değiştikten sonra modeli sığdır",
|
||||
"prefs.navigator_fit_note": "Görünüm küpünün bir yüzü, köşesi veya oku ya da gizmonun bir ekseni, modelin ortasına da modeli her yönden sığdıran tek bir uzaklıktan (sınırlayıcı küresi) bakar. Bu açıkken Ana görünüm aynı yakınlaştırmayı ve merkezi kullanır; böylece sonrasında seçilen görünümler yalnızca döner. Kapalı: görünüm hedefini ve yakınlaştırmasını koruyarak yerinde döner.",
|
||||
"prefs.network_host_remove": "Kaldır",
|
||||
"prefs.network_locations": "Tasarımlarda ağ konumlarına izin ver",
|
||||
"prefs.network_locations_note": "Kapalı: bir tasarımın başka bir bilgisayardaki bağlı dosyaları (\\\\server\\paylaşım) yalnızca burada listelenen sunuculardan veya sorulduğunda izin verdiklerinizden açılır.",
|
||||
@@ -2254,6 +2287,8 @@
|
||||
"prefs.render_previous_geometry_budget": "Önceki Render önizleme geometri bütçesini kullan",
|
||||
"prefs.render_previous_geometry_budget_help": "Cihaza dayalı bütçe yerine önceki sabit geometri sınırını (64 bit masaüstünde 512 MiB, diğer yerlerde 128 MiB) geri getirir.",
|
||||
"prefs.reports_saved": "{n} kayıtlı",
|
||||
"prefs.ribbon_click_ends_tool": "Çalışan aracın düğmesine tıklamak onu bitirir",
|
||||
"prefs.ribbon_click_ends_tool_note": "Kullanımdaki eskiz aracının yanan şerit düğmesine tıklamak aracı bitirir; böylece görünümdeki tıklamalar yeniden seçim yapar. Kapalı: tıklama aracı yeniden başlatır.",
|
||||
"prefs.ribbon_reveal": "Arama sonuçlarını şeritte göster",
|
||||
"prefs.ribbon_reveal_note": "Komutlarda ara içindeki bir sonucun üzerine gelmek, komutun şeritte nerede olduğunu gösterir: sekmesini, düğmesini veya bir panelin açılır menüsündeki satırını. Aramadan başlatılan bir komut orada yanıp söner ve bir açılır menüden çalışan bir komut panelini işaretler.",
|
||||
"prefs.right_click": "Sağ tıklama",
|
||||
@@ -2283,6 +2318,8 @@
|
||||
"prefs.sketch_exit_view.previous": "Önceki görünüme dön",
|
||||
"prefs.sketch_exit_view.unless_rotated": "Döndürülmediyse dön",
|
||||
"prefs.sketch_exit_view_note": "Eskizi Bitir, İptal veya çift Esc sonrası görünümün nereye gittiği.",
|
||||
"prefs.sketch_face_activates_part": "Bir parçanın yüzüne eskiz çizmek o parçayı etkin yapar",
|
||||
"prefs.sketch_face_activates_part_note": "Başka bir bileşenin gövdesinin yüzünde başlatılan eskiz, önce o bileşeni etkinleştirir; böylece eskiz ona ait olur ve tarayıcıda o parçayla birlikte kalır. Parça sonrasında da etkin kalır. Kapalı: eskiz etkin bileşene ait olur.",
|
||||
"prefs.sketch_open_look_at": "Eskiz açıldığında görünümü ona doğru döndür",
|
||||
"prefs.sketch_open_look_at_note": "Kapalı: eskiz oluşturmak veya düzenlemek görünümü olduğu gibi bırakır. Bak komutu, istediğinizde görünümü eskize çevirir.",
|
||||
"prefs.sketch_recent_snap": "Son merkezlere ve orta noktalara otomatik yapış",
|
||||
@@ -2314,7 +2351,24 @@
|
||||
"prefs.status_error": "Hata",
|
||||
"prefs.status_selected": "Seçildi",
|
||||
"prefs.status_warning": "Uyarı",
|
||||
"prefs.structure.appearance": "Görünüm",
|
||||
"prefs.structure.appearance_note": "Renkler, metin ve erişilebilirlik.",
|
||||
"prefs.structure.in_profile": "Profilde",
|
||||
"prefs.structure.layout": "Düzen",
|
||||
"prefs.structure.layout_note": "Pencerenin parçalarının yeri.",
|
||||
"prefs.structure.look": "Görünüş",
|
||||
"prefs.structure.measure_pointer": "Ölçüm ve imleç",
|
||||
"prefs.structure.mouse3d": "3B fare",
|
||||
"prefs.structure.mouse3d_note": "SpaceMouse ve SpaceNavigator.",
|
||||
"prefs.structure.navigation_note": "Fare ve trackpad. 3B farenin kendi sayfası var.",
|
||||
"prefs.structure.profile": "Profil",
|
||||
"prefs.structure.profile_edited": "Kaydedildikten sonra değiştirildi",
|
||||
"prefs.structure.profile_hover": "Ayar profillerinde kaydedildi",
|
||||
"prefs.structure.scale_percent": "%{percent}",
|
||||
"prefs.structure.sketch": "Eskiz",
|
||||
"prefs.structure.tools": "Araçlar",
|
||||
"prefs.structure.tools_note": "Modelleme araçlarının nasıl davrandığı.",
|
||||
"prefs.structure.work": "Çalışma",
|
||||
"prefs.supersampling_brief": "Görünümü daha büyük çizip aşağı süzer: çok net, ama daha ağır.",
|
||||
"prefs.tab_accent_note": "Vurgu rengi etkin sekmeyi izler",
|
||||
"prefs.theme_file": "tema dosyası",
|
||||
@@ -3812,6 +3866,7 @@
|
||||
"sketch_text.formula": "Kelimeleri parametrelerden oluşturmak için = ile başlayın: ='W' + number(Width)",
|
||||
"sketch_text.italic": "İtalik",
|
||||
"sketch_text.loading_fonts": "Yüklü yazı tipleri aranıyor…",
|
||||
"sketch_text.no_bold": "Yerleşik yazı tipinin kalın biçimi yok: yüklü bir yazı tipi seçin",
|
||||
"sketch_text.on_path": "Metin eğrisini izler: konumunu eğri belirler",
|
||||
"sketch_text.path_clear": "Metni eğriden ayır",
|
||||
"sketch_text.path_hint": "Metnin izleyeceği eskiz eğrisi: önce buraya, ardından eğriye tıklayın",
|
||||
@@ -4214,6 +4269,9 @@
|
||||
"ui.ai_permissions": "İzinler",
|
||||
"ui.ai_preset": "Şablon",
|
||||
"ui.ai_protocol": "Protokol",
|
||||
"ui.ai_reasoning_effort": "Akıl yürütme çabası",
|
||||
"ui.ai_reasoning_effort_field_hint": "boş = sağlayıcının varsayılanı",
|
||||
"ui.ai_reasoning_effort_hint": "İsteğe bağlı. Uç noktaya `reasoning_effort` olarak gönderilir; kabul edilen değerler sağlayıcıya göre değişir (örneğin `low`, `high` veya `max`). Burada belirlediğiniz değerin yerine SolidCraft hiçbir zaman başka bir değer koymaz. Hiçbir şey göndermemek ve sağlayıcının varsayılanını korumak için boş bırakın.",
|
||||
"ui.ai_remove": "Kaldır",
|
||||
"ui.ai_request_timed_out": "Model {seconds} sn'den uzun sürdü. Bu uç nokta için istek zaman aşımını Tercihler › Asistan altında yükseltebilirsiniz.",
|
||||
"ui.ai_request_timeout": "İstek zaman aşımı",
|
||||
@@ -4379,6 +4437,7 @@
|
||||
"ui.dark": "Koyu",
|
||||
"ui.default_modelling_orientation": "Varsayılan modelleme yönü",
|
||||
"ui.delete": "Sil",
|
||||
"ui.delete_select_first": "Önce silinecekleri seçin: özellikler, gövdeler, bileşenler veya eskiz geometrisi",
|
||||
"ui.depth": "Derinlik",
|
||||
"ui.design": "Tasarım",
|
||||
"ui.design_properties.author": "Yazar",
|
||||
@@ -4471,6 +4530,7 @@
|
||||
"ui.edit_in_place_saved_elsewhere": "Parça başka bir dosyaya kaydedildi; montaj hâlâ {name} dosyasına bağlı",
|
||||
"ui.edit_in_place_wait": "Tasarımda çalışan işlem bitene kadar bekleyin",
|
||||
"ui.edit_section_analysis": "Kesit analizini düzenle",
|
||||
"ui.edit_sketch_empty": "Düzenlenecek eskiz yok",
|
||||
"ui.ellipse": "Elips",
|
||||
"ui.emboss_wrap_note": "Desteklenen tek bir silindire teğet olan dik çizgi profilleri otomatik olarak sarılır. Hedef seçmek için bir cidar seçin.",
|
||||
"ui.end_radius": "Bitiş yarıçapı",
|
||||
@@ -4481,7 +4541,9 @@
|
||||
"ui.equation_explicit": "Açık fonksiyon",
|
||||
"ui.equation_form": "Biçim",
|
||||
"ui.equation_note": "Değişken düz bir sayıdır: sin(t) onu radyan olarak alır. Birimi olmayan bir sonuç tasarımın birimindedir.",
|
||||
"ui.equation_note_range": "Değişken, aralığı gibi bir uzunluktur: uzunluk parametreleriyle birleştirin ve sin() için bir uzunluğa bölün. Birimi olmayan bir sonuç tasarımın birimindedir.",
|
||||
"ui.equation_parametric": "Parametrik",
|
||||
"ui.equation_variable_unit": "Değişken, aralığın birimine sahiptir",
|
||||
"ui.equation_x_of_t": "x(t)",
|
||||
"ui.equation_y_of_t": "y(t)",
|
||||
"ui.equation_y_of_x": "y(x)",
|
||||
@@ -4656,12 +4718,14 @@
|
||||
"ui.joints": "Bağlantılar",
|
||||
"ui.keyboard_shortcuts": "Klavye kısayolları",
|
||||
"ui.language": "Dil",
|
||||
"ui.lasso_select": "Kement (serbest bir halka sürükleyin: tamamen içinde kalanları alır)",
|
||||
"ui.layout_compact": "Kompakt",
|
||||
"ui.layout_floating": "Kayan",
|
||||
"ui.length": "Uzunluk",
|
||||
"ui.length_decimals": "Uzunluk ondalık basamakları",
|
||||
"ui.light": "Açık",
|
||||
"ui.line": "Çizgi",
|
||||
"ui.line_arc_hint": "Teğet yay: yayın ucuna tıklayın. Çizgilere dönmek için son noktada yeniden bekleyin; bitirmek için sağ tıklayın veya Esc tuşuna basın",
|
||||
"ui.link_copies": "Aynı Kopyaları Bağla",
|
||||
"ui.link_copies_keep": "Tut",
|
||||
"ui.link_copies_link": "Bağla",
|
||||
@@ -4750,6 +4814,7 @@
|
||||
"ui.part_size_shared": "Parçayı yeniden oluşturur ve eklenen tüm özelliklerin yerine geçer. Bu bileşenin tüm örnekleri güncellenir.",
|
||||
"ui.paste": "Yapıştır",
|
||||
"ui.path": "Yol",
|
||||
"ui.pattern_object_type_tip": "Gövdeler: katı biçimler, görünümde veya Tarayıcı'da seçilir; her biri bütün olarak kopyalanır.\nÖzellikler: Geçmiş'in adımları, örneğin bir ekstrüzyon veya delik; Geçmiş'te ya da oluşturdukları bir yüzde seçilir; adım tekrarlanır.\nEskiz bunlardan hiçbiri değildir: eğrilerini eskizin kendi Dizi veya Aynala komutuyla tekrarlayın ya da önce ekstrüzyon yapın.",
|
||||
"ui.pattern_on_path": "Yol üzerinde desen",
|
||||
"ui.perforate_margin_default": "çaptan",
|
||||
"ui.perforate_pitch": "Adım",
|
||||
@@ -4795,9 +4860,9 @@
|
||||
"ui.polygon": "Çokgen",
|
||||
"ui.preferences": "Ayarlar",
|
||||
"ui.press_a_key": "bir tuşa basın…",
|
||||
"ui.print_face_body_not_sent": "Bu yüzeyin gövdesi gönderilmiyor: önce onu Gövdeler'e ekleyin",
|
||||
"ui.print_face_cleared": "{body}: baskı yüzeyi temizlendi",
|
||||
"ui.print_face_input": "Baskı yüzeyi",
|
||||
"ui.print_face_one_body": "Baskı yüzeyi tek bir gövdeye aittir: değiştirmek için o gövdeyi tek başına gönderin",
|
||||
"ui.print_face_pick": "Baskı yüzeyi yapmak için katı bir gövdenin düz bir yüzeyini seçin",
|
||||
"ui.print_face_set": "{body}: baskı yüzeyi ayarlandı; Dilimleyiciye gönder gövdeyi bu yüzeyin üzerine koyar",
|
||||
"ui.profile": "Profil",
|
||||
@@ -5025,12 +5090,15 @@
|
||||
"ui.view_angles": "Görünüm açıları",
|
||||
"ui.view_angles_convention": "Yukarı eksen etrafında derece cinsinden. Sapma 0° Ön görünümdür, 90° Sol, −90° Sağ, 180° Arka; yatıklık 90° Üst görünümdür; pozitif yuvarlanma modeli saat yönünde döndürür.",
|
||||
"ui.view_angles_help": "Görünümün yatıklık, sapma ve yuvarlanma açılarını derece cinsinden ve izdüşüm türünü yönlendirme gizmosunun altında gösterir. Tam açıları yazmak için göstergeye tıklayın.",
|
||||
"ui.view_angles_lock": "{axis} değerini kilitle",
|
||||
"ui.view_angles_ortho": "Ortografik",
|
||||
"ui.view_angles_persp": "Perspektif",
|
||||
"ui.view_angles_pitch": "Yatıklık",
|
||||
"ui.view_angles_readout": "Yatıklık {pitch} · Sapma {yaw} · Yuvarlanma {roll}",
|
||||
"ui.view_angles_roll": "Yuvarlanma",
|
||||
"ui.view_angles_unlock": "{axis} değerinin kilidini aç",
|
||||
"ui.view_angles_yaw": "Sapma",
|
||||
"ui.view_angles_zero": "{axis} değerini 0° yap",
|
||||
"ui.view_centre": "Görünüm merkezi",
|
||||
"ui.viewport_status_clear": "Kapat: {name}",
|
||||
"ui.viewport_status_clear_hint": "Kapat: {name} · Esc",
|
||||
|
||||
+71
-1
@@ -793,6 +793,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (прив'язку втрачено)",
|
||||
"browser.sketch_editing": "{name} (редагується)",
|
||||
"browser.sketch_name": "Ескіз{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "уже в своїй деталі",
|
||||
"browser.sketches_to_parts_design_body": "на тілі самого проєкту",
|
||||
"browser.sketches_to_parts_moved": "Переміщено ескізів у деталь, на якій вони лежать: {moved}",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "на грані без точної прив'язки",
|
||||
"browser.sketches_to_parts_not_on_face": "не на грані деталі",
|
||||
"browser.sketches_to_parts_part_missing": "їхньої деталі більше немає",
|
||||
"browser.sketches_to_parts_result": "Переміщено ескізів у деталь, на якій вони лежать: {moved}; на місці залишилося: {stayed} — {why}",
|
||||
"browser.units": "Одиниці: {units}",
|
||||
"cam.flat": "Плоский",
|
||||
"cam.follower": "Штовхач",
|
||||
@@ -860,6 +867,7 @@
|
||||
"cmd.component_move_bodies": "Перемістити тіла до компонента",
|
||||
"cmd.component_move_features": "Перемістити операції до компонента",
|
||||
"cmd.component_move_sketches": "Перемістити ескізи до компонента",
|
||||
"cmd.component_move_sketches_to_parts": "Перемістити ескізи до їхніх деталей",
|
||||
"cmd.component_paste_from_design": "Вставити з іншого проєкту як компонент",
|
||||
"cmd.component_paste_new": "Вставити як новий",
|
||||
"cmd.component_properties": "Фізичні властивості компонента",
|
||||
@@ -1195,6 +1203,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Сплайн за контрольними точками (5-й степінь)",
|
||||
"cmd.sketch_spline_fit_point": "Сплайн за точками проходження",
|
||||
"cmd.sketch_spline_insert_point": "Додати точку сплайна",
|
||||
"cmd.sketch_spline_remove_point": "Видалити точку сплайна",
|
||||
"cmd.sketch_spline_reverse": "Обернути напрямок сплайна",
|
||||
"cmd.sketch_spun_profile": "Профіль обертання",
|
||||
"cmd.sketch_toggle_diameter": "Перемкнути радіус / діаметр",
|
||||
@@ -1287,8 +1296,14 @@
|
||||
"color.hex_hint": "#rrggbb або #rrggbbaa",
|
||||
"color.hue": "Відтінок",
|
||||
"color.linear": "Значення кольору в лінійному просторі 0-1",
|
||||
"color.recent": "Недавні кольори",
|
||||
"color.recent_help": "Кольори, які ви вибираєте в будь-якому полі кольору, з'являються тут",
|
||||
"color.saturation": "Насиченість (у відсотках)",
|
||||
"color.saturation_value": "Насиченість і яскравість",
|
||||
"color.selected": "Вибраний колір",
|
||||
"color.step_down": "Зменшити на крок",
|
||||
"color.step_up": "Збільшити на крок",
|
||||
"color.value": "Значення, тобто яскравість (у відсотках)",
|
||||
"config.column_filter_empty": "Немає збігів",
|
||||
"contact.components_count": "Компонентів: {count}",
|
||||
"contact.enabled_label": "увімкнено",
|
||||
@@ -1335,6 +1350,7 @@
|
||||
"dialog.dimension_name": "Розмір: {name}",
|
||||
"dialog.edit_title": "РЕДАГУВАННЯ {title}",
|
||||
"dialog.no_shown_body": "Немає видимих тіл для виконання операції",
|
||||
"dialog.pattern_sketch_not_object": "Виберіть тіла або операції: ескіз не є ні тим, ні іншим. Повторіть його криві власним інструментом «Масив» чи «Дзеркало» в ескізі або спершу видавіть його.",
|
||||
"dialog.select": "↖ Вибрати",
|
||||
"dialog.selected_count": "↖ Вибрано: {count}",
|
||||
"dialog.selection_guidance": "Виберіть вхідні дані, щоб змінити їх. Клацніть вибрані об'єкти, щоб прибрати їх; натискання з Ctrl/Cmd також перемикає їх. Перегляньте попередній перегляд, а потім натисніть OK.",
|
||||
@@ -1904,6 +1920,7 @@
|
||||
"nav.hfss.help": "Alt+ліва панорамує, Shift+Alt+ліва масштабує, Ctrl+ліва з порожнього місця обертає",
|
||||
"nav.inventor.help": "Середня панорамує, Shift+середня обертає, Ctrl+середня масштабує, права обертає",
|
||||
"nav.maya.help": "Alt+ліва обертає, Alt+середня панорамує, Alt+права масштабує; середня також панорамує",
|
||||
"nav.nx.help": "Середня обертає, Shift+середня панорамує (у NX — середня + права), Ctrl+середня масштабує (у NX — середня + ліва); права не рухає камеру",
|
||||
"nav.openscad.help": "Ліва обертає по порожньому місцю, Shift+ліва виділяє рамкою, геометрію ескізу перетягують для редагування; права або середня панорамує, Shift+права або Shift+середня масштабує",
|
||||
"nav.perspective.sketch": "Редагування ескізу використовує ортографічний вид. Завершіть ескіз, щоб увімкнути перспективу.",
|
||||
"nav.right_drag_orbits.help": "Перетягування правою кнопкою миші обертає вид",
|
||||
@@ -2125,6 +2142,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Криві поверхні",
|
||||
"prefs.curves": "Криві поверхні",
|
||||
"prefs.dark_parts_shading": "Зберігати форму темних деталей",
|
||||
"prefs.dark_parts_shading_help": "Затінювати вид у лінійному світлі з м'яким заповнювальним світлом неба, щоб темні грані зберігали градієнт, а не ставали чорними",
|
||||
"prefs.dead_zone_note": "Заштрихована смуга — це мертва зона: рух у її межах ігнорується.",
|
||||
"prefs.default": "типово",
|
||||
"prefs.design": "Типові параметри проєкту",
|
||||
@@ -2139,6 +2158,8 @@
|
||||
"prefs.edges": "Ребра та затінення",
|
||||
"prefs.extend": "Розширення",
|
||||
"prefs.extra_navigation": "Ctrl+перетягування лівою на порожньому місці обертає; утримуйте Space, щоб панорамувати.",
|
||||
"prefs.extrude_view_isometric": "Повертати вид до ізометрії під час відкриття «Видавлювання»",
|
||||
"prefs.extrude_view_return": "Повертати попередній вид під час закриття «Видавлювання»",
|
||||
"prefs.follows_os": "Як у системі",
|
||||
"prefs.font": "Шрифт",
|
||||
"prefs.font_fallback": "Символи, яких немає у вибраному шрифті, беруться з вбудованого, тож жоден напис не показує порожній квадрат.",
|
||||
@@ -2154,6 +2175,7 @@
|
||||
"prefs.graphics_card_auto": "Автоматично",
|
||||
"prefs.graphics_card_missing": "Збережена відеокарта недоступна; використовується режим «Автоматично».",
|
||||
"prefs.graphics_card_restart": "Набуває чинності після перезапуску SolidCraft. Змінна WGPU_ADAPTER_NAME перевизначає цей вибір.",
|
||||
"prefs.graphics_card_unused": "Збережену відеокарту для цього вікна не використано; використану відеокарту показано в меню Довідка › Про SolidCraft і в рядку стану. Спробуйте «Автоматично» або вкажіть для WGPU_ADAPTER_NAME унікальну частину назви відеокарти.",
|
||||
"prefs.graphics_note": "Обміняйте швидкість на плавність. Кожен варіант показує свій ефект на зразку.",
|
||||
"prefs.graphics_presets_note": "Набори задають наведені нижче параметри разом.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Ховати позначки обмежень на віддаленому виді",
|
||||
@@ -2168,6 +2190,8 @@
|
||||
"prefs.history_sketches_note": "Якщо для «Ескізи» вибрано «Під своєю операцією», Історія ліворуч показує кожен ескіз на один рівень глибше, під першою операцією, побудованою з нього, коли панель достатньо широка для назв. Часовий ланцюжок знизу завжди зберігає порядок створення.",
|
||||
"prefs.history_sketches_under_feature": "Під своєю операцією",
|
||||
"prefs.hold_stop_snapping": "Утримуйте, щоб вимкнути прив'язування",
|
||||
"prefs.home_fit_size": "Розмір для «Головний вид» і «Вмістити»",
|
||||
"prefs.home_fit_size_note": "Яку частину виду займає модель, коли її кадрують «Головний вид», «Вмістити», відкритий проєкт або зміна виду в навігаторі, незалежно від того, чи ввімкнено параметр нижче. 100 %: заповнює вид між панелями, компасом і панеллю навігації.",
|
||||
"prefs.icons_only": "Лише піктограми",
|
||||
"prefs.imported_file": "Імпортований файл",
|
||||
"prefs.in_browser": "У браузері",
|
||||
@@ -2176,13 +2200,20 @@
|
||||
"prefs.interface_language": "Мова інтерфейсу",
|
||||
"prefs.interface_note": "Як виглядає SolidCraft і де розташовані його частини.",
|
||||
"prefs.interval_minutes": "{n} хв",
|
||||
"prefs.invert_navigator_arrows": "Інвертувати стрілки обертання куба виду",
|
||||
"prefs.invert_navigator_arrows_note": "Стрілки навколо куба обертають камеру в той бік, куди вказують, як у Fusion та Inventor: стрілка ліворуч вказує праворуч і повертає камеру праворуч. Вимк.: стрілка обертає куб у той бік, де вона розташована, тож ліва стрілка повертає камеру ліворуч. Вигнуті стрілки крену не змінюються.",
|
||||
"prefs.keep_work_on_tab_switch": "Зберігати відкриті діалоги та інструменти під час перемикання між проєктами",
|
||||
"prefs.keep_work_on_tab_switch_note": "Кожна вкладка проєкту зберігає свій відкритий діалог чи інструмент разом із тим, що ви в ньому вибрали й ввели, а також свою вкладку панелі інструментів. Вони знову на місці, коли ви повертаєтеся до проєкту. Вимк.: перемикання проєктів скасовує їх і показує вкладку «Модель».",
|
||||
"prefs.labels": "Підписи",
|
||||
"prefs.language_system": "Системна",
|
||||
"prefs.layout": "Компонування",
|
||||
"prefs.layout_note": "«Компактне» прикріплює панелі до країв вікна. Прозорість браузера доступна в режимі «Плаваюче». Сторону Асистента можна змінити на сторінці «Асистент».",
|
||||
"prefs.left": "Ліворуч",
|
||||
"prefs.left_click": "Клацання лівою",
|
||||
"prefs.line_drag_draws_one": "Перетягування відрізка малює один елемент",
|
||||
"prefs.line_drag_draws_one_note": "Замість безперервного малювання при перетягуванні",
|
||||
"prefs.line_hover_tangent_arc": "Затриматися на останній точці прямої, щоб перейти до дотичної дуги",
|
||||
"prefs.line_hover_tangent_arc_note": "В інструменті «Пряма» після того, як сегмент накреслено, відведіть вказівник від останньої точки ланцюжка, поверніться й утримуйте його на ній нерухомо: наступне клацання завершить дотичну дугу, а той самий жест поверне до прямих. Вимк.: затримка там нічого не робить.",
|
||||
"prefs.linked_designs": "Пов'язані проєкти",
|
||||
"prefs.live_input": "Поточне введення",
|
||||
"prefs.live_preview": "Попередній перегляд у реальному часі",
|
||||
@@ -2206,6 +2237,8 @@
|
||||
"prefs.mouse_mapping": "Призначення кнопок миші",
|
||||
"prefs.navigation": "Навігація",
|
||||
"prefs.navigation_note": "Як миша, трекпад і 3D-миша рухають вид.",
|
||||
"prefs.navigator_fit": "Вміщувати модель після зміни виду в навігаторі",
|
||||
"prefs.navigator_fit_note": "Грань, кут або стрілка куба виду чи вісь гізмо також дивляться на середину моделі з єдиної відстані, з якої її видно цілком з усіх боків (її обмежувальна сфера). «Головний вид» за ввімкненого параметра використовує той самий масштаб і центр, тож види, вибрані після нього, лише повертаються. Вимк.: вид повертається на місці, зберігаючи свою ціль і масштаб.",
|
||||
"prefs.network_host_remove": "Вилучити",
|
||||
"prefs.network_locations": "Дозволяти мережеві розташування в проєктах",
|
||||
"prefs.network_locations_note": "Вимкнено: пов'язані файли проєкту на іншому комп'ютері (\\\\server\\share) відкриваються лише з перелічених тут серверів або після того, як ви дозволите один, коли запитають.",
|
||||
@@ -2254,6 +2287,8 @@
|
||||
"prefs.render_previous_geometry_budget": "Бюджет попередньої геометрії для візуалізації",
|
||||
"prefs.render_previous_geometry_budget_help": "Максимальний обсяг пам'яті для кешу геометрії попереднього стану",
|
||||
"prefs.reports_saved": "Збережено: {n}",
|
||||
"prefs.ribbon_click_ends_tool": "Клацання кнопки запущеного інструмента завершує його",
|
||||
"prefs.ribbon_click_ends_tool_note": "Клацання підсвіченої кнопки використовуваного інструмента ескізу на стрічці завершує інструмент, тож клацання на виді знову виділяють об'єкти. Вимк.: клацання знову запускає інструмент.",
|
||||
"prefs.ribbon_reveal": "Показувати результати пошуку на стрічці",
|
||||
"prefs.ribbon_reveal_note": "Наведення курсора на результат у «Пошуку команд» показує, де команда розташована на стрічці: її вкладку, кнопку або рядок у випадному меню панелі. Команда, запущена з пошуку, блимає там, а запущена команда з випадного меню позначає свою панель.",
|
||||
"prefs.right_click": "Клацання правою",
|
||||
@@ -2283,6 +2318,8 @@
|
||||
"prefs.sketch_exit_view.previous": "Повернутися до попереднього виду",
|
||||
"prefs.sketch_exit_view.unless_rotated": "Повернутися, якщо вид не оберталося",
|
||||
"prefs.sketch_exit_view_note": "Куди переходить вид після «Завершити ескіз», «Скасувати» або подвійного Esc.",
|
||||
"prefs.sketch_face_activates_part": "Ескіз на грані деталі робить цю деталь активною",
|
||||
"prefs.sketch_face_activates_part_note": "Ескіз, розпочатий на грані тіла іншого компонента, спершу активує цей компонент, тож ескіз належить йому й у браузері залишається з цією деталлю. Після цього деталь залишається активною. Вимк.: ескіз належить активному компоненту.",
|
||||
"prefs.sketch_open_look_at": "Повертати вид до ескізу під час його відкриття",
|
||||
"prefs.sketch_open_look_at_note": "Вимкнено: створення чи редагування ескізу не змінює вид. «Наведення» повертає вид до ескізу, коли ви цього захочете.",
|
||||
"prefs.sketch_recent_snap": "Автоприв'язка до нещодавніх центрів і середніх точок",
|
||||
@@ -2314,6 +2351,24 @@
|
||||
"prefs.status_error": "Помилка",
|
||||
"prefs.status_selected": "Вибрано",
|
||||
"prefs.status_warning": "Попередження",
|
||||
"prefs.structure.appearance": "Вигляд",
|
||||
"prefs.structure.appearance_note": "Кольори, текст і спеціальні можливості.",
|
||||
"prefs.structure.in_profile": "У профілі",
|
||||
"prefs.structure.layout": "Компонування",
|
||||
"prefs.structure.layout_note": "Де розташовані частини вікна.",
|
||||
"prefs.structure.look": "Оформлення",
|
||||
"prefs.structure.measure_pointer": "Вимірювання та вказівник",
|
||||
"prefs.structure.mouse3d": "3D-миша",
|
||||
"prefs.structure.mouse3d_note": "SpaceMouse і SpaceNavigator.",
|
||||
"prefs.structure.navigation_note": "Миша й трекпад. У 3D-миші є власна сторінка.",
|
||||
"prefs.structure.profile": "Профіль",
|
||||
"prefs.structure.profile_edited": "Змінено після збереження",
|
||||
"prefs.structure.profile_hover": "Збережено в профілях налаштувань",
|
||||
"prefs.structure.scale_percent": "{percent}%",
|
||||
"prefs.structure.sketch": "Ескіз",
|
||||
"prefs.structure.tools": "Інструменти",
|
||||
"prefs.structure.tools_note": "Як поводяться інструменти моделювання.",
|
||||
"prefs.structure.work": "Робота",
|
||||
"prefs.supersampling_brief": "Малює вид у більшому розмірі й зменшує його: дуже чітко, але потребує більше ресурсів.",
|
||||
"prefs.tab_accent_note": "Акцентний колір відповідає активній вкладці",
|
||||
"prefs.theme_file": "файл теми",
|
||||
@@ -3811,6 +3866,7 @@
|
||||
"sketch_text.formula": "Почніть із =, щоб скласти слова з параметрів: ='W' + number(Width)",
|
||||
"sketch_text.italic": "Курсив",
|
||||
"sketch_text.loading_fonts": "Пошук встановлених шрифтів…",
|
||||
"sketch_text.no_bold": "Вбудований шрифт не має напівжирного накреслення: виберіть встановлений шрифт",
|
||||
"sketch_text.on_path": "Текст дотримується своєї траєкторії: крива його розміщує",
|
||||
"sketch_text.path_clear": "Зняти текст з його кривої",
|
||||
"sketch_text.path_hint": "Крива ескізу, якої дотримується текст: клацніть тут, а потім на кривій",
|
||||
@@ -4213,6 +4269,9 @@
|
||||
"ui.ai_permissions": "Дозволи",
|
||||
"ui.ai_preset": "Набір",
|
||||
"ui.ai_protocol": "Протокол",
|
||||
"ui.ai_reasoning_effort": "Рівень міркувань",
|
||||
"ui.ai_reasoning_effort_field_hint": "порожньо = типове значення постачальника",
|
||||
"ui.ai_reasoning_effort_hint": "Необов'язково. Надсилається на кінцеву точку як `reasoning_effort`; допустимі значення залежать від постачальника (наприклад, `low`, `high` або `max`). Значення, яке ви тут задали, SolidCraft ніколи не замінює. Залиште поле порожнім, щоб нічого не надсилати й зберегти типове значення постачальника.",
|
||||
"ui.ai_remove": "Видалити",
|
||||
"ui.ai_request_timed_out": "Модель відповідала довше за {seconds} с. Тайм-аут запиту для цієї кінцевої точки можна збільшити в Налаштування › Асистент.",
|
||||
"ui.ai_request_timeout": "Тайм-аут запиту",
|
||||
@@ -4378,6 +4437,7 @@
|
||||
"ui.dark": "Темна",
|
||||
"ui.default_modelling_orientation": "Типова орієнтація моделювання",
|
||||
"ui.delete": "Видалити",
|
||||
"ui.delete_select_first": "Спершу виберіть, що видалити: операції, тіла, компоненти або геометрію ескізу",
|
||||
"ui.depth": "Глибина",
|
||||
"ui.design": "Проєкт",
|
||||
"ui.design_properties.author": "Автор",
|
||||
@@ -4470,6 +4530,7 @@
|
||||
"ui.edit_in_place_saved_elsewhere": "Деталь збережено в інший файл; збірка досі пов'язана з {name}",
|
||||
"ui.edit_in_place_wait": "Зачекайте, поки завершиться те, що виконується в проєкті",
|
||||
"ui.edit_section_analysis": "Редагувати аналіз перерізу",
|
||||
"ui.edit_sketch_empty": "Немає ескізів для редагування",
|
||||
"ui.ellipse": "Еліпс",
|
||||
"ui.emboss_wrap_note": "Ортогональні профілі з ліній, дотичні до одного підтримуваного циліндра, обгортаються автоматично. Виберіть стінку, щоб задати ціль.",
|
||||
"ui.end_radius": "Кінцевий радіус",
|
||||
@@ -4480,7 +4541,9 @@
|
||||
"ui.equation_explicit": "Явна",
|
||||
"ui.equation_form": "Форма",
|
||||
"ui.equation_note": "Змінна — звичайне число: sin(t) сприймає радіани. Результат без одиниць виміру береться в одиницях проєкту.",
|
||||
"ui.equation_note_range": "Змінна — довжина, як і її діапазон: поєднуйте її з параметрами довжини, а для sin() діліть її на довжину. Результат без одиниць виміру береться в одиницях проєкту.",
|
||||
"ui.equation_parametric": "Параметрична",
|
||||
"ui.equation_variable_unit": "Змінна має одиниці виміру діапазону",
|
||||
"ui.equation_x_of_t": "X(t)",
|
||||
"ui.equation_y_of_t": "Y(t)",
|
||||
"ui.equation_y_of_x": "Y(x)",
|
||||
@@ -4647,6 +4710,7 @@
|
||||
"ui.insert_design.saved_file": "{name} має незбережені зміни, тому вставлено його збережений файл. Збережіть його, потім оновіть зв'язки, щоб перенести зміни.",
|
||||
"ui.insert_design.unsaved": "Незбережені зміни",
|
||||
"ui.inspect": "Перевірити",
|
||||
"ui.instances": "Екземпляри",
|
||||
"ui.intersect": "Перетин",
|
||||
"ui.isolate": "Ізолювати",
|
||||
"ui.join": "З'єднати",
|
||||
@@ -4655,12 +4719,14 @@
|
||||
"ui.joints": "З'єднання",
|
||||
"ui.keyboard_shortcuts": "Комбінації клавіш",
|
||||
"ui.language": "Мова",
|
||||
"ui.lasso_select": "Ласо (перетягніть довільний контур: виділяє все, що повністю всередині)",
|
||||
"ui.layout_compact": "Компактне",
|
||||
"ui.layout_floating": "Плаваюче",
|
||||
"ui.length": "Довжина",
|
||||
"ui.length_decimals": "Знаки після коми для довжин",
|
||||
"ui.light": "Світла",
|
||||
"ui.line": "Пряма",
|
||||
"ui.line_arc_hint": "Дотична дуга: клацніть у кінці дуги. Знову затримайтеся на останній точці, щоб повернутися до прямих; клацніть правою кнопкою або натисніть Esc, щоб завершити",
|
||||
"ui.link_copies": "Пов'язати однакові копії",
|
||||
"ui.link_copies_keep": "Залишити",
|
||||
"ui.link_copies_link": "Пов'язати",
|
||||
@@ -4749,6 +4815,7 @@
|
||||
"ui.part_size_shared": "Перебудовує деталь, замінюючи всі додані елементи. Усі входження цього компонента оновлюються.",
|
||||
"ui.paste": "Вставити",
|
||||
"ui.path": "Траєкторія",
|
||||
"ui.pattern_object_type_tip": "Тіла: суцільні форми, які вибирають на виді або в браузері; кожне копіюється цілком.\nОперації: кроки Історії, як-от видавлювання чи отвір, які вибирають в Історії або на грані, створеній ними; крок повторюється.\nЕскіз не є ні тим, ні іншим: повторіть його криві власним інструментом «Масив» чи «Дзеркало» в ескізі або спершу видавіть його.",
|
||||
"ui.pattern_on_path": "Масив на траєкторії",
|
||||
"ui.perforate_margin_default": "від діаметра",
|
||||
"ui.perforate_pitch": "Крок",
|
||||
@@ -4794,9 +4861,9 @@
|
||||
"ui.polygon": "Багатокутник",
|
||||
"ui.preferences": "Налаштування",
|
||||
"ui.press_a_key": "натисніть клавішу…",
|
||||
"ui.print_face_body_not_sent": "Тіло цієї грані не надсилається: спершу додайте його до Тіла",
|
||||
"ui.print_face_cleared": "{body}: грань друку скинуто",
|
||||
"ui.print_face_input": "Грань друку",
|
||||
"ui.print_face_one_body": "Грань друку належить одному тілу: надішліть це тіло окремо, щоб змінити її",
|
||||
"ui.print_face_pick": "Виберіть плоску грань твердого тіла, щоб зробити її гранню друку",
|
||||
"ui.print_face_set": "{body}: грань друку задано; «Надіслати в слайсер» кладе тіло на неї",
|
||||
"ui.profile": "Профіль",
|
||||
@@ -5024,12 +5091,15 @@
|
||||
"ui.view_angles": "Кути виду",
|
||||
"ui.view_angles_convention": "Градуси навколо вертикальної осі. Рискання 0° — вид спереду, 90° — зліва, −90° — справа, 180° — ззаду; тангаж 90° — вид зверху; додатний крен повертає модель за годинниковою стрілкою.",
|
||||
"ui.view_angles_help": "Показує тангаж, рискання та крен виду в градусах, а також його проєкцію під елементом орієнтації. Клацніть показник, щоб ввести точні кути.",
|
||||
"ui.view_angles_lock": "Заблокувати {axis}",
|
||||
"ui.view_angles_ortho": "Орто",
|
||||
"ui.view_angles_persp": "Персп.",
|
||||
"ui.view_angles_pitch": "Тангаж",
|
||||
"ui.view_angles_readout": "Тангаж {pitch} · Рискання {yaw} · Крен {roll}",
|
||||
"ui.view_angles_roll": "Крен",
|
||||
"ui.view_angles_unlock": "Розблокувати {axis}",
|
||||
"ui.view_angles_yaw": "Рискання",
|
||||
"ui.view_angles_zero": "Встановити {axis} на 0°",
|
||||
"ui.view_centre": "Центр виду",
|
||||
"ui.viewport_status_clear": "Вимкнути {name}",
|
||||
"ui.viewport_status_clear_hint": "Вимкнути {name} · Esc",
|
||||
|
||||
+118
-12
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "Hình chữ nhật",
|
||||
"Rectangular Tube": "Thép hộp chữ nhật",
|
||||
"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",
|
||||
"Relief Width": "Chiều rộng rãnh thoát",
|
||||
"Remove Tools": "Loại bỏ công cụ cắt",
|
||||
@@ -494,7 +495,7 @@
|
||||
"Rename Folder": "Đổi tên thư mục",
|
||||
"Render Stage": "Giai đoạn kết xuất",
|
||||
"Reopen designs": "Mở lại thiết kế",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "Mở lại các thiết kế đã mở khi đóng SolidCraft lần trước",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "Mở lại các thiết kế đã mở lần trước",
|
||||
"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 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.",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "Mặt cắt",
|
||||
"Section (optional)": "Mặt cắt (tùy chọn)",
|
||||
"Section position": "Vị trí mặt cắt",
|
||||
"Sections": "Các mặt cắt",
|
||||
"Sections (one sketch each)": "Các mặt cắt (mỗi mặt cắt một phác thảo)",
|
||||
"Select Other": "Chọn đối tượng khác",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Chọn đối tượng khác: giữ nút chuột trái đứng yên trên mô hình (hoặc nhấp chuột phải › Chọn đối tượng khác…) để liệt kê mọi đối tượng dưới con trỏ; nhấp lại vào cùng vị trí đó để chọn đối tượng kế tiếp. Nhấp đúp chuột giữa để về Góc nhìn chuẩn (Tùy chọn cài đặt › Điều hướng).",
|
||||
@@ -793,6 +795,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (mất liên kết)",
|
||||
"browser.sketch_editing": "{name} (đang sửa)",
|
||||
"browser.sketch_name": "Phác thảo{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "đã ở trong chi tiết của chúng",
|
||||
"browser.sketches_to_parts_design_body": "nằm trên một khối của chính thiết kế",
|
||||
"browser.sketches_to_parts_moved": "Đã chuyển {moved} bản phác thảo vào chi tiết chứa chúng",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "nằm trên mặt không có liên kết chính xác",
|
||||
"browser.sketches_to_parts_not_on_face": "không nằm trên mặt của một chi tiết",
|
||||
"browser.sketches_to_parts_part_missing": "chi tiết của chúng không còn",
|
||||
"browser.sketches_to_parts_result": "Đã chuyển {moved} bản phác thảo vào chi tiết chứa chúng; {stayed} giữ nguyên: {why}",
|
||||
"browser.units": "Đơn vị: {units}",
|
||||
"cam.flat": "Bề mặt phẳng",
|
||||
"cam.follower": "Con đội cam",
|
||||
@@ -852,6 +861,7 @@
|
||||
"cmd.component_extract": "Trích xuất ra tệp riêng",
|
||||
"cmd.component_finish_edit": "Hoàn tất chỉnh sửa",
|
||||
"cmd.component_from_bodies": "Tạo cụm chi tiết từ các thân khối",
|
||||
"cmd.component_hide_history": "Ẩn lịch sử thành phần",
|
||||
"cmd.component_insert_design": "Chèn thiết kế vào cụm",
|
||||
"cmd.component_links": "Danh sách liên kết",
|
||||
"cmd.component_list": "Danh sách cụm chi tiết",
|
||||
@@ -860,6 +870,7 @@
|
||||
"cmd.component_move_bodies": "Di chuyển các thân khối vào cụm chi tiết",
|
||||
"cmd.component_move_features": "Di chuyển các tính năng vào cụm chi tiết",
|
||||
"cmd.component_move_sketches": "Di chuyển các phác thảo vào cụm chi tiết",
|
||||
"cmd.component_move_sketches_to_parts": "Chuyển bản phác thảo vào chi tiết của chúng",
|
||||
"cmd.component_paste_from_design": "Dán từ thiết kế khác",
|
||||
"cmd.component_paste_new": "Dán Tạo mới",
|
||||
"cmd.component_properties": "Thuộc tính cơ lý của cụm chi tiết",
|
||||
@@ -867,6 +878,7 @@
|
||||
"cmd.component_rename": "Đổi tên cụm chi tiết",
|
||||
"cmd.component_replace_with_link": "Thay thế bằng liên kết",
|
||||
"cmd.component_revert_position": "Hoàn nguyên vị trí cụm",
|
||||
"cmd.component_show_history": "Hiện lịch sử thành phần",
|
||||
"cmd.component_unlock": "Mở khóa nội dung cụm",
|
||||
"cmd.component_update_links": "Cập nhật liên kết",
|
||||
"cmd.config_activate": "Kích hoạt cấu hình",
|
||||
@@ -877,6 +889,7 @@
|
||||
"cmd.config_rule": "Quy tắc cấu hình",
|
||||
"cmd.config_rules": "Danh sách quy tắc cấu hình",
|
||||
"cmd.config_table": "Hiển thị bảng cấu hình",
|
||||
"cmd.construct.plane.axis_point": "Mặt phẳng qua trục và điểm",
|
||||
"cmd.construct_axis_edge": "Trục đi qua cạnh",
|
||||
"cmd.construct_axis_normal_to_face": "Trục vuông góc với bề mặt",
|
||||
"cmd.construct_axis_through_cylinder": "Trục qua hình trụ/nón/xuyến",
|
||||
@@ -925,6 +938,11 @@
|
||||
"cmd.explode_show": "Hiển thị chế độ xem phân rã",
|
||||
"cmd.expr_evaluate": "Tính giá trị biểu thức",
|
||||
"cmd.face_delete": "Xóa mặt",
|
||||
"cmd.file.export_3mf": "Xuất tệp 3MF",
|
||||
"cmd.file.export_obj": "Xuất tệp OBJ",
|
||||
"cmd.file.export_step": "Xuất tệp STEP",
|
||||
"cmd.file.export_stl": "Xuất tệp STL",
|
||||
"cmd.file.export_urdf": "Xuất tệp URDF",
|
||||
"cmd.file_export": "Xuất tệp",
|
||||
"cmd.file_insert_mesh": "Chèn lưới đa giác (Mesh)",
|
||||
"cmd.file_insert_step": "Chèn tệp STEP",
|
||||
@@ -942,6 +960,7 @@
|
||||
"cmd.inspect_colored_components": "Bật màu cụm chi tiết",
|
||||
"cmd.inspect_colored_components_disable": "Tắt màu cụm chi tiết",
|
||||
"cmd.inspect_compare": "So sánh các thân khối",
|
||||
"cmd.inspect_compare_picture": "So sánh với ảnh tham chiếu",
|
||||
"cmd.inspect_continuity": "Phân tích độ liên tục mặt",
|
||||
"cmd.inspect_curvature_comb": "Phân tích lược độ cong (Curvature Comb)",
|
||||
"cmd.inspect_curvature_map": "Phân tích bản đồ độ cong (Curvature Map)",
|
||||
@@ -1032,6 +1051,9 @@
|
||||
"cmd.pointcloud_edit": "Chỉnh sửa đám mây điểm",
|
||||
"cmd.pointcloud_insert": "Chèn đám mây điểm",
|
||||
"cmd.pointcloud_list": "Đám mây điểm",
|
||||
"cmd.render.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_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",
|
||||
@@ -1195,6 +1217,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "Đường cong Spline qua điểm điều khiển (Bậc 5)",
|
||||
"cmd.sketch_spline_fit_point": "Đường cong Spline qua các điểm",
|
||||
"cmd.sketch_spline_insert_point": "Thêm điểm vào đường cong Spline",
|
||||
"cmd.sketch_spline_remove_point": "Xóa điểm khỏi đường cong Spline",
|
||||
"cmd.sketch_spline_reverse": "Đảo ngược hướng đường cong Spline",
|
||||
"cmd.sketch_spun_profile": "Biên dạng xoay tròn (Spun Profile)",
|
||||
"cmd.sketch_toggle_diameter": "Chuyển đổi bán kính / đường kính",
|
||||
@@ -1245,6 +1268,7 @@
|
||||
"cmd.timeline_rollback": "Di chuyển con trỏ lịch sử thiết kế",
|
||||
"cmd.timeline_stop": "Dừng lịch sử",
|
||||
"cmd.timeline_suppress": "Tạm nén tính năng",
|
||||
"cmd.ui.assistant": "Bật/tắt trợ lý",
|
||||
"cmd.view.render_browser": "Trình duyệt kết xuất",
|
||||
"cmd.view.render_dock": "Di chuyển neo kết xuất",
|
||||
"cmd.view.render_focus": "Chế độ xem tập trung kết xuất",
|
||||
@@ -1279,9 +1303,11 @@
|
||||
"cmd.view_tool": "Công cụ điều hướ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",
|
||||
"color.copy": "Nhấp để sao chép giá trị màu",
|
||||
"color.edit": "Nhấp để sửa màu",
|
||||
"color.gamma": "Đang hiển thị giá trị màu trong không gian gamma 0-255",
|
||||
"color.green": "Xanh lá",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "Enter #rrggbb or #rrggbbaa; six digits preserve opacity",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1289,6 +1315,7 @@
|
||||
"color.linear": "Đang hiển thị giá trị màu trong không gian tuyến tính 0-1",
|
||||
"color.recent": "Màu gần đây",
|
||||
"color.recent_help": "Các màu bạn chọn ở bất kỳ ô màu nào sẽ hiện ở đây",
|
||||
"color.red": "Đỏ",
|
||||
"color.saturation": "Độ bão hòa (phần trăm)",
|
||||
"color.saturation_value": "Độ bão hòa và giá trị",
|
||||
"color.selected": "Màu đã chọn",
|
||||
@@ -1296,6 +1323,8 @@
|
||||
"color.step_up": "Tăng một bước",
|
||||
"color.value": "Giá trị, độ sáng (phần trăm)",
|
||||
"config.column_filter_empty": "Không có cột phù hợp",
|
||||
"config.rule_parameter_columns": "Chỉ những tham số có cột trong bảng mới được liệt kê. Thêm cột bằng “Thêm cột” (+) để dùng tham số đó ở đây.",
|
||||
"config.rule_suppression_columns": "Chỉ những tính năng có cột “Bỏ kích hoạt (Suppress)” trong bảng mới được liệt kê. Thêm cột bằng “Thêm cột” (+) để điều khiển tính năng đó ở đây.",
|
||||
"contact.components_count": "{count} chi tiết",
|
||||
"contact.enabled_label": "đã bật",
|
||||
"coupling.gear_1": "Bánh răng 1",
|
||||
@@ -1344,7 +1373,11 @@
|
||||
"dialog.pattern_sketch_not_object": "Hãy chọn thân khối hoặc tính năng: bản phác thảo không phải là cả hai. Lặp lại các đường cong của nó bằng Mảng hoặc Lấy đối xứng trong chính bản phác thảo, hoặc đùn khối nó trước.",
|
||||
"dialog.select": "↖ Chọn",
|
||||
"dialog.selected_count": "↖ {count} đã chọn",
|
||||
"dialog.selection_constraint": "Ràng buộc {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · đường cong {id}",
|
||||
"dialog.selection_guidance": "Chọn một mục đầu vào để thay đổi. Nhấp vào đối tượng đã chọn để bỏ chọn; Ctrl/Cmd-nhấp để bật/tắt chọn. Xem trước kết quả, sau đó nhấn OK.",
|
||||
"dialog.selection_midpoint": "Trung điểm · {id}",
|
||||
"dialog.selection_profile": "{sketch} · biên dạng {number}",
|
||||
"dialog.selection_step": "{step} / {total}: Chọn {input}",
|
||||
"dialog.step": "Bước",
|
||||
"document.changed_on_disk": "{name} đã thay đổi trên đĩa",
|
||||
@@ -1427,6 +1460,7 @@
|
||||
"help.construct.axis.two_points": "Tạo trục dựng hình đi qua hai điểm, đỉnh hoặc điểm phác thảo. Dùng cho trục chạy giữa hai vị trí đã biết.",
|
||||
"help.construct.plane.along_path": "Tạo mặt phẳng dựng hình vuông góc với một cạnh hoặc đường cong, tại một vị trí dọc theo nó. Dùng để phác thảo biên dạng cho Quét theo đường dẫn hoặc mặt cắt của đường dẫn.",
|
||||
"help.construct.plane.at_angle": "Tạo mặt phẳng dựng hình xoay một góc so với mặt phẳng hoặc mặt, quanh một trục hoặc cạnh. Dùng cho phác thảo nghiêng, chẳng hạn giá đỡ vát góc.",
|
||||
"help.construct.plane.axis_point": "Tạo mặt phẳng dựng chứa một trục và một điểm nằm ngoài trục đó. Chọn trục gốc, trục dựng, cạnh thẳng hoặc đường thẳng phác thảo, rồi chọn đỉnh, điểm phác thảo hoặc điểm dựng. Điểm nằm trên trục không xác định được một mặt phẳng duy nhất.",
|
||||
"help.construct.plane.midplane": "Tạo mặt phẳng dựng hình nằm giữa hai mặt phẳng, hoặc phân giác của chúng nơi chúng giao nhau theo một góc. Dùng làm mặt phẳng đối xứng khi Lấy đối xứng.",
|
||||
"help.construct.plane.offset": "Tạo mặt phẳng dựng hình song song với một mặt phẳng, cách một khoảng hoặc đi qua một điểm. Dùng để phác thảo ở độ cao xác định phía trên một mặt.",
|
||||
"help.construct.plane.perpendicular": "Tạo mặt phẳng dựng hình chứa một đường hoặc cạnh và vuông góc với một mặt phẳng hoặc mặt. Dùng để phác thảo thẳng đứng trên mô hình dọc theo một cạnh.",
|
||||
@@ -1549,7 +1583,7 @@
|
||||
"help.sketch.constrainer": "Áp dụng ràng buộc mà một hoặc hai đường cong đã chọn gần như đã thỏa, như nằm ngang, song song, tiếp tuyến hay bằng nhau. Dùng để chỉnh lại phác thảo vẽ tay từng quan hệ một.",
|
||||
"help.sketch.constraint.coincident": "Làm một điểm trùng với điểm khác, nằm trên một đường cong hoặc trục phác thảo. Dùng để đóng khe hở trong biên dạng hoặc đặt điểm lên đường thẳng.",
|
||||
"help.sketch.constraint.collinear": "Làm hai đường thẳng nằm trên cùng một đường thẳng. Dùng để căn thẳng hàng các cạnh bị ngắt quãng, như hai bên của một khe khuyết.",
|
||||
"help.sketch.constraint.concentric": "Cho hai đường tròn hoặc cung cùng một tâm. Dùng cho vòng, lỗ khoét bậc và trục có vai.",
|
||||
"help.sketch.constraint.concentric": "Cho hai đường tròn hoặc cung cùng một tâm, hoặc đặt tâm của đường tròn hay cung tại một điểm phác thảo, kể cả gốc tọa độ. Chọn cặp đối tượng theo thứ tự bất kỳ.",
|
||||
"help.sketch.constraint.curvature": "Làm hai đường cong gặp nhau tại đầu mút tiếp tục với cùng độ cong (G2). Dùng khi nối tiếp tuyến chưa đủ mượt, chẳng hạn trên các bề mặt nhìn thấy.",
|
||||
"help.sketch.constraint.equal": "Làm các đường thẳng bằng chiều dài, hoặc các đường tròn và cung bằng bán kính. Dùng để một kích thước điều khiển nhiều chi tiết giống nhau.",
|
||||
"help.sketch.constraint.fix": "Cố định điểm hoặc đường cong tại chỗ, hoặc bỏ cố định chúng. Dùng cho hình học không được di chuyển khi bạn kéo hoặc giải phác thảo.",
|
||||
@@ -1803,6 +1837,8 @@
|
||||
"joint_move.blocked_hint": "Kiểm tra loại khớp nối (chẳng hạn khớp nối cố định), trạng thái cố định của chi tiết, giới hạn khớp nối và các giá trị do tham số điều khiển.",
|
||||
"joint_move.blocked_title": "Không thể di chuyển chi tiết này",
|
||||
"joint_move.coarse": "Bước xoay khi ở gần trục (°)",
|
||||
"joint_move.conflict": "Một khớp nối của chi tiết này xung đột với các khớp nối khác: {joint}.",
|
||||
"joint_move.conflict_hint": "Chỉnh sửa, tạm nén hoặc xóa khớp nối xung đột để các khớp nối khác có thể định vị chi tiết.",
|
||||
"joint_move.distance": "Quãng đường từ vị trí bắt đầu",
|
||||
"joint_move.done": "Xong",
|
||||
"joint_move.fine": "Bước xoay khi ở xa trục (°)",
|
||||
@@ -1902,6 +1938,8 @@
|
||||
"motion.track_name": "Rãnh {number}",
|
||||
"nav.alias.help": "Shift+Alt+kéo chuột trái để xoay, Shift+Alt+chuột giữa để trượt, Shift+Alt+chuột phải để thu phóng; kéo chuột giữa cũng trượt và kéo chuột phải xoay",
|
||||
"nav.blender.help": "Kéo chuột giữa để xoay, Shift+chuột giữa để trượt, Ctrl+chuột giữa để thu phóng; kéo chuột phải giữ nguyên camera",
|
||||
"nav.creo.help": "Kéo nút giữa để xoay, Shift+nút giữa để di chuyển, Ctrl+kéo nút giữa theo chiều dọc để thu phóng (kéo xuống để phóng to); lăn con lăn để thu phóng",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "Chuột và chuột 3D xoay tự do qua cả hai cực và giữ nguyên độ lăn nghiêng (Roll). Bắt dính trục bằng phím Shift sẽ bị tắt.",
|
||||
"nav.free_orbit.label": "Xoay góc nhìn tự do",
|
||||
"nav.free_orbit.snap_help": "Chuột và chuột 3D xoay qua cả hai cực và giữ nguyên góc nghiêng cuộn. Giữ Shift trong khi kéo xoay quỹ đạo để bắt dính hướng nhìn và góc nghiêng cuộn.",
|
||||
@@ -2054,12 +2092,19 @@
|
||||
"parts.family.button_head_cap_screw": "Vít đầu tròn lục giác chìm",
|
||||
"parts.family.dowel_pin": "Chốt trụ",
|
||||
"parts.family.hex_bolt": "Bu lông đầu lục giác",
|
||||
"parts.family.hex_cap_screw_inch": "Bu lông đầu lục giác (inch)",
|
||||
"parts.family.hex_nut": "Đai ốc lục giác",
|
||||
"parts.family.hex_nut_inch": "Đai ốc lục giác (inch)",
|
||||
"parts.family.set_screw": "Vít trí lục giác chìm, đầu bằng",
|
||||
"parts.family.socket_head_cap_screw": "Bu lông lục giác chìm đầu trụ",
|
||||
"parts.family.washer": "Vòng đệm phẳng",
|
||||
"parts.family.washer_sae": "Vòng đệm phẳng, SAE (hẹp)",
|
||||
"parts.family.washer_uss": "Vòng đệm phẳng, USS (rộng)",
|
||||
"parts.holes": "Lỗ (tùy chọn)",
|
||||
"parts.placement_hint": "chọn một hoặc nhiều lỗ để lắp một chi tiết vào mỗi lỗ, hoặc nhấn OK để đặt tại gốc tọa độ",
|
||||
"parts.system": "Hệ đơn vị",
|
||||
"parts.system.inch": "Inch",
|
||||
"parts.system.metric": "Hệ mét",
|
||||
"pattern.alignment_2": "Căn chỉnh 2",
|
||||
"pattern.alignment_hint": "Số lượng bao gồm cả bản gốc. Căn giữa sẽ cân bằng các bản sao; Đổi hướng sẽ chọn phía bổ sung cho tổng số chẵn.",
|
||||
"pattern.direction_1": "Hướng 1",
|
||||
@@ -2121,6 +2166,8 @@
|
||||
"prefs.button_left": "Kéo chuột trái",
|
||||
"prefs.button_middle": "Kéo chuột giữa",
|
||||
"prefs.button_right": "Kéo chuột phải",
|
||||
"prefs.carousel_next": "Tiếp",
|
||||
"prefs.carousel_previous": "Trước",
|
||||
"prefs.choose_font": "Chọn tệp phông chữ…",
|
||||
"prefs.click": "Nhấp chuột",
|
||||
"prefs.contact_shading": "Đổ bóng tiếp xúc mềm",
|
||||
@@ -2133,6 +2180,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "Các mặt cong",
|
||||
"prefs.curves": "Các đường cong",
|
||||
"prefs.cut_preview_tint": "Hiển thị vật liệu bị loại bỏ bằng màu đỏ",
|
||||
"prefs.cut_preview_tint_help": "Bản xem trước tô vật liệu mà đặc trưng loại bỏ, và hình dạng mà nó thay thế, bằng một bóng mờ màu đỏ. Khi tắt, các phần tô này hoàn toàn trong suốt và chỉ vẽ đường viền của chúng.",
|
||||
"prefs.dark_parts_shading": "Giữ hình dạng của chi tiết tối",
|
||||
"prefs.dark_parts_shading_help": "Tô bóng khung nhìn bằng ánh sáng tuyến tính với ánh sáng bầu trời dịu, để các mặt tối giữ được dải chuyển màu thay vì thành màu đen",
|
||||
"prefs.dead_zone_note": "Dải sọc chéo là vùng chết: các chuyển động bên trong vùng này sẽ bị bỏ qua.",
|
||||
@@ -2157,7 +2206,19 @@
|
||||
"prefs.font_sample": "Kiểm tra phông chữ · Ø12.50 mm · 45.0° · 0123456789",
|
||||
"prefs.font_unreadable": "Không thể đọc tệp phông chữ đã chọn.",
|
||||
"prefs.general": "Cài đặt chung",
|
||||
"prefs.general_note": "Ngôn ngữ, lưu tệp và cập nhật.",
|
||||
"prefs.general.active": "Đang dùng",
|
||||
"prefs.general.crash_reports": "Báo cáo sự cố",
|
||||
"prefs.general.edited": "Đã sửa",
|
||||
"prefs.general.held": "Được lưu",
|
||||
"prefs.general.import": "Nhập...",
|
||||
"prefs.general.last_check": "Kiểm tra lần cuối: {time}",
|
||||
"prefs.general.minutes": "Phút",
|
||||
"prefs.general.name_setup": "Đặt tên cấu hình này",
|
||||
"prefs.general.profile_name": "Tên hồ sơ",
|
||||
"prefs.general.save_new": "Lưu thành mới",
|
||||
"prefs.general.step_import": "STEP và nhập",
|
||||
"prefs.general.this_computer": "Máy tính này",
|
||||
"prefs.general_note": "Hồ sơ, ngôn ngữ, tệp và cập nhật.",
|
||||
"prefs.gesture_orbit": "Xoay góc nhìn",
|
||||
"prefs.gesture_pan": "Trượt góc nhìn",
|
||||
"prefs.gesture_zoom": "Thu phóng",
|
||||
@@ -2191,6 +2252,8 @@
|
||||
"prefs.interface_language": "Ngôn ngữ giao diện",
|
||||
"prefs.interface_note": "Diện mạo của SolidCraft và cách bố trí các bảng điều khiển.",
|
||||
"prefs.interval_minutes": "{n} phút",
|
||||
"prefs.invert_curved_roll_arrows": "Đảo ngược mũi tên cuộn cong",
|
||||
"prefs.invert_curved_roll_arrows_note": "Hai mũi tên cong ở góc khối cuộn khung nhìn trong mặt phẳng màn hình. Khi bật: mũi tên theo chiều kim đồng hồ cuộn ngược chiều kim đồng hồ và ngược lại. Khi tắt: mỗi mũi tên cuộn khung nhìn theo chiều cong của nó.",
|
||||
"prefs.invert_navigator_arrows": "Đảo ngược mũi tên xoay của khối điều hướng",
|
||||
"prefs.invert_navigator_arrows_note": "Các mũi tên quanh khối xoay camera theo hướng mũi tên chỉ, như trong Fusion và Inventor: mũi tên bên trái chỉ sang phải và đưa camera sang phải. Khi tắt: mũi tên xoay khối về phía cạnh chứa nó, nên mũi tên bên trái đưa camera sang trái. Hai mũi tên cuộn cong không bị ảnh hưởng.",
|
||||
"prefs.keep_work_on_tab_switch": "Giữ hộp thoại và công cụ mở khi chuyển giữa các thiết kế",
|
||||
@@ -2212,10 +2275,10 @@
|
||||
"prefs.logo_accent": "Màu nhấn",
|
||||
"prefs.logo_theme": "Màu của chủ đề",
|
||||
"prefs.max_file_size": "Kích thước tệp tối đa để mở",
|
||||
"prefs.max_file_size_design": "Tệp thiết kế",
|
||||
"prefs.max_file_size_design": "Tệp lớn nhất có thể mở: Thiết kế",
|
||||
"prefs.max_file_size_flag": "Lần chạy này bắt đầu với --max-file-size {size}, có hiệu lực đến khi đóng SolidCraft. Các cài đặt này áp dụng khi khởi động không có tùy chọn đó.",
|
||||
"prefs.max_file_size_note": "Mặc định: 512 MB và 256 MB; tối đa 16 GB. Mở tệp cần lượng bộ nhớ gấp nhiều lần kích thước tệp. Thiết kế giữ dữ liệu nhập lớn có thể cần giới hạn tệp thiết kế cao hơn để lưu và mở lại.",
|
||||
"prefs.max_file_size_step": "Tệp STEP và tệp nhập",
|
||||
"prefs.max_file_size_step": "Tệp lớn nhất có thể mở: STEP và nhập",
|
||||
"prefs.max_file_size_web": "Ứng dụng web mở tệp STEP và tệp nhập tối đa 512 MB, thiết kế tối đa 256 MB: thẻ trình duyệt không chứa được tệp lớn hơn. Ứng dụng máy tính cho phép tăng các giới hạn này.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "Biểu tượng lớn",
|
||||
@@ -2232,7 +2295,7 @@
|
||||
"prefs.navigator_fit_note": "Nhấp vào một mặt, góc hoặc mũi tên của khối lập phương, hoặc một trục của gizmo, cũng nhìn vào tâm mô hình từ một khoảng cách duy nhất chứa trọn nó từ mọi phía (khối cầu bao). Khi bật, góc nhìn chuẩn (Home) dùng cùng mức thu phóng và tâm đó, nên các góc nhìn chọn sau chỉ xoay. Tắt: góc nhìn xoay tại chỗ, giữ nguyên điểm nhìn và mức thu phóng.",
|
||||
"prefs.network_host_remove": "Xóa",
|
||||
"prefs.network_locations": "Cho phép các vị trí mạng trong thiết kế",
|
||||
"prefs.network_locations_note": "Khi tắt: các tệp liên kết của thiết kế trên máy tính khác (\\\\server\\share) chỉ được mở từ các máy chủ được liệt kê tại đây, hoặc sau khi bạn đồng ý khi được hỏi.",
|
||||
"prefs.network_locations_note": "Dành cho tổ chức lưu thiết kế trên máy chủ tệp. Khi tắt: chỉ các máy chủ bạn cho phép.",
|
||||
"prefs.network_locations_section": "Vị trí mạng",
|
||||
"prefs.not_on_gpu": "GPU này không hỗ trợ",
|
||||
"prefs.on": "Bật",
|
||||
@@ -2250,15 +2313,17 @@
|
||||
"prefs.placement": "Vị trí đặt",
|
||||
"prefs.prefer_free_sketch_points": "Ưu tiên điểm phác thảo có thể di chuyển khi các điểm chồng lên nhau",
|
||||
"prefs.preview_choices": "Xem trước các lựa chọn của bạn",
|
||||
"prefs.preview_tint_new_faces": "Tô màu bề mặt mới trong bản xem trước",
|
||||
"prefs.preview_tint_new_faces_help": "Bản xem trước của một đặc trưng làm thay đổi một khối có thể vẽ bề mặt được thêm vào bằng màu xem trước. Khi tắt, khối được vẽ bằng màu của chính nó.",
|
||||
"prefs.print_face_highlight": "Tô sáng mặt in",
|
||||
"prefs.print_face_highlight_tip": "Mức tô màu và viền mặt in của thân trong khung nhìn. 0 % không tô sáng.",
|
||||
"prefs.profile_active": "Hồ sơ đang dùng",
|
||||
"prefs.profile_applied": "Đã áp dụng hồ sơ.",
|
||||
"prefs.profile_changed": "Cài đặt đã thay đổi kể từ khi lưu hồ sơ. “Lưu” sẽ ghi chúng vào hồ sơ.",
|
||||
"prefs.profile_deleted": "Đã xóa hồ sơ.",
|
||||
"prefs.profile_export": "Xuất tệp…",
|
||||
"prefs.profile_export": "Xuất",
|
||||
"prefs.profile_exported": "Đã xuất hồ sơ.",
|
||||
"prefs.profile_imported": "Đã nhập hồ sơ. Chọn hồ sơ trong “Hồ sơ đang dùng” để chuyển sang.",
|
||||
"prefs.profile_imported": "Đã nhập hồ sơ.",
|
||||
"prefs.profile_name_hint": "Tên hồ sơ, ví dụ Laptop",
|
||||
"prefs.profile_rename": "Đổi tên",
|
||||
"prefs.profile_renamed": "Đã đổi tên hồ sơ.",
|
||||
@@ -2278,12 +2343,13 @@
|
||||
"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}",
|
||||
"prefs.restore_page_defaults": "Khôi phục mặc định của trang",
|
||||
"prefs.ribbon_click_ends_tool": "Nhấp vào nút của công cụ đang chạy sẽ kết thúc nó",
|
||||
"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.right_click": "Nhấp chuột phải",
|
||||
"prefs.saving": "Tự động lưu và khôi phục",
|
||||
"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%.",
|
||||
"prefs.scale_text": "Tỉ lệ và văn bản",
|
||||
"prefs.search": "Tìm kiếm cài đặt",
|
||||
@@ -2547,6 +2613,10 @@
|
||||
"render.base_color": "Màu cơ sở · sRGB",
|
||||
"render.base_metallic": "Tỷ lệ kim loại",
|
||||
"render.base_opaque_only": "Trình chỉnh sửa này giữ nguyên ngữ nghĩa khối đục. Dùng các điều khiển thủy tinh riêng biệt để chỉnh độ truyền quang học.",
|
||||
"render.batch_all_shots": "Mọi cảnh chụp ({count})",
|
||||
"render.batch_empty": "Hãy lưu một cảnh chụp trước khi kết xuất mọi cảnh chụp.",
|
||||
"render.batch_limit": "Kết xuất mọi cảnh chụp giới hạn ở 32 cảnh chụp.",
|
||||
"render.batch_status": "{running} đang chạy · {waiting} đang chờ",
|
||||
"render.bloom_enable": "Hiển thị lóe sáng (Bloom)",
|
||||
"render.bloom_help": "Hiệu ứng phát sáng sáng tạo cho đầu ra từ các điểm nhấn tuyến tính dương, được khuếch tán qua ba lượt lọc chuẩn hóa. Bán kính tính theo pixel đầu ra. Được cộng vào ánh sáng tuyến tính hiển thị trước sRGB; không phải ống kính thực tế, hiệu ứng thể tích hay cải thiện hội tụ. Tệp EXR thô không đổi.",
|
||||
"render.bloom_opaque": "Hiệu ứng lóe sáng yêu cầu đầu ra đục. PNG trong suốt sẽ bị từ chối chứ không âm thầm làm phẳng nền.",
|
||||
@@ -2657,6 +2727,13 @@
|
||||
"render.choose_look": "Hãy chọn một diện mạo trong Diện mạo trước",
|
||||
"render.chromatic_offset": "Độ lệch sắc sai viền",
|
||||
"render.clear_stage": "Xóa sân khấu đã lưu",
|
||||
"render.compare_divider": "Vạch so sánh",
|
||||
"render.compare_live": "Trực tiếp",
|
||||
"render.compare_live_preview": "Xem trước trực tiếp",
|
||||
"render.compare_render": "Kết xuất {number}",
|
||||
"render.compare_side": "Kết xuất {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} giây · {renderer} · ghi tại bản sửa {revision}",
|
||||
"render.compare_swap": "Hoán đổi",
|
||||
"render.copy_request": "Sao chép JSON",
|
||||
"render.cpu_aa_help": "Số mẫu không gian: tăng số mẫu giúp cạnh mượt hơn nhưng tăng thời gian kết xuất.",
|
||||
"render.cpu_area_add": "Thêm nguồn sáng vùng chữ nhật CPU",
|
||||
@@ -3406,6 +3483,10 @@
|
||||
"render.result_retained": "Đang hiển thị hình ảnh hoàn thành gần nhất; kết xuất mới hơn chưa thay thế nó.",
|
||||
"render.result_sequence": "Điểm kiểm tra trung gian",
|
||||
"render.result_title": "Kết quả kết xuất",
|
||||
"render.results_choose": "Kết quả được giữ lại",
|
||||
"render.results_label": "Kết xuất {number} · {shot} · {seconds} giây",
|
||||
"render.results_stale_label": "{result} [cũ]",
|
||||
"render.results_unnamed": "Ảnh",
|
||||
"render.return_design": "Quay lại thiết kế",
|
||||
"render.review_stale": "Kết quả xem xét này đã cũ. Hãy xử lý hoặc kiểm tra lại trước khi áp dụng.",
|
||||
"render.ribbon_aging": "Lão hóa",
|
||||
@@ -4066,6 +4147,7 @@
|
||||
"ui.agent_chat_auth_terminal": "Chạy lệnh này trong cửa sổ dòng lệnh, sau đó nhấp Kết nối lại:",
|
||||
"ui.agent_chat_auth_title": "Đăng nhập vào AI Agent",
|
||||
"ui.agent_chat_cancel": "Hủy bỏ",
|
||||
"ui.agent_chat_cancel_timeout": "Tác nhân không phản hồi yêu cầu Dừng nên đã bị ngắt kết nối. Hãy kết nối lại để tiếp tục.",
|
||||
"ui.agent_chat_cannot_start": "Đã cài đặt nhưng không khởi động được: {error}",
|
||||
"ui.agent_chat_changes": "Thay đổi: {n}, trong 1 bước hoàn tác",
|
||||
"ui.agent_chat_changes_n": "{n} thay đổi",
|
||||
@@ -4075,11 +4157,13 @@
|
||||
"ui.agent_chat_command": "Lệnh của AI Agent",
|
||||
"ui.agent_chat_connect": "Kết nối",
|
||||
"ui.agent_chat_copy": "Sao chép",
|
||||
"ui.agent_chat_copy_message": "Sao chép tin nhắn",
|
||||
"ui.agent_chat_ctx_bodies": "Khối",
|
||||
"ui.agent_chat_ctx_elements": "Phần tử",
|
||||
"ui.agent_chat_ctx_features": "Tính năng",
|
||||
"ui.agent_chat_ctx_more": "+{n} mục khác",
|
||||
"ui.agent_chat_ctx_other": "Khác",
|
||||
"ui.agent_chat_custom_heading": "Mô hình tùy chỉnh",
|
||||
"ui.agent_chat_custom_hint": "Bất kỳ chương trình nào hỗ trợ giao thức Agent Client Protocol qua stdin và stdout. Không chạy qua shell hệ thống.",
|
||||
"ui.agent_chat_denied": "Đã từ chối",
|
||||
"ui.agent_chat_deny": "Từ chối",
|
||||
@@ -4123,8 +4207,16 @@
|
||||
"ui.agent_chat_mode_all": "Tất cả",
|
||||
"ui.agent_chat_mode_ask": "Hỏi",
|
||||
"ui.agent_chat_mode_edits": "Chỉnh sửa",
|
||||
"ui.agent_chat_model_custom": "Dùng tùy chỉnh: {model}",
|
||||
"ui.agent_chat_model_no_tools": "{model} không thể sử dụng các công cụ của SolidCraft nên chỉ có thể trò chuyện. Hãy chọn mô hình có hỗ trợ công cụ.",
|
||||
"ui.agent_chat_model_rejected": "Tác tử không chấp nhận {model}: {error}",
|
||||
"ui.agent_chat_model_search": "Tìm mô hình…",
|
||||
"ui.agent_chat_model_tip": "Mô hình AI đang sử dụng. Lựa chọn của bạn sẽ được ghi nhớ cho Agent này.",
|
||||
"ui.agent_chat_models_failed": "Không thể lấy danh sách mô hình: {error}",
|
||||
"ui.agent_chat_models_heading": "Các mô hình có sẵn",
|
||||
"ui.agent_chat_models_listing": "Đang lấy danh sách mô hình…",
|
||||
"ui.agent_chat_models_none": "Điểm cuối không liệt kê mô hình nào",
|
||||
"ui.agent_chat_models_not_a_list": "Phản hồi không phải là danh sách mô hình",
|
||||
"ui.agent_chat_new": "Cuộc trò chuyện mới",
|
||||
"ui.agent_chat_new_confirm": "Bắt đầu cuộc trò chuyện mới? Cuộc trò chuyện hiện tại sẽ bị xóa.",
|
||||
"ui.agent_chat_next_picture": "Ảnh kế tiếp",
|
||||
@@ -4386,6 +4478,8 @@
|
||||
"ui.coming_later": "sắp ra mắt",
|
||||
"ui.command_done": "Hoàn tất lệnh {command}",
|
||||
"ui.command_palette_hint": "Nhập một lệnh, ví dụ: {example} — hoặc nhấn S để tìm kiếm",
|
||||
"ui.components_from_bodies_hint": "Tạo một thành phần cho mỗi vật thể đã chọn, đặt tên theo vật thể đó, bên dưới thành phần đang hoạt động.",
|
||||
"ui.components_from_bodies_required": "Chọn ít nhất một vật thể.",
|
||||
"ui.concentric": "Đồng tâm",
|
||||
"ui.cone": "Hình nón",
|
||||
"ui.conic_curve": "Đường cong Conic",
|
||||
@@ -4398,9 +4492,11 @@
|
||||
"ui.construction.contact_hint": "Tiếp tuyến: chọn đường tròn/cung hoặc spline/conic hở rồi chọn tiếp điểm (đường cong tự do: chỉ đầu mút)",
|
||||
"ui.construction.external_hint": "Tiếp tuyến: chọn đường tròn hoặc cung, điểm ngoài rồi chọn nghiệm mong muốn",
|
||||
"ui.construction.free_hint": "Đường dựng hình: nhấp hai điểm; nhấp chuột phải để bắt đầu lại",
|
||||
"ui.construction.normal_needs_length": "Đường vô hạn luôn là đường dựng hình: nhập chiều dài để vẽ đường thường",
|
||||
"ui.construction.point_hint": "Đường dựng hình: nhấp điểm đi qua",
|
||||
"ui.construction.reference_hint": "Đường dựng hình: chọn đường thẳng rồi chọn điểm; nhấp chuột phải để đổi tham chiếu",
|
||||
"ui.construction.ribbon_label": "Đường phụ",
|
||||
"ui.construction.side_hint": "Đường dựng hình: nhấp phía mà đường kéo dài tới; nhấp chuột phải để bắt đầu lại",
|
||||
"ui.context_menu": "Menu ngữ cảnh",
|
||||
"ui.continuity_g0": "Nếp gập G0",
|
||||
"ui.continuity_g1": "G1 tiếp tuyến",
|
||||
@@ -4666,6 +4762,7 @@
|
||||
"ui.hideOrigin": "Ẩn Gốc tọa độ",
|
||||
"ui.hidePlanes": "Ẩn các mặt phẳng",
|
||||
"ui.hideProfile": "Ẩn biên dạng",
|
||||
"ui.hide_component_history": "Ẩn lịch sử của nó",
|
||||
"ui.hide_origin_by_default": "Mặc định ẩn gốc tọa độ",
|
||||
"ui.hide_origin_note": "Bản vẽ mới bắt đầu với thư mục Gốc tọa độ được ẩn. Thiết kế sẽ giữ lại cài đặt Gốc tọa độ riêng sau khi bạn ẩn hoặc hiện nó.",
|
||||
"ui.history_back": "Lùi một bước",
|
||||
@@ -4673,6 +4770,7 @@
|
||||
"ui.history_edge_hint": "nhấp đúp vào cạnh",
|
||||
"ui.history_first": "Về đầu",
|
||||
"ui.history_forward": "Tiến một bước",
|
||||
"ui.history_hide_hidden": "Ẩn các thành phần bị ẩn",
|
||||
"ui.history_hide_names": "Ẩn tên",
|
||||
"ui.history_last": "Đến cuối",
|
||||
"ui.history_marker_tip": "Điểm đánh dấu lịch sử: kéo để lùi mô hình về bước trước; các tính năng mới sẽ được thêm vào đây",
|
||||
@@ -4682,10 +4780,12 @@
|
||||
"ui.history_play_button_show": "Hiện nút Phát",
|
||||
"ui.history_play_finish_action": "Hoàn tất thao tác hiện tại trước khi phát lịch sử",
|
||||
"ui.history_play_roll_back": "Đưa điểm đánh dấu lịch sử lùi lại trước khi phát",
|
||||
"ui.history_show_hidden": "Hiện các thành phần bị ẩn",
|
||||
"ui.history_show_names": "Hiện tên",
|
||||
"ui.history_sketch_listed_under": "Được liệt kê dưới {feature}",
|
||||
"ui.history_title": "Lịch sử",
|
||||
"ui.history_toggle_names": "Hiện hoặc ẩn tên tính năng",
|
||||
"ui.history_waits_for_editor": "Hoàn tác, Làm lại và Lịch sử sẽ chờ cho đến khi trình chỉnh sửa tính năng mở xong",
|
||||
"ui.hole": "Tạo lỗ",
|
||||
"ui.hole_tap_type": "Kiểu ta-rô ren lỗ",
|
||||
"ui.hole_type": "Loại lỗ",
|
||||
@@ -4701,6 +4801,7 @@
|
||||
"ui.insert_design.saved_file": "{name} có thay đổi chưa lưu, nên tệp đã lưu của nó đã được chèn. Hãy lưu nó, rồi dùng Cập nhật liên kết để đưa các thay đổi vào.",
|
||||
"ui.insert_design.unsaved": "Thay đổi chưa lưu",
|
||||
"ui.inspect": "Kiểm tra",
|
||||
"ui.instances": "Số bản",
|
||||
"ui.intersect": "Giao nhau",
|
||||
"ui.isolate": "Cô lập đối tượng",
|
||||
"ui.join": "Nối liền",
|
||||
@@ -4851,9 +4952,9 @@
|
||||
"ui.polygon": "Đa giác",
|
||||
"ui.preferences": "Tùy chọn cài đặt",
|
||||
"ui.press_a_key": "nhấn một phím…",
|
||||
"ui.print_face_body_not_sent": "Thân của mặt này không được gửi: hãy thêm nó vào Thân khối trước",
|
||||
"ui.print_face_cleared": "{body}: đã xóa mặt in",
|
||||
"ui.print_face_input": "Mặt in",
|
||||
"ui.print_face_one_body": "Mặt in được chọn cho một thân: hãy gửi riêng thân đó để thay đổi",
|
||||
"ui.print_face_pick": "Chọn một mặt phẳng của thân đặc để đặt làm mặt in",
|
||||
"ui.print_face_set": "{body}: đã đặt mặt in; Gửi đến Slicer sẽ đặt thân lên mặt này",
|
||||
"ui.profile": "Biên dạng",
|
||||
@@ -4965,6 +5066,7 @@
|
||||
"ui.showDimension": "Hiển thị Kích thước",
|
||||
"ui.showOrigin": "Hiển thị Gốc tọa độ",
|
||||
"ui.showPlanes": "Hiển thị các mặt phẳng",
|
||||
"ui.show_component_history": "Hiện lịch sử của nó",
|
||||
"ui.show_constraints": "Các ràng buộc",
|
||||
"ui.show_construction_geometries": "Đối tượng dựng hình (nét đứt)",
|
||||
"ui.show_dimensions": "Các kích thước",
|
||||
@@ -4996,7 +5098,7 @@
|
||||
"ui.slicer_no_response": "{slicer} không phản hồi kịp thời. Tệp đã được lưu tại {path}: hãy gửi lại sau khi phần mềm cắt lớp khởi động xong.",
|
||||
"ui.slicer_resend_hint": "Gửi lại sẽ thay thế tệp đang mở trong phần mềm cắt lớp.",
|
||||
"ui.slicer_single_instance": "Mở tệp trong phần mềm cắt lớp đang chạy sẵn",
|
||||
"ui.slicer_single_instance_tip": "PrusaSlicer, SuperSlicer và Cura sẽ tải tệp vào cửa sổ đang mở thay vì khởi động cửa sổ mới. Các phần mềm cắt lớp khác được khởi động như bình thường. PrusaSlicer 3.0 luôn hoạt động theo cách này: trình khởi chạy của nó không thể nhận tệp theo cách nào khác.",
|
||||
"ui.slicer_single_instance_tip": "Dành cho phần mềm cắt lớp tải tệp vào cửa sổ đang chạy.",
|
||||
"ui.slicer_starting": "Đang khởi động {slicer}…",
|
||||
"ui.slicer_still_starting": "Vẫn đang chờ {slicer} khởi động. Chưa có dữ liệu nào khác được gửi.",
|
||||
"ui.slicer_stl_fallback": "Không thể ghi tệp 3MF nên định dạng xuất là STL.",
|
||||
@@ -5081,12 +5183,15 @@
|
||||
"ui.view_angles": "Góc hướng nhìn",
|
||||
"ui.view_angles_convention": "Độ quanh trục hướng lên. Lệch 0° là góc nhìn phía trước, 90° bên trái, −90° bên phải, 180° phía sau; chúc 90° là góc nhìn từ trên; nghiêng dương xoay mô hình trên màn hình theo chiều kim đồng hồ.",
|
||||
"ui.view_angles_help": "Hiển thị góc chúc, góc lệch và góc nghiêng của góc nhìn theo độ cùng kiểu chiếu, bên dưới gizmo định hướng. Bấm vào phần hiển thị để nhập góc chính xác.",
|
||||
"ui.view_angles_lock": "Khóa {axis}",
|
||||
"ui.view_angles_ortho": "Trực giao",
|
||||
"ui.view_angles_persp": "Phối cảnh",
|
||||
"ui.view_angles_pitch": "Chúc",
|
||||
"ui.view_angles_readout": "Chúc {pitch} · Lệch {yaw} · Nghiêng {roll}",
|
||||
"ui.view_angles_roll": "Nghiêng",
|
||||
"ui.view_angles_unlock": "Mở khóa {axis}",
|
||||
"ui.view_angles_yaw": "Lệch",
|
||||
"ui.view_angles_zero": "Đặt {axis} về 0°",
|
||||
"ui.view_centre": "Tâm góc nhìn",
|
||||
"ui.viewport_status_clear": "Tắt {name}",
|
||||
"ui.viewport_status_clear_hint": "Tắt {name} · Esc",
|
||||
@@ -5138,7 +5243,7 @@
|
||||
"update.available": "Đã có phiên bản SolidCraft {version}",
|
||||
"update.button": "Cập nhật",
|
||||
"update.button_version": "Cập nhật lên {version}",
|
||||
"update.check_automatically": "Tự động kiểm tra bản cập nhật",
|
||||
"update.check_automatically": "Kiểm tra khi khởi động và mỗi ngày một lần",
|
||||
"update.check_for_updates": "Kiểm tra bản cập nhật",
|
||||
"update.check_now": "Kiểm tra ngay",
|
||||
"update.check_unavailable": "Không thể kiểm tra bản cập nhật cho bản cài đặt này.",
|
||||
@@ -5188,6 +5293,7 @@
|
||||
"web.open_link": "Mở thiết kế liên kết?",
|
||||
"web.open_link_confirm": "Có những thay đổi chưa lưu. Mở thiết kế liên kết trong tab mới?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ chọn · Enter / Tab hoàn tất · Esc bỏ qua",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ để chọn · Enter để áp dụng giá trị này",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ để chọn · Enter để áp dụng nội dung đã nhập · Esc để đóng",
|
||||
"⌘Enter continues on GitHub.": "Nhấn ⌘Enter để tiếp tục trên GitHub."
|
||||
}
|
||||
|
||||
@@ -793,6 +793,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (附著遺失)",
|
||||
"browser.sketch_editing": "{name} (編輯中)",
|
||||
"browser.sketch_name": "草圖{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "已在所屬零件中",
|
||||
"browser.sketches_to_parts_design_body": "位於設計本身的實體上",
|
||||
"browser.sketches_to_parts_moved": "已將 {moved} 個草圖移至其所在的零件",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "位於沒有精確附著的面上",
|
||||
"browser.sketches_to_parts_not_on_face": "不在零件的面上",
|
||||
"browser.sketches_to_parts_part_missing": "所屬零件已不存在",
|
||||
"browser.sketches_to_parts_result": "已將 {moved} 個草圖移至其所在的零件;{stayed} 個未移動:{why}",
|
||||
"browser.units": "單位: {units}",
|
||||
"cam.flat": "平底",
|
||||
"cam.follower": "從動件",
|
||||
@@ -860,6 +867,7 @@
|
||||
"cmd.component_move_bodies": "將實體移至元件",
|
||||
"cmd.component_move_features": "將特徵移至元件",
|
||||
"cmd.component_move_sketches": "將草圖移至元件",
|
||||
"cmd.component_move_sketches_to_parts": "將草圖移至所屬零件",
|
||||
"cmd.component_paste_from_design": "從其他設計貼上",
|
||||
"cmd.component_paste_new": "貼上為新物件",
|
||||
"cmd.component_properties": "元件物理屬性",
|
||||
@@ -1195,6 +1203,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "控制點雲形線(5 次)",
|
||||
"cmd.sketch_spline_fit_point": "擬合點雲形線",
|
||||
"cmd.sketch_spline_insert_point": "新增雲形線點",
|
||||
"cmd.sketch_spline_remove_point": "刪除雲形線點",
|
||||
"cmd.sketch_spline_reverse": "反向雲形線方向",
|
||||
"cmd.sketch_spun_profile": "旋轉輪廓",
|
||||
"cmd.sketch_toggle_diameter": "切換半徑 / 直徑",
|
||||
@@ -1287,8 +1296,14 @@
|
||||
"color.hex_hint": "#rrggbb",
|
||||
"color.hue": "色相",
|
||||
"color.linear": "以 0–1 線性空間顯示顏色值",
|
||||
"color.recent": "最近使用的顏色",
|
||||
"color.recent_help": "您在任何顏色欄位中選擇的顏色會顯示於此",
|
||||
"color.saturation": "飽和度(百分比)",
|
||||
"color.saturation_value": "飽和度和明度",
|
||||
"color.selected": "所選顏色",
|
||||
"color.step_down": "減少",
|
||||
"color.step_up": "增加",
|
||||
"color.value": "明度,即亮度(百分比)",
|
||||
"config.column_filter_empty": "沒有符合的欄",
|
||||
"contact.components_count": "{count} 個元件",
|
||||
"contact.enabled_label": "已啟用",
|
||||
@@ -1335,6 +1350,7 @@
|
||||
"dialog.dimension_name": "尺寸:{name}",
|
||||
"dialog.edit_title": "編輯 {title}",
|
||||
"dialog.no_shown_body": "沒有可變更的已顯示實體:請顯示一個,或選擇「新建實體」",
|
||||
"dialog.pattern_sketch_not_object": "請選取實體或特徵:草圖兩者皆非。請用草圖自己的「陣列」或「鏡射」重複其曲線,或先將它擠出。",
|
||||
"dialog.select": "↖ 選擇",
|
||||
"dialog.selected_count": "↖ 已選擇 {count} 個",
|
||||
"dialog.selection_guidance": "選擇輸入項進行修改。點擊已選物件可取消選擇;按住 Ctrl/Cmd 點擊也可切換選擇。檢查預覽後,再按「確定」。",
|
||||
@@ -1796,6 +1812,8 @@
|
||||
"joint_move.blocked_hint": "請檢查接頭類型(例如剛性)、固定狀態、接頭限制,以及由參數驅動的值。",
|
||||
"joint_move.blocked_title": "無法移動此零件",
|
||||
"joint_move.coarse": "靠近軸心時的旋轉步距(°)",
|
||||
"joint_move.conflict": "此零件的一個接頭與其他接頭衝突:{joint}。",
|
||||
"joint_move.conflict_hint": "請編輯、抑制或刪除衝突的接頭,讓其他接頭可以放置此零件。",
|
||||
"joint_move.distance": "與起點的距離",
|
||||
"joint_move.done": "完成",
|
||||
"joint_move.fine": "遠離軸心時的旋轉步距(°)",
|
||||
@@ -1904,6 +1922,7 @@
|
||||
"nav.hfss.help": "Alt+左鍵拖曳可平移,Shift+Alt+左鍵可縮放,在空白處按 Ctrl+左鍵拖曳可環轉",
|
||||
"nav.inventor.help": "中鍵拖曳可平移,Shift+中鍵可環繞,Ctrl+中鍵可縮放,右鍵拖曳可環繞",
|
||||
"nav.maya.help": "Alt+左鍵拖曳可環轉,Alt+中鍵可平移,Alt+右鍵可縮放;中鍵拖曳同樣可平移",
|
||||
"nav.nx.help": "中鍵拖曳可環繞,Shift+中鍵可平移(NX 的中鍵 + 右鍵),Ctrl+中鍵可縮放(NX 的中鍵 + 左鍵);右鍵拖曳不會移動相機",
|
||||
"nav.openscad.help": "在空白處以左鍵拖曳可環繞,Shift+左鍵拖曳可框選,拖曳草圖幾何可編輯;右鍵或中鍵拖曳可平移,Shift+右鍵或 Shift+中鍵拖曳可縮放",
|
||||
"nav.perspective.sketch": "編輯草圖時會使用正交檢視。完成草圖後才能切換為透視。",
|
||||
"nav.right_drag_orbits.help": "關閉:右鍵拖曳不會移動相機,右鍵只會開啟右鍵選單。選擇 SolidWorks 滑鼠對應會將其關閉。",
|
||||
@@ -2125,6 +2144,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "曲面",
|
||||
"prefs.curves": "曲面",
|
||||
"prefs.dark_parts_shading": "保留深色零件的形狀",
|
||||
"prefs.dark_parts_shading_help": "以線性光搭配柔和的天空補光對檢視進行著色,讓深色的面保有漸層,而不會變成全黑",
|
||||
"prefs.dead_zone_note": "斜線區域為死區:此範圍內的移動會被忽略。",
|
||||
"prefs.default": "預設",
|
||||
"prefs.design": "設計預設值",
|
||||
@@ -2139,6 +2160,8 @@
|
||||
"prefs.edges": "邊與著色",
|
||||
"prefs.extend": "延伸",
|
||||
"prefs.extra_navigation": "在空白處按住 Ctrl 並以左鍵拖曳可環轉;按住空白鍵可平移。",
|
||||
"prefs.extrude_view_isometric": "開啟「擠出」時將檢視轉為等角視圖",
|
||||
"prefs.extrude_view_return": "關閉「擠出」時回到先前的檢視",
|
||||
"prefs.follows_os": "跟隨作業系統",
|
||||
"prefs.font": "字型",
|
||||
"prefs.font_fallback": "所選字型缺少的字元會改用內建字型,因此標籤不會出現空白方框。",
|
||||
@@ -2169,6 +2192,8 @@
|
||||
"prefs.history_sketches_note": "「草圖」設為「位於其特徵下」時,左側的歷程記錄在寬度足以顯示名稱時,會把每個草圖縮排一層,列在由它建立的第一個特徵之下。底部的時間軸一律保持建立順序。",
|
||||
"prefs.history_sketches_under_feature": "位於其特徵下",
|
||||
"prefs.hold_stop_snapping": "按住以停用鎖點",
|
||||
"prefs.home_fit_size": "主檢視與符合檢視的大小",
|
||||
"prefs.home_fit_size_note": "在主檢視、符合檢視、開啟設計或檢視導覽器切換檢視而為模型取景時,模型佔滿檢視的程度,不論下方的設定是否開啟。100 %:填滿面板、羅盤與導覽列之間的檢視區域。",
|
||||
"prefs.icons_only": "僅圖示",
|
||||
"prefs.imported_file": "已匯入的檔案",
|
||||
"prefs.in_browser": "瀏覽器內",
|
||||
@@ -2177,13 +2202,20 @@
|
||||
"prefs.interface_language": "介面語言",
|
||||
"prefs.interface_note": "SolidCraft 的外觀與各面板的位置。",
|
||||
"prefs.interval_minutes": "{n} 分鐘",
|
||||
"prefs.invert_navigator_arrows": "反轉檢視立方體的旋轉箭頭",
|
||||
"prefs.invert_navigator_arrows_note": "立方體周圍的箭頭會依其所指方向環繞相機,與 Fusion 和 Inventor 相同:左側箭頭指向右方,並將相機向右擺動。關閉時:箭頭會將立方體轉向它所在的一側,因此左側箭頭會將相機向左擺動。彎曲的滾轉箭頭不受影響。",
|
||||
"prefs.keep_work_on_tab_switch": "切換設計時保持對話方塊與工具開啟",
|
||||
"prefs.keep_work_on_tab_switch_note": "每個設計分頁都會保留其開啟的對話方塊或工具、您在其中選取與輸入的內容,以及其工具列索引標籤。回到該設計時,它們會恢復原狀。關閉時:切換設計會取消它們,並顯示「模型」索引標籤。",
|
||||
"prefs.labels": "標籤",
|
||||
"prefs.language_system": "系統",
|
||||
"prefs.layout": "配置",
|
||||
"prefs.layout_note": "精簡模式會將面板停靠在視窗邊緣。瀏覽器半透明僅適用於浮動模式。助理的位置請在「助理」頁面中變更。",
|
||||
"prefs.left": "左側",
|
||||
"prefs.left_click": "左鍵點擊",
|
||||
"prefs.line_drag_draws_one": "按下並拖曳繪製一條直線",
|
||||
"prefs.line_drag_draws_one_note": "在「直線」工具中,按下、拖曳再放開,會從按下處到放開處繪製一條直線,並在該處結束。按一下仍會繪製連續線段。關閉:拖曳會在放開處放置一個點,與按一下相同。",
|
||||
"prefs.line_hover_tangent_arc": "停在直線的最後一點上即可切換為相切圓弧",
|
||||
"prefs.line_hover_tangent_arc_note": "在「直線」工具中繪製一段後,移離連續線段的最後一點,再回來並將指標停在該點上:下一次點擊會決定相切圓弧的終點,同樣的動作則會切回直線。關閉時:停在該處不會有任何作用。",
|
||||
"prefs.linked_designs": "連結設計",
|
||||
"prefs.live_input": "即時輸入",
|
||||
"prefs.live_preview": "即時預覽",
|
||||
@@ -2207,6 +2239,8 @@
|
||||
"prefs.mouse_mapping": "滑鼠對應",
|
||||
"prefs.navigation": "導覽",
|
||||
"prefs.navigation_note": "滑鼠、觸控板和 3D 滑鼠移動檢視的方式。",
|
||||
"prefs.navigator_fit": "變更導覽器檢視後使模型符合視圖",
|
||||
"prefs.navigator_fit_note": "檢視立方體的面、角或箭頭,或方向控制器的某個軸,也會從能容納整個模型各側的單一距離(其邊界球)看向模型中心。啟用時,「主檢視」使用相同的縮放與中心,因此之後選擇的檢視只會旋轉。關閉時:檢視原地旋轉,保留其目標與縮放。",
|
||||
"prefs.network_host_remove": "移除",
|
||||
"prefs.network_locations": "允許設計中的網路位置",
|
||||
"prefs.network_locations_note": "關閉:設計中位於其他電腦 (\\\\server\\share) 上的連結檔案,只會從此處列出的伺服器開啟,或在詢問時由您允許某個伺服器後才開啟。",
|
||||
@@ -2255,6 +2289,8 @@
|
||||
"prefs.render_previous_geometry_budget": "使用先前的彩現預覽幾何預算",
|
||||
"prefs.render_previous_geometry_budget_help": "還原先前固定的幾何上限(64 位元桌面版為 512 MiB,其他平台為 128 MiB),而非依裝置決定的預算。",
|
||||
"prefs.reports_saved": "已儲存 {n} 份",
|
||||
"prefs.ribbon_click_ends_tool": "按一下執行中工具的按鈕即可結束該工具",
|
||||
"prefs.ribbon_click_ends_tool_note": "按一下使用中草圖工具亮起的功能區按鈕會結束該工具,讓在檢視中的點擊恢復為選取。關閉時:點擊會重新啟動該工具。",
|
||||
"prefs.ribbon_reveal": "在功能區中顯示搜尋結果",
|
||||
"prefs.ribbon_reveal_note": "在「搜尋命令」中將指標停留在結果上,會顯示該命令在功能區中的位置:其索引標籤、按鈕,或面板下拉選單中的列。從搜尋啟動的命令會在該處閃爍,而從下拉選單執行中的命令會標示其所在面板。",
|
||||
"prefs.right_click": "右鍵點擊",
|
||||
@@ -2284,6 +2320,8 @@
|
||||
"prefs.sketch_exit_view.previous": "返回先前的檢視",
|
||||
"prefs.sketch_exit_view.unless_rotated": "未旋轉時返回",
|
||||
"prefs.sketch_exit_view_note": "「完成草圖」、取消或連按兩次 Esc 後檢視的去向。",
|
||||
"prefs.sketch_face_activates_part": "在零件的面上繪製草圖會使該零件成為作用中",
|
||||
"prefs.sketch_face_activates_part_note": "在其他元件之實體的面上開始的草圖,會先啟用該元件,因此草圖屬於該元件,並在瀏覽器中與該零件放在一起。之後該零件仍保持作用中。關閉時:草圖屬於目前作用中的元件。",
|
||||
"prefs.sketch_open_look_at": "開啟草圖時轉向正對草圖",
|
||||
"prefs.sketch_open_look_at_note": "關閉:建立或編輯草圖時維持目前的檢視。需要時可用「注視」正對草圖。",
|
||||
"prefs.sketch_recent_snap": "自動鎖點至最近使用的圓心與中點",
|
||||
@@ -2315,6 +2353,24 @@
|
||||
"prefs.status_error": "錯誤",
|
||||
"prefs.status_selected": "已選取",
|
||||
"prefs.status_warning": "警告",
|
||||
"prefs.structure.appearance": "外觀",
|
||||
"prefs.structure.appearance_note": "顏色、文字與無障礙功能。",
|
||||
"prefs.structure.in_profile": "在設定檔中",
|
||||
"prefs.structure.layout": "配置",
|
||||
"prefs.structure.layout_note": "視窗各部分的位置。",
|
||||
"prefs.structure.look": "風格",
|
||||
"prefs.structure.measure_pointer": "測量與指標",
|
||||
"prefs.structure.mouse3d": "3D 滑鼠",
|
||||
"prefs.structure.mouse3d_note": "SpaceMouse 與 SpaceNavigator。",
|
||||
"prefs.structure.navigation_note": "滑鼠與觸控板。3D 滑鼠有專屬頁面。",
|
||||
"prefs.structure.profile": "設定檔",
|
||||
"prefs.structure.profile_edited": "儲存後已變更",
|
||||
"prefs.structure.profile_hover": "儲存於設定檔中",
|
||||
"prefs.structure.scale_percent": "{percent}%",
|
||||
"prefs.structure.sketch": "草圖",
|
||||
"prefs.structure.tools": "工具",
|
||||
"prefs.structure.tools_note": "建模工具的行為方式。",
|
||||
"prefs.structure.work": "工作",
|
||||
"prefs.supersampling_brief": "以更高解析度繪製檢視再縮小:非常清晰,但負載較高。",
|
||||
"prefs.tab_accent_note": "強調色跟隨目前的分頁",
|
||||
"prefs.theme_file": "主題檔案",
|
||||
@@ -3812,6 +3868,7 @@
|
||||
"sketch_text.formula": "以 = 開頭,可由參數組出文字:='W' + number(Width)",
|
||||
"sketch_text.italic": "斜體",
|
||||
"sketch_text.loading_fonts": "正在查詢已安裝字型…",
|
||||
"sketch_text.no_bold": "內建字型沒有粗體:請選擇已安裝的字型",
|
||||
"sketch_text.on_path": "文字沿路徑排列:由曲線放置",
|
||||
"sketch_text.path_clear": "將文字移離其曲線",
|
||||
"sketch_text.path_hint": "文字所跟隨的草圖曲線:先點擊此處,再選擇曲線",
|
||||
@@ -4214,6 +4271,9 @@
|
||||
"ui.ai_permissions": "權限",
|
||||
"ui.ai_preset": "預設",
|
||||
"ui.ai_protocol": "通訊協定",
|
||||
"ui.ai_reasoning_effort": "推理強度",
|
||||
"ui.ai_reasoning_effort_field_hint": "留空 = 供應商預設值",
|
||||
"ui.ai_reasoning_effort_hint": "可選。會以 `reasoning_effort` 傳送給端點;可接受的值因供應商而異(例如 `low`、`high` 或 `max`)。您在此設定的值絕不會被 SolidCraft 取代。留空則不傳送任何內容,並沿用供應商的預設值。",
|
||||
"ui.ai_remove": "移除",
|
||||
"ui.ai_request_timed_out": "模型耗時超過 {seconds} s。您可以在「偏好設定 › 助理」中調高此端點的請求逾時。",
|
||||
"ui.ai_request_timeout": "請求逾時",
|
||||
@@ -4379,6 +4439,7 @@
|
||||
"ui.dark": "深色",
|
||||
"ui.default_modelling_orientation": "預設建模方向",
|
||||
"ui.delete": "刪除",
|
||||
"ui.delete_select_first": "請先選取要刪除的項目:特徵、實體、元件或草圖幾何",
|
||||
"ui.depth": "深度",
|
||||
"ui.design": "設計",
|
||||
"ui.design_properties.author": "作者",
|
||||
@@ -4471,6 +4532,7 @@
|
||||
"ui.edit_in_place_saved_elsewhere": "零件已儲存到另一個檔案;組合件仍連結 {name}",
|
||||
"ui.edit_in_place_wait": "請等待設計中正在執行的作業完成",
|
||||
"ui.edit_section_analysis": "編輯截面分析",
|
||||
"ui.edit_sketch_empty": "沒有可編輯的草圖",
|
||||
"ui.ellipse": "橢圓",
|
||||
"ui.emboss_wrap_note": "與受支援圓柱相切的正交線輪廓會自動纏繞。選取一個壁以指定目標。",
|
||||
"ui.end_radius": "終點半徑",
|
||||
@@ -4481,7 +4543,9 @@
|
||||
"ui.equation_explicit": "顯式",
|
||||
"ui.equation_form": "形式",
|
||||
"ui.equation_note": "變數為純數字:sin(t) 以弧度計算。沒有單位的結果會使用設計的單位。",
|
||||
"ui.equation_note_range": "變數為長度,其範圍亦同:可與長度參數混合運算,用於 sin() 時請除以一個長度。沒有單位的結果會使用設計的單位。",
|
||||
"ui.equation_parametric": "參數式",
|
||||
"ui.equation_variable_unit": "變數使用範圍的單位",
|
||||
"ui.equation_x_of_t": "x(t)",
|
||||
"ui.equation_y_of_t": "y(t)",
|
||||
"ui.equation_y_of_x": "y(x)",
|
||||
@@ -4648,6 +4712,7 @@
|
||||
"ui.insert_design.saved_file": "{name} 有未儲存的變更,因此插入的是其已儲存的檔案。請儲存後執行「更新連結」以帶入變更。",
|
||||
"ui.insert_design.unsaved": "未儲存的變更",
|
||||
"ui.inspect": "檢查",
|
||||
"ui.instances": "實例數",
|
||||
"ui.intersect": "相交",
|
||||
"ui.isolate": "隔離",
|
||||
"ui.join": "接合",
|
||||
@@ -4656,12 +4721,14 @@
|
||||
"ui.joints": "接頭",
|
||||
"ui.keyboard_shortcuts": "鍵盤快捷鍵",
|
||||
"ui.language": "語言",
|
||||
"ui.lasso_select": "套索(拖曳出手繪的封閉圈:選取完全位於圈內的項目)",
|
||||
"ui.layout_compact": "精簡",
|
||||
"ui.layout_floating": "浮動",
|
||||
"ui.length": "長度",
|
||||
"ui.length_decimals": "長度小數位數",
|
||||
"ui.light": "淺色",
|
||||
"ui.line": "直線",
|
||||
"ui.line_arc_hint": "相切圓弧:點擊圓弧的終點。再次停在最後一點上可回到直線;按一下右鍵或按 Esc 結束",
|
||||
"ui.link_copies": "連結相同的副本",
|
||||
"ui.link_copies_keep": "保留",
|
||||
"ui.link_copies_link": "連結",
|
||||
@@ -4750,6 +4817,7 @@
|
||||
"ui.part_size_shared": "重建零件並取代額外新增的特徵。此元件的所有實例都會更新。",
|
||||
"ui.paste": "貼上",
|
||||
"ui.path": "路徑",
|
||||
"ui.pattern_object_type_tip": "實體:實心形狀,在檢視或瀏覽器中選取;每個都會整個複製。\n特徵:歷程記錄中的步驟,例如擠出或孔,在歷程記錄中或其所建立的面上選取;該步驟會被重複。\n草圖兩者皆非:請用草圖自己的「陣列」或「鏡射」重複其曲線,或先將它擠出。",
|
||||
"ui.pattern_on_path": "路徑陣列",
|
||||
"ui.perforate_margin_default": "依直徑決定",
|
||||
"ui.perforate_pitch": "間距",
|
||||
@@ -4795,9 +4863,9 @@
|
||||
"ui.polygon": "多邊形",
|
||||
"ui.preferences": "偏好設定",
|
||||
"ui.press_a_key": "按一個鍵…",
|
||||
"ui.print_face_body_not_sent": "此面的實體不會傳送:請先將其加入實體",
|
||||
"ui.print_face_cleared": "{body}:已清除列印底面",
|
||||
"ui.print_face_input": "列印底面",
|
||||
"ui.print_face_one_body": "列印底面屬於單一實體:請單獨傳送該實體以變更它",
|
||||
"ui.print_face_pick": "選取實體的一個平面,將其設為列印底面",
|
||||
"ui.print_face_set": "{body}:已設定列印底面;傳送到切片軟體時實體會放在此面上",
|
||||
"ui.profile": "輪廓",
|
||||
@@ -5025,12 +5093,15 @@
|
||||
"ui.view_angles": "檢視角度",
|
||||
"ui.view_angles_convention": "以向上軸為準的角度(單位為度)。偏航 0° 為前視圖,90° 為左視圖,−90° 為右視圖,180° 為後視圖;俯仰 90° 為俯視圖;滾轉為正值時,模型順時針轉動。",
|
||||
"ui.view_angles_help": "在方向控制器下方顯示檢視的俯仰、偏航與滾轉角度(單位為度)及其投影方式。按一下讀值即可輸入精確角度。",
|
||||
"ui.view_angles_lock": "鎖定 {axis}",
|
||||
"ui.view_angles_ortho": "正交",
|
||||
"ui.view_angles_persp": "透視",
|
||||
"ui.view_angles_pitch": "俯仰",
|
||||
"ui.view_angles_readout": "俯仰 {pitch} · 偏航 {yaw} · 滾轉 {roll}",
|
||||
"ui.view_angles_roll": "滾轉",
|
||||
"ui.view_angles_unlock": "解鎖 {axis}",
|
||||
"ui.view_angles_yaw": "偏航",
|
||||
"ui.view_angles_zero": "將 {axis} 設為 0°",
|
||||
"ui.view_centre": "檢視中心",
|
||||
"ui.viewport_status_clear": "關閉 {name}",
|
||||
"ui.viewport_status_clear_hint": "關閉 {name} · Esc",
|
||||
|
||||
+118
-12
@@ -477,6 +477,7 @@
|
||||
"Rectangular": "矩形",
|
||||
"Rectangular Tube": "矩形管",
|
||||
"Reference face": "参考面",
|
||||
"Reference plane": "参考平面",
|
||||
"Relief Depth": "止裂槽深度",
|
||||
"Relief Width": "止裂槽宽度",
|
||||
"Remove Tools": "移除工具",
|
||||
@@ -494,7 +495,7 @@
|
||||
"Rename Folder": "重命名文件夹",
|
||||
"Render Stage": "渲染阶段",
|
||||
"Reopen designs": "重新打开设计",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "重新打开上次关闭 SolidCraft 时打开的设计",
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "重新打开上次打开的设计",
|
||||
"Replace existing library file": "替换现有库文件",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "报告缺陷 — 复核截图并开启 GitHub 草稿",
|
||||
"Report copied. Keep this text to submit on GitHub when you are online.": "报告已复制。请保留此文本,联网后在 GitHub 上提交。",
|
||||
@@ -556,6 +557,7 @@
|
||||
"Section": "剖面",
|
||||
"Section (optional)": "截面(可选)",
|
||||
"Section position": "截面位置",
|
||||
"Sections": "截面",
|
||||
"Sections (one sketch each)": "截面(每个一个草图)",
|
||||
"Select Other": "选择其他",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "“选择其他”:在模型上按住左键(或右键单击 › 选择其他…)列出光标下的所有对象;在同一位置再次单击可选择该处的下一个对象。双击中键转到主视图(首选项 › 导航)。",
|
||||
@@ -793,6 +795,13 @@
|
||||
"browser.sketch_attachment_lost": "{name} (附着丢失)",
|
||||
"browser.sketch_editing": "{name} (编辑中)",
|
||||
"browser.sketch_name": "草图{number}",
|
||||
"browser.sketches_to_parts_already_in_part": "已在其零件中",
|
||||
"browser.sketches_to_parts_design_body": "位于设计本身的实体上",
|
||||
"browser.sketches_to_parts_moved": "已将 {moved} 个草图移到其所在的零件",
|
||||
"browser.sketches_to_parts_no_exact_attachment": "位于没有精确附着的面上",
|
||||
"browser.sketches_to_parts_not_on_face": "不在零件的面上",
|
||||
"browser.sketches_to_parts_part_missing": "其零件已不存在",
|
||||
"browser.sketches_to_parts_result": "已将 {moved} 个草图移到其所在的零件;{stayed} 个保留原处:{why}",
|
||||
"browser.units": "单位: {units}",
|
||||
"cam.flat": "平底",
|
||||
"cam.follower": "从动件",
|
||||
@@ -852,6 +861,7 @@
|
||||
"cmd.component_extract": "提取到文件",
|
||||
"cmd.component_finish_edit": "完成编辑",
|
||||
"cmd.component_from_bodies": "由实体创建组件",
|
||||
"cmd.component_hide_history": "隐藏组件历史记录",
|
||||
"cmd.component_insert_design": "插入设计",
|
||||
"cmd.component_links": "列出链接",
|
||||
"cmd.component_list": "列出组件",
|
||||
@@ -860,6 +870,7 @@
|
||||
"cmd.component_move_bodies": "将实体移至组件",
|
||||
"cmd.component_move_features": "将特征移至组件",
|
||||
"cmd.component_move_sketches": "将草图移至组件",
|
||||
"cmd.component_move_sketches_to_parts": "将草图移到各自的零件",
|
||||
"cmd.component_paste_from_design": "从其他设计粘贴",
|
||||
"cmd.component_paste_new": "粘贴为新对象",
|
||||
"cmd.component_properties": "组件物理属性",
|
||||
@@ -867,6 +878,7 @@
|
||||
"cmd.component_rename": "重命名组件",
|
||||
"cmd.component_replace_with_link": "替换为链接",
|
||||
"cmd.component_revert_position": "恢复位置",
|
||||
"cmd.component_show_history": "显示组件历史记录",
|
||||
"cmd.component_unlock": "解锁内容",
|
||||
"cmd.component_update_links": "更新链接",
|
||||
"cmd.config_activate": "激活配置",
|
||||
@@ -877,6 +889,7 @@
|
||||
"cmd.config_rule": "配置规则",
|
||||
"cmd.config_rules": "配置规则",
|
||||
"cmd.config_table": "显示配置表",
|
||||
"cmd.construct.plane.axis_point": "通过轴和点的平面",
|
||||
"cmd.construct_axis_edge": "过边的轴",
|
||||
"cmd.construct_axis_normal_to_face": "垂直于面的轴",
|
||||
"cmd.construct_axis_through_cylinder": "过圆柱/圆锥/圆环的轴",
|
||||
@@ -925,6 +938,11 @@
|
||||
"cmd.explode_show": "显示爆炸图",
|
||||
"cmd.expr_evaluate": "计算表达式",
|
||||
"cmd.face_delete": "删除面",
|
||||
"cmd.file.export_3mf": "导出 3MF",
|
||||
"cmd.file.export_obj": "导出 OBJ",
|
||||
"cmd.file.export_step": "导出 STEP",
|
||||
"cmd.file.export_stl": "导出 STL",
|
||||
"cmd.file.export_urdf": "导出 URDF",
|
||||
"cmd.file_export": "导出",
|
||||
"cmd.file_insert_mesh": "插入网格",
|
||||
"cmd.file_insert_step": "插入 STEP",
|
||||
@@ -942,6 +960,7 @@
|
||||
"cmd.inspect_colored_components": "启用组件颜色",
|
||||
"cmd.inspect_colored_components_disable": "禁用组件颜色",
|
||||
"cmd.inspect_compare": "比较实体",
|
||||
"cmd.inspect_compare_picture": "比较参考图",
|
||||
"cmd.inspect_continuity": "连续性分析",
|
||||
"cmd.inspect_curvature_comb": "曲率梳分析",
|
||||
"cmd.inspect_curvature_map": "曲率图分析",
|
||||
@@ -1032,6 +1051,9 @@
|
||||
"cmd.pointcloud_edit": "编辑点云",
|
||||
"cmd.pointcloud_insert": "插入点云",
|
||||
"cmd.pointcloud_list": "点云",
|
||||
"cmd.render.compare.divider": "移动渲染比较分隔线",
|
||||
"cmd.render.result.discard": "丢弃渲染结果",
|
||||
"cmd.render.result.select": "选择渲染结果",
|
||||
"cmd.render_aging_describe": "分层表面老化",
|
||||
"cmd.render_backends": "渲染后端",
|
||||
"cmd.render_batch": "渲染所有镜头",
|
||||
@@ -1195,6 +1217,7 @@
|
||||
"cmd.sketch_spline_control_point_5": "控制点样条曲线(5 次)",
|
||||
"cmd.sketch_spline_fit_point": "拟合点样条曲线",
|
||||
"cmd.sketch_spline_insert_point": "添加样条曲线点",
|
||||
"cmd.sketch_spline_remove_point": "删除样条曲线点",
|
||||
"cmd.sketch_spline_reverse": "反向样条曲线方向",
|
||||
"cmd.sketch_spun_profile": "旋转轮廓",
|
||||
"cmd.sketch_toggle_diameter": "切换半径 / 直径",
|
||||
@@ -1245,6 +1268,7 @@
|
||||
"cmd.timeline_rollback": "移动时间线标记",
|
||||
"cmd.timeline_stop": "停止历史播放",
|
||||
"cmd.timeline_suppress": "抑制特征",
|
||||
"cmd.ui.assistant": "切换助手显示",
|
||||
"cmd.view.render_browser": "渲染浏览器",
|
||||
"cmd.view.render_dock": "移动渲染停靠面板",
|
||||
"cmd.view.render_focus": "渲染专注视图",
|
||||
@@ -1279,9 +1303,11 @@
|
||||
"cmd.view_tool": "导航工具",
|
||||
"cmd.view_top": "俯视图",
|
||||
"cmd.view_visual_style": "视觉样式",
|
||||
"color.blue": "蓝色",
|
||||
"color.copy": "单击复制颜色值",
|
||||
"color.edit": "单击编辑颜色",
|
||||
"color.gamma": "以 0–255 伽马空间显示颜色值",
|
||||
"color.green": "绿色",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "输入 #rrggbb 或 #rrggbbaa;六位数保留不透明度",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
@@ -1289,6 +1315,7 @@
|
||||
"color.linear": "以 0–1 线性空间显示颜色值",
|
||||
"color.recent": "最近使用的颜色",
|
||||
"color.recent_help": "在任意颜色字段中选择的颜色会显示在这里",
|
||||
"color.red": "红色",
|
||||
"color.saturation": "饱和度(百分比)",
|
||||
"color.saturation_value": "饱和度和明度",
|
||||
"color.selected": "所选颜色",
|
||||
@@ -1296,6 +1323,8 @@
|
||||
"color.step_up": "增加一步",
|
||||
"color.value": "明度(百分比)",
|
||||
"config.column_filter_empty": "没有匹配的列",
|
||||
"config.rule_parameter_columns": "此处仅列出在表中有列的参数。使用“添加列”(+) 添加一列,即可在此使用该参数。",
|
||||
"config.rule_suppression_columns": "此处仅列出在表中有“抑制”列的特征。使用“添加列”(+) 添加一列,即可在此控制该特征。",
|
||||
"contact.components_count": "{count} 个组件",
|
||||
"contact.enabled_label": "已启用",
|
||||
"coupling.gear_1": "齿轮 1",
|
||||
@@ -1344,7 +1373,11 @@
|
||||
"dialog.pattern_sketch_not_object": "请选择实体或特征:草图两者都不是。请用草图自身的阵列或镜像重复其曲线,或先将其拉伸。",
|
||||
"dialog.select": "↖ 选择",
|
||||
"dialog.selected_count": "↖ 已选择 {count} 个",
|
||||
"dialog.selection_constraint": "约束 {id}",
|
||||
"dialog.selection_external_curve": "{sketch} · 曲线 {id}",
|
||||
"dialog.selection_guidance": "选择输入项进行修改。点击已选对象可取消选择;按住 Ctrl/Cmd 点击也可切换选择。检查预览后,再按“确定”。",
|
||||
"dialog.selection_midpoint": "中点 · {id}",
|
||||
"dialog.selection_profile": "{sketch} · 轮廓 {number}",
|
||||
"dialog.selection_step": "第 {step} 步,共 {total} 步:选择{input}",
|
||||
"dialog.step": "步骤",
|
||||
"document.changed_on_disk": "{name} 已在磁盘上更改",
|
||||
@@ -1427,6 +1460,7 @@
|
||||
"help.construct.axis.two_points": "通过两个点、顶点或草图点创建构造轴。用于连接两个已知位置的轴。",
|
||||
"help.construct.plane.along_path": "在边或曲线上的指定位置创建垂直于它的构造平面。用于为扫掠绘制轮廓,或绘制路径的截面。",
|
||||
"help.construct.plane.at_angle": "创建一个由平面或面绕轴或边旋转一定角度而成的构造平面。用于倾斜的草图,例如斜支架。",
|
||||
"help.construct.plane.axis_point": "创建包含一条轴和一个轴外点的构造平面。先选择原点轴、构造轴、直边或草图直线,再选择顶点、草图点或构造点。轴上的点无法确定唯一的平面。",
|
||||
"help.construct.plane.midplane": "在两个平面或平整面的正中间创建构造平面;若二者成角相交,则取其角平分面。用作镜像的对称面。",
|
||||
"help.construct.plane.offset": "创建与平面或平整面平行的构造平面,按距离或过某点确定。用于在面上方指定高度处绘制草图。",
|
||||
"help.construct.plane.perpendicular": "创建一个包含某直线或边、并垂直于某平面或面的构造平面。用于沿边在模型上垂直地绘制草图。",
|
||||
@@ -1549,7 +1583,7 @@
|
||||
"help.sketch.constrainer": "应用所选的一两条曲线已近乎满足的约束,例如水平、平行、相切或相等。用于逐个关系地整理手绘草图。",
|
||||
"help.sketch.constraint.coincident": "使一个点位于另一个点、曲线或草图轴上。用于闭合轮廓中的间隙,或把点放到直线上。",
|
||||
"help.sketch.constraint.collinear": "使两条直线位于同一条直线上。用于对齐被打断的边,例如缺口的两侧。",
|
||||
"help.sketch.constraint.concentric": "使两个圆或圆弧具有相同的圆心。用于圆环、沉孔和带台阶的轴。",
|
||||
"help.sketch.constraint.concentric": "使两个圆或圆弧具有相同的圆心,也可将圆或圆弧的圆心约束到草图点(包括原点)。两者的选择顺序不限。",
|
||||
"help.sketch.constraint.curvature": "使在端点相接的两条曲线以相同的曲率延续 (G2)。用于相切连接不够平滑的地方,例如可见曲面。",
|
||||
"help.sketch.constraint.equal": "使直线长度相等,或使圆和圆弧半径相等。用于让一个尺寸同时驱动多个相同的特征。",
|
||||
"help.sketch.constraint.fix": "把点或曲线固定在当前位置,或再次解除固定。用于拖动或解算草图时不得移动的几何体。",
|
||||
@@ -1803,6 +1837,8 @@
|
||||
"joint_move.blocked_hint": "请检查关节类型(例如 Rigid)、固定状态、关节限位和参数驱动值。",
|
||||
"joint_move.blocked_title": "无法移动此零部件",
|
||||
"joint_move.coarse": "靠近轴线时的旋转步长(°)",
|
||||
"joint_move.conflict": "此零部件的一个关节与其他关节冲突:{joint}。",
|
||||
"joint_move.conflict_hint": "请编辑、抑制或删除冲突的关节,以便其他关节可以放置该零部件。",
|
||||
"joint_move.distance": "相对起点的移动距离",
|
||||
"joint_move.done": "完成",
|
||||
"joint_move.fine": "远离轴线时的旋转步长(°)",
|
||||
@@ -1902,6 +1938,8 @@
|
||||
"motion.track_name": "轨道 {number}",
|
||||
"nav.alias.help": "Shift+Alt+左键拖动旋转,Shift+Alt+中键拖动平移,Shift+Alt+右键拖动缩放;中键拖动也可平移,右键拖动旋转",
|
||||
"nav.blender.help": "中键拖动旋转,Shift+中键拖动平移,Ctrl+中键拖动缩放;右键拖动不会移动相机",
|
||||
"nav.creo.help": "中键拖动旋转,Shift+中键平移,Ctrl+中键上下拖动缩放(向下放大);滚动滚轮也可缩放",
|
||||
"nav.creo.label": "Creo",
|
||||
"nav.free_orbit.help": "鼠标和3D鼠标可保持滚转并越过两个极点旋转。Shift轴吸附将被禁用。",
|
||||
"nav.free_orbit.label": "自由旋转",
|
||||
"nav.free_orbit.snap_help": "鼠标和3D鼠标可越过两极旋转,并保留画面旋转角度。在拖动环绕时按住Shift,可吸附视图方向和画面旋转角度。",
|
||||
@@ -2054,12 +2092,19 @@
|
||||
"parts.family.button_head_cap_screw": "圆头内六角螺钉",
|
||||
"parts.family.dowel_pin": "圆柱销",
|
||||
"parts.family.hex_bolt": "六角头螺栓",
|
||||
"parts.family.hex_cap_screw_inch": "六角头螺栓(英制)",
|
||||
"parts.family.hex_nut": "六角螺母",
|
||||
"parts.family.hex_nut_inch": "六角螺母(英制)",
|
||||
"parts.family.set_screw": "内六角平端紧定螺钉",
|
||||
"parts.family.socket_head_cap_screw": "内六角圆柱头螺钉",
|
||||
"parts.family.washer": "平垫圈",
|
||||
"parts.family.washer_sae": "平垫圈,SAE(窄型)",
|
||||
"parts.family.washer_uss": "平垫圈,USS(宽型)",
|
||||
"parts.holes": "孔(可选)",
|
||||
"parts.placement_hint": "选择一个或多个孔,在每个孔中各装一个零件;或按确定将其放在原点",
|
||||
"parts.system": "单位制",
|
||||
"parts.system.inch": "英制",
|
||||
"parts.system.metric": "公制",
|
||||
"pattern.alignment_2": "对齐 2",
|
||||
"pattern.alignment_hint": "数量包含原始图形。居中时副本分布在两侧;数量为偶数时,反转可切换多一个副本的一侧。",
|
||||
"pattern.direction_1": "方向 1",
|
||||
@@ -2121,6 +2166,8 @@
|
||||
"prefs.button_left": "左键拖动",
|
||||
"prefs.button_middle": "中键拖动",
|
||||
"prefs.button_right": "右键拖动",
|
||||
"prefs.carousel_next": "下一个",
|
||||
"prefs.carousel_previous": "上一个",
|
||||
"prefs.choose_font": "选择字体文件…",
|
||||
"prefs.click": "单击",
|
||||
"prefs.contact_shading": "柔和接触阴影",
|
||||
@@ -2133,6 +2180,8 @@
|
||||
"prefs.ctrl_cmd": "Ctrl / Cmd",
|
||||
"prefs.curved_surfaces": "曲面",
|
||||
"prefs.curves": "曲面",
|
||||
"prefs.cut_preview_tint": "以红色显示被移除的材料",
|
||||
"prefs.cut_preview_tint_help": "预览会用红色半透明幻影填充特征所移除的材料以及被替换的形状。关闭时,这些填充完全透明,仅绘制其轮廓。",
|
||||
"prefs.dark_parts_shading": "保持深色零件的形状",
|
||||
"prefs.dark_parts_shading_help": "以线性光照和柔和的天空补光着色视图,使深色面保留渐变而不变成一片黑色",
|
||||
"prefs.dead_zone_note": "斜线区域为死区:此范围内的移动将被忽略。",
|
||||
@@ -2157,7 +2206,19 @@
|
||||
"prefs.font_sample": "字体示例 · Ø12.50 mm · 45.0° · 0123456789",
|
||||
"prefs.font_unreadable": "无法读取所选字体。",
|
||||
"prefs.general": "常规",
|
||||
"prefs.general_note": "语言、保存和更新。",
|
||||
"prefs.general.active": "当前",
|
||||
"prefs.general.crash_reports": "崩溃报告",
|
||||
"prefs.general.edited": "已修改",
|
||||
"prefs.general.held": "包含",
|
||||
"prefs.general.import": "导入...",
|
||||
"prefs.general.last_check": "上次检查:{time}",
|
||||
"prefs.general.minutes": "分钟",
|
||||
"prefs.general.name_setup": "为此配置命名",
|
||||
"prefs.general.profile_name": "配置文件名称",
|
||||
"prefs.general.save_new": "另存为新配置",
|
||||
"prefs.general.step_import": "STEP 和导入",
|
||||
"prefs.general.this_computer": "此计算机",
|
||||
"prefs.general_note": "配置文件、语言、文件和更新。",
|
||||
"prefs.gesture_orbit": "环绕",
|
||||
"prefs.gesture_pan": "平移",
|
||||
"prefs.gesture_zoom": "缩放",
|
||||
@@ -2191,6 +2252,8 @@
|
||||
"prefs.interface_language": "界面语言",
|
||||
"prefs.interface_note": "SolidCraft 的外观和面板布局。",
|
||||
"prefs.interval_minutes": "{n} 分钟",
|
||||
"prefs.invert_curved_roll_arrows": "反转弯曲的翻滚箭头",
|
||||
"prefs.invert_curved_roll_arrows_note": "立方体角落的两个弯曲箭头在屏幕平面内翻滚视图。开启:顺时针箭头逆时针翻滚,逆时针箭头顺时针翻滚。关闭:每个箭头按其弯曲方向翻滚视图。",
|
||||
"prefs.invert_navigator_arrows": "反转视图立方体的旋转箭头",
|
||||
"prefs.invert_navigator_arrows_note": "立方体周围的箭头按箭头所指的方向转动相机,与 Fusion 和 Inventor 相同:左侧箭头指向右,相机向右转动。关闭:箭头把立方体转向它所在的那一面,因此左侧箭头使相机向左转动。弯曲的翻滚箭头不受影响。",
|
||||
"prefs.keep_work_on_tab_switch": "切换设计时保持对话框和工具打开",
|
||||
@@ -2212,10 +2275,10 @@
|
||||
"prefs.logo_accent": "强调色",
|
||||
"prefs.logo_theme": "主题颜色",
|
||||
"prefs.max_file_size": "可打开的最大文件",
|
||||
"prefs.max_file_size_design": "设计文件",
|
||||
"prefs.max_file_size_design": "可打开的最大文件:设计",
|
||||
"prefs.max_file_size_flag": "本次启动指定了 --max-file-size {size},该值在关闭 SolidCraft 前有效。不带此选项启动时才会应用这些设置。",
|
||||
"prefs.max_file_size_note": "默认值:512 MB 和 256 MB;上限为 16 GB。打开文件需要数倍于文件大小的内存。保留大型导入数据的设计可能需要提高设计文件上限,才能保存并重新打开。",
|
||||
"prefs.max_file_size_step": "STEP 和导入文件",
|
||||
"prefs.max_file_size_step": "可打开的最大文件:STEP 和导入",
|
||||
"prefs.max_file_size_web": "网页版可打开最大 512 MB 的 STEP 和导入文件,以及最大 256 MB 的设计:浏览器标签页无法容纳更大的文件。桌面版可提高这些上限。",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "大图标",
|
||||
@@ -2232,7 +2295,7 @@
|
||||
"prefs.navigator_fit_note": "点击立方体的面、角或箭头,或坐标轴指示器的轴时,也会从各个方向都能容纳模型的同一距离(其包围球)注视模型中心。开启期间主视图使用相同的缩放和中心,因此之后选择的视图只改变方向。关闭:视图原地旋转,保持目标点和缩放。",
|
||||
"prefs.network_host_remove": "删除",
|
||||
"prefs.network_locations": "允许设计中的网络位置",
|
||||
"prefs.network_locations_note": "关闭:设计中位于其他计算机 (\\\\server\\share) 上的链接文件,只会从此处列出的服务器打开,或在询问时你允许的服务器打开。",
|
||||
"prefs.network_locations_note": "适用于将设计存放在文件服务器上的组织。关闭时:仅允许您授权的服务器。",
|
||||
"prefs.network_locations_section": "网络位置",
|
||||
"prefs.not_on_gpu": "此 GPU 不支持",
|
||||
"prefs.on": "开启",
|
||||
@@ -2250,15 +2313,17 @@
|
||||
"prefs.placement": "放置",
|
||||
"prefs.prefer_free_sketch_points": "草图点重合时优先选择可移动的点",
|
||||
"prefs.preview_choices": "所选设置预览",
|
||||
"prefs.preview_tint_new_faces": "在预览中为新表面着色",
|
||||
"prefs.preview_tint_new_faces_help": "更改实体的特征,其预览可以用预览颜色绘制新增的表面。关闭时,实体按自身颜色绘制。",
|
||||
"prefs.print_face_highlight": "打印底面高亮",
|
||||
"prefs.print_face_highlight_tip": "视图中实体打印底面的着色和轮廓强度。0 % 不显示高亮。",
|
||||
"prefs.profile_active": "当前配置文件",
|
||||
"prefs.profile_applied": "已应用配置文件。",
|
||||
"prefs.profile_changed": "保存配置文件后设置已更改。“保存”会将其写入配置文件。",
|
||||
"prefs.profile_deleted": "已删除配置文件。",
|
||||
"prefs.profile_export": "导出…",
|
||||
"prefs.profile_export": "导出",
|
||||
"prefs.profile_exported": "已导出配置文件。",
|
||||
"prefs.profile_imported": "已导入配置文件。在“当前配置文件”中选择它即可切换。",
|
||||
"prefs.profile_imported": "已导入配置文件。",
|
||||
"prefs.profile_name_hint": "配置文件名称,例如 笔记本",
|
||||
"prefs.profile_rename": "重命名",
|
||||
"prefs.profile_renamed": "已重命名配置文件。",
|
||||
@@ -2278,12 +2343,13 @@
|
||||
"prefs.render_previous_geometry_budget": "使用先前的渲染预览几何内存预算",
|
||||
"prefs.render_previous_geometry_budget_help": "恢复先前的固定几何内存上限(64位桌面为512 MiB,其他平台为128 MiB),替代基于设备的预算。",
|
||||
"prefs.reports_saved": "已保存 {n} 份",
|
||||
"prefs.restore_page_defaults": "恢复本页默认值",
|
||||
"prefs.ribbon_click_ends_tool": "单击正在运行的工具的按钮将结束该工具",
|
||||
"prefs.ribbon_click_ends_tool_note": "单击正在使用的草图工具的高亮功能区按钮会结束该工具,在视图中单击即可再次选择。关闭:单击会重新启动该工具。",
|
||||
"prefs.ribbon_reveal": "在功能区中显示搜索结果",
|
||||
"prefs.ribbon_reveal_note": "将指针悬停在“搜索命令”的结果上时,会显示该命令在功能区中的位置:其选项卡、按钮或面板下拉菜单中的行。从搜索启动的命令会在那里闪烁,从下拉菜单运行的命令会标记其面板。",
|
||||
"prefs.right_click": "右键单击",
|
||||
"prefs.saving": "自动保存和恢复",
|
||||
"prefs.saving": "启动和保存",
|
||||
"prefs.scale_note": "也可在窗口中使用 Ctrl + / Ctrl −。100% 时按钮高度为 28 像素。",
|
||||
"prefs.scale_text": "缩放与文字",
|
||||
"prefs.search": "搜索设置",
|
||||
@@ -2547,6 +2613,10 @@
|
||||
"render.base_color": "基础颜色 · sRGB",
|
||||
"render.base_metallic": "金属比例",
|
||||
"render.base_opaque_only": "此编辑器保留不透明实体语义;光学透射请使用明确的玻璃控件。",
|
||||
"render.batch_all_shots": "所有镜头 ({count})",
|
||||
"render.batch_empty": "渲染所有镜头前请先保存镜头。",
|
||||
"render.batch_limit": "一次最多渲染32个镜头。",
|
||||
"render.batch_status": "{running} 个运行中 · {waiting} 个等待中",
|
||||
"render.bloom_enable": "显示泛光",
|
||||
"render.bloom_help": "仅用于输出的创意辉光,将正线性高光经过三次归一化滤波扩散。半径以输出像素计,在 sRGB 前叠加;不模拟镜头或体积,也不改善收敛。原始 EXR 不变。",
|
||||
"render.bloom_opaque": "泛光需要不透明输出;会拒绝透明 PNG,而非暗中合成背景。",
|
||||
@@ -2657,6 +2727,13 @@
|
||||
"render.choose_look": "请先在外观库中选择外观",
|
||||
"render.chromatic_offset": "边缘色差偏移",
|
||||
"render.clear_stage": "移除已保存的舞台",
|
||||
"render.compare_divider": "比较分隔线",
|
||||
"render.compare_live": "实时",
|
||||
"render.compare_live_preview": "实时预览",
|
||||
"render.compare_render": "渲染 {number}",
|
||||
"render.compare_side": "渲染 {number} · {shot}",
|
||||
"render.compare_summary": "{shot} · {width} × {height} · PNG · {seconds} 秒 · {renderer} · 捕获于修订版 {revision}",
|
||||
"render.compare_swap": "交换",
|
||||
"render.copy_request": "复制 JSON",
|
||||
"render.cpu_aa_help": "增加空间采样可改善边缘,但会增加渲染时间。",
|
||||
"render.cpu_area_add": "添加 CPU 矩形光源",
|
||||
@@ -3406,6 +3483,10 @@
|
||||
"render.result_retained": "正在显示最后完成的图像;较新的渲染尚未替换它。",
|
||||
"render.result_sequence": "中间结果",
|
||||
"render.result_title": "渲染结果",
|
||||
"render.results_choose": "保留的结果",
|
||||
"render.results_label": "渲染 {number} · {shot} · {seconds} 秒",
|
||||
"render.results_stale_label": "{result} [已过期]",
|
||||
"render.results_unnamed": "图像",
|
||||
"render.return_design": "返回设计",
|
||||
"render.review_stale": "此审阅结果已过期;应用前请重新解析或检查。",
|
||||
"render.ribbon_aging": "老化",
|
||||
@@ -4066,6 +4147,7 @@
|
||||
"ui.agent_chat_auth_terminal": "在终端中运行以下命令,然后再次点击连接:",
|
||||
"ui.agent_chat_auth_title": "登录智能体",
|
||||
"ui.agent_chat_cancel": "取消",
|
||||
"ui.agent_chat_cancel_timeout": "代理未响应停止请求,已断开连接。请重新连接以继续。",
|
||||
"ui.agent_chat_cannot_start": "已安装,但无法启动:{error}",
|
||||
"ui.agent_chat_changes": "{n} 项更改,一步撤销",
|
||||
"ui.agent_chat_changes_n": "{n} 项更改",
|
||||
@@ -4075,11 +4157,13 @@
|
||||
"ui.agent_chat_command": "智能体命令",
|
||||
"ui.agent_chat_connect": "连接",
|
||||
"ui.agent_chat_copy": "复制",
|
||||
"ui.agent_chat_copy_message": "复制消息",
|
||||
"ui.agent_chat_ctx_bodies": "实体",
|
||||
"ui.agent_chat_ctx_elements": "元素",
|
||||
"ui.agent_chat_ctx_features": "特征",
|
||||
"ui.agent_chat_ctx_more": "另有 {n} 项",
|
||||
"ui.agent_chat_ctx_other": "其他",
|
||||
"ui.agent_chat_custom_heading": "自定义模型",
|
||||
"ui.agent_chat_custom_hint": "任何通过标准输入输出使用 Agent Client Protocol 的程序。不经过 shell 运行。",
|
||||
"ui.agent_chat_denied": "已拒绝",
|
||||
"ui.agent_chat_deny": "拒绝",
|
||||
@@ -4123,8 +4207,16 @@
|
||||
"ui.agent_chat_mode_all": "全部",
|
||||
"ui.agent_chat_mode_ask": "询问",
|
||||
"ui.agent_chat_mode_edits": "编辑",
|
||||
"ui.agent_chat_model_custom": "使用自定义:{model}",
|
||||
"ui.agent_chat_model_no_tools": "{model} 无法使用 SolidCraft 的工具,只能聊天。请选择支持工具的模型。",
|
||||
"ui.agent_chat_model_rejected": "智能体未接受 {model}:{error}",
|
||||
"ui.agent_chat_model_search": "搜索模型…",
|
||||
"ui.agent_chat_model_tip": "智能体使用的模型。你的选择会为此智能体记住。",
|
||||
"ui.agent_chat_models_failed": "无法列出模型:{error}",
|
||||
"ui.agent_chat_models_heading": "可用模型",
|
||||
"ui.agent_chat_models_listing": "正在列出模型…",
|
||||
"ui.agent_chat_models_none": "该端点未列出任何模型",
|
||||
"ui.agent_chat_models_not_a_list": "响应不是模型列表",
|
||||
"ui.agent_chat_new": "新对话",
|
||||
"ui.agent_chat_new_confirm": "开始新对话?当前对话将被清除。",
|
||||
"ui.agent_chat_next_picture": "下一张图片",
|
||||
@@ -4386,6 +4478,8 @@
|
||||
"ui.coming_later": "即将推出",
|
||||
"ui.command_done": "{command} 完成",
|
||||
"ui.command_palette_hint": "输入命令,例如 {example} — 或按 S 搜索",
|
||||
"ui.components_from_bodies_hint": "在活动组件下,为每个选定实体创建一个以该实体命名的组件。",
|
||||
"ui.components_from_bodies_required": "请至少选择一个实体。",
|
||||
"ui.concentric": "同心",
|
||||
"ui.cone": "圆锥体",
|
||||
"ui.conic_curve": "圆锥曲线",
|
||||
@@ -4398,9 +4492,11 @@
|
||||
"ui.construction.contact_hint": "切线:先选圆/圆弧或开放样条曲线/圆锥曲线,再选接点(自由曲线仅支持端点)",
|
||||
"ui.construction.external_hint": "切线:依次选择圆或圆弧、外部点和所需解",
|
||||
"ui.construction.free_hint": "构造直线:单击两点;右键单击重新开始",
|
||||
"ui.construction.normal_needs_length": "无限直线始终是构造线:输入长度以绘制普通直线",
|
||||
"ui.construction.point_hint": "构造直线:单击要经过的点",
|
||||
"ui.construction.reference_hint": "构造直线:先选直线,再选一点;右键单击更换参照",
|
||||
"ui.construction.ribbon_label": "辅助线",
|
||||
"ui.construction.side_hint": "构造直线:单击直线延伸的一侧;右键单击重新开始",
|
||||
"ui.context_menu": "上下文菜单",
|
||||
"ui.continuity_g0": "G0 折痕",
|
||||
"ui.continuity_g1": "G1 相切",
|
||||
@@ -4666,6 +4762,7 @@
|
||||
"ui.hideOrigin": "隐藏原点",
|
||||
"ui.hidePlanes": "隐藏平面",
|
||||
"ui.hideProfile": "隐藏轮廓",
|
||||
"ui.hide_component_history": "隐藏其历史记录",
|
||||
"ui.hide_origin_by_default": "默认隐藏原点",
|
||||
"ui.hide_origin_note": "新设计开始时原点文件夹处于隐藏状态。显示或隐藏过原点的设计会保留自己的原点设置。",
|
||||
"ui.history_back": "后退一步",
|
||||
@@ -4673,6 +4770,7 @@
|
||||
"ui.history_edge_hint": "双击边缘",
|
||||
"ui.history_first": "转到开头",
|
||||
"ui.history_forward": "前进一步",
|
||||
"ui.history_hide_hidden": "隐藏已隐藏的组件",
|
||||
"ui.history_hide_names": "隐藏名称",
|
||||
"ui.history_last": "转到末尾",
|
||||
"ui.history_marker_tip": "历史标记:拖动以回退模型;新特征将在此处添加",
|
||||
@@ -4682,10 +4780,12 @@
|
||||
"ui.history_play_button_show": "显示播放按钮",
|
||||
"ui.history_play_finish_action": "请先完成当前操作,再播放历史",
|
||||
"ui.history_play_roll_back": "播放前请将历史标记向后移动",
|
||||
"ui.history_show_hidden": "显示隐藏的组件",
|
||||
"ui.history_show_names": "显示名称",
|
||||
"ui.history_sketch_listed_under": "列在 {feature} 下",
|
||||
"ui.history_title": "历史",
|
||||
"ui.history_toggle_names": "显示或隐藏特征名称",
|
||||
"ui.history_waits_for_editor": "撤销、重做和历史将等待特征编辑器打开",
|
||||
"ui.hole": "孔",
|
||||
"ui.hole_tap_type": "孔攻丝类型",
|
||||
"ui.hole_type": "孔类型",
|
||||
@@ -4701,6 +4801,7 @@
|
||||
"ui.insert_design.saved_file": "{name} 有未保存的更改,因此插入的是其已保存的文件。请先保存,再使用“更新链接”导入这些更改。",
|
||||
"ui.insert_design.unsaved": "未保存的更改",
|
||||
"ui.inspect": "检查",
|
||||
"ui.instances": "实例数",
|
||||
"ui.intersect": "相交",
|
||||
"ui.isolate": "隔离",
|
||||
"ui.join": "连接",
|
||||
@@ -4851,9 +4952,9 @@
|
||||
"ui.polygon": "多边形",
|
||||
"ui.preferences": "首选项",
|
||||
"ui.press_a_key": "按一个键…",
|
||||
"ui.print_face_body_not_sent": "此面所属的实体不会发送:请先将其添加到实体",
|
||||
"ui.print_face_cleared": "{body}:已清除打印底面",
|
||||
"ui.print_face_input": "打印底面",
|
||||
"ui.print_face_one_body": "打印底面属于单个实体:请单独发送该实体以更改它",
|
||||
"ui.print_face_pick": "选择实体的一个平面,将其设为打印底面",
|
||||
"ui.print_face_set": "{body}:已设置打印底面;发送到切片软件时实体将放在此面上",
|
||||
"ui.profile": "轮廓",
|
||||
@@ -4965,6 +5066,7 @@
|
||||
"ui.showDimension": "显示尺寸",
|
||||
"ui.showOrigin": "显示原点",
|
||||
"ui.showPlanes": "显示平面",
|
||||
"ui.show_component_history": "显示其历史记录",
|
||||
"ui.show_constraints": "约束",
|
||||
"ui.show_construction_geometries": "构造几何图元",
|
||||
"ui.show_dimensions": "尺寸",
|
||||
@@ -4996,7 +5098,7 @@
|
||||
"ui.slicer_no_response": "{slicer} 未在规定时间内响应。文件已保存到 {path}:切片软件启动后请再次发送。",
|
||||
"ui.slicer_resend_hint": "再次发送会替换切片软件中已打开的文件。",
|
||||
"ui.slicer_single_instance": "在已运行的切片软件中打开文件",
|
||||
"ui.slicer_single_instance_tip": "PrusaSlicer、SuperSlicer 和 Cura 会在已打开的窗口中加载文件,而不是再启动一个。其他切片软件按原样启动。PrusaSlicer 3.0 始终如此:其启动器无法以其他方式接收文件。",
|
||||
"ui.slicer_single_instance_tip": "适用于将文件加载到正在运行的窗口中的切片软件。",
|
||||
"ui.slicer_starting": "正在启动 {slicer}…",
|
||||
"ui.slicer_still_starting": "仍在等待 {slicer} 启动。没有发送其他内容。",
|
||||
"ui.slicer_stl_fallback": "无法写入 3MF,因此发送的是 STL。",
|
||||
@@ -5081,12 +5183,15 @@
|
||||
"ui.view_angles": "视图角度",
|
||||
"ui.view_angles_convention": "绕向上轴的角度(度)。偏航 0° 为前视图,90° 为左视图,−90° 为右视图,180° 为后视图;俯仰 90° 为俯视图;滚转为正时,模型在屏幕上顺时针旋转。",
|
||||
"ui.view_angles_help": "在方向小部件下方显示视图的俯仰、偏航和滚转角(度)以及投影方式。单击读数可输入精确角度。",
|
||||
"ui.view_angles_lock": "锁定{axis}",
|
||||
"ui.view_angles_ortho": "正交",
|
||||
"ui.view_angles_persp": "透视",
|
||||
"ui.view_angles_pitch": "俯仰",
|
||||
"ui.view_angles_readout": "俯仰 {pitch} · 偏航 {yaw} · 滚转 {roll}",
|
||||
"ui.view_angles_roll": "滚转",
|
||||
"ui.view_angles_unlock": "解锁{axis}",
|
||||
"ui.view_angles_yaw": "偏航",
|
||||
"ui.view_angles_zero": "将{axis}设为 0°",
|
||||
"ui.view_centre": "视图中心",
|
||||
"ui.viewport_status_clear": "关闭{name}",
|
||||
"ui.viewport_status_clear_hint": "关闭{name} · Esc",
|
||||
@@ -5138,7 +5243,7 @@
|
||||
"update.available": "SolidCraft {version} 已发布",
|
||||
"update.button": "更新",
|
||||
"update.button_version": "更新到 {version}",
|
||||
"update.check_automatically": "自动检查更新",
|
||||
"update.check_automatically": "启动时及每天检查一次",
|
||||
"update.check_for_updates": "检查更新",
|
||||
"update.check_now": "立即检查",
|
||||
"update.check_unavailable": "此安装版本无法检查更新。",
|
||||
@@ -5188,6 +5293,7 @@
|
||||
"web.open_link": "打开链接的设计?",
|
||||
"web.open_link_confirm": "有未保存的更改。要在新标签页中打开链接的设计吗?",
|
||||
"↑ ↓ choose · Enter / Tab complete · Esc dismiss": "↑ ↓ 选择 · Enter / Tab 补全 · Esc 关闭",
|
||||
"↑ ↓ choose · Enter applies this value": "↑ ↓ 选择 · Enter 应用此值",
|
||||
"↑ ↓ choose · Enter applies what you typed · Esc dismiss": "↑ ↓ 选择 · Enter 按原样应用 · Esc 关闭",
|
||||
"⌘Enter continues on GitHub.": "按⌘Enter在GitHub中继续。"
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ fn near_z(c: vec4<f32>) -> f32 {
|
||||
|
||||
@group(1) @binding(2) var acc_t: texture_2d<f32>;
|
||||
@group(2) @binding(0) var ao_depth: texture_2d<u32>;
|
||||
@group(2) @binding(1) var ao_map: texture_2d<f32>;
|
||||
|
||||
fn clipped(p: vec3<f32>) -> bool {
|
||||
return length(u.clip.xyz) > 0.5 && dot(u.clip.xyz, p) > u.clip.w;
|
||||
@@ -62,7 +63,7 @@ fn out_color(c: vec4<f32>) -> vec4<f32> {
|
||||
}
|
||||
|
||||
struct TOut {
|
||||
@builtin(position) pos: vec4<f32>,
|
||||
@invariant @builtin(position) pos: vec4<f32>,
|
||||
@location(0) n: vec3<f32>,
|
||||
@location(1) c: vec4<f32>,
|
||||
@location(2) wp: vec3<f32>,
|
||||
@@ -107,7 +108,7 @@ fn vs_tri_inst(
|
||||
/// stores every vertex's outputs: more of them cost more than the triangles' shared vertices
|
||||
/// save).
|
||||
struct FOut {
|
||||
@builtin(position) pos: vec4<f32>,
|
||||
@invariant @builtin(position) pos: vec4<f32>,
|
||||
@location(0) rp: vec3<f32>,
|
||||
@location(1) c: vec4<f32>,
|
||||
};
|
||||
@@ -209,6 +210,12 @@ fn fs_ao_depth_flat(i: FOut, @builtin(front_facing) front: bool) -> AoDepth {
|
||||
return opaque_depth(t, front);
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_ao_term_flat(i: FOut, @builtin(front_facing) front: bool) -> @location(0) vec4<f32> {
|
||||
let t = flat_fragment(i, front);
|
||||
return ao_term(t, front);
|
||||
}
|
||||
|
||||
/// Opaque model surfaces: cut by the section plane/// Opaque model surfaces: cut by the section plane, the inside shows as a flat cap colour.
|
||||
@fragment
|
||||
fn fs_solid(i: TOut, @builtin(front_facing) front: bool) -> @location(0) vec4<f32> {
|
||||
@@ -346,11 +353,89 @@ fn opaque_depth(i: TOut, front: bool) -> AoDepth {
|
||||
return AoDepth(depth, bitcast<u32>(depth));
|
||||
}
|
||||
|
||||
/// The term for an opaque surface's fragment, on the prepass's depth. A section's cut face has none.
|
||||
fn ao_term(i: TOut, front: bool) -> vec4<f32> {
|
||||
if (clipped(i.wp) || length(i.n) < 0.5) { discard; }
|
||||
if (!front && length(u.clip.xyz) > 0.5) { return vec4<f32>(1.0); }
|
||||
return vec4<f32>(occlusion_term(i));
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_ao_term(i: TOut, @builtin(front_facing) front: bool) -> @location(0) vec4<f32> {
|
||||
return ao_term(i, front);
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vs_ao_blur(@builtin(vertex_index) v: u32) -> @builtin(position) vec4<f32> {
|
||||
let p = vec2<f32>(f32((v << 1u) & 2u), f32(v & 2u));
|
||||
return vec4<f32>(p * 2.0 - 1.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
/// The depth map at `p`, reading 0 near and 1 far whichever way the buffer runs.
|
||||
fn map_depth(p: vec2<i32>) -> f32 {
|
||||
let at = clamp(p, vec2<i32>(0), vec2<i32>(textureDimensions(ao_depth)) - vec2<i32>(1));
|
||||
let d = bitcast<f32>(textureLoad(ao_depth, at, 0).x);
|
||||
return select(d, 1.0 - d, reversed());
|
||||
}
|
||||
|
||||
/// The raw term (`ao_map` in this pass) averaged over a 4 x 4 window, which holds every turn of
|
||||
/// [`ao_turn`]'s pattern once. A neighbour counts when it lies on this pixel's plane, the slope
|
||||
/// taken from the nearer side across each axis, so no surface's shade crosses an edge.
|
||||
@fragment
|
||||
fn fs_ao_blur(@builtin(position) pos: vec4<f32>) -> @location(0) vec4<f32> {
|
||||
let c = vec2<i32>(pos.xy);
|
||||
let d = map_depth(c);
|
||||
if (d >= 1.0) { return vec4<f32>(1.0); }
|
||||
let left = d - map_depth(c + vec2<i32>(-1, 0));
|
||||
let right = map_depth(c + vec2<i32>(1, 0)) - d;
|
||||
let above = d - map_depth(c + vec2<i32>(0, -1));
|
||||
let below = map_depth(c + vec2<i32>(0, 1)) - d;
|
||||
let dx = select(right, left, abs(left) < abs(right));
|
||||
let dy = select(below, above, abs(above) < abs(below));
|
||||
let tolerance = 1.5 * (abs(dx) + abs(dy)) + 1e-6;
|
||||
let last = vec2<i32>(textureDimensions(ao_map)) - vec2<i32>(1);
|
||||
var sum = 0.0;
|
||||
var weight = 0.0;
|
||||
for (var y = -2; y < 2; y = y + 1) {
|
||||
for (var x = -2; x < 2; x = x + 1) {
|
||||
let p = c + vec2<i32>(x, y);
|
||||
let s = map_depth(p);
|
||||
if (s < 1.0 && abs(s - (d + dx * f32(x) + dy * f32(y))) <= tolerance) {
|
||||
sum = sum + textureLoad(ao_map, clamp(p, vec2<i32>(0), last), 0).x;
|
||||
weight = weight + 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return vec4<f32>(sum / max(weight, 1.0));
|
||||
}
|
||||
|
||||
/// A pixel's turn of the sample pattern: every 4 x 4 window holds each of 16 turns once.
|
||||
fn ao_turn(pos: vec2<f32>) -> f32 {
|
||||
let x = u32(pos.x) & 3u;
|
||||
let y = u32(pos.y) & 3u;
|
||||
// WGSL won't mix shifts and `|` without parentheses; naga takes it, browsers refuse it (#11080).
|
||||
let v = (((x ^ y) & 1u) << 3u) | ((y & 1u) << 2u) | (((x ^ y) >> 1u) << 1u) | (y >> 1u);
|
||||
return (f32(v) + 0.5) / 16.0;
|
||||
}
|
||||
|
||||
/// The lit pass's occlusion: the blurred term where this point falls in the map, whatever the
|
||||
/// window's viewport offset and supersampling.
|
||||
fn occlusion(i: TOut) -> f32 {
|
||||
let size = vec2<i32>(textureDimensions(ao_map));
|
||||
// The maps are 1 × 1 stand-ins until a frame draws into them (the product preview draws no
|
||||
// prepass, and AO off releases them): they tell nothing, so leave the surface unoccluded.
|
||||
if (u.ao.x <= 0.0 || length(i.n) < 0.5 || size.x < 2 || size.y < 2) { return 1.0; }
|
||||
let q = u.vp * vec4<f32>(i.wp, 1.0);
|
||||
if (q.w <= 0.0) { return 1.0; }
|
||||
let uv = vec2<f32>(q.x / q.w * 0.5 + 0.5, 0.5 - q.y / q.w * 0.5);
|
||||
return textureLoad(ao_map, clamp(vec2<i32>(uv * vec2<f32>(size)), vec2<i32>(0), size - vec2<i32>(1)), 0).x;
|
||||
}
|
||||
|
||||
// Cosine-weighted hemisphere samples, the pattern turned by a different angle at each pixel.
|
||||
// Projecting the sample and a point one radius toward the eye supplies a depth range in both
|
||||
// orthographic and perspective views; distant foreground geometry cannot cast a dark halo onto
|
||||
// a background surface.
|
||||
fn occlusion(i: TOut) -> f32 {
|
||||
fn occlusion_term(i: TOut) -> f32 {
|
||||
let radius = u.ao.x;
|
||||
if (radius <= 0.0 || length(i.n) < 0.5) { return 1.0; }
|
||||
var n = normalize(i.n);
|
||||
@@ -364,10 +449,9 @@ fn occlusion(i: TOut) -> f32 {
|
||||
if (size.x < 2 || size.y < 2) { return 1.0; }
|
||||
// The same offsets at every pixel made each sample darken its own shifted copy of a nearby
|
||||
// part's outline: faint ghosts of the part where the radius is large on screen (#4167),
|
||||
// stepped bands along contacts where it is small (#3407). Turned by interleaved gradient
|
||||
// noise (Jimenez 2014), neighbouring pixels sample different points and the copies break up
|
||||
// into an even grain.
|
||||
let turn = fract(52.9829189 * fract(dot(i.pos.xy, vec2<f32>(0.06711056, 0.00583715))));
|
||||
// stepped bands along contacts where it is small (#3407). Turned, neighbouring pixels sample
|
||||
// different points, the copies break up into grain, and the blur takes the grain out (#9327).
|
||||
let turn = ao_turn(i.pos.xy);
|
||||
let t = tangent * cos(turn * 6.28318531) + bitangent * sin(turn * 6.28318531);
|
||||
let b = cross(n, t);
|
||||
// One comparison for both depth directions: depths read as 0 near / 1 far (mirrored when
|
||||
|
||||
@@ -75,6 +75,7 @@ impl Tokens {
|
||||
font_compass: 13.5,
|
||||
font_subtitle: 15.0,
|
||||
font_intro: 16.0,
|
||||
font_page_heading: 20.0,
|
||||
font_article_heading: 17.0,
|
||||
font_title: 22.0,
|
||||
font_display: 26.0,
|
||||
@@ -226,6 +227,31 @@ impl Tokens {
|
||||
dialog_label: Color32::from_rgb(58, 70, 86),
|
||||
selected_ink: Color32::from_rgb(26, 80, 127),
|
||||
selected_bg: Color32::from_rgb(232, 241, 250),
|
||||
figure_ground: Color32::from_rgb(247, 248, 250),
|
||||
figure_line: Color32::from_rgb(111, 124, 143),
|
||||
figure_highlight: Color32::WHITE,
|
||||
figure_shadow: Color32::BLACK,
|
||||
figure_face_top: Color32::from_rgb(255, 255, 255),
|
||||
figure_face_left: Color32::from_rgb(228, 233, 240),
|
||||
figure_face_right: Color32::from_rgb(201, 210, 222),
|
||||
figure_card: Color32::from_rgb(255, 255, 255),
|
||||
figure_white: Color32::WHITE,
|
||||
figure_black: Color32::BLACK,
|
||||
figure_accent: Color32::from_rgb(35, 110, 173),
|
||||
figure_reference: Color32::from_rgb(138, 83, 0),
|
||||
figure_axis_x: Color32::from_rgb(214, 69, 69),
|
||||
figure_axis_y: Color32::from_rgb(46, 158, 91),
|
||||
figure_axis_z: Color32::from_rgb(47, 111, 208),
|
||||
figure_ok: Color32::from_rgb(47, 125, 91),
|
||||
figure_warn: Color32::from_rgb(201, 138, 0),
|
||||
figure_conflict: Color32::from_rgb(184, 74, 0),
|
||||
pref_well: Color32::from_rgb(238, 241, 245),
|
||||
pref_note: Color32::from_rgb(183, 74, 0),
|
||||
pref_note_fill: Color32::from_rgb(251, 235, 221),
|
||||
pref_on_accent: Color32::from_rgb(255, 255, 255),
|
||||
pref_ok: Color32::from_rgb(39, 107, 44),
|
||||
pref_ok_fill: Color32::from_rgb(229, 242, 230),
|
||||
pref_disabled: Color32::from_rgb(138, 148, 163),
|
||||
timeline_item: Color32::WHITE,
|
||||
timeline_item_error: Color32::from_rgb(250, 220, 220),
|
||||
timeline_marker: Color32::from_rgb(70, 76, 88),
|
||||
@@ -345,6 +371,27 @@ impl Tokens {
|
||||
t.dialog_label = t.text;
|
||||
t.selected_ink = Color32::from_rgb(156, 199, 245);
|
||||
t.selected_bg = Color32::from_rgb(42, 70, 110);
|
||||
t.figure_ground = Color32::from_rgb(28, 31, 38);
|
||||
t.figure_line = Color32::from_rgb(163, 174, 194);
|
||||
t.figure_face_top = Color32::from_rgb(107, 119, 144);
|
||||
t.figure_face_left = Color32::from_rgb(81, 91, 114);
|
||||
t.figure_face_right = Color32::from_rgb(57, 66, 86);
|
||||
t.figure_card = Color32::from_rgb(47, 52, 62);
|
||||
t.figure_accent = Color32::from_rgb(140, 190, 245);
|
||||
t.figure_reference = Color32::from_rgb(232, 194, 74);
|
||||
t.figure_axis_x = Color32::from_rgb(242, 122, 122);
|
||||
t.figure_axis_y = Color32::from_rgb(98, 212, 142);
|
||||
t.figure_axis_z = Color32::from_rgb(108, 168, 247);
|
||||
t.figure_ok = Color32::from_rgb(127, 209, 174);
|
||||
t.figure_warn = Color32::from_rgb(232, 194, 74);
|
||||
t.figure_conflict = Color32::from_rgb(240, 162, 122);
|
||||
t.pref_well = Color32::from_rgb(47, 52, 62);
|
||||
t.pref_note = Color32::from_rgb(240, 162, 122);
|
||||
t.pref_note_fill = Color32::from_rgb(74, 51, 38);
|
||||
t.pref_on_accent = Color32::from_rgb(15, 22, 32);
|
||||
t.pref_ok = Color32::from_rgb(127, 209, 174);
|
||||
t.pref_ok_fill = Color32::from_rgb(31, 58, 49);
|
||||
t.pref_disabled = Color32::from_rgb(138, 148, 163);
|
||||
t.construction_geom = icon_primary(crate::icon_svg::CONSTRUCTION_PRIMARY[1]);
|
||||
t.error = Color32::from_rgb(240, 100, 96);
|
||||
t.sketch_conflict = t.error;
|
||||
|
||||
@@ -202,12 +202,16 @@ fn map_plane_ref(m: &Mat, p: &mut PlaneRef) {
|
||||
}
|
||||
// Named origin planes and construction planes are the component's own.
|
||||
PlaneRef::Origin { .. } | PlaneRef::Construction { .. } => {}
|
||||
PlaneRef::Tangent { face, at } => {
|
||||
PlaneRef::Tangent { face, at, reference, .. } => {
|
||||
*at = apply_point(m, *at);
|
||||
map_geo_ref(m, face);
|
||||
if let Some(r) = reference {
|
||||
map_geo_ref(m, r);
|
||||
}
|
||||
}
|
||||
PlaneRef::Midplane { .. }
|
||||
| PlaneRef::TwoEdges { .. }
|
||||
| PlaneRef::AxisPoint { .. }
|
||||
| PlaneRef::ThreePoints { .. }
|
||||
| PlaneRef::AlongPath { .. }
|
||||
| PlaneRef::Perpendicular { .. } => {
|
||||
@@ -418,6 +422,19 @@ impl Document {
|
||||
false
|
||||
}
|
||||
|
||||
/// Is `c` (or one of its ancestors) marked out of the History bar (#10393)? The root (0)
|
||||
/// never is: its own features always show.
|
||||
pub fn component_history_hidden(&self, mut c: u64) -> bool {
|
||||
for _ in 0..1000 {
|
||||
match self.components.iter().find(|x| x.id == c) {
|
||||
Some(x) if x.history_hidden => return true,
|
||||
Some(x) if x.parent != 0 => c = x.parent,
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Would putting a placement of `component` into `parent` put it inside itself: is `parent`
|
||||
/// the component, or inside it, created in it or placed in it at any depth? Placements can sit
|
||||
/// apart from where their component was created ([`Self::component_within`] follows only
|
||||
|
||||
@@ -15,8 +15,10 @@ pub struct Canvas {
|
||||
pub name: String,
|
||||
/// `png` or `jpeg`.
|
||||
pub format: String,
|
||||
/// The image file, base64.
|
||||
pub data: String,
|
||||
/// The image file, base64. Shared rather than owned: every edit clones the document (the undo
|
||||
/// history keeps the old one), and an image of up to 16 MiB copied into each of the 200 undo
|
||||
/// states would hold gigabytes. Serialized as the same JSON string as before.
|
||||
pub data: std::sync::Arc<str>,
|
||||
/// Pixel size of the image.
|
||||
pub pixels: [u32; 2],
|
||||
pub plane: PlaneRef,
|
||||
|
||||
@@ -316,7 +316,7 @@ fn checked_plane(p: &PlaneRef, depth: usize) -> crate::Result<()> {
|
||||
checked_geo(g, depth + 1)?;
|
||||
}
|
||||
}
|
||||
PlaneRef::Midplane { a, b } | PlaneRef::TwoEdges { a, b } => {
|
||||
PlaneRef::Midplane { a, b } | PlaneRef::TwoEdges { a, b } | PlaneRef::AxisPoint { axis: a, point: b } => {
|
||||
checked_geo(a, depth + 1)?;
|
||||
checked_geo(b, depth + 1)?;
|
||||
}
|
||||
@@ -333,7 +333,12 @@ fn checked_plane(p: &PlaneRef, depth: usize) -> crate::Result<()> {
|
||||
checked_geo(b, depth + 1)?;
|
||||
checked_geo(c, depth + 1)?;
|
||||
}
|
||||
PlaneRef::Tangent { face, .. } => checked_geo(face, depth + 1)?,
|
||||
PlaneRef::Tangent { face, reference, .. } => {
|
||||
checked_geo(face, depth + 1)?;
|
||||
if let Some(r) = reference {
|
||||
checked_geo(r, depth + 1)?;
|
||||
}
|
||||
}
|
||||
PlaneRef::AlongPath { path, .. } => checked_geo(path, depth + 1)?,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
+113
-4
@@ -116,6 +116,11 @@ pub struct Construct {
|
||||
pub feature: u64,
|
||||
pub name: String,
|
||||
pub geom: ConstructGeom,
|
||||
/// The bodies it was built from, when every pick it reads lies on a body
|
||||
/// ([`source_bodies`]): a later Move of all of them carries it along (#10127). Empty when
|
||||
/// it also reads something fixed (a point, a plane, a sketch), which keeps it in place.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub bodies: Vec<String>,
|
||||
}
|
||||
|
||||
/// Everything a reference needs to be resolved.
|
||||
@@ -497,6 +502,48 @@ fn surface_normal(s: FaceSurface, p: Vec3) -> Option<(Vec3, Vec3)> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A point over `s` where the face turns towards `towards` (a reference plane's normal): around
|
||||
/// the axis of a cylinder, cone or torus, turned `angle` about it; straight at it on a sphere. Of
|
||||
/// the two sides, the one `at` is on (#8822).
|
||||
fn facing(s: FaceSurface, at: Vec3, towards: Vec3, angle: f64) -> Result<Vec3> {
|
||||
let side = |d: Vec3, w: Vec3| if d.dot(w) < 0.0 { d * -1.0 } else { d };
|
||||
let around = |axis: Vec3, w: Vec3| -> Result<(Vec3, f64)> {
|
||||
let d = (towards - axis * towards.dot(axis))
|
||||
.normalized()
|
||||
.ok_or_else(|| bad("the reference is square to the face's axis: every plane tangent to the face meets it at a right angle"))?;
|
||||
let d = d * angle.cos() + axis.cross(d) * angle.sin();
|
||||
let r = w - axis * w.dot(axis);
|
||||
Ok((side(d, r), r.len()))
|
||||
};
|
||||
if !angle.is_finite() {
|
||||
return Err(bad("the angle is not a number"));
|
||||
}
|
||||
Ok(match s {
|
||||
FaceSurface::Plane { .. } => at,
|
||||
FaceSurface::Cylinder { origin, axis, radius } => {
|
||||
let w = at - origin;
|
||||
let (d, _) = around(axis.into(), w)?;
|
||||
origin + axis * w.dot(axis) + d * radius
|
||||
}
|
||||
FaceSurface::Cone { apex, axis, .. } => {
|
||||
let w = at - apex;
|
||||
let (d, r) = around(axis.into(), w)?;
|
||||
apex + axis * w.dot(axis) + d * r.max(1e-6)
|
||||
}
|
||||
// On the outer equator, where the tube faces away from the axis.
|
||||
FaceSurface::Torus { center, axis, major, minor } => {
|
||||
let (d, _) = around(axis.into(), at - center)?;
|
||||
center + d * (major + minor)
|
||||
}
|
||||
FaceSurface::Sphere { center, radius } => {
|
||||
if angle.abs() > 1e-12 {
|
||||
return Err(bad("a sphere has no axis to turn the plane about: leave the angle out"));
|
||||
}
|
||||
center + side(towards, at - center) * radius
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn face_surface_at(ctx: &Ctx, g: &GeoRef) -> Result<(FaceSurface, Vec3)> {
|
||||
match g {
|
||||
GeoRef::Face { at, name } => {
|
||||
@@ -538,6 +585,46 @@ fn line_line(a: (Vec3, Vec3), b: (Vec3, Vec3)) -> Option<Vec3> {
|
||||
Some(((p + u * s) + (q + v * t)) * 0.5)
|
||||
}
|
||||
|
||||
/// The bodies a construction built from `refs` lies on: each face, edge and vertex pick's body,
|
||||
/// and the bodies of the constructions it is built from. Empty when any pick is not on a body
|
||||
/// (a fixed point, the origin, a plane, a world axis, a sketch) or is not found: such a
|
||||
/// construction is held by something a move of the bodies leaves where it is, so it stays.
|
||||
pub(crate) fn source_bodies(ctx: &Ctx, refs: &[&GeoRef]) -> Vec<String> {
|
||||
let mut out: Vec<String> = Vec::new();
|
||||
for g in refs {
|
||||
let found: Vec<String> = match g {
|
||||
GeoRef::Face { at, name } => face_of(ctx.st, *at, name).map(|(b, ..)| vec![b.definition_name().to_string()]).unwrap_or_default(),
|
||||
GeoRef::Edge { at, name } => edge_pick(ctx.st, *at, name).map(|(b, ..)| vec![b.definition_name().to_string()]).unwrap_or_default(),
|
||||
GeoRef::Vertex { .. } => {
|
||||
point(ctx, g).ok().and_then(|p| vertex_body(ctx.st, p)).map(|b| vec![b.definition_name().to_string()]).unwrap_or_default()
|
||||
}
|
||||
GeoRef::Axis { name } | GeoRef::ConstructPoint { name } => {
|
||||
ctx.st.construct.iter().find(|c| c.name.as_str() == AsRef::<str>::as_ref(name)).map(|c| c.bodies.clone()).unwrap_or_default()
|
||||
}
|
||||
_ => Vec::new(),
|
||||
};
|
||||
if found.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
for b in found {
|
||||
if !out.contains(&b) {
|
||||
out.push(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// The body with a vertex (an end of one of its edges) at `p`.
|
||||
fn vertex_body(st: &ModelState, p: Vec3) -> Option<&ModelBody> {
|
||||
bodies(st).find(|b| {
|
||||
let tol = (b.body.size() * 1e-3).max(1e-3);
|
||||
b.body
|
||||
.edges(tol)
|
||||
.is_ok_and(|edges| edges.iter().flat_map(|e| [e.points.first(), e.points.last()]).flatten().any(|v| v.dist(p) <= 1e-6 * (1.0 + p.len())))
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_axis(ctx: &Ctx, d: &AxisDef) -> Result<(Vec3, Vec3)> {
|
||||
match d {
|
||||
AxisDef::ThroughFace { face } => line(ctx, face),
|
||||
@@ -620,6 +707,16 @@ pub(crate) fn resolve_geo_plane(ctx: &Ctx, p: &PlaneRef) -> Result<Plane> {
|
||||
};
|
||||
Plane::new(o1, d1, n.cross(d1)).ok_or_else(degenerate)
|
||||
}
|
||||
PlaneRef::AxisPoint { axis, point: at } => {
|
||||
let (origin, dir) = line(ctx, axis)?;
|
||||
let dir = dir.normalized().ok_or_else(|| bad("the axis has no direction"))?;
|
||||
let delta = point(ctx, at)? - origin;
|
||||
let across = delta - dir * delta.dot(dir);
|
||||
if !across.len().is_finite() || across.len() < 1e-9 {
|
||||
return Err(bad("the point is on the axis: pick a point off the axis to define a unique plane"));
|
||||
}
|
||||
Plane::new(origin, dir, across).ok_or_else(degenerate)
|
||||
}
|
||||
PlaneRef::ThreePoints { a, b, c } => {
|
||||
let (pa, pb, pc) = (point(ctx, a)?, point(ctx, b)?, point(ctx, c)?);
|
||||
if (pb - pa).cross(pc - pa).len() < 1e-9 {
|
||||
@@ -627,9 +724,17 @@ pub(crate) fn resolve_geo_plane(ctx: &Ctx, p: &PlaneRef) -> Result<Plane> {
|
||||
}
|
||||
Plane::new(pa, pb - pa, pc - pa).ok_or_else(degenerate)
|
||||
}
|
||||
PlaneRef::Tangent { face, at } => {
|
||||
PlaneRef::Tangent { face, at, reference, angle } => {
|
||||
let (s, _) = face_surface_at(ctx, face)?;
|
||||
let (on, n) = surface_normal(s, *at).ok_or_else(|| bad("the point is on the face's axis"))?;
|
||||
let at = match reference {
|
||||
Some(r) => {
|
||||
let towards = plane(ctx, r)?.normal().normalized().ok_or_else(degenerate)?;
|
||||
let angle = angle.as_ref().map(|a| Document::eval_in(ctx.vals, a, Kind::Angle)).transpose()?.unwrap_or(0.0);
|
||||
facing(s, *at, towards, angle)?
|
||||
}
|
||||
None => *at,
|
||||
};
|
||||
let (on, n) = surface_normal(s, at).ok_or_else(|| bad("the point is on the face's axis"))?;
|
||||
Plane::from_normal(on, n).ok_or_else(degenerate)
|
||||
}
|
||||
PlaneRef::Perpendicular { plane: base, line: l } => {
|
||||
@@ -768,10 +873,14 @@ impl PlaneRef {
|
||||
/// The references of a plane built from model geometry.
|
||||
pub fn refs_mut(&mut self) -> Vec<&mut GeoRef> {
|
||||
match self {
|
||||
PlaneRef::Midplane { a, b } | PlaneRef::TwoEdges { a, b } => vec![a.as_mut(), b.as_mut()],
|
||||
PlaneRef::Midplane { a, b } | PlaneRef::TwoEdges { a, b } | PlaneRef::AxisPoint { axis: a, point: b } => vec![a.as_mut(), b.as_mut()],
|
||||
PlaneRef::Perpendicular { plane, line } => vec![plane.as_mut(), line.as_mut()],
|
||||
PlaneRef::ThreePoints { a, b, c } => vec![a.as_mut(), b.as_mut(), c.as_mut()],
|
||||
PlaneRef::Tangent { face, .. } => vec![face.as_mut()],
|
||||
PlaneRef::Tangent { face, reference, .. } => {
|
||||
let mut v = vec![face.as_mut()];
|
||||
v.extend(reference.as_deref_mut());
|
||||
v
|
||||
}
|
||||
PlaneRef::AlongPath { path, .. } => vec![path.as_mut()],
|
||||
PlaneRef::ThroughPoint { base, point } => {
|
||||
let mut refs = base.refs_mut();
|
||||
|
||||
+236
-11
@@ -97,10 +97,21 @@ pub enum PlaneRef {
|
||||
Midplane { a: Box<GeoRef>, b: Box<GeoRef> },
|
||||
/// Through two straight edges (lines) that lie in one plane.
|
||||
TwoEdges { a: Box<GeoRef>, b: Box<GeoRef> },
|
||||
/// Through a line (axis) and a point not on that line.
|
||||
AxisPoint { axis: Box<GeoRef>, point: Box<GeoRef> },
|
||||
/// Through three points.
|
||||
ThreePoints { a: Box<GeoRef>, b: Box<GeoRef>, c: Box<GeoRef> },
|
||||
/// Tangent to a face (cylinder, cone, sphere, torus) where the point `at` lies over it.
|
||||
Tangent { face: Box<GeoRef>, at: Vec3 },
|
||||
/// Tangent to a face (cylinder, cone, sphere, torus) where the point `at` lies over it. With
|
||||
/// a `reference` (a plane or planar face), it touches where the face turns towards the
|
||||
/// reference instead, turned by `angle` about the face's axis, on the side of `at` (#8822).
|
||||
Tangent {
|
||||
face: Box<GeoRef>,
|
||||
at: Vec3,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
reference: Option<Box<GeoRef>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
angle: Option<Expr>,
|
||||
},
|
||||
/// Square to an edge or sketch curve, a fraction `t` (expression, 0…1) of the way along it.
|
||||
AlongPath { path: Box<GeoRef>, t: Expr },
|
||||
/// Through a straight edge (or line, axis) and square to a plane (or planar face).
|
||||
@@ -1206,9 +1217,10 @@ pub enum FeatureKind {
|
||||
body: Option<BodyName>,
|
||||
},
|
||||
/// A perforation field (#732): round holes of `diameter` on a staggered (60°) or square grid of `pitch` over the
|
||||
/// whole flat sheet, kept `margin` (default: the diameter) clear of its edges, cut-outs and bend zones. Stored like
|
||||
/// a cosmetic thread: the holes go to the flat pattern, the DXF and the measured volume, not into the B-rep, so a
|
||||
/// panel can carry tens of thousands of them.
|
||||
/// whole flat sheet, or inside the closed profiles of `sketch` when it is given, kept `margin` (default: the
|
||||
/// diameter) clear of its edges, cut-outs, bend zones and the outline. Stored like a cosmetic thread: the holes go
|
||||
/// to the flat pattern, the DXF and the measured volume, not into the B-rep, so a panel can carry tens of thousands
|
||||
/// of them.
|
||||
SheetPerforate {
|
||||
#[walk(length = "Diameter")]
|
||||
diameter: Expr,
|
||||
@@ -1221,6 +1233,13 @@ pub enum FeatureKind {
|
||||
pattern: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
body: Option<BodyName>,
|
||||
/// The field's outline: a sketch on a face of the sheet. None: the whole sheet.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
sketch: Option<SketchId>,
|
||||
/// Which of the outline sketch's profiles bound the field. None: all of them.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[walk(profiles_of = sketch)]
|
||||
profiles: Option<ProfileSel>,
|
||||
},
|
||||
/// Fold a sheet along a line on its base face: a sketch line (followed when the sketch
|
||||
/// changes), else the points `a`, `b`.
|
||||
@@ -2019,7 +2038,7 @@ fn fair_keep_edges() -> bool {
|
||||
/// Triangles of one imported mesh body (millimetres). The arrays are shared by clones: every
|
||||
/// edit keeps the design as it was for undo, and a scanned part's triangles (hundreds of
|
||||
/// megabytes) are not copied for that.
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct MeshData {
|
||||
pub name: String,
|
||||
/// x, y, z per vertex.
|
||||
@@ -2030,6 +2049,20 @@ pub struct MeshData {
|
||||
pub color: Option<[f32; 3]>,
|
||||
}
|
||||
|
||||
/// The same mesh: shared arrays are the same without reading them, so the dirty check and
|
||||
/// autosave after an edit elsewhere do not walk a scan's millions of coordinates. Arrays that
|
||||
/// are not shared compare value by value, as before. Coordinates are finite (a design that
|
||||
/// holds others does not load), so the shortcut only skips work.
|
||||
impl PartialEq for MeshData {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
use std::sync::Arc;
|
||||
self.name == other.name
|
||||
&& self.color == other.color
|
||||
&& (Arc::ptr_eq(&self.triangles, &other.triangles) || self.triangles == other.triangles)
|
||||
&& (Arc::ptr_eq(&self.positions, &other.positions) || self.positions == other.positions)
|
||||
}
|
||||
}
|
||||
|
||||
/// Points of a sketch, by id.
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, solidcraft_derive::Walk)]
|
||||
pub struct SketchPoints {
|
||||
@@ -2067,6 +2100,11 @@ pub struct Component {
|
||||
/// Free text properties of the definition (material grade, finish, supplier…), by name.
|
||||
#[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")]
|
||||
pub properties: std::collections::BTreeMap<String, String>,
|
||||
/// Keeps this component's features (and its children's) out of the History bar (#10393).
|
||||
/// The features still rebuild and the component still shows in the Browser; imported copies
|
||||
/// whose features only exist to rebuild the copy can carry this flag.
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
pub history_hidden: bool,
|
||||
}
|
||||
|
||||
/// One loft section: profiles of a sketch.
|
||||
@@ -2202,7 +2240,11 @@ pub enum PatternKind {
|
||||
reversed2: bool,
|
||||
},
|
||||
Circular {
|
||||
origin: Position,
|
||||
/// A point on the axis the copies turn about: numbers, or length expressions it follows
|
||||
/// as the parameters change (#10852).
|
||||
#[serde(with = "point_expr")]
|
||||
#[walk(placement)]
|
||||
origin: PointExpr,
|
||||
axis: Dir,
|
||||
#[walk(unitless = "Quantity")]
|
||||
count: Expr,
|
||||
@@ -2412,6 +2454,167 @@ impl FeatureKind {
|
||||
FeatureKind::MeshConvert { .. } => "MeshConvertFeature",
|
||||
}
|
||||
}
|
||||
|
||||
/// Design-file variant key (e.g. `"extrude"`, `"box"`, `"sheet_flange"`).
|
||||
pub fn kind_key(&self) -> &'static str {
|
||||
match self {
|
||||
FeatureKind::BodyTransfer { .. } => "body_transfer",
|
||||
FeatureKind::Sketch { .. } => "sketch",
|
||||
FeatureKind::Extrude { .. } => "extrude",
|
||||
FeatureKind::Revolve { .. } => "revolve",
|
||||
FeatureKind::Fillet { .. } => "fillet",
|
||||
FeatureKind::Chamfer { .. } => "chamfer",
|
||||
FeatureKind::Box { .. } => "box",
|
||||
FeatureKind::Cylinder { .. } => "cylinder",
|
||||
FeatureKind::Sphere { .. } => "sphere",
|
||||
FeatureKind::Torus { .. } => "torus",
|
||||
FeatureKind::Cone { .. } => "cone",
|
||||
FeatureKind::Combine { .. } => "combine",
|
||||
FeatureKind::Pattern { .. } => "pattern",
|
||||
FeatureKind::Mirror { .. } => "mirror",
|
||||
FeatureKind::Hole { .. } => "hole",
|
||||
FeatureKind::Thread { .. } => "thread",
|
||||
FeatureKind::Loft { .. } => "loft",
|
||||
FeatureKind::Sweep { .. } => "sweep",
|
||||
FeatureKind::Shell { .. } => "shell",
|
||||
FeatureKind::Draft { .. } => "draft",
|
||||
FeatureKind::ConstructionPlane { .. } => "construction_plane",
|
||||
FeatureKind::Split { .. } => "split",
|
||||
FeatureKind::ConstructionAxis { .. } => "construction_axis",
|
||||
FeatureKind::ConstructionPoint { .. } => "construction_point",
|
||||
FeatureKind::SplitFace { .. } => "split_face",
|
||||
FeatureKind::Scale { .. } => "scale",
|
||||
FeatureKind::OffsetFace { .. } => "offset_face",
|
||||
FeatureKind::DeleteFace { .. } => "delete_face",
|
||||
FeatureKind::BoundingSolid { .. } => "bounding_solid",
|
||||
FeatureKind::Pipe { .. } => "pipe",
|
||||
FeatureKind::FrameMember { .. } => "frame_member",
|
||||
FeatureKind::Emboss { .. } => "emboss",
|
||||
FeatureKind::Rib { .. } => "rib",
|
||||
FeatureKind::ReplaceFace { .. } => "replace_face",
|
||||
FeatureKind::Align { .. } => "align",
|
||||
FeatureKind::Coil { .. } => "coil",
|
||||
FeatureKind::SheetBase { .. } => "sheet_base",
|
||||
FeatureKind::SheetContour { .. } => "sheet_contour",
|
||||
FeatureKind::SheetFlange { .. } => "sheet_flange",
|
||||
FeatureKind::SheetHem { .. } => "sheet_hem",
|
||||
FeatureKind::SheetPerforate { .. } => "sheet_perforate",
|
||||
FeatureKind::SheetFold { .. } => "sheet_fold",
|
||||
FeatureKind::SheetPath { .. } => "sheet_path",
|
||||
FeatureKind::SheetUnfold { .. } => "sheet_unfold",
|
||||
FeatureKind::SheetConvert { .. } => "sheet_convert",
|
||||
FeatureKind::Boss { .. } => "boss",
|
||||
FeatureKind::Lip { .. } => "lip",
|
||||
FeatureKind::SnapFit { .. } => "snap_fit",
|
||||
FeatureKind::Rest { .. } => "rest",
|
||||
FeatureKind::BoundaryFill { .. } => "boundary_fill",
|
||||
FeatureKind::Remove { .. } => "remove",
|
||||
FeatureKind::Patch { .. } => "patch",
|
||||
FeatureKind::Stitch { .. } => "stitch",
|
||||
FeatureKind::Thicken { .. } => "thicken",
|
||||
FeatureKind::SurfaceExtrude { .. } => "surface_extrude",
|
||||
FeatureKind::Unstitch { .. } => "unstitch",
|
||||
FeatureKind::ReverseNormal { .. } => "reverse_normal",
|
||||
FeatureKind::SurfaceUntrim { .. } => "surface_untrim",
|
||||
FeatureKind::SurfaceRevolve { .. } => "surface_revolve",
|
||||
FeatureKind::SurfaceSweep { .. } => "surface_sweep",
|
||||
FeatureKind::SurfaceLoft { .. } => "surface_loft",
|
||||
FeatureKind::SurfaceOffset { .. } => "surface_offset",
|
||||
FeatureKind::SurfaceRuled { .. } => "surface_ruled",
|
||||
FeatureKind::SurfaceExtend { .. } => "surface_extend",
|
||||
FeatureKind::SurfaceBlend { .. } => "surface_blend",
|
||||
FeatureKind::SurfaceBoundary { .. } => "surface_boundary",
|
||||
FeatureKind::SurfaceNetwork { .. } => "surface_network",
|
||||
FeatureKind::SurfaceMatch { .. } => "surface_match",
|
||||
FeatureKind::SurfaceFair { .. } => "surface_fair",
|
||||
FeatureKind::SurfaceTrim { .. } => "surface_trim",
|
||||
FeatureKind::Move { .. } => "move",
|
||||
FeatureKind::Import { .. } => "import",
|
||||
FeatureKind::MeshConvert { .. } => "mesh_convert",
|
||||
FeatureKind::MeshImport { .. } => "mesh_import",
|
||||
}
|
||||
}
|
||||
|
||||
/// Map a display or Rust type name (e.g. `"HoleFeature"`, `"EdgeFlangeFeature"`)
|
||||
/// to the canonical design-file variant key (`"hole"`, `"sheet_flange"`).
|
||||
pub fn type_name_to_kind_key(name: &str) -> Option<&'static str> {
|
||||
Some(match name {
|
||||
"BodyTransferFeature" => "body_transfer",
|
||||
"Sketch" => "sketch",
|
||||
"ExtrudeFeature" => "extrude",
|
||||
"RevolveFeature" => "revolve",
|
||||
"FilletFeature" => "fillet",
|
||||
"ChamferFeature" => "chamfer",
|
||||
"BoxFeature" => "box",
|
||||
"CylinderFeature" => "cylinder",
|
||||
"SphereFeature" => "sphere",
|
||||
"TorusFeature" => "torus",
|
||||
"ConeFeature" => "cone",
|
||||
"CombineFeature" => "combine",
|
||||
"RectangularPatternFeature" | "PathPatternFeature" | "CircularPatternFeature" => "pattern",
|
||||
"MirrorFeature" => "mirror",
|
||||
"ConstructionPlane" => "construction_plane",
|
||||
"HoleFeature" => "hole",
|
||||
"ThreadFeature" => "thread",
|
||||
"LoftFeature" => "loft",
|
||||
"SweepFeature" => "sweep",
|
||||
"ShellFeature" => "shell",
|
||||
"DraftFeature" => "draft",
|
||||
"SplitBodyFeature" => "split",
|
||||
"SplitFaceFeature" => "split_face",
|
||||
"ConstructionAxis" => "construction_axis",
|
||||
"ConstructionPoint" => "construction_point",
|
||||
"MoveFeature" => "move",
|
||||
"ScaleFeature" => "scale",
|
||||
"OffsetFacesFeature" => "offset_face",
|
||||
"DeleteFaceFeature" => "delete_face",
|
||||
"OffsetSurfaceFeature" => "surface_offset",
|
||||
"BoundingSolidFeature" => "bounding_solid",
|
||||
"PipeFeature" => "pipe",
|
||||
"FrameMemberFeature" => "frame_member",
|
||||
"EmbossFeature" => "emboss",
|
||||
"BossFeature" => "boss",
|
||||
"LipFeature" | "GrooveFeature" => "lip",
|
||||
"SnapFitFeature" => "snap_fit",
|
||||
"RestFeature" => "rest",
|
||||
"BoundaryFillFeature" => "boundary_fill",
|
||||
"BaseFlangeFeature" => "sheet_base",
|
||||
"ContourFlangeFeature" => "sheet_contour",
|
||||
"EdgeFlangeFeature" => "sheet_flange",
|
||||
"HemFeature" => "sheet_hem",
|
||||
"PerforateFeature" => "sheet_perforate",
|
||||
"FoldFeature" => "sheet_fold",
|
||||
"PathFlangeFeature" => "sheet_path",
|
||||
"UnfoldFeature" | "RefoldFeature" => "sheet_unfold",
|
||||
"ConvertToSheetMetalFeature" => "sheet_convert",
|
||||
"CoilFeature" => "coil",
|
||||
"RibFeature" | "WebFeature" => "rib",
|
||||
"ReplaceFaceFeature" => "replace_face",
|
||||
"AlignFeature" => "align",
|
||||
"RemoveFeature" => "remove",
|
||||
"PatchFeature" => "patch",
|
||||
"SurfaceExtrudeFeature" => "surface_extrude",
|
||||
"SurfaceRevolveFeature" => "surface_revolve",
|
||||
"SurfaceSweepFeature" => "surface_sweep",
|
||||
"SurfaceLoftFeature" => "surface_loft",
|
||||
"BlendFeature" => "surface_blend",
|
||||
"BoundaryFeature" => "surface_boundary",
|
||||
"MatchFeature" => "surface_match",
|
||||
"FairFeature" => "surface_fair",
|
||||
"StitchFeature" => "stitch",
|
||||
"ThickenFeature" => "thicken",
|
||||
"TrimFeature" => "surface_trim",
|
||||
"ExtendFeature" => "surface_extend",
|
||||
"RuledSurfaceFeature" => "surface_ruled",
|
||||
"UnstitchFeature" => "unstitch",
|
||||
"ReverseNormalFeature" => "reverse_normal",
|
||||
"UntrimFeature" => "surface_untrim",
|
||||
"BaseFeature" => "import",
|
||||
"MeshFeature" => "mesh_import",
|
||||
"MeshConvertFeature" => "mesh_convert",
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
/// Default name prefix (`Extrude` → `Extrude1`).
|
||||
pub fn base_name(&self) -> &'static str {
|
||||
match self {
|
||||
@@ -3143,8 +3346,8 @@ impl Document {
|
||||
|
||||
/// The single body rename (old name, new name) that turns `previous` into this design,
|
||||
/// when that is all that changed: the features' stored body names (`made` gives the names
|
||||
/// each feature's bodies have now, by feature id) and what [`Document::rename_body_keys`]
|
||||
/// renames. `None` for any other change.
|
||||
/// each feature's bodies have now, by feature id), typed body references, participants and
|
||||
/// what [`Document::rename_body_keys`] renames. `None` for any other change.
|
||||
pub fn body_rename_from(&self, previous: &Document, made: impl Fn(u64) -> Vec<String>) -> Option<(String, String)> {
|
||||
if self.features.len() != previous.features.len() {
|
||||
return None;
|
||||
@@ -3175,8 +3378,16 @@ impl Document {
|
||||
}
|
||||
let (old, new) = rename?;
|
||||
let mut expected = previous.clone();
|
||||
let bodies = std::collections::BTreeMap::from([(old.clone(), new.clone())]);
|
||||
for (f, g) in expected.features.iter_mut().zip(&self.features) {
|
||||
f.body_names.clone_from(&g.body_names);
|
||||
// Updating references to the renamed body changes no feature input other than its
|
||||
// name (#6287). Keep the full comparison below: geometry, expressions, sketch links
|
||||
// and face anchors must still agree before the model may reuse its geometry.
|
||||
f.kind.remap_bodies(&bodies);
|
||||
for body in f.participants.iter_mut().filter(|body| **body == old) {
|
||||
body.clone_from(&new);
|
||||
}
|
||||
}
|
||||
expected.rename_body_keys(&old, &new);
|
||||
(expected == *self).then_some((old, new))
|
||||
@@ -3471,6 +3682,8 @@ impl Document {
|
||||
}
|
||||
|
||||
pub fn feature(&self, id: u64) -> Option<&Feature> {
|
||||
#[cfg(test)]
|
||||
crate::eval::cost_tests::count_feature_search();
|
||||
self.features.iter().find(|f| f.id == id)
|
||||
}
|
||||
pub fn feature_mut(&mut self, id: u64) -> Option<&mut Feature> {
|
||||
@@ -3573,12 +3786,18 @@ impl Document {
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let id = self.next_id.max(1);
|
||||
self.next_id = id.checked_add(1).ok_or_else(|| DocError::Invalid("too many items in the design".into()))?;
|
||||
let name = match name {
|
||||
Some(n) if !n.trim().is_empty() => n.trim().to_string(),
|
||||
_ => self.unique_name(kind.base_name()),
|
||||
};
|
||||
// Plane references share the feature namespace. A duplicate would silently select
|
||||
// the first feature. Other shared names (a sketch and its extrude) remain supported.
|
||||
let is_plane = matches!(kind, FeatureKind::ConstructionPlane { .. });
|
||||
if self.features.iter().any(|f| f.name == name && (is_plane || matches!(f.kind, FeatureKind::ConstructionPlane { .. }))) {
|
||||
return Err(DocError::Invalid(format!("another feature is called `{name}`")));
|
||||
}
|
||||
let id = self.next_id.max(1);
|
||||
self.next_id = id.checked_add(1).ok_or_else(|| DocError::Invalid("too many items in the design".into()))?;
|
||||
let f = Feature {
|
||||
id,
|
||||
name,
|
||||
@@ -3976,6 +4195,7 @@ impl Document {
|
||||
}
|
||||
PlaneRef::Midplane { .. }
|
||||
| PlaneRef::TwoEdges { .. }
|
||||
| PlaneRef::AxisPoint { .. }
|
||||
| PlaneRef::ThreePoints { .. }
|
||||
| PlaneRef::Tangent { .. }
|
||||
| PlaneRef::AlongPath { .. }
|
||||
@@ -4173,6 +4393,11 @@ impl Document {
|
||||
analysis: solidcraft_sketch::Analysis,
|
||||
) -> Result<solidcraft_sketch::SolveReport> {
|
||||
self.apply_dimension_values(vals, sk)?;
|
||||
// A text built anew since it was stored (an expression gave it other words) has other
|
||||
// outline entities: the stored sketch is taken with this sketch's, so the points still
|
||||
// pair up for the side checks and the walk below.
|
||||
let paired = moved_from.and_then(|from| sk.stored_with_rebuilt_texts(from));
|
||||
let moved_from = paired.as_ref().or(moved_from);
|
||||
let was = |sk: &Sketch| moved_from.and_then(|from| sk.positions_in(from));
|
||||
let offsets = !sk.offsets.is_empty() || !sk.freeform_offsets.is_empty();
|
||||
// Offsets always require another solve after refresh. Only that last report is
|
||||
|
||||
+386
-41
@@ -40,6 +40,11 @@ pub struct ModelBody {
|
||||
joint_faces: Arc<OnceLock<JointFaces>>,
|
||||
/// Lazy canonical reference triangles for a proper rigid occurrence placement.
|
||||
mesh_placement: Option<Arc<placed_mesh::PlacedMesh>>,
|
||||
/// For a mirrored copy: its faces as a world state shows them, a rigid move of the source's
|
||||
/// [`placed_mesh::OppositeHand`]. Display only: the model reads the copy's own triangles.
|
||||
mirrored_mesh: Option<Arc<placed_mesh::PlacedMesh>>,
|
||||
/// The first mirrored copy made of this body, shared by its clones.
|
||||
opposite_hand: Arc<OnceLock<placed_mesh::OppositeHand>>,
|
||||
/// The display plan (none when the body has no plan: a mesh body, or one the planner
|
||||
/// refuses), shared like the mesh. A GPU viewport draws it.
|
||||
plan: Arc<[OnceLock<Option<Arc<TessPlan>>>; 4]>,
|
||||
@@ -86,6 +91,8 @@ impl ModelBody {
|
||||
mesh: Arc::new(OnceLock::new()),
|
||||
joint_faces: Arc::new(OnceLock::new()),
|
||||
mesh_placement: None,
|
||||
mirrored_mesh: None,
|
||||
opposite_hand: Arc::new(OnceLock::new()),
|
||||
plan: Arc::new(Default::default()),
|
||||
plan_mesh: Arc::new(Default::default()),
|
||||
key: 0,
|
||||
@@ -458,7 +465,12 @@ fn thread_on_impl(
|
||||
let (body, cyl) = st
|
||||
.bodies
|
||||
.iter()
|
||||
.filter_map(|b| kernel::cylinder_face_at(&b.body, p).map(|c| (b.name.clone(), c)))
|
||||
.filter(|b| !more::beyond_cylinder_reach(b, p))
|
||||
.filter_map(|b| {
|
||||
#[cfg(test)]
|
||||
cost_tests::count(|c| c.cylinder_search += 1);
|
||||
kernel::cylinder_face_at(&b.body, p).map(|c| (b.name.clone(), c))
|
||||
})
|
||||
.min_by(|a, b| {
|
||||
let dist = |c: &kernel::CylinderFace| ((p - c.axis_point - c.axis * (p - c.axis_point).dot(c.axis)).len() - c.radius).abs();
|
||||
dist(&a.1).total_cmp(&dist(&b.1))
|
||||
@@ -993,6 +1005,7 @@ impl ModelState {
|
||||
&& let Some(lines) = olds.first().and_then(|x| x.body.split_lines())
|
||||
{
|
||||
b.body = b.body.clone().with_split_lines(Some(lines.clone()));
|
||||
b.mirrored_mesh = None;
|
||||
}
|
||||
// An imported look outlives the operations on the body.
|
||||
if b.body.paint().is_none()
|
||||
@@ -1213,6 +1226,8 @@ impl ModelState {
|
||||
}
|
||||
|
||||
pub fn body(&self, name: &str) -> Option<&ModelBody> {
|
||||
#[cfg(test)]
|
||||
cost_tests::count(|c| c.body_searches += 1);
|
||||
self.bodies.iter().find(|b| b.name == name)
|
||||
}
|
||||
|
||||
@@ -1808,6 +1823,8 @@ fn hash_placement(m: &Mat, h: &mut impl Hasher) {
|
||||
fn mirror_placements_digest(doc: &Document, f: &Feature, placements: &[Vec<u64>], bodies: &[crate::BodyName]) -> u64 {
|
||||
let mut h = std::collections::hash_map::DefaultHasher::new();
|
||||
doc.component_transform(f.component).iter().flatten().for_each(|x| x.to_bits().hash(&mut h));
|
||||
// A feature added after the mirror that builds on its copies needs them made for that.
|
||||
placement_reuse::copies_only_shown(doc, f).hash(&mut h);
|
||||
for placed in placements.iter().map(|path| mirror_copy_frame(doc, path)) {
|
||||
placed.is_some().hash(&mut h);
|
||||
if let Some((component, frame, picked)) = placed {
|
||||
@@ -1958,13 +1975,7 @@ fn placement_digest(doc: &Document, f: &Feature, st: Option<&ModelState>) -> u64
|
||||
None => u8::MAX.hash(&mut h),
|
||||
}
|
||||
}
|
||||
for name in &reads.bodies {
|
||||
name.hash(&mut h);
|
||||
match st.and_then(|st| st.body(name)) {
|
||||
Some(b) => hash_placement(&crate::mat_mul(&inverse, &body_world(doc, b)), &mut h),
|
||||
None => u8::MAX.hash(&mut h),
|
||||
}
|
||||
}
|
||||
hash_placed_bodies(doc, st, &reads.bodies, &inverse, &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() {
|
||||
@@ -1974,6 +1985,38 @@ fn placement_digest(doc: &Document, f: &Feature, st: Option<&ModelState>) -> u64
|
||||
h.finish() | 1
|
||||
}
|
||||
|
||||
/// The bodies part of [`placement_digest`]: each body read, in name order, and where it is placed
|
||||
/// relative to the reading frame `inverse` (`u8::MAX` for one not in `st`), as [`body_world`]
|
||||
/// places it. The bodies, their components and the components' frames are looked up once: a
|
||||
/// join or cut without targets reads every body of its component, and searching the model, the
|
||||
/// timeline and the placements again for each of them made every evaluation, even one served
|
||||
/// from the cache, quadratic in the bodies.
|
||||
fn hash_placed_bodies(doc: &Document, st: Option<&ModelState>, names: &std::collections::BTreeSet<String>, inverse: &Mat, h: &mut impl Hasher) {
|
||||
let (Some(st), false) = (st, names.is_empty()) else {
|
||||
names.iter().for_each(|name| (name, u8::MAX).hash(h));
|
||||
return;
|
||||
};
|
||||
// The first body of a name, as `ModelState::body` finds it.
|
||||
let mut index: std::collections::HashMap<&str, usize> = std::collections::HashMap::with_capacity(st.bodies.len());
|
||||
for (i, b) in st.bodies.iter().enumerate() {
|
||||
index.entry(b.name.as_str()).or_insert(i);
|
||||
}
|
||||
let components = doc.model_body_components(st);
|
||||
let mut frames: std::collections::HashMap<u64, Mat> = std::collections::HashMap::new();
|
||||
for name in names {
|
||||
name.hash(h);
|
||||
let found = index.get(name.as_str()).and_then(|&i| Some((st.bodies.get(i)?, *components.get(i)?)));
|
||||
match found {
|
||||
Some((b, component)) => {
|
||||
let m = *frames.entry(component).or_insert_with(|| doc.component_transform(component));
|
||||
let world = doc.model_body_offset(b).map(|offset| crate::mat_mul(&m, offset)).unwrap_or(m);
|
||||
hash_placement(&crate::mat_mul(inverse, &world), h);
|
||||
}
|
||||
None => u8::MAX.hash(h),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Every occurrence's placement and every body offset (a feature read from a frame that cannot
|
||||
/// be inverted).
|
||||
fn placements_hash(doc: &Document) -> u64 {
|
||||
@@ -2385,6 +2428,16 @@ impl Model {
|
||||
_ => mats,
|
||||
};
|
||||
next.stamp(&state, f, key, mats);
|
||||
// Evaluated again only for what came before it (a mirror moved by a drag).
|
||||
if let Some(old) = self.results.get(i).filter(|old| old.id == f.id) {
|
||||
let made = old.state.bodies.iter().filter(|b| b.feature == f.id).map(|b| (b.name.as_str(), b));
|
||||
let made: std::collections::HashMap<&str, &ModelBody> = made.collect();
|
||||
for b in next.bodies.iter_mut().filter(|b| b.feature == f.id) {
|
||||
if let Some(old) = made.get(b.name.as_str()) {
|
||||
b.keep_meshes_of(old);
|
||||
}
|
||||
}
|
||||
}
|
||||
for b in &mut next.bodies {
|
||||
if !b.transferred {
|
||||
b.component = match doc.body_components.get(&b.name) {
|
||||
@@ -3260,10 +3313,29 @@ fn apply_traced_with(
|
||||
let affected: std::collections::BTreeMap<String, usize> =
|
||||
target_idx.iter().filter_map(|i| state.bodies.get(*i)).map(|b| (b.name.clone(), b.body.lumps().map_or(1, |l| l.len()))).collect();
|
||||
let mut remove = Vec::new();
|
||||
// A cut hands a body its tools cannot reach back as it was, but only after a whole
|
||||
// boolean: in a design of many bodies that was most of a hole's time (#10225). Such a
|
||||
// body is left as it is. Its box and the tools' are their meshes' grown past how far
|
||||
// the exact faces can curve out from them (a dome rises above the outline of its
|
||||
// edges), so a tool that touches a body, or only comes near it, still meets it in the
|
||||
// boolean. A body shown in another frame, and a mesh body, are cut as before; an
|
||||
// intersect removes the bodies it misses, and is left as it was.
|
||||
let reach: Option<Vec<Option<solidcraft_geom::Aabb3>>> =
|
||||
(op == Operation::Cut && !tools.iter().any(|t| t.body.is_mesh())).then(|| tools.iter().map(|t| more::body_box(&t.body)).collect());
|
||||
for &ti in &target_idx {
|
||||
let Some(mut acc) = state.bodies.get(ti).map(|b| Some(b.traced())) else { continue };
|
||||
// The tools meet the target where it is shown: in its frame (#3383).
|
||||
let into = state.bodies.get(ti).and_then(|b| tool_frame(&b.name));
|
||||
if let (Some(reach), None, Some(mb)) = (&reach, into, state.bodies.get(ti))
|
||||
&& !reach.is_empty()
|
||||
&& !mb.body.is_mesh()
|
||||
&& let Some(target) = more::mesh_box(mb)
|
||||
&& reach.iter().all(|tool| more::boxes_apart(&Some(target), tool))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
#[cfg(test)]
|
||||
cost_tests::count(|c| c.cut += 1);
|
||||
for t in &tools {
|
||||
let t = &tool_in(t, into)?;
|
||||
acc = match acc {
|
||||
@@ -3864,8 +3936,9 @@ fn body_at(st: &ModelState, body: &Option<crate::BodyName>, points: &[Vec3]) ->
|
||||
|
||||
/// A hole stays on its face when the model changes: a position now inside a body moves back
|
||||
/// against the drilling direction to the surface, one now above it moves down onto it. Only the
|
||||
/// bodies in `scope` count (every body when it is empty, see [`hole_scope`]).
|
||||
fn hole_on_surface(st: &ModelState, p: Vec3, dir: Vec3, scope: &[String]) -> Vec3 {
|
||||
/// bodies in `scope` count (every body when it is empty, see [`hole_scope`]). Edit Hole seeds from
|
||||
/// this point so the dialog's face pick matches where evaluation drills (#10689).
|
||||
pub fn hole_on_surface(st: &ModelState, p: Vec3, dir: Vec3, scope: &[String]) -> Vec3 {
|
||||
let size = st.bodies.iter().map(|b| b.body.size()).fold(1.0, f64::max);
|
||||
let eps = size * 1e-6;
|
||||
let mut best: Option<Vec3> = None;
|
||||
@@ -3913,6 +3986,18 @@ fn faces_through(st: &ModelState, p: Vec3, reach: f64) -> Vec<(f64, Vec3, Option
|
||||
((v - axis * v.dot(axis)).normalized(), None)
|
||||
}
|
||||
Some(kernel::FaceSurface::Sphere { center, .. }) => ((p - center).normalized(), None),
|
||||
// Square to the line through the apex, not the nearest facet's normal (#10698).
|
||||
Some(kernel::FaceSurface::Cone { apex, axis, half_angle }) => {
|
||||
let v = p - apex;
|
||||
let out = (v - axis * v.dot(axis)).normalized();
|
||||
(out.and_then(|u| (u * half_angle.cos() - axis * half_angle.sin()).normalized()), None)
|
||||
}
|
||||
// Away from the nearest point of the ring the tube goes round.
|
||||
Some(kernel::FaceSurface::Torus { center, axis, major, .. }) => {
|
||||
let v = p - center;
|
||||
let ring = (v - axis * v.dot(axis)).normalized().map(|u| center + u * major);
|
||||
(ring.and_then(|q| (p - q).normalized()), None)
|
||||
}
|
||||
_ => (None, None),
|
||||
};
|
||||
let n = match exact {
|
||||
@@ -4282,7 +4367,8 @@ fn blend_edges_on(mb: &ModelBody, f: &Feature, edges: &[crate::Position], idx: &
|
||||
Some((_, Err(_))) | None => {
|
||||
let mut w = None;
|
||||
let refs: Vec<EdgeRef> = f.edge_refs.get(i).cloned().into_iter().collect();
|
||||
let skipped = format!("edge `{}` no longer exists and was skipped", stored.map(String::as_str).unwrap_or("?"));
|
||||
let skipped =
|
||||
format!("edge `{}` no longer exists and was skipped", stored.map_or_else(|| "?".into(), |n| crate::name_lookup::quoted(n)));
|
||||
// The name is gone. A pick on the axis of a round must not fall through to the
|
||||
// tangency seam (#775). A sharp edge that moved still resolves from the point,
|
||||
// which an edit relies on.
|
||||
@@ -4291,7 +4377,7 @@ fn blend_edges_on(mb: &ModelBody, f: &Feature, edges: &[crate::Position], idx: &
|
||||
} else if let Ok(q) = resolve_edges(&mb.body, &[p], &refs, &mut w) {
|
||||
out.extend(q);
|
||||
notes.push(match stored {
|
||||
Some(n) => format!("edge `{n}` no longer exists; using the edge at the picked point"),
|
||||
Some(n) => format!("edge `{}` no longer exists; using the edge at the picked point", crate::name_lookup::quoted(n)),
|
||||
None => "an edge reference without a name was found by its point".into(),
|
||||
});
|
||||
} else {
|
||||
@@ -4985,6 +5071,29 @@ pub fn revolve_extent_with_convention(
|
||||
symmetric: bool,
|
||||
to: Option<Vec3>,
|
||||
convention: crate::RevolveConvention,
|
||||
) -> Result<RevolveExtent> {
|
||||
let extent = revolve_extent_for_handle(st, plane, regions, (origin, axis), (angle, angle2), symmetric, to, convention)?;
|
||||
// A solid needs an open sweep inside one full turn. Zero and a turn past 360° keep a
|
||||
// handle frame (`revolve_extent_for_handle`) and still fail here, with the same message.
|
||||
if !(extent.sweep.abs() > 1e-9 && extent.sweep.abs() <= std::f64::consts::TAU + 1e-9) {
|
||||
return Err(DocError::Invalid("the revolve angle must be more than 0 and at most 360°".into()));
|
||||
}
|
||||
Ok(extent)
|
||||
}
|
||||
|
||||
/// Where the angle handle sits. A sweep of zero, or past a full turn, builds no solid and
|
||||
/// [`revolve_extent_with_convention`] refuses it. The boundary is still a point on the circle,
|
||||
/// and the grab point stays there so the angle can be dragged back into range.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn revolve_extent_for_handle(
|
||||
st: &ModelState,
|
||||
plane: &Plane,
|
||||
regions: &[Region2],
|
||||
(origin, axis): (Vec3, Vec3),
|
||||
(angle, angle2): (f64, Option<f64>),
|
||||
symmetric: bool,
|
||||
to: Option<Vec3>,
|
||||
convention: crate::RevolveConvention,
|
||||
) -> Result<RevolveExtent> {
|
||||
let (start, sweep) = match (to, angle2, symmetric) {
|
||||
(Some(point), _, _) => {
|
||||
@@ -5024,7 +5133,7 @@ pub fn revolve_extent_with_convention(
|
||||
(None, None, true) => (-angle / 2.0, angle),
|
||||
(None, None, false) => (0.0, angle),
|
||||
};
|
||||
if !(sweep.is_finite() && start.is_finite() && sweep.abs() > 1e-9 && sweep.abs() <= std::f64::consts::TAU + 1e-9) {
|
||||
if !(sweep.is_finite() && start.is_finite()) {
|
||||
return Err(DocError::Invalid("the revolve angle must be more than 0 and at most 360°".into()));
|
||||
}
|
||||
let points: Vec<Vec2> = regions.iter().flat_map(|r| r.outer.polyline(1e-2)).collect();
|
||||
@@ -5225,11 +5334,15 @@ const APART_GAP: f64 = 1e-3;
|
||||
|
||||
/// The tool bodies a feature adds or removes (extrude, revolve, primitives), in the current state.
|
||||
/// `to_body` is the body a To Object extent runs to (an extrude), when it found one.
|
||||
/// `copy` builds the tool of a pattern's or mirror's source: the state already holds the source
|
||||
/// feature's own material, so a join end that reads as buried in the target there may be bare
|
||||
/// material where the copy lands — its ends are left as drawn (#6452).
|
||||
fn feature_tools_with_constructor(
|
||||
doc: &Document,
|
||||
vals: &BTreeMap<String, Value>,
|
||||
f: &Feature,
|
||||
st: &ModelState,
|
||||
copy: bool,
|
||||
constructor: &mut Option<ExtrudeConstructor>,
|
||||
to_body: &mut Option<String>,
|
||||
) -> Result<Vec<Traced>> {
|
||||
@@ -5416,7 +5529,26 @@ fn feature_tools_with_constructor(
|
||||
let (mut lo, mut hi) = if a <= b { (a, b) } else { (b, a) };
|
||||
// To a curved (or slanted) face: run right through the target body, then keep what
|
||||
// lies before its surface (the tool less the body, the piece at the start).
|
||||
let curved = curved_face(&tst, to_face, Some(n));
|
||||
let mut curved = curved_face(&tst, to_face, Some(n));
|
||||
// A Cut to the far side of a curved wall (a window through a duct's side): ending the
|
||||
// tool on that face leaves it lying in the wall's face, where the boolean fails, and
|
||||
// the tool less the body keeps only the air before the wall, which cuts nothing. Where
|
||||
// the target's material ends at the face under the whole profile, the tool ends flat
|
||||
// in the gap beyond it instead: it takes what lies before the face and meets no face.
|
||||
if *operation == Operation::Cut
|
||||
&& off == 0.0
|
||||
&& let (Some(bi), Some((_, fi))) = (curved, to_face)
|
||||
&& let Some(tb) = tst.bodies.get(bi)
|
||||
&& let Some(end) = cut_end_past_face(tb, fi, &ss.plane, ®ions, if hi > 0.0 { 1.0 } else { -1.0 })
|
||||
&& (if hi > 0.0 { end > lo } else { end < hi })
|
||||
{
|
||||
if hi > 0.0 {
|
||||
hi = end;
|
||||
} else {
|
||||
lo = end;
|
||||
}
|
||||
curved = None;
|
||||
}
|
||||
if let Some(bi) = curved
|
||||
&& let Some(tb) = tst.bodies.get(bi)
|
||||
{
|
||||
@@ -5471,7 +5603,7 @@ fn feature_tools_with_constructor(
|
||||
}
|
||||
}
|
||||
let surroundings = if exact_planar_cut || touching_batch { None } else { Surroundings::of(st, *operation, &plain_targets) };
|
||||
let join_surroundings = if *operation == Operation::Join && !touching_batch {
|
||||
let join_surroundings = if *operation == Operation::Join && !touching_batch && !copy {
|
||||
// Trimming may discard a tool's material, so only bodies this feature
|
||||
// actually joins can establish that the discarded end is already filled.
|
||||
let participants = if plain_targets.is_empty() { &f.participants } else { &plain_targets };
|
||||
@@ -6469,11 +6601,19 @@ fn pattern_transforms(doc: &Document, vals: &BTreeMap<String, Value>, st: &Model
|
||||
}
|
||||
}
|
||||
crate::PatternKind::Circular { origin, axis, count: c, angle, alignment, reversed, axis_ref, axis_feature } => {
|
||||
// On an edge or round face: about it as it is now; on a construction axis: where it is.
|
||||
// On an edge or round face: about it as it is now; on a construction axis: where it is;
|
||||
// else through its origin, whose expressions follow the parameters (#10852).
|
||||
let (origin, axis) = match (axis_ref, axis_feature) {
|
||||
(Some(g), _) => crate::construct::model_axis(&construction_state(doc, st, component), g)?,
|
||||
(None, Some(name)) => construction_axis_in(Some(doc), st, name, component)?,
|
||||
(None, None) => (origin.0, axis.0),
|
||||
(None, None) => {
|
||||
let [x, y, z] = origin;
|
||||
let o = Vec3::new(val(vals, x, Kind::Length)?, val(vals, y, Kind::Length)?, val(vals, z, Kind::Length)?);
|
||||
if ![o.x, o.y, o.z].iter().all(|c| c.is_finite() && c.abs() < 1e9) {
|
||||
return Err(DocError::Invalid("the pattern's axis origin is out of range".into()));
|
||||
}
|
||||
(o, axis.0)
|
||||
}
|
||||
};
|
||||
let n = count(c)?;
|
||||
let total = val(vals, angle, Kind::Angle)?;
|
||||
@@ -6548,6 +6688,35 @@ fn path_at(segs: &[kernel::PathSeg], d: f64) -> (Vec3, Vec3) {
|
||||
}
|
||||
|
||||
/// Whether a copy has no material to cut in any of its eligible targets.
|
||||
/// The bodies a hole cuts: those its feature names, else its component's and its own (as
|
||||
/// `apply_traced` picks a cut's targets).
|
||||
fn hole_targets<'a>(st: &'a ModelState, f: &Feature) -> Vec<&'a ModelBody> {
|
||||
let all = f.participants.len() == 1 && f.participants.first().is_some_and(|t| t == "*");
|
||||
st.bodies
|
||||
.iter()
|
||||
.filter(|b| all || if f.participants.is_empty() { b.component == f.component || b.feature == f.id } else { f.participants.contains(&b.name) })
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Whether hole tool `tool` is clear of every body the hole cuts, by their boxes (#10701). Boxes
|
||||
/// only: a probe boolean for every hole of a large hole pattern would double its work, and the
|
||||
/// bodies' meshes are there already (the hole found its face on them). A body of another
|
||||
/// component is in another frame: the hole is then not known to miss.
|
||||
fn hole_misses(st: &ModelState, f: &Feature, tool: &Body) -> bool {
|
||||
let targets = hole_targets(st, f);
|
||||
let bounds = tool.outline_bounds();
|
||||
if targets.is_empty()
|
||||
|| targets.iter().any(|b| b.component != f.component)
|
||||
|| bounds.is_empty()
|
||||
|| !bounds.min.is_finite()
|
||||
|| !bounds.max.is_finite()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
let tool_box = Some(bounds.inflated(1e-6));
|
||||
targets.iter().all(|b| more::boxes_apart(&tool_box, &more::mesh_box(b)))
|
||||
}
|
||||
|
||||
fn copy_misses(st: &ModelState, f: &Feature, targets: &[String], tools: &[Traced]) -> bool {
|
||||
let targets = if targets.is_empty() { &f.participants } else { targets };
|
||||
let all = targets.len() == 1 && targets.first().is_some_and(|t| t == "*");
|
||||
@@ -6624,7 +6793,17 @@ fn with_named_face_points(doc: &Document, state: &ModelState, f: &Feature) -> Op
|
||||
if n.is_empty() {
|
||||
continue;
|
||||
}
|
||||
match crate::name_lookup::locate_face_on_body(&fst, f.kind.face_body(), n, *p) {
|
||||
let hit = crate::name_lookup::locate_face_on_body(&fst, f.kind.face_body(), n, *p);
|
||||
// A hole on one of two bodies with the same face name (two bodies of one extrude, the
|
||||
// pieces of a split body) is on that body's face, whole: the name is not split there
|
||||
// unless that body has pieces of it too (#10699).
|
||||
let hit = match hit {
|
||||
Some(h) if hole_axis.is_some() && h.split && h.on_point => {
|
||||
fst.bodies.get(h.body).and_then(|b| crate::name_lookup::locate_face_on_body(&fst, Some(&b.name), n, *p)).or(Some(h))
|
||||
}
|
||||
other => other,
|
||||
};
|
||||
match hit {
|
||||
Some(hit) => {
|
||||
if let Some(note) = hit.note(n) {
|
||||
notes.push(note);
|
||||
@@ -6838,7 +7017,7 @@ fn replay(
|
||||
// points follow their named faces, as the feature's own did (#2121): a To Object
|
||||
// extrude's copies reach the face where it is now, not where it was picked.
|
||||
let moved = with_named_face_points(doc, st, src).map(|(g, _)| g);
|
||||
let tools = feature_tools_with_constructor(doc, vals, moved.as_ref().unwrap_or(src), st, &mut None, &mut None)?;
|
||||
let tools = feature_tools_with_constructor(doc, vals, moved.as_ref().unwrap_or(src), st, true, &mut None, &mut None)?;
|
||||
for (k, m) in mats.iter().enumerate() {
|
||||
let start = copies.len();
|
||||
for t in &tools {
|
||||
@@ -7045,6 +7224,73 @@ fn trim_to_body(tools: Vec<Traced>, target: &ModelBody, dist_to_start: impl Fn(V
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// For a Cut To the curved face `fi` of `target`: the height above `plane` (along its normal) at
|
||||
/// which a flat-ended tool over `regions`, travelling along `travel` × the normal (±1), stops in
|
||||
/// the gap beyond that face. Rays over the profiles must each cross the face once, with material
|
||||
/// just before it and none just after; the end is halfway across the narrowest gap to the next
|
||||
/// material (at most the usual run past a curved target). `None` when that does not hold.
|
||||
fn cut_end_past_face(target: &ModelBody, fi: usize, plane: &Plane, regions: &[Region2], travel: f64) -> Option<f64> {
|
||||
let m = target.mesh();
|
||||
let n = plane.normal();
|
||||
let dir = n * travel;
|
||||
let size = target.body.size();
|
||||
let eps = (size * 1e-4).max(1e-6);
|
||||
let margin = size * 0.05 + 1.0;
|
||||
// Sample points: the outline of each profile and a grid over its inside.
|
||||
let mut samples: Vec<Vec2> = Vec::new();
|
||||
for r in regions {
|
||||
let ring: Vec<Vec2> = r.outer.polyline(1e-2).into_iter().collect();
|
||||
let step = (ring.len() / 64).max(1);
|
||||
samples.extend(ring.iter().step_by(step).copied());
|
||||
let (mut lo, mut hi) = (Vec2::new(f64::MAX, f64::MAX), Vec2::new(f64::MIN, f64::MIN));
|
||||
for q in &ring {
|
||||
lo = Vec2::new(lo.x.min(q.x), lo.y.min(q.y));
|
||||
hi = Vec2::new(hi.x.max(q.x), hi.y.max(q.y));
|
||||
}
|
||||
for i in 0..12 {
|
||||
for j in 0..12 {
|
||||
let q = Vec2::new(lo.x + (hi.x - lo.x) * (i as f64 + 0.5) / 12.0, lo.y + (hi.y - lo.y) * (j as f64 + 0.5) / 12.0);
|
||||
if r.contains(q) {
|
||||
samples.push(q);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let height = |q: Vec3| (q - plane.origin).dot(n);
|
||||
let reach = size * 4.0 + 1.0;
|
||||
let (mut face_end, mut gap_end) = (f64::MIN, f64::MAX);
|
||||
for q2 in samples {
|
||||
let q = plane.to_world(q2);
|
||||
// Every crossing of the line through `q`, in travel order, from well before the body.
|
||||
let back = travel * height(q) + reach + size;
|
||||
let mut o = q - dir * back;
|
||||
let mut hits: Vec<(f64, usize)> = Vec::new();
|
||||
while let Some((t, tri)) = m.raycast(o, dir) {
|
||||
let at = o + dir * t;
|
||||
hits.push((travel * height(at), *m.tri_face.get(tri)? as usize));
|
||||
o = at + dir * eps;
|
||||
if hits.len() > 256 {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let on_face: Vec<usize> = (0..hits.len()).filter(|&k| hits[k].1 == fi).collect();
|
||||
let [k] = on_face[..] else { return None };
|
||||
let e = hits[k].0;
|
||||
let at = |t: f64| plane.origin + n * (travel * t) + (q - plane.origin - n * height(q));
|
||||
if !m.contains(at(e - eps * 10.0)) || m.contains(at(e + eps * 10.0)) {
|
||||
return None;
|
||||
}
|
||||
face_end = face_end.max(e);
|
||||
let next = hits.iter().map(|h| h.0).filter(|&t| t > e + eps * 10.0).fold(f64::MAX, f64::min);
|
||||
gap_end = gap_end.min(next);
|
||||
}
|
||||
if face_end == f64::MIN || gap_end - face_end < eps * 100.0 {
|
||||
return None;
|
||||
}
|
||||
let end = face_end + ((gap_end - face_end) * 0.5).min(margin);
|
||||
Some(travel * end)
|
||||
}
|
||||
|
||||
/// One point on each face of a body (a triangle centre, so it lies on the face).
|
||||
fn face_samples(b: &ModelBody) -> Vec<Vec3> {
|
||||
let m = b.mesh();
|
||||
@@ -7187,10 +7433,10 @@ fn face_plane(st: &ModelState, picked: &Plane, at: Vec3, name: Option<&str>) ->
|
||||
let note = if crate::naming::legacy_name(name).is_some() {
|
||||
None
|
||||
} else if !crate::naming::face_has_name(b, f.index, name) {
|
||||
Some(format!("face `{name}` was split; using the piece nearest the picked point"))
|
||||
Some(format!("face `{}` was split; using the piece nearest the picked point", crate::name_lookup::quoted(name)))
|
||||
} else if !crate::naming::face_name_exact(b, f.index) && (f.centroid - at).dot(n).abs() > tol {
|
||||
// Named from geometry and moved off the picked plane: geometry alone decided.
|
||||
Some(format!("face `{name}` was found by geometry, not exact history; check it"))
|
||||
Some(format!("face `{}` was found by geometry, not exact history; check it", crate::name_lookup::quoted(name)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -7370,7 +7616,7 @@ fn eval_feature(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature, st:
|
||||
| FeatureKind::Sweep { operation, targets, .. } => {
|
||||
let mut constructor = None;
|
||||
let mut to_body = None;
|
||||
let tools = feature_tools_with_constructor(doc, vals, f, st, &mut constructor, &mut to_body)?;
|
||||
let tools = feature_tools_with_constructor(doc, vals, f, st, false, &mut constructor, &mut to_body)?;
|
||||
// Cuts through sheet metal become cut-outs of its flat pattern.
|
||||
if matches!(operation, Operation::Cut | Operation::Intersect) && matches!(f.kind, FeatureKind::Extrude { .. }) && !st.sheets.is_empty() {
|
||||
let bodies: Vec<Body> = tools.iter().map(|t| t.body.clone()).collect();
|
||||
@@ -7397,7 +7643,23 @@ fn eval_feature(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature, st:
|
||||
let handled = sheet_hole_cut(st, f, &tools, warning)?;
|
||||
let tools: Vec<Traced> = tools.into_iter().map(|(tool, _)| tool).collect();
|
||||
if handled.is_empty() {
|
||||
// A hole moved off the material (a changed position, parameter or sketch point)
|
||||
// cuts nothing: it says so, as a pattern does of its copies (#10701).
|
||||
let missed = tools.iter().filter(|tool| hole_misses(st, f, &tool.body)).count();
|
||||
let total = tools.len();
|
||||
let targets: Vec<String> = hole_targets(st, f).iter().map(|b| b.name.clone()).collect();
|
||||
apply_traced(doc, st, f, tools, Operation::Cut, &[])?;
|
||||
match (missed, total) {
|
||||
(0, _) => {}
|
||||
(_, 1) => append_warning(warning, "the hole removes no material".into()),
|
||||
_ => append_warning(warning, format!("{missed} of {total} holes remove no material")),
|
||||
}
|
||||
// A hole wider than the part takes all of it away (#10702): almost never what a
|
||||
// hole means, so it says which bodies are gone.
|
||||
let gone: Vec<&str> = targets.iter().map(String::as_str).filter(|n| st.body(n).is_none()).collect();
|
||||
if !gone.is_empty() {
|
||||
append_warning(warning, format!("the hole removes all of {}", gone.join(", ")));
|
||||
}
|
||||
} else {
|
||||
apply_beside_sheet_cut(doc, st, f, tools, Operation::Cut, &[], &handled)?;
|
||||
}
|
||||
@@ -7558,23 +7820,38 @@ fn eval_feature(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature, st:
|
||||
// Picks on bodies of other (moved) components are where those bodies are shown.
|
||||
None => doc.resolve_plane_in(vals, plane, 0, Some(&construction_state(doc, st, f.component)))?,
|
||||
};
|
||||
st.construct.push(crate::construct::Construct { feature: f.id, name: f.name.clone(), geom: crate::construct::ConstructGeom::Plane(pl) });
|
||||
st.construct.push(crate::construct::Construct {
|
||||
feature: f.id,
|
||||
name: f.name.clone(),
|
||||
geom: crate::construct::ConstructGeom::Plane(pl),
|
||||
bodies: Vec::new(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
FeatureKind::ConstructionAxis { def } => {
|
||||
let (origin, dir) =
|
||||
crate::construct::resolve_axis(&crate::construct::Ctx { doc, vals, st: &construction_state(doc, st, f.component), depth: 0 }, def)?;
|
||||
let cst = construction_state(doc, st, f.component);
|
||||
let ctx = crate::construct::Ctx { doc, vals, st: &cst, depth: 0 };
|
||||
let (origin, dir) = crate::construct::resolve_axis(&ctx, def)?;
|
||||
let bodies = crate::construct::source_bodies(&ctx, &def.refs());
|
||||
st.construct.push(crate::construct::Construct {
|
||||
feature: f.id,
|
||||
name: f.name.clone(),
|
||||
geom: crate::construct::ConstructGeom::Axis { origin, dir },
|
||||
bodies,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
FeatureKind::ConstructionPoint { def } => {
|
||||
let p =
|
||||
crate::construct::resolve_point(&crate::construct::Ctx { doc, vals, st: &construction_state(doc, st, f.component), depth: 0 }, def)?;
|
||||
st.construct.push(crate::construct::Construct { feature: f.id, name: f.name.clone(), geom: crate::construct::ConstructGeom::Point(p) });
|
||||
let cst = construction_state(doc, st, f.component);
|
||||
let ctx = crate::construct::Ctx { doc, vals, st: &cst, depth: 0 };
|
||||
let p = crate::construct::resolve_point(&ctx, def)?;
|
||||
let bodies = crate::construct::source_bodies(&ctx, &def.refs());
|
||||
st.construct.push(crate::construct::Construct {
|
||||
feature: f.id,
|
||||
name: f.name.clone(),
|
||||
geom: crate::construct::ConstructGeom::Point(p),
|
||||
bodies,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
FeatureKind::BoundaryFill { tools, cells, operation, remove_tools } => {
|
||||
@@ -7747,6 +8024,9 @@ fn eval_feature(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature, st:
|
||||
}
|
||||
let m = mirror_matrix(&pl);
|
||||
let mut output = 0;
|
||||
// Copies evaluated on each frame a placement is dragged, which nothing cuts or joins:
|
||||
// searching their revolved faces again was nearly all that took.
|
||||
let shown_only = !*combine && !placements.is_empty() && placement_reuse::copies_only_shown(doc, f);
|
||||
for (k, n) in bodies.iter().enumerate() {
|
||||
let i = st.bodies.iter().position(|b| &b.name == n).ok_or_else(|| DocError::Unknown(format!("body `{n}`")))?;
|
||||
let Some(mb) = st.bodies.get(i).cloned() else { continue };
|
||||
@@ -7763,7 +8043,12 @@ fn eval_feature(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature, st:
|
||||
}
|
||||
None => (None, m, m),
|
||||
};
|
||||
let mirrored = moved_copy(&mb, kernel::transform_matrix(&mb.body, if *combine { own } else { copy })?, 1);
|
||||
let copied = if shown_only {
|
||||
kernel::place_matrix(&mb.body, copy)
|
||||
} else {
|
||||
kernel::transform_matrix(&mb.body, if *combine { own } else { copy })
|
||||
};
|
||||
let mirrored = moved_copy(&mb, copied?, 1);
|
||||
if *combine {
|
||||
let joined = kernel::boolean(&mb.body, &mirrored.body, BoolOp::Union)?
|
||||
.ok_or_else(|| DocError::Invalid("the mirror join produced nothing".into()))?;
|
||||
@@ -7785,6 +8070,9 @@ fn eval_feature(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature, st:
|
||||
let name = copy_body_name(st, f, output, &mb.name);
|
||||
copy_sheet(st, &mb.name, &name, ©);
|
||||
let mut body = ModelBody::traced_new(name, mirrored, f.id);
|
||||
if !placements.is_empty() {
|
||||
body.mirrored_mesh = placed_mesh::PlacedMesh::mirrored(&mb, &body.body, ©).map(Arc::new);
|
||||
}
|
||||
if let Some(c) = component.filter(|c| *c != f.component) {
|
||||
// Kept in that component as a moved body is (and through later features).
|
||||
body.component = c;
|
||||
@@ -8043,6 +8331,9 @@ fn eval_feature(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature, st:
|
||||
*slot = ModelBody::traced_new(mb.name.clone(), Traced::step(nb, &[&mb.traced()]), mb.feature);
|
||||
}
|
||||
}
|
||||
if !*copy {
|
||||
move_constructions_with(doc, st, bodies, &crate::rigid(t, Vec3::ZERO, axis, ang));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
FeatureKind::MeshConvert { body, tolerance } => {
|
||||
@@ -8178,6 +8469,36 @@ fn construction_state<'a>(doc: &Document, st: &'a ModelState, comp: u64) -> std:
|
||||
state_in_frame(doc, st, comp)
|
||||
}
|
||||
|
||||
/// Construction axes and points built on bodies a Move moves go with them (#10127): an axis
|
||||
/// through a cylinder turns with the cylinder, as its faces do. Only those built from the moved
|
||||
/// bodies alone (`Construct::bodies`) follow; one also held by a fixed point, a plane or another
|
||||
/// body stays. `m` is the move in the bodies' own frame; each construction is kept in its
|
||||
/// component's frame (`construction_state`), so the move is carried over into that frame.
|
||||
fn move_constructions_with(doc: &Document, st: &mut ModelState, moved: &[crate::BodyName], m: &Mat) {
|
||||
let frame_of = |feature: u64| {
|
||||
let comp = doc.feature(feature).map(|f| f.component).unwrap_or(0);
|
||||
if comp == 0 && !doc.occurrences.is_empty() { crate::IDENTITY } else { doc.component_transform(comp) }
|
||||
};
|
||||
let world: BTreeMap<&str, Mat> =
|
||||
st.bodies.iter().filter(|b| moved.iter().any(|n| *n == b.name)).map(|b| (b.name.as_str(), body_world(doc, b))).collect();
|
||||
let mut placed = Vec::new();
|
||||
for (k, c) in st.construct.iter().enumerate() {
|
||||
let Some(body) = c.bodies.first().and_then(|b| world.get(b.as_str())) else { continue };
|
||||
if !c.bodies.iter().all(|b| world.contains_key(b.as_str())) {
|
||||
continue;
|
||||
}
|
||||
let frame = frame_of(c.feature);
|
||||
let (Some(body_inv), Some(frame_inv)) = (crate::mat_inverse(body), crate::mat_inverse(&frame)) else { continue };
|
||||
let in_frame = crate::mat_mul(&crate::mat_mul(&frame_inv, body), &crate::mat_mul(m, &crate::mat_mul(&body_inv, &frame)));
|
||||
placed.push((k, c.geom.placed(&in_frame)));
|
||||
}
|
||||
for (k, geom) in placed {
|
||||
if let Some(c) = st.construct.get_mut(k) {
|
||||
c.geom = geom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether `plane` is given by the origin planes and numbers alone.
|
||||
fn plane_is_fixed(plane: &PlaneRef) -> bool {
|
||||
match plane {
|
||||
@@ -8198,6 +8519,7 @@ fn plane_reads_geometry(plane: &PlaneRef) -> bool {
|
||||
PlaneRef::ThroughPoint { .. }
|
||||
| PlaneRef::Midplane { .. }
|
||||
| PlaneRef::TwoEdges { .. }
|
||||
| PlaneRef::AxisPoint { .. }
|
||||
| PlaneRef::ThreePoints { .. }
|
||||
| PlaneRef::Tangent { .. }
|
||||
| PlaneRef::AlongPath { .. }
|
||||
@@ -8462,7 +8784,8 @@ impl PlacedBodies {
|
||||
if let Some(placed) = self.taken(b, m, &name) {
|
||||
return placed;
|
||||
}
|
||||
match kernel::place_matrix(&b.body, *m) {
|
||||
let body = if crate::is_identity(m) { Ok(b.body.clone()) } else { kernel::place_matrix(&b.body, *m) };
|
||||
match body {
|
||||
Ok(body) => {
|
||||
let mut fresh = ModelBody::new(name, body, b.feature);
|
||||
fresh.mesh_placement = placed_mesh::PlacedMesh::new(b, m).map(Arc::new);
|
||||
@@ -8568,6 +8891,12 @@ fn placed_key(b: &ModelBody, m: &crate::Mat) -> (usize, [u64; 16]) {
|
||||
(Arc::as_ptr(&b.mesh) as usize, bits)
|
||||
}
|
||||
|
||||
/// Whether a world state shows `b`, placed by `m`, apart from the model's body. A mirrored copy
|
||||
/// always is: it is drawn from its source's opposite hand, and the model keeps its own triangles.
|
||||
fn placed_apart(b: &ModelBody, m: &crate::Mat) -> bool {
|
||||
!crate::is_identity(m) || b.mirrored_mesh.is_some()
|
||||
}
|
||||
|
||||
/// [`world_state`], reusing the bodies `placed` holds from an earlier call and leaving it holding
|
||||
/// this call's.
|
||||
pub fn world_state_reusing(doc: &Document, st: &ModelState, placed_before: &mut PlacedBodies) -> ModelState {
|
||||
@@ -8575,7 +8904,10 @@ pub fn world_state_reusing(doc: &Document, st: &ModelState, placed_before: &mut
|
||||
return st.clone();
|
||||
}
|
||||
let mut placed_now = PlacedBodies::default();
|
||||
let comp_of_feature = |id: u64| doc.feature(id).map(|f| f.component).unwrap_or(0);
|
||||
// Looked up once: every sketch, construction and tap asked the timeline, and every placement
|
||||
// went through all bodies, each time searching (a large assembly paid this every command).
|
||||
let feature_components: std::collections::HashMap<u64, u64> = doc.features.iter().rev().map(|f| (f.id, f.component)).collect();
|
||||
let comp_of_feature = |id: u64| feature_components.get(&id).copied().unwrap_or(0);
|
||||
let first_path = |comp: u64| -> Vec<u64> {
|
||||
let mut path = Vec::new();
|
||||
let mut c = comp;
|
||||
@@ -8598,11 +8930,28 @@ pub fn world_state_reusing(doc: &Document, st: &ModelState, placed_before: &mut
|
||||
let mut jobs = Vec::new();
|
||||
let owners = doc.model_body_components(st);
|
||||
let names = crate::WorldNames::new(doc, st);
|
||||
let bodies_of = |comp: u64| st.bodies.iter().zip(&owners).filter(move |(_, owner)| **owner == comp).map(|(b, _)| b);
|
||||
let mut by_component: std::collections::HashMap<u64, Vec<&ModelBody>> = std::collections::HashMap::new();
|
||||
for (b, owner) in st.bodies.iter().zip(&owners) {
|
||||
by_component.entry(*owner).or_default().push(b);
|
||||
}
|
||||
let bodies_of = |comp: u64| by_component.get(&comp).into_iter().flatten().copied();
|
||||
// Each tap's component and the offset of the body it is on (the first body of that name).
|
||||
let mut first_body: std::collections::HashMap<&str, (&ModelBody, u64)> = std::collections::HashMap::new();
|
||||
for (b, owner) in st.bodies.iter().zip(&owners).rev() {
|
||||
first_body.insert(b.name.as_str(), (b, *owner));
|
||||
}
|
||||
let tap_frames: Vec<(u64, Option<&crate::Mat>)> = st
|
||||
.threads
|
||||
.iter()
|
||||
.map(|t| match first_body.get(t.body.as_str()) {
|
||||
Some((body, owner)) => (*owner, doc.model_body_offset(body)),
|
||||
None => (comp_of_feature(t.feature), None),
|
||||
})
|
||||
.collect();
|
||||
for (comp, path, m) in &placements {
|
||||
for b in bodies_of(*comp) {
|
||||
let bm = body_matrix(b, m);
|
||||
if !crate::is_identity(&bm) {
|
||||
if placed_apart(b, &bm) {
|
||||
jobs.push((b, bm, names.name(&b.name, *comp, path)));
|
||||
}
|
||||
}
|
||||
@@ -8620,7 +8969,7 @@ pub fn world_state_reusing(doc: &Document, st: &ModelState, placed_before: &mut
|
||||
let bm = body_matrix(b, &m);
|
||||
// Identical geometry also shares its display mesh cell with the definition.
|
||||
// A changed placement keeps a distinct mesh, at that placement's coordinates.
|
||||
let mut placed = if crate::is_identity(&bm) {
|
||||
let mut placed = if !placed_apart(b, &bm) {
|
||||
let mut placed = b.clone();
|
||||
placed.name = names.name(&b.name, comp, &path);
|
||||
placed
|
||||
@@ -8678,14 +9027,10 @@ pub fn world_state_reusing(doc: &Document, st: &ModelState, placed_before: &mut
|
||||
}
|
||||
}
|
||||
// Unlike editable sketch guides, cosmetic surfaces belong to every occurrence.
|
||||
for t in st
|
||||
.threads
|
||||
.iter()
|
||||
.filter(|t| st.body(&t.body).map(|body| doc.model_body_component(body)).unwrap_or_else(|| comp_of_feature(t.feature)) == comp)
|
||||
{
|
||||
for (t, (_, offset)) in st.threads.iter().zip(&tap_frames).filter(|(_, (owner, _))| *owner == comp) {
|
||||
let mut t2 = t.clone();
|
||||
t2.body = names.name(&t.body, comp, &path);
|
||||
let frame = st.body(&t.body).and_then(|body| doc.model_body_offset(body)).map(|offset| crate::mat_mul(&m, offset)).unwrap_or(m);
|
||||
let frame = offset.map(|offset| crate::mat_mul(&m, offset)).unwrap_or(m);
|
||||
t2.axis_point = crate::apply_point(&frame, t.axis_point);
|
||||
t2.axis = crate::apply_vector(&frame, *t.axis).unit().unwrap_or(t.axis);
|
||||
out.threads.push(t2);
|
||||
@@ -8870,7 +9215,7 @@ mod chain_tests {
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "eval_cost_tests.rs"]
|
||||
mod cost_tests;
|
||||
pub(crate) mod cost_tests;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "eval_whole_face_tests.rs"]
|
||||
|
||||
@@ -12,20 +12,37 @@ use solidcraft_geom::{Plane, Vec2, Vec3};
|
||||
use solidcraft_kernel as kernel;
|
||||
use solidcraft_sketch::Sketch;
|
||||
|
||||
use crate::eval::{PlacementReads, body_world, hash_placement, placement_digest};
|
||||
use crate::*;
|
||||
|
||||
/// Work counted on this thread: bodies moved, STEP texts digested, joint faces and bodies
|
||||
/// measured.
|
||||
/// measured, bodies searched for by name, features searched for by id, bodies a cut (or
|
||||
/// intersect) ran its booleans on, and bodies searched for the cylinder a tap marking lies on.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
pub(super) struct Counts {
|
||||
pub moved: usize,
|
||||
pub digested: usize,
|
||||
pub joint_faces: usize,
|
||||
pub measures: usize,
|
||||
pub body_searches: usize,
|
||||
pub feature_searches: usize,
|
||||
pub cut: usize,
|
||||
pub cylinder_search: usize,
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static COUNTS: Cell<Counts> = const { Cell::new(Counts { moved: 0, digested: 0, joint_faces: 0, measures: 0 }) };
|
||||
static COUNTS: Cell<Counts> = const {
|
||||
Cell::new(Counts {
|
||||
moved: 0,
|
||||
digested: 0,
|
||||
joint_faces: 0,
|
||||
measures: 0,
|
||||
body_searches: 0,
|
||||
feature_searches: 0,
|
||||
cut: 0,
|
||||
cylinder_search: 0,
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
pub(super) fn count(f: impl FnOnce(&mut Counts)) {
|
||||
@@ -52,6 +69,17 @@ fn written(f: impl FnOnce()) -> usize {
|
||||
WRITTEN.with(Cell::get)
|
||||
}
|
||||
|
||||
/// A search on this thread through all of a design's features for one.
|
||||
pub(crate) fn count_feature_search() {
|
||||
count(|c| c.feature_searches += 1);
|
||||
}
|
||||
|
||||
/// The searches `f` makes on this thread through all features or all bodies.
|
||||
fn searched(f: impl FnOnce()) -> usize {
|
||||
let n = counted(f);
|
||||
n.feature_searches + n.body_searches
|
||||
}
|
||||
|
||||
/// The work `f` does on this thread.
|
||||
fn counted(f: impl FnOnce()) -> Counts {
|
||||
COUNTS.with(|c| c.set(Counts::default()));
|
||||
@@ -159,6 +187,25 @@ fn joint_face_measurements_never_follow_a_replaced_body_or_stale_offset() {
|
||||
assert_eq!(work.joint_faces, 0, "offsets are applied after the shared face measurements");
|
||||
}
|
||||
|
||||
/// Every solve of a drag resolves its edge origins again: they share the joint faces' one
|
||||
/// measurement, as a face origin does, instead of tessellating the whole body each time.
|
||||
#[test]
|
||||
fn an_edge_origin_measures_its_faces_once() {
|
||||
use crate::joints::{JointOrigin, Snap};
|
||||
let doc = Document::new("edge origin");
|
||||
let mut state = ModelState::default();
|
||||
state.bodies.push(ModelBody::new("Block".into(), kernel::box_solid(Vec3::ZERO, Vec3::new(10.0, 10.0, 10.0)).unwrap(), 0));
|
||||
let pick = Vec3::new(5.0, 0.0, 10.0);
|
||||
let origin = JointOrigin { occurrence: 0, snap: Snap::Edge { pick, edge: None, face: None, corner: None, axis: None } };
|
||||
let work = counted(|| {
|
||||
for _ in 0..8 {
|
||||
let frame = joints::resolve_origin(&doc, &state.clone(), &origin).unwrap();
|
||||
assert!(apply_point(&frame, Vec3::ZERO).dist(pick) < 1e-9);
|
||||
}
|
||||
});
|
||||
assert_eq!(work.joint_faces, 1, "a solve must not measure the faces again: {work:?}");
|
||||
}
|
||||
|
||||
/// `parts` imported files of two boxes each, every one in its own component placed away from
|
||||
/// the origin (as inserted parts are).
|
||||
fn placed_imports(parts: usize) -> Document {
|
||||
@@ -851,3 +898,449 @@ fn saved_states_read_back_their_sketch_keys() {
|
||||
assert!(with_sets(sets).final_state().is_err(), "base {base}");
|
||||
}
|
||||
}
|
||||
|
||||
/// The dirty check and autosave compare the whole document after every edit; a scanned
|
||||
/// part's shared triangles must not be read for that. A coordinate that equals nothing (not a
|
||||
/// number) shows whether the shared arrays were read: only reading them makes a mesh differ
|
||||
/// from its own copy.
|
||||
#[test]
|
||||
fn comparing_a_copied_mesh_does_not_read_its_shared_arrays() {
|
||||
let mesh = MeshData {
|
||||
name: "Scan".into(),
|
||||
positions: Arc::from([0., 0., 0., 1., 0., 0., 0., 1., f64::NAN]),
|
||||
triangles: Arc::from([0, 1, 2]),
|
||||
color: None,
|
||||
};
|
||||
let copy = mesh.clone();
|
||||
assert!(copy == mesh, "a copy shares the arrays and is the same mesh without reading them");
|
||||
let mut renamed = mesh.clone();
|
||||
renamed.name = "Other".into();
|
||||
assert!(renamed != mesh);
|
||||
let mut coloured = mesh.clone();
|
||||
coloured.color = Some([0.2, 0.4, 0.6]);
|
||||
assert!(coloured != mesh);
|
||||
let unshared = MeshData { positions: Arc::from(mesh.positions.to_vec()), ..mesh.clone() };
|
||||
assert!(unshared != mesh, "arrays that are not shared still compare value by value");
|
||||
let finite = MeshData { positions: Arc::from([0., 0., 0., 1., 0., 0., 0., 1., 0.]), ..mesh.clone() };
|
||||
let equal = MeshData { positions: Arc::from(finite.positions.to_vec()), triangles: Arc::from(finite.triangles.to_vec()), ..finite.clone() };
|
||||
assert!(equal == finite, "equal values in other arrays are the same mesh");
|
||||
let flipped = MeshData { triangles: Arc::from([0, 2, 1]), ..finite.clone() };
|
||||
assert!(flipped != finite);
|
||||
}
|
||||
|
||||
/// `blocks` boxes, each its own body, in component `comp`, then a cut box without targets
|
||||
/// through each of the first `cuts` (it cuts every body of its component).
|
||||
fn blocks_with_cuts(doc: &mut Document, comp: u64, blocks: usize, cuts: usize) {
|
||||
let y = 100.0 * comp as f64;
|
||||
for k in 0..blocks {
|
||||
let corner = point_expr(Vec3::new(20.0 * k as f64, y, 0.0));
|
||||
let kind = FeatureKind::Box { corner, length: "10".into(), width: "10".into(), height: "4".into(), operation: Operation::NewBody };
|
||||
let id = doc.add_feature(kind, None).unwrap();
|
||||
doc.feature_mut(id).unwrap().component = comp;
|
||||
}
|
||||
for k in 0..cuts {
|
||||
let corner = point_expr(Vec3::new(20.0 * k as f64 + 4.0, y + 4.0, -1.0));
|
||||
let kind = FeatureKind::Box { corner, length: "2".into(), width: "2".into(), height: "6".into(), operation: Operation::Cut };
|
||||
let id = doc.add_feature(kind, None).unwrap();
|
||||
doc.feature_mut(id).unwrap().component = comp;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_evaluation_from_the_cache_does_not_search_the_bodies_for_each_cut() {
|
||||
// Each cut reads where every body of its component is placed; looking each one up by name
|
||||
// made an evaluation quadratic in the bodies, even with every feature taken from the cache.
|
||||
let searches = |blocks: usize| {
|
||||
let mut doc = Document::new("blocks");
|
||||
blocks_with_cuts(&mut doc, 0, blocks, 3);
|
||||
let mut model = Model::new();
|
||||
model.evaluate(&doc);
|
||||
assert!(model.results.iter().all(|r| r.error.is_none()), "{:?}", model.results.iter().find(|r| r.error.is_some()));
|
||||
let work = counted(|| model.evaluate(&doc));
|
||||
assert_eq!(model.last_recomputed, 0);
|
||||
work.body_searches
|
||||
};
|
||||
let (few, many) = (searches(4), searches(24));
|
||||
assert_eq!(few, many, "searches grew with the bodies: {few} for 4 blocks, {many} for 24");
|
||||
}
|
||||
|
||||
/// The bodies part of the placement digest as it was before the bodies were looked up once:
|
||||
/// each body searched by name, placed through [`body_world`].
|
||||
fn digest_searching_each_body(doc: &Document, f: &Feature, st: &ModelState) -> u64 {
|
||||
use std::hash::{Hash, Hasher};
|
||||
let mut reads = PlacementReads::default();
|
||||
reads.drawn_targets(Some(st), f);
|
||||
let inverse = mat_inverse(&doc.component_transform(f.component)).unwrap();
|
||||
let mut h = std::collections::hash_map::DefaultHasher::new();
|
||||
for name in &reads.bodies {
|
||||
name.hash(&mut h);
|
||||
match st.body(name) {
|
||||
Some(b) => hash_placement(&mat_mul(&inverse, &body_world(doc, b)), &mut h),
|
||||
None => u8::MAX.hash(&mut h),
|
||||
}
|
||||
}
|
||||
h.finish() | 1
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn looking_bodies_up_once_keeps_every_placement_digest() {
|
||||
// Two parts placed apart, a body moved into the other part and one moved within its part:
|
||||
// each cut's digest is the one searching each body gave, bit for bit, so saved caches stay
|
||||
// valid.
|
||||
let mut doc = Document::new("digests");
|
||||
let a = doc.add_component(Some("A"), 0).unwrap();
|
||||
let b = doc.add_component(Some("B"), 0).unwrap();
|
||||
blocks_with_cuts(&mut doc, a, 5, 2);
|
||||
blocks_with_cuts(&mut doc, b, 4, 2);
|
||||
blocks_with_cuts(&mut doc, 0, 3, 1);
|
||||
place(&mut doc, a, rigid(Vec3::new(3.0, -7.0, 1.5), Vec3::ZERO, Vec3::Z, 0.4));
|
||||
place(&mut doc, b, rigid(Vec3::new(-20.0, 5.0, 0.0), Vec3::ZERO, Vec3::X, 0.25));
|
||||
let mut model = Model::new();
|
||||
model.evaluate(&doc);
|
||||
let names: Vec<String> = model.state().bodies.iter().map(|b| b.name.clone()).collect();
|
||||
assert_eq!(names.len(), 12);
|
||||
doc.body_components.insert(names[1].clone(), b);
|
||||
doc.body_offsets.insert(names[2].clone(), rigid(Vec3::new(0.0, 0.0, 2.0), Vec3::ZERO, Vec3::Y, 0.1));
|
||||
doc.body_offsets.insert(names[6].clone(), rigid(Vec3::new(1.0, 1.0, 0.0), Vec3::ZERO, Vec3::Z, -0.3));
|
||||
model.evaluate(&doc);
|
||||
let st = model.state();
|
||||
let cuts: Vec<&Feature> = doc.features.iter().filter(|f| matches!(f.kind, FeatureKind::Box { operation: Operation::Cut, .. })).collect();
|
||||
assert_eq!(cuts.len(), 5);
|
||||
for f in cuts {
|
||||
let digest = placement_digest(&doc, f, Some(&st));
|
||||
assert_ne!(digest, 0);
|
||||
assert_eq!(digest, digest_searching_each_body(&doc, f, &st), "cut {}", f.id);
|
||||
}
|
||||
}
|
||||
|
||||
/// `comps` parts, each with `bodies` boxes, `sketches` sketches and `taps` taps, placed `copies`
|
||||
/// times: a design and its state, made without evaluating it.
|
||||
fn placed_parts(comps: usize, bodies: usize, sketches: usize, taps: usize, copies: usize) -> (Document, ModelState) {
|
||||
let mut doc = Document::new("placed parts");
|
||||
let mut one = Document::new("one sketch");
|
||||
one.add_feature(line_sketch(PlaneRef::Origin { name: "XY".into() }, Vec2::new(3.0, 4.0)), None).unwrap();
|
||||
let mut m = Model::new();
|
||||
m.evaluate(&one);
|
||||
let solved = m.state().sketches.first().unwrap().clone();
|
||||
let mut st = ModelState::default();
|
||||
let solid = kernel::box_solid(Vec3::ZERO, Vec3::new(2.0, 3.0, 4.0)).unwrap();
|
||||
for c in 0..comps {
|
||||
let comp = doc.add_component(Some(&format!("C{c}")), 0).unwrap();
|
||||
let o = doc.occurrences.iter_mut().find(|o| o.component == comp).unwrap();
|
||||
o.transform = rigid(Vec3::new(10.0 * c as f64, 0.0, 0.0), Vec3::ZERO, Vec3::Z, 0.01 * c as f64);
|
||||
for k in 1..copies {
|
||||
doc.add_occurrence(comp, 0, rigid(Vec3::new(10.0 * c as f64, 20.0 * k as f64, 0.0), Vec3::ZERO, Vec3::Z, 0.02 * k as f64)).unwrap();
|
||||
}
|
||||
let mut names = Vec::new();
|
||||
for b in 0..bodies {
|
||||
let kind =
|
||||
serde_json::from_value(serde_json::json!({"type": "box", "corner": [0, 0, 0], "length": "2", "width": "3", "height": "4"})).unwrap();
|
||||
let name = format!("B{c}_{b}");
|
||||
let id = doc.add_feature(kind, Some(&name)).unwrap();
|
||||
doc.feature_mut(id).unwrap().component = comp;
|
||||
st.bodies.push(ModelBody::new(name.clone(), solid.clone(), id));
|
||||
names.push((name, id));
|
||||
}
|
||||
for s in 0..sketches {
|
||||
let id = doc.add_feature(line_sketch(PlaneRef::Origin { name: "XY".into() }, Vec2::new(1.0 + s as f64, 2.0)), None).unwrap();
|
||||
doc.feature_mut(id).unwrap().component = comp;
|
||||
let mut s2 = SolvedSketch::clone(&solved);
|
||||
s2.feature = id;
|
||||
st.sketches.push(Arc::new(s2));
|
||||
}
|
||||
for t in 0..taps {
|
||||
let (body, feature) = names[t % names.len()].clone();
|
||||
st.threads.push(ThreadInfo {
|
||||
feature,
|
||||
body,
|
||||
designation: "M6".into(),
|
||||
major_diameter: 6.0,
|
||||
radius: 3.0,
|
||||
pitch: 1.0,
|
||||
internal: true,
|
||||
handedness: Default::default(),
|
||||
axis_point: Vec3::new(1.0, 1.0, 0.0),
|
||||
axis: solidcraft_geom::UnitVec3::Z,
|
||||
start: 0.0,
|
||||
end: 4.0,
|
||||
});
|
||||
}
|
||||
}
|
||||
(doc, st)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn placing_parts_looks_each_sketch_and_tap_up_once() {
|
||||
// A large assembly searched all features for every sketch and construction, and all bodies
|
||||
// twice for every tap and placement, each time its world state was made (every command).
|
||||
let searches = |sketches: usize, taps: usize| {
|
||||
let (doc, state) = placed_parts(4, 2, sketches, taps, 3);
|
||||
let mut world = ModelState::default();
|
||||
let n = searched(|| world = world_state(&doc, &state));
|
||||
assert_eq!(world.bodies.len(), 4 * 2 * 3);
|
||||
assert_eq!(world.sketches.len(), 4 * sketches);
|
||||
assert_eq!(world.threads.len(), 4 * taps * 3);
|
||||
n
|
||||
};
|
||||
assert_eq!(searches(8, 6), searches(1, 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_placed_tap_follows_its_body_offset() {
|
||||
let (mut doc, state) = placed_parts(1, 1, 0, 1, 1);
|
||||
let comp = doc.components[0].id;
|
||||
doc.body_offsets.insert("B0_0".into(), rigid(Vec3::new(0.0, 0.0, 7.0), Vec3::ZERO, Vec3::Z, 0.0));
|
||||
let world = world_state(&doc, &state);
|
||||
let tap = world.threads.first().unwrap();
|
||||
let expected =
|
||||
apply_point(&mat_mul(&doc.component_transform(comp), &rigid(Vec3::new(0.0, 0.0, 7.0), Vec3::ZERO, Vec3::Z, 0.0)), Vec3::new(1.0, 1.0, 0.0));
|
||||
assert!(tap.axis_point.dist(expected) < 1e-9, "{:?} {expected:?}", tap.axis_point);
|
||||
}
|
||||
|
||||
/// What adding a feature of `kind` at the end of `doc` did to the bodies already there: how many
|
||||
/// its cut ran booleans on, and which it left exactly as they were (the same geometry, its name,
|
||||
/// key and face names carried over). A cut that cannot reach a body hands it back as it was, and
|
||||
/// must not pay a whole boolean to find that out (#10225); the sheet metal hole (#10226) and the
|
||||
/// cosmetic thread (#10227) check their own shortcuts with it too.
|
||||
pub(super) struct CutWork {
|
||||
pub cut: usize,
|
||||
pub kept: Vec<String>,
|
||||
pub model: Model,
|
||||
}
|
||||
|
||||
pub(super) fn cut_work(doc: &mut Document, kind: FeatureKind) -> CutWork {
|
||||
let mut model = Model::new();
|
||||
model.evaluate(doc);
|
||||
let id = doc.add_feature(kind, None).unwrap();
|
||||
let work = counted(|| model.evaluate(doc));
|
||||
let result = model.result(id).unwrap();
|
||||
assert_eq!(result.error, None, "the feature builds");
|
||||
let before = model.state_before(id);
|
||||
let kept = result
|
||||
.state
|
||||
.bodies
|
||||
.iter()
|
||||
.filter(|b| {
|
||||
before
|
||||
.body(&b.name)
|
||||
.is_some_and(|old| old.body.same(&b.body) && old.key == b.key && crate::naming::face_names(old) == crate::naming::face_names(b))
|
||||
})
|
||||
.map(|b| b.name.clone())
|
||||
.collect();
|
||||
CutWork { cut: work.cut, kept, model }
|
||||
}
|
||||
|
||||
/// A box body named `name`, `size` long on each side from `corner`.
|
||||
fn block(doc: &mut Document, name: &str, corner: Vec3, size: [f64; 3]) {
|
||||
let id = doc
|
||||
.add_feature(
|
||||
FeatureKind::Box {
|
||||
corner: point_expr(corner),
|
||||
length: size[0].to_string().into(),
|
||||
width: size[1].to_string().into(),
|
||||
height: size[2].to_string().into(),
|
||||
operation: Operation::NewBody,
|
||||
},
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
doc.feature_mut(id).unwrap().body_names = vec![name.into()];
|
||||
}
|
||||
|
||||
/// A cylinder that cuts, standing on `base` along Z.
|
||||
fn cutter(base: Vec3, radius: f64, height: f64) -> FeatureKind {
|
||||
FeatureKind::Cylinder {
|
||||
base: point_expr(base),
|
||||
axis: Dir(Vec3::Z),
|
||||
radius: radius.to_string().into(),
|
||||
height: height.to_string().into(),
|
||||
operation: Operation::Cut,
|
||||
references: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A cut leaves the bodies its tool cannot reach as they were, without a boolean on each: in a
|
||||
/// design of many bodies those booleans were most of a hole's time (#10225).
|
||||
#[test]
|
||||
fn a_cut_leaves_bodies_it_cannot_reach_as_they_were() {
|
||||
let mut doc = Document::new("far");
|
||||
block(&mut doc, "Near", Vec3::ZERO, [50.0, 40.0, 30.0]);
|
||||
// 0.5 mm clear of the tool, and far off.
|
||||
block(&mut doc, "Beside", Vec3::new(13.5, 0.0, 0.0), [20.0, 40.0, 30.0]);
|
||||
for i in 0..5 {
|
||||
block(&mut doc, &format!("Far{i}"), Vec3::new(200.0 * (i + 1) as f64, 0.0, 0.0), [50.0, 40.0, 30.0]);
|
||||
}
|
||||
// Near and Beside overlap; the hole goes through Near only, beside Beside.
|
||||
let work = cut_work(&mut doc, cutter(Vec3::new(10.0, 20.0, -5.0), 3.0, 40.0));
|
||||
assert_eq!(work.cut, 1, "only the body the tool reaches goes through the boolean");
|
||||
assert_eq!(work.kept, ["Beside", "Far0", "Far1", "Far2", "Far3", "Far4"]);
|
||||
assert!(volume(&work.model, "Near") < 60_000.0 - 800.0, "and it is cut");
|
||||
}
|
||||
|
||||
/// A cut still goes through the boolean wherever its tool reaches a body or comes close: on a
|
||||
/// face, along a line, a hair beside it, into a dome above its edge circle. (Bodies its tool
|
||||
/// cannot reach are left out of it: `a_cut_leaves_bodies_it_cannot_reach_as_they_were`.)
|
||||
#[test]
|
||||
fn a_cut_still_runs_where_its_tool_touches_grazes_or_meets_a_curved_face() {
|
||||
// A cylinder whose side is tangent to the face x = 50, one a hair clear of it, one 0.1 mm in.
|
||||
for (x, cuts) in [(53.0, false), (53.0005, false), (52.9, true)] {
|
||||
let mut doc = Document::new("graze");
|
||||
block(&mut doc, "A", Vec3::ZERO, [50.0, 40.0, 30.0]);
|
||||
let work = cut_work(&mut doc, cutter(Vec3::new(x, 20.0, 0.0), 3.0, 30.0));
|
||||
assert_eq!(work.cut, 1, "the tool at x = {x} goes through the boolean");
|
||||
let v = volume(&work.model, "A");
|
||||
assert_eq!(v < 60_000.0 - 1.0, cuts, "the tool at x = {x} cuts material: {v}");
|
||||
assert!(!cuts || work.kept.is_empty(), "a body cut is a new body");
|
||||
}
|
||||
// A box meeting the block only along its top edge.
|
||||
let mut doc = Document::new("edge");
|
||||
block(&mut doc, "A", Vec3::ZERO, [50.0, 40.0, 30.0]);
|
||||
let along = FeatureKind::Box {
|
||||
corner: point_expr(Vec3::new(50.0, 0.0, 30.0)),
|
||||
length: "10".into(),
|
||||
width: "40".into(),
|
||||
height: "10".into(),
|
||||
operation: Operation::Cut,
|
||||
};
|
||||
assert_eq!(cut_work(&mut doc, along).cut, 1);
|
||||
// A dome: a sphere's cap rising 10 mm above the circle it stands on. A thin tool drilled into
|
||||
// its top near the apex, well above that circle, cuts it.
|
||||
let mut doc = Document::new("dome");
|
||||
doc.add_feature(FeatureKind::Sphere { center: point_expr(Vec3::new(0.0, 0.0, -10.0)), radius: "20".into(), operation: Operation::NewBody }, None)
|
||||
.unwrap();
|
||||
let below = FeatureKind::Box {
|
||||
corner: point_expr(Vec3::new(-30.0, -30.0, -40.0)),
|
||||
length: "60".into(),
|
||||
width: "60".into(),
|
||||
height: "40".into(),
|
||||
operation: Operation::Cut,
|
||||
};
|
||||
doc.add_feature(below, None).unwrap();
|
||||
let mut model = Model::new();
|
||||
model.evaluate(&doc);
|
||||
let dome = model.state().bodies[0].name.clone();
|
||||
let whole = volume(&model, &dome);
|
||||
let work = cut_work(&mut doc, cutter(Vec3::new(2.0, 1.0, 8.0), 1.0, 5.0));
|
||||
assert_eq!(work.cut, 1);
|
||||
assert!(volume(&work.model, &dome) < whole - 1.0, "the drill cuts into the dome");
|
||||
assert!(work.kept.is_empty());
|
||||
}
|
||||
|
||||
/// A cylinder body named `name` standing on `base` along Z.
|
||||
fn shaft(doc: &mut Document, name: &str, base: Vec3, radius: f64, height: f64) {
|
||||
let id = doc
|
||||
.add_feature(
|
||||
FeatureKind::Cylinder {
|
||||
base: point_expr(base),
|
||||
axis: Dir(Vec3::Z),
|
||||
radius: radius.to_string().into(),
|
||||
height: height.to_string().into(),
|
||||
operation: Operation::NewBody,
|
||||
references: Vec::new(),
|
||||
},
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
doc.feature_mut(id).unwrap().body_names = vec![name.into()];
|
||||
}
|
||||
|
||||
/// The work of adding `kind` at the end of `doc`, and the tap markings the design then has.
|
||||
fn marking_work(doc: &mut Document, kind: FeatureKind) -> (Counts, Vec<ThreadInfo>) {
|
||||
let mut model = Model::new();
|
||||
model.evaluate(doc);
|
||||
let id = doc.add_feature(kind, None).unwrap();
|
||||
let work = counted(|| model.evaluate(doc));
|
||||
assert_eq!(model.result(id).unwrap().error, None, "the feature builds");
|
||||
(work, model.state().threads.clone())
|
||||
}
|
||||
|
||||
/// A cosmetic tap marking at `p`.
|
||||
fn marking(p: Vec3) -> FeatureKind {
|
||||
FeatureKind::Thread { face: FacePick(p), designation: Some("M6".into()), length: None, modeled: false, handedness: ThreadHandedness::Right }
|
||||
}
|
||||
|
||||
/// A tap marking looks for its cylinder in the bodies near it only: each body searched was
|
||||
/// tessellated again, so many markings in a design of many bodies took quadratic time (#10227).
|
||||
#[test]
|
||||
fn a_tap_marking_searches_only_the_bodies_near_it() {
|
||||
let mut doc = Document::new("far");
|
||||
shaft(&mut doc, "Shaft", Vec3::ZERO, 3.0, 6.0);
|
||||
for i in 0..5 {
|
||||
block(&mut doc, &format!("Far{i}"), Vec3::new(200.0 * (i + 1) as f64, 0.0, 0.0), [50.0, 40.0, 30.0]);
|
||||
}
|
||||
let (work, threads) = marking_work(&mut doc, marking(Vec3::new(3.0, 0.0, 3.0)));
|
||||
assert_eq!(work.cylinder_search, 1, "only the shaft is searched");
|
||||
assert_eq!(threads.len(), 1);
|
||||
assert_eq!(threads[0].body, "Shaft");
|
||||
assert_eq!(threads[0].designation, "M6");
|
||||
assert!((threads[0].radius - 3.0).abs() < 1e-6 && !threads[0].internal);
|
||||
|
||||
// A tapped hole in a plate among far bodies: only the plate is searched for its wall.
|
||||
let mut doc = Document::new("tapped");
|
||||
block(&mut doc, "Plate", Vec3::ZERO, [60.0, 60.0, 12.0]);
|
||||
for i in 0..5 {
|
||||
block(&mut doc, &format!("Far{i}"), Vec3::new(200.0 * (i + 1) as f64, 0.0, 0.0), [50.0, 40.0, 30.0]);
|
||||
}
|
||||
let hole = FeatureKind::Hole {
|
||||
position: FacePick(Vec3::new(30.0, 30.0, 12.0)),
|
||||
at: None,
|
||||
direction: Dir(-Vec3::Z),
|
||||
diameter: "5".into(),
|
||||
depth: Some("10".into()),
|
||||
hole: HoleKind::default(),
|
||||
points: None,
|
||||
thread: Some("M6".into()),
|
||||
thread_depth: None,
|
||||
handedness: ThreadHandedness::Right,
|
||||
to: None,
|
||||
references: Vec::new(),
|
||||
};
|
||||
let (work, threads) = marking_work(&mut doc, hole);
|
||||
assert_eq!(work.cylinder_search, 1, "only the plate is searched");
|
||||
assert_eq!(threads.len(), 1);
|
||||
assert_eq!(threads[0].body, "Plate");
|
||||
assert!(threads[0].internal && (threads[0].radius - 2.5).abs() < 1e-6);
|
||||
}
|
||||
|
||||
/// Bodies within reach of a tap marking are still searched: the shaft it lies on, a block 3 mm
|
||||
/// off (within a twentieth of the block's size, where the cylinder search itself looks), and one
|
||||
/// 3.6 mm off (just past it, but within the margin the boxes keep); a block 4.5 mm off is not.
|
||||
/// The marking stays on the shaft.
|
||||
#[test]
|
||||
fn a_tap_marking_still_searches_the_bodies_within_its_reach() {
|
||||
let mut doc = Document::new("near");
|
||||
shaft(&mut doc, "Shaft", Vec3::ZERO, 3.0, 6.0);
|
||||
// Each block is 50 x 40 x 30 (a twentieth of its size is 3.54 mm), its face x = 3 + gap.
|
||||
for (name, gap) in [("Close", 3.0), ("Edge", 3.6), ("Clear", 4.5)] {
|
||||
block(&mut doc, name, Vec3::new(3.0 + gap, -20.0, 0.0), [50.0, 40.0, 30.0]);
|
||||
}
|
||||
let (work, threads) = marking_work(&mut doc, marking(Vec3::new(3.0, 0.0, 3.0)));
|
||||
assert_eq!(work.cylinder_search, 3, "the shaft, Close and Edge are searched");
|
||||
assert_eq!(threads.len(), 1);
|
||||
assert_eq!(threads[0].body, "Shaft");
|
||||
|
||||
// A pin in a hole a hair wider: a marking on the pin, and one on the hole's wall.
|
||||
for (p, body, internal) in [(Vec3::new(2.0, 0.0, 5.0), "Pin", false), (Vec3::new(2.5, 0.0, 5.0), "Plate", true)] {
|
||||
let mut doc = Document::new("pin");
|
||||
block(&mut doc, "Plate", Vec3::new(-20.0, -20.0, 0.0), [40.0, 40.0, 10.0]);
|
||||
let bore = FeatureKind::Cylinder {
|
||||
base: point_expr(Vec3::new(0.0, 0.0, -1.0)),
|
||||
axis: Dir(Vec3::Z),
|
||||
radius: "2.5".into(),
|
||||
height: "12".into(),
|
||||
operation: Operation::Cut,
|
||||
references: Vec::new(),
|
||||
};
|
||||
doc.add_feature(bore, None).unwrap();
|
||||
shaft(&mut doc, "Pin", Vec3::ZERO, 2.0, 10.0);
|
||||
let mut kind = marking(p);
|
||||
if let FeatureKind::Thread { designation, .. } = &mut kind {
|
||||
*designation = None;
|
||||
}
|
||||
let (work, threads) = marking_work(&mut doc, kind);
|
||||
assert_eq!(work.cylinder_search, 2, "both bodies are searched for {body}");
|
||||
assert_eq!(threads.len(), 1);
|
||||
assert_eq!(threads[0].body, body);
|
||||
assert_eq!(threads[0].internal, internal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -684,7 +684,7 @@ pub(super) fn sheet_eval(
|
||||
tag_flanges(sh, n0, f.id);
|
||||
rebuild_sheet(st, i)
|
||||
}
|
||||
FeatureKind::SheetPerforate { diameter, pitch, margin, pattern, body } => {
|
||||
FeatureKind::SheetPerforate { diameter, pitch, margin, pattern, body, sketch, profiles } => {
|
||||
// Without a body name the field goes on the newest sheet, as the command's own body lookup does.
|
||||
let i = match body {
|
||||
Some(_) => sheet_for(st, body, None)?,
|
||||
@@ -706,8 +706,18 @@ pub(super) fn sheet_eval(
|
||||
"perforation needs diameter > 0, pitch > diameter and margin >= 0 (diameter {d}, pitch {pitch}, margin {margin})"
|
||||
)));
|
||||
}
|
||||
// The outline sketch's profiles, where they lie on the sheet now.
|
||||
let outline = match sketch {
|
||||
Some(sk) => {
|
||||
let ss = st.sketch(sk.0).ok_or_else(|| DocError::Unknown(format!("sketch {sk}")))?;
|
||||
let regions = select_profiles(ss, profiles.as_ref().unwrap_or(&ProfileSel::All))?;
|
||||
let sh = st.sheets.get(i).ok_or_else(|| DocError::Invalid("sheet".into()))?;
|
||||
Some(sh.perforation_outline(&ss.plane, ®ions)?)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
let sh = st.sheets.get_mut(i).ok_or_else(|| DocError::Invalid("sheet".into()))?;
|
||||
sh.perforations.push(crate::sheet::SheetPerforation { feature: f.id, diameter: d, pitch, margin, square, from_right: false });
|
||||
sh.perforations.push(crate::sheet::SheetPerforation { feature: f.id, diameter: d, pitch, margin, square, from_right: false, outline });
|
||||
Ok(())
|
||||
}
|
||||
FeatureKind::SheetHem { edges, length, gap, flip, body } => {
|
||||
@@ -802,7 +812,10 @@ pub(super) fn sheet_eval(
|
||||
hit.point
|
||||
}
|
||||
Some((n, Err(_))) => {
|
||||
append_warning(warning, format!("edge `{n}` no longer exists; using the edge at the picked point"));
|
||||
append_warning(
|
||||
warning,
|
||||
format!("edge `{}` no longer exists; using the edge at the picked point", crate::name_lookup::quoted(n)),
|
||||
);
|
||||
p.0
|
||||
}
|
||||
None => p.0,
|
||||
@@ -1556,13 +1569,13 @@ fn sheet_prism_cut(
|
||||
|
||||
/// A box around `body`, grown past its mesh's deflection from the exact faces (and a hair
|
||||
/// more): bodies whose boxes are apart do not meet. None when it can't be worked out.
|
||||
fn body_box(body: &Body) -> Option<Aabb3> {
|
||||
pub(super) fn body_box(body: &Body) -> Option<Aabb3> {
|
||||
let tol = (body.size() * 1e-3).max(1e-4);
|
||||
padded_box(&body.tessellate(tol).ok()?, tol)
|
||||
}
|
||||
|
||||
/// [`body_box`] of a model body, from the mesh it keeps.
|
||||
fn mesh_box(mb: &ModelBody) -> Option<Aabb3> {
|
||||
pub(super) fn mesh_box(mb: &ModelBody) -> Option<Aabb3> {
|
||||
padded_box(&mb.mesh(), mb.display_tol())
|
||||
}
|
||||
|
||||
@@ -1571,8 +1584,25 @@ fn padded_box(mesh: &solidcraft_geom::Mesh, tol: f64) -> Option<Aabb3> {
|
||||
(!b.is_empty() && b.min.is_finite() && b.max.is_finite()).then(|| b.inflated(2.0 * tol + 1e-6))
|
||||
}
|
||||
|
||||
/// Whether [`kernel::cylinder_face_at`] is known to find no face of `mb` at `p`. It takes a
|
||||
/// face only when its own mesh of the body comes within a twentieth of the body's size of the
|
||||
/// point. That mesh lies in the body's box ([`mesh_box`]) give or take its own deflection, which
|
||||
/// the reach allows for, so a box farther away than that holds no face for it. Working that out from the mesh the body keeps
|
||||
/// spares a tessellation of every body in the design for each tap marking (#10227). A mesh
|
||||
/// body, and a body whose box is unknown, are searched as before.
|
||||
pub(super) fn beyond_cylinder_reach(mb: &ModelBody, p: Vec3) -> bool {
|
||||
if mb.body.is_mesh() {
|
||||
return false;
|
||||
}
|
||||
let Some(b) = mesh_box(mb) else { return false };
|
||||
let size = mb.body.size();
|
||||
let reach = size * 0.05 + 2.0 * (size * 1e-3).max(1e-3);
|
||||
let off = (b.min - p).max(p - b.max).max(Vec3::ZERO);
|
||||
off.len() > reach
|
||||
}
|
||||
|
||||
/// Whether two boxes are known to be apart (an unknown box is never apart).
|
||||
fn boxes_apart(a: &Option<Aabb3>, b: &Option<Aabb3>) -> bool {
|
||||
pub(super) fn boxes_apart(a: &Option<Aabb3>, b: &Option<Aabb3>) -> bool {
|
||||
let (Some(a), Some(b)) = (a, b) else { return false };
|
||||
a.max.x < b.min.x || b.max.x < a.min.x || a.max.y < b.min.y || b.max.y < a.min.y || a.max.z < b.min.z || b.max.z < a.min.z
|
||||
}
|
||||
|
||||
+69
-1
@@ -304,6 +304,28 @@ pub fn value_in_unit(v: Value, unit: &str) -> f64 {
|
||||
}
|
||||
}
|
||||
|
||||
/// The number a reply reports for a value: millimetres for a length, degrees for an angle,
|
||||
/// as-is for a unit-less or compound value. One meaning of `value` in every reply (#10747).
|
||||
pub fn reported_value(v: Value) -> f64 {
|
||||
match v.kind() {
|
||||
Some(Kind::Angle) => v.v.to_degrees(),
|
||||
_ => v.v,
|
||||
}
|
||||
}
|
||||
|
||||
/// The number a person would read for `v`: the shortest decimal that reads back after
|
||||
/// rounding off the last few bits (12 significant digits), so `30 deg` stored as
|
||||
/// 29.999999999999996 reports 30 and `0.1 + 0.2` reports 0.3. Only what is *reported*
|
||||
/// is rounded; stored values and evaluation keep full precision (#10747).
|
||||
pub fn round_reported(v: f64) -> f64 {
|
||||
if !v.is_finite() {
|
||||
return v;
|
||||
}
|
||||
// `:.11e` keeps 12 significant digits; parsing back gives the f64 nearest that decimal,
|
||||
// which serializes as the shortest form that reads back to it.
|
||||
format!("{v:.11e}").parse().unwrap_or(v)
|
||||
}
|
||||
|
||||
fn unit(name: &str) -> Option<Value> {
|
||||
UNITS.iter().find(|(n, _, _)| *n == name).map(|(_, k, s)| match k {
|
||||
Kind::Angle => Value::angle(*s),
|
||||
@@ -950,7 +972,11 @@ fn call(f: &str, args: &[Value], d: Defaults) -> Result<Value, DocError> {
|
||||
let a = one()?;
|
||||
unit_fn(a.v.abs(), a)
|
||||
}
|
||||
"sign" => Value::num(one()?.v.signum()),
|
||||
"sign" => {
|
||||
let x = one()?.v;
|
||||
// `f64::signum` gives 1 for +0 and -1 for -0; the sign of zero is 0.
|
||||
Value::num(if x == 0.0 { 0.0 } else { x.signum() })
|
||||
}
|
||||
"floor" | "ceil" | "round" | "trunc" => {
|
||||
let a = one()?;
|
||||
// Round in the value's own default unit (round(2.4 in) = 2 in for an inch parameter).
|
||||
@@ -1408,6 +1434,16 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// `sign(0)` is 0, not 1 (Rust's `signum` takes the sign bit, so it calls zero positive).
|
||||
#[test]
|
||||
fn sign_of_zero_is_zero() {
|
||||
for (e, want) in
|
||||
[("sign(0)", 0.0), ("sign(-0)", 0.0), ("sign(0 mm)", 0.0), ("sign(5)", 1.0), ("sign(-2 mm)", -1.0), ("sign(width - 40)", 0.0)]
|
||||
{
|
||||
assert_eq!(ev(e, Kind::Unitless).unwrap(), want, "{e}");
|
||||
}
|
||||
}
|
||||
|
||||
/// A count from a length (`(L - 20) / 210`, the 210 read as unit-less) names the fix (#5901).
|
||||
#[test]
|
||||
fn a_length_where_a_count_is_expected_says_to_divide_by_a_length() {
|
||||
@@ -1637,4 +1673,36 @@ mod tests {
|
||||
defs[2].expr = "bad +".into();
|
||||
assert!(eval_params(&defs, 1.0).1.contains_key("c"));
|
||||
}
|
||||
|
||||
/// #10747: reported numbers are the shortest decimals that read back after rounding off
|
||||
/// the last few bits (12 significant digits); stored values keep full precision.
|
||||
#[test]
|
||||
fn round_reported_reads_as_a_person_would() {
|
||||
for (noisy, read) in [
|
||||
(30f64.to_radians().to_degrees(), 30.0),
|
||||
(0.1 + 0.2, 0.3),
|
||||
(3.0 * 25.4, 76.2),
|
||||
(1.2 * 25.4, 30.48),
|
||||
(29.999999999999996, 30.0),
|
||||
(0.30000000000000004, 0.3),
|
||||
(-0.1 - 0.2, -0.3),
|
||||
] {
|
||||
assert_eq!(round_reported(noisy), read, "{noisy}");
|
||||
}
|
||||
// A number with real digits of its own keeps them up to 12 significant digits.
|
||||
assert_eq!(round_reported(1.234567890123), 1.23456789012);
|
||||
assert_eq!(round_reported(1.0000000000001), 1.0);
|
||||
// Zero and non-finite values pass through.
|
||||
assert_eq!(round_reported(0.0), 0.0);
|
||||
assert!(round_reported(f64::INFINITY).is_infinite());
|
||||
assert!(round_reported(f64::NAN).is_nan());
|
||||
}
|
||||
|
||||
/// #10747: `value` means millimetres and degrees in every reply.
|
||||
#[test]
|
||||
fn reported_value_is_mm_and_degrees() {
|
||||
assert_eq!(reported_value(Value::length(50.8)), 50.8);
|
||||
assert_eq!(round_reported(reported_value(Value::angle(30f64.to_radians()))), 30.0);
|
||||
assert_eq!(reported_value(Value::num(4.0)), 4.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,12 +13,30 @@ pub struct FaceAnchor {
|
||||
pub component: u64,
|
||||
pub path: Vec<u64>,
|
||||
pub author_path: Vec<u64>,
|
||||
/// Source component to author component at capture, for an independently placed part.
|
||||
/// Body offsets and source geometry remain live; later assembly moves do not reshape it.
|
||||
/// Older attachments without this field retain their live placement relationship.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub placement: Option<crate::Mat>,
|
||||
/// Origin and x direction in the exact canonical face plane's coordinates.
|
||||
pub coordinates: [f64; 2],
|
||||
pub x_axis: [f64; 2],
|
||||
}
|
||||
|
||||
impl FaceAnchor {
|
||||
/// Re-express a captured context when definitions (not their occurrences) change frames.
|
||||
pub fn reframe_placement(&mut self, author: &crate::Mat, source: &crate::Mat) -> Result<()> {
|
||||
if let Some(placement) = self.placement {
|
||||
let inverse = crate::mat_inverse(source).ok_or_else(|| DocError::Invalid("attached source frame is invalid".into()))?;
|
||||
let placement = crate::mat_mul(&crate::mat_mul(author, &placement), &inverse);
|
||||
if !crate::valid_transfer_matrix(&placement) {
|
||||
return Err(DocError::Invalid("invalid captured face placement".into()));
|
||||
}
|
||||
self.placement = Some(placement);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remap_target(
|
||||
&mut self,
|
||||
features: &std::collections::BTreeMap<u64, u64>,
|
||||
@@ -47,6 +65,7 @@ impl FaceAnchor {
|
||||
|| self.path.iter().chain(&self.author_path).any(|id| *id == 0)
|
||||
|| (self.component == 0 && !self.path.is_empty())
|
||||
|| (self.component != 0 && self.path.is_empty())
|
||||
|| self.placement.as_ref().is_some_and(|m| !crate::valid_transfer_matrix(m))
|
||||
|| self.coordinates.iter().any(|value| !value.is_finite() || value.abs() > 1e9)
|
||||
|| self.x_axis.iter().any(|value| !value.is_finite())
|
||||
|| (self.x_axis[0].hypot(self.x_axis[1]) - 1.).abs() > 1e-6
|
||||
@@ -70,13 +89,11 @@ impl FaceAnchor {
|
||||
}
|
||||
let inverse = crate::mat_inverse(&self.author_frame(doc, author)?)
|
||||
.ok_or_else(|| DocError::Invalid("attached sketch author frame is invalid".into()))?;
|
||||
let source = EmbossFace {
|
||||
body: self.target.body.clone().into(),
|
||||
at: crate::FacePick(self.target.point),
|
||||
path: self.path.clone(),
|
||||
author_path: self.author_path.clone(),
|
||||
};
|
||||
Ok(crate::mat_mul(&inverse, &source.body_frame(doc, body)?))
|
||||
// Resolve both paths even with a captured placement: a missing or reparented source
|
||||
// must still fail explicitly rather than silently reusing an unrelated definition.
|
||||
let source = EmbossFace::component_frame(doc, self.component, &self.path)?;
|
||||
let relative = self.placement.unwrap_or_else(|| crate::mat_mul(&inverse, &source));
|
||||
Ok(doc.model_body_offset(body).map_or(relative, |offset| crate::mat_mul(&relative, offset)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,6 +198,7 @@ impl Document {
|
||||
component: self.model_body_component(body),
|
||||
path: target_path.to_vec(),
|
||||
author_path: author_path.to_vec(),
|
||||
placement: None,
|
||||
coordinates: [0., 0.],
|
||||
x_axis: [1., 0.],
|
||||
};
|
||||
@@ -191,6 +209,15 @@ impl Document {
|
||||
let delta = picked.origin - canonical.origin;
|
||||
anchor.coordinates = [delta.dot(canonical.x), delta.dot(canonical.y)];
|
||||
anchor.x_axis = [picked.x.dot(canonical.x), picked.x.dot(canonical.y)];
|
||||
// A part built against another component uses that assembly context to define its
|
||||
// own shape. Moving either occurrence later must not move the sketch inside the part.
|
||||
// Root sketches and references inside the same component keep their live placements.
|
||||
if author != 0 && author != anchor.component {
|
||||
let inverse = crate::mat_inverse(&anchor.author_frame(self, author)?)
|
||||
.ok_or_else(|| DocError::Invalid("attached sketch author frame is invalid".into()))?;
|
||||
let source = EmbossFace::component_frame(self, anchor.component, &anchor.path)?;
|
||||
anchor.placement = Some(crate::mat_mul(&inverse, &source));
|
||||
}
|
||||
anchor.validate()?;
|
||||
Ok(anchor)
|
||||
}
|
||||
@@ -389,7 +416,8 @@ impl Document {
|
||||
|
||||
pub fn validate_face_anchor_json(value: &Value) -> Result<()> {
|
||||
let fields = value.as_object().ok_or_else(|| DocError::Invalid("face anchor must be an object".into()))?;
|
||||
if fields.len() != 6 || fields.keys().any(|key| !["target", "component", "path", "author_path", "coordinates", "x_axis"].contains(&key.as_str()))
|
||||
if !(6..=7).contains(&fields.len())
|
||||
|| fields.keys().any(|key| !["target", "component", "path", "author_path", "placement", "coordinates", "x_axis"].contains(&key.as_str()))
|
||||
{
|
||||
return Err(DocError::Invalid("unknown or missing face anchor fields".into()));
|
||||
}
|
||||
@@ -423,6 +451,15 @@ pub fn validate_face_anchor_json(value: &Value) -> Result<()> {
|
||||
return Err(DocError::Invalid("face anchor basis must be normalized".into()));
|
||||
}
|
||||
}
|
||||
if let Some(placement) = fields.get("placement").filter(|v| !v.is_null()) {
|
||||
if placement.as_array().is_none_or(|rows| rows.len() != 4 || rows.iter().any(|row| row.as_array().is_none_or(|row| row.len() != 4))) {
|
||||
return Err(DocError::Invalid("face anchor placement requires a 4x4 matrix".into()));
|
||||
}
|
||||
let matrix: crate::Mat = serde_json::from_value(placement.clone()).map_err(|_| DocError::Invalid("invalid face anchor placement".into()))?;
|
||||
if !crate::valid_transfer_matrix(&matrix) {
|
||||
return Err(DocError::Invalid("face anchor placement must be a finite rigid transform".into()));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -531,6 +568,44 @@ mod tests {
|
||||
.unwrap();
|
||||
assert!(doc.resolve_face_anchor(&anchor, 0, &state).is_err(), "reused name cannot replace the producer");
|
||||
}
|
||||
#[test]
|
||||
fn captured_component_placement_is_rigid_and_legacy_anchors_stay_live() {
|
||||
let (mut doc, body, index, picked) = fixture();
|
||||
let author = doc.add_component(Some("Part"), 0).unwrap();
|
||||
let path = doc.first_path(author);
|
||||
let anchor = doc.capture_face_anchor(&body, index, &picked, &[], author, &path).unwrap();
|
||||
assert_eq!(anchor.placement, Some(crate::IDENTITY));
|
||||
let raw = serde_json::to_value(&anchor).unwrap();
|
||||
assert!(validate_face_anchor_json(&raw).is_ok());
|
||||
let restored: FaceAnchor = serde_json::from_value(raw.clone()).unwrap();
|
||||
assert_eq!(restored, anchor);
|
||||
for placement in [
|
||||
serde_json::json!([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0]]),
|
||||
serde_json::json!([[2, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]),
|
||||
serde_json::json!([[-1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]),
|
||||
serde_json::json!([[1, 0, 0, 1], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]),
|
||||
serde_json::json!([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [1e10, 0, 0, 1]]),
|
||||
] {
|
||||
let mut invalid = raw.clone();
|
||||
invalid["placement"] = placement;
|
||||
assert!(validate_face_anchor_json(&invalid).is_err(), "{invalid}");
|
||||
if let Ok(invalid) = serde_json::from_value::<FaceAnchor>(invalid) {
|
||||
assert!(invalid.validate().is_err());
|
||||
}
|
||||
}
|
||||
let mut legacy = raw;
|
||||
legacy.as_object_mut().unwrap().remove("placement");
|
||||
assert!(validate_face_anchor_json(&legacy).is_ok());
|
||||
let legacy: FaceAnchor = serde_json::from_value(legacy).unwrap();
|
||||
doc.occurrences.iter_mut().find(|o| o.component == author).unwrap().transform[3][0] = 40.;
|
||||
let mut state = ModelState::default();
|
||||
state.bodies.push(body);
|
||||
assert!(doc.resolve_face_anchor(&anchor, author, &state).unwrap().origin.dist(picked.origin) < 1e-9);
|
||||
assert!(doc.resolve_face_anchor(&legacy, author, &state).unwrap().origin.dist(picked.origin - Vec3::X * 40.) < 1e-9);
|
||||
doc.occurrences.clear();
|
||||
assert!(doc.resolve_face_anchor(&anchor, author, &state).is_err(), "a captured context still validates its paths");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_anchor_offset_tracks_source_and_raw_budgets_precede_decode() {
|
||||
let (doc, body, index, picked) = fixture();
|
||||
|
||||
@@ -202,6 +202,7 @@ fn construction_names_follow_renames_in_every_definition() {
|
||||
let planes = [
|
||||
json!({"type": "midplane", "a": plane, "b": plane}),
|
||||
json!({"type": "two_edges", "a": axis, "b": axis}),
|
||||
json!({"type": "axis_point", "axis": axis, "point": point}),
|
||||
json!({"type": "three_points", "a": point, "b": point, "c": point}),
|
||||
json!({"type": "tangent", "face": plane, "at": [0, 0, 0]}),
|
||||
json!({"type": "along_path", "path": axis, "t": "0.5"}),
|
||||
|
||||
@@ -148,7 +148,10 @@ pub(super) fn fixtures() -> Vec<(&'static str, Value)> {
|
||||
("sheet_hem", json!({"type": "sheet_hem", "edges": [o], "length": "hem.length", "gap": "hem.gap"})),
|
||||
(
|
||||
"sheet_perforate",
|
||||
json!({"type": "sheet_perforate", "diameter": "perforate.diameter", "pitch": "perforate.pitch", "margin": "perforate.margin"}),
|
||||
json!({
|
||||
"type": "sheet_perforate", "diameter": "perforate.diameter", "pitch": "perforate.pitch", "margin": "perforate.margin",
|
||||
"sketch": 1, "profiles": {"type": "all"}
|
||||
}),
|
||||
),
|
||||
(
|
||||
"sheet_fold",
|
||||
|
||||
@@ -107,7 +107,7 @@ pattern_circular
|
||||
inputs:
|
||||
Quantity = circ.count (Unitless)
|
||||
Total angle = circ.angle (Angle)
|
||||
expressions: circ.count, circ.angle
|
||||
expressions: circ.count, circ.angle, 0, 0, 0
|
||||
pattern_path
|
||||
inputs:
|
||||
Quantity = path.count (Unitless)
|
||||
|
||||
@@ -47,6 +47,7 @@ pattern_rectangular
|
||||
moves: pattern.dir1[0], pattern.dir1[1], pattern.dir2[1], pattern.dir2[2]
|
||||
pattern_circular
|
||||
moves: pattern.axis[0], pattern.axis[2], pattern.origin[0], pattern.origin[1], pattern.origin[2]
|
||||
placement: 0, 0, 0
|
||||
pattern_path
|
||||
mirror
|
||||
moves: plane.axis_dir[0], plane.axis_dir[1], plane.axis_origin[0], plane.axis_origin[1], plane.axis_origin[2]
|
||||
@@ -133,6 +134,7 @@ sheet_flange
|
||||
sheet_hem
|
||||
moves: edges[0][0], edges[0][1], edges[0][2]
|
||||
sheet_perforate
|
||||
profiles: 1 all
|
||||
sheet_fold
|
||||
moves: a[0], a[1], a[2], b[1], b[2], fixed[0], fixed[1], fixed[2]
|
||||
sheet_path
|
||||
|
||||
@@ -143,6 +143,8 @@ sheet_flange
|
||||
sheet_hem
|
||||
remap_bodies: body
|
||||
sheet_perforate
|
||||
sketches: [1]
|
||||
remap_refs: sketch
|
||||
remap_bodies: body
|
||||
sheet_fold
|
||||
sketches: [7]
|
||||
|
||||
@@ -724,6 +724,13 @@ pub fn read(s: &str) -> Result<Document> {
|
||||
let mut grouped_features = std::collections::BTreeSet::new();
|
||||
let joint_ids: std::collections::BTreeSet<_> = d.assembly.joints.iter().map(|joint| joint.id).collect();
|
||||
let mut grouped_joints = std::collections::BTreeSet::new();
|
||||
// A folder entry naming a feature or joint the design doesn't have, or one a folder already
|
||||
// holds, is dropped, and the design opens: folders only group the timeline, so nothing else
|
||||
// is lost. Editing a feature inside a folder used to leave such entries (#9373).
|
||||
for group in &mut d.timeline_groups {
|
||||
group.joints.retain(|joint| joint_ids.contains(joint) && grouped_joints.insert(*joint));
|
||||
group.features.retain(|feature| feature_ids.contains(feature) && grouped_features.insert(*feature));
|
||||
}
|
||||
for group in &d.timeline_groups {
|
||||
if group.name.trim().is_empty() || group.name.len() > 128 || !group_ids.insert(group.id) {
|
||||
return Err(DocError::Invalid("damaged design: invalid timeline folder".into()));
|
||||
@@ -731,12 +738,6 @@ pub fn read(s: &str) -> Result<Document> {
|
||||
if group.parent.is_some_and(|parent| parent == group.id) {
|
||||
return Err(DocError::Invalid("damaged design: timeline folder cannot contain itself".into()));
|
||||
}
|
||||
if group.joints.iter().any(|joint| !joint_ids.contains(joint) || !grouped_joints.insert(*joint)) {
|
||||
return Err(DocError::Invalid("damaged design: invalid joint in a timeline folder".into()));
|
||||
}
|
||||
if group.features.iter().any(|feature| !feature_ids.contains(feature) || !grouped_features.insert(*feature)) {
|
||||
return Err(DocError::Invalid("damaged design: invalid feature in a timeline folder".into()));
|
||||
}
|
||||
}
|
||||
for group in &d.timeline_groups {
|
||||
if let Some(parent) = group.parent {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
//! A component marked out of the History bar saves `history_hidden: true` on its component
|
||||
//! entry, so its features (and its children's) stay out of the History bar while still
|
||||
//! rebuilding. Older designs carry no such flag and every component shows its features, which
|
||||
//! is exactly the flag's default; the last release refuses this newer format and asks for an
|
||||
//! update instead of silently dropping the flag on the next save.
|
||||
@@ -0,0 +1,5 @@
|
||||
//! Cross-component face anchors in a part save the source-to-author component placement at
|
||||
//! creation, so assembly moves do not reshape the part. Source geometry and body offsets stay
|
||||
//! associative. Older anchors without `placement` retain their live placement relationship;
|
||||
//! redefining their sketch plane captures a new context. The last release refuses this newer
|
||||
//! format and asks for an update instead of dropping the captured placement.
|
||||
@@ -0,0 +1,3 @@
|
||||
//! Construction planes can store an associative axis and off-axis point reference.
|
||||
//! Older designs keep their existing plane definitions unchanged. The last release
|
||||
//! asks for an update rather than opening a design with this new plane definition.
|
||||
@@ -0,0 +1,4 @@
|
||||
//! A circular pattern's axis `origin` can hold length expressions (strings) as well as numbers,
|
||||
//! so a pattern about a parameter-driven point follows the parameter (#10852). An origin of
|
||||
//! plain numbers is saved as before, and older designs need no transformation. The last release
|
||||
//! refuses this newer format and asks for an update instead of failing to read the origin.
|
||||
@@ -0,0 +1,4 @@
|
||||
//! A perforation field can be bounded by a sketch: a `sheet_perforate` feature saves an optional `sketch` and
|
||||
//! `profiles` (its outline), and a sheet's perforation the outline in flat coordinates (`outline`). Holes go only
|
||||
//! inside the outline. Older designs need no transformation: a field without an outline covers the whole sheet as
|
||||
//! before. The last release refuses a design saved with this (the newer format number).
|
||||
@@ -0,0 +1,5 @@
|
||||
//! A tangent construction plane can save a `reference` (a plane or planar face) and an `angle`
|
||||
//! (#8822): the plane touches the face where it turns towards the reference, turned by the angle
|
||||
//! about the face's axis. An older design has neither, and its tangent planes touch where they
|
||||
//! were picked, as before. The last release refuses a design that has them (it asks for an
|
||||
//! update) instead of opening those planes at the picked point, away from their reference.
|
||||
@@ -50,7 +50,7 @@ fn edge_index(b: &ModelBody, name: Option<&str>, pick: Vec3) -> Option<usize> {
|
||||
fn face_of(b: &ModelBody, i: usize, name: Option<&str>, pick: Vec3) -> Result<(usize, Vec3, Vec3)> {
|
||||
let m = b.mesh();
|
||||
let beside: Vec<usize> = m.edge_faces.get(i).map(|fs| fs.iter().map(|f| *f as usize).collect()).unwrap_or_default();
|
||||
let infos = b.body.faces(tol(b))?;
|
||||
let infos = b.joint_faces()?;
|
||||
let planar = |f: usize| infos.iter().find(|x| x.index == f).and_then(|x| x.plane_normal.map(|n| (x.index, n, x.centroid, x.area)));
|
||||
let chosen = match name {
|
||||
Some(n) => {
|
||||
|
||||
@@ -16,14 +16,11 @@ const TURNS: [f64; 4] = [0.0, std::f64::consts::FRAC_PI_2, std::f64::consts::PI,
|
||||
/// Starts tried at most (angle values beyond the first three start at 0).
|
||||
const MAX_STARTS: usize = 64;
|
||||
|
||||
/// The values of relation `j` that move B least from where it is now: B's joint frame as it sits
|
||||
/// in A's, met as nearly as the relation allows (its origin as near as it can be to where it is,
|
||||
/// and its axes, each weighed as a point a part's size along it). `None` for a joint that is not a relation, or whose
|
||||
/// origins can't be found.
|
||||
/// The values of joint `j` that move B least from where it is now: B's joint frame as it sits
|
||||
/// in A's, met as nearly as the joint allows (its origin as near as it can be to where it is,
|
||||
/// and its axes, each weighed as a point a part's size along it). Used for relations and for
|
||||
/// typed joints that close a loop. `None` when the origins or finite values can't be found.
|
||||
pub fn relation_values(doc: &Document, st: &ModelState, j: &Joint) -> Option<Vec<f64>> {
|
||||
if !j.kind.is_relation() {
|
||||
return None;
|
||||
}
|
||||
let world = |occ: u64| if occ == 0 { IDENTITY } else { occurrence_world(doc, occ) };
|
||||
let fa = mat_mul(&world(j.a.occurrence), &resolve_origin(doc, st, &j.a).ok()?);
|
||||
let fb = mat_mul(&world(j.b.occurrence), &resolve_origin(doc, st, &j.b).ok()?);
|
||||
|
||||
@@ -86,10 +86,10 @@ pub use assembly::{
|
||||
pub use document::*;
|
||||
pub use eval::{
|
||||
EdgeRef, FeatureResult, Model, ModelBody, ModelState, PlacedBodies, RevolveExtent, SavedModel, SolvedSketch, ThreadInfo, curve_pick_error,
|
||||
edge_names_for, edge_refs, face_point_names, face_point_state, hole_direction_at, import_body_names, join_pieces_added, map_wire,
|
||||
metric_thread_sizes, object_height, parse_metric_thread, pattern_matrices, revolve_extent, revolve_extent_with_convention, select_profiles,
|
||||
sketch_hole_direction, sketch_line_plane, state_in_frame, state_in_frame_at, suggested_thread, tangent_face_chain, through_all_distance,
|
||||
world_state, world_state_reusing,
|
||||
edge_names_for, edge_refs, face_point_names, face_point_state, hole_direction_at, 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,
|
||||
tangent_face_chain, through_all_distance, world_state, world_state_reusing,
|
||||
};
|
||||
pub use eval::{EvalCache, KeptResults};
|
||||
pub use expr::Expr;
|
||||
|
||||
@@ -587,10 +587,14 @@ pub(crate) fn order_lumps(bodies: &mut [crate::ModelBody], origin: &Arc<crate::n
|
||||
let keys: Option<Vec<(String, usize)>> = members.iter().map(|i| bodies.get(*i).and_then(least).map(|k| (k, *i))).collect();
|
||||
let Some(mut keys) = keys else { continue };
|
||||
keys.sort();
|
||||
let names: Vec<String> = members.iter().filter_map(|i| bodies.get(*i).map(|b| b.name.clone())).collect();
|
||||
for (name, (_, i)) in names.into_iter().zip(keys) {
|
||||
// A name moves with the feature it belongs to: in a cut's family the first name is the
|
||||
// cut body's own feature's and the rest are the cut's, and `body.rename` stores a name in
|
||||
// the `body_names` of the feature a body says made it (#10438).
|
||||
let names: Vec<(String, u64)> = members.iter().filter_map(|i| bodies.get(*i).map(|b| (b.name.clone(), b.feature))).collect();
|
||||
for ((name, feature), (_, i)) in names.into_iter().zip(keys) {
|
||||
if let Some(b) = bodies.get_mut(i) {
|
||||
b.name = name;
|
||||
b.feature = feature;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,11 +223,15 @@ impl Writer {
|
||||
}
|
||||
|
||||
fn sheet(&mut self, sheet: &crate::sheet::SheetBody) -> Option<usize> {
|
||||
let record = serde_json::to_value(sheet).ok()?;
|
||||
let key = record.to_string();
|
||||
// Keyed by the sheet written as text, straight from the sheet: every state asks for each
|
||||
// of its sheets, and building the record (a JSON tree) each time only to find it written
|
||||
// already took most of a sheet-metal design's save (906 states, 300 sheets: 0.8 s). Two
|
||||
// sheets are the same text exactly when they are the same record.
|
||||
let key = serde_json::to_string(sheet).ok()?;
|
||||
if let Some(i) = self.sheet_index.get(&key) {
|
||||
return Some(*i);
|
||||
}
|
||||
let record = serde_json::to_value(sheet).ok()?;
|
||||
self.sheets.push(record);
|
||||
self.sheet_index.insert(key, self.sheets.len() - 1);
|
||||
Some(self.sheets.len() - 1)
|
||||
|
||||
@@ -203,11 +203,11 @@ impl FaceHit {
|
||||
/// What a feature says about how it found a face by its stored name `name` (`None`: exactly).
|
||||
pub fn note(&self, name: &str) -> Option<String> {
|
||||
if self.split {
|
||||
Some(format!("face `{name}` was split; using the piece nearest the picked point"))
|
||||
Some(format!("face `{}` was split; using the piece nearest the picked point", quoted(name)))
|
||||
} else if self.merged {
|
||||
Some(format!("face `{name}` was merged into another face; using that face"))
|
||||
} else if !self.verified && !self.on_point && !self.legacy {
|
||||
Some(format!("face `{name}` was found by geometry, not exact history; check it"))
|
||||
Some(format!("face `{}` was found by geometry, not exact history; check it", quoted(name)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -243,13 +243,29 @@ impl FaceHit {
|
||||
}
|
||||
}
|
||||
|
||||
/// A stored name as a message quotes it: whole up to about 80 characters, else its start and
|
||||
/// end around an ellipsis. Generated names grow with the history (a split edge's name holds both
|
||||
/// its faces' names), and a chamfer's warning that quoted fourteen of them in full ran to 122,701
|
||||
/// characters (#8541).
|
||||
pub(crate) fn quoted(name: &str) -> String {
|
||||
const HEAD: usize = 48;
|
||||
const TAIL: usize = 24;
|
||||
let n = name.chars().count();
|
||||
if n <= HEAD + TAIL + 8 {
|
||||
return name.to_string();
|
||||
}
|
||||
let head: String = name.chars().take(HEAD).collect();
|
||||
let tail: String = name.chars().skip(n - TAIL).collect();
|
||||
format!("{head}…{tail}")
|
||||
}
|
||||
|
||||
impl EdgeHit {
|
||||
/// What a feature says about how it found an edge by its stored name `name` (`None`: exactly).
|
||||
pub fn note(&self, name: &str) -> Option<String> {
|
||||
if self.split {
|
||||
Some(format!("edge `{name}` was split; using the piece nearest the picked point"))
|
||||
Some(format!("edge `{}` was split; using the piece nearest the picked point", quoted(name)))
|
||||
} else if !self.verified && !self.on_point && !self.legacy {
|
||||
Some(format!("edge `{name}` was found by geometry, not exact history; check it"))
|
||||
Some(format!("edge `{}` was found by geometry, not exact history; check it", quoted(name)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -90,7 +90,11 @@ pub struct ParamRow {
|
||||
pub name: String,
|
||||
pub expression: String,
|
||||
pub unit: String,
|
||||
/// Always millimetres for a length, degrees for an angle, whatever the parameter's own
|
||||
/// unit — the same meaning `value` has in every reply (#10747).
|
||||
pub value: Option<f64>,
|
||||
/// The value in the parameter's own `unit`, as the Parameters window shows it.
|
||||
pub display_value: Option<f64>,
|
||||
pub error: Option<String>,
|
||||
pub comment: String,
|
||||
/// "user", "sketch" (a dimension) or "feature".
|
||||
@@ -367,14 +371,20 @@ impl Document {
|
||||
}
|
||||
}
|
||||
let used_by = |n: &str| -> Vec<String> { users.get(n).cloned().unwrap_or_default() };
|
||||
let value = |n: &str, k: Kind| vals.get(n).and_then(|v| v.to_kind(k).ok()).map(|x| if k == Kind::Angle { x.to_degrees() } else { x });
|
||||
let value = |n: &str, k: Kind| {
|
||||
vals.get(n).and_then(|v| v.to_kind(k).ok()).map(|x| if k == Kind::Angle { x.to_degrees() } else { x }).map(expr::round_reported)
|
||||
};
|
||||
// `value` is mm / degrees in every row (#10747); the parameter's own unit goes to
|
||||
// `display_value`. Both carry the number a person would read, not the float noise.
|
||||
let unified = |n: &str| vals.get(n).map(|v| expr::round_reported(expr::reported_value(*v)));
|
||||
let mut rows = Vec::new();
|
||||
for p in &self.params {
|
||||
rows.push(ParamRow {
|
||||
name: p.name.clone(),
|
||||
expression: p.expr.clone(),
|
||||
unit: p.unit.clone(),
|
||||
value: self.param_display_value(&vals, &p.name),
|
||||
value: unified(&p.name),
|
||||
display_value: self.param_display_value(&vals, &p.name).map(expr::round_reported),
|
||||
error: errs.get(&p.name).cloned(),
|
||||
comment: p.comment.clone(),
|
||||
source: if p.model { "sketch" } else { "user" },
|
||||
@@ -396,6 +406,8 @@ impl Document {
|
||||
expression: e,
|
||||
unit: unit_of(k).into(),
|
||||
value: value(name, k),
|
||||
// A feature input's own unit is mm / deg, so its display value is its value.
|
||||
display_value: value(name, k),
|
||||
error: errs.get(name).cloned(),
|
||||
comment: self.param_comments.get(name).cloned().unwrap_or_default(),
|
||||
source: "feature",
|
||||
@@ -415,6 +427,7 @@ impl Document {
|
||||
expression: p.text.clone(),
|
||||
unit: crate::text_params::TEXT_UNIT.into(),
|
||||
value: None,
|
||||
display_value: None,
|
||||
error: None,
|
||||
comment: p.comment.clone(),
|
||||
source: "user",
|
||||
@@ -460,7 +473,8 @@ impl Document {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// User parameters as CSV (`name,unit,expression,value,comment`).
|
||||
/// User parameters as CSV (`name,unit,expression,value,comment`). The `value` column is the
|
||||
/// number in the `unit` column, rounded for reading (#10747); import reads `expression`.
|
||||
pub fn params_csv(&self) -> String {
|
||||
let q = |s: &str| if s.contains([',', '"', '\r', '\n']) { format!("\"{}\"", s.replace('"', "\"\"")) } else { s.to_string() };
|
||||
let mut out = String::from("name,unit,expression,value,comment,favorite\n");
|
||||
@@ -470,7 +484,7 @@ impl Document {
|
||||
q(&r.name),
|
||||
q(&r.unit),
|
||||
q(&r.expression),
|
||||
r.value.map(|v| format!("{v}")).unwrap_or_default(),
|
||||
r.display_value.or(r.value).map(|v| format!("{v}")).unwrap_or_default(),
|
||||
q(&r.comment),
|
||||
r.favorite
|
||||
);
|
||||
|
||||
@@ -16,23 +16,57 @@ pub(super) struct PlacedMesh {
|
||||
/// each placement draws them moved, so a part is planned once however often it is placed.
|
||||
plans: Arc<[OnceLock<Option<Arc<TessPlan>>>; 4]>,
|
||||
plan_meshes: Arc<[OnceLock<Option<Arc<Mesh>>>; 4]>,
|
||||
/// The definition's own opposite hand: a copy of a copy is a rigid move of that.
|
||||
opposite_hand: Arc<OnceLock<OppositeHand>>,
|
||||
transform: crate::Mat,
|
||||
pub(super) tolerance: f64,
|
||||
}
|
||||
|
||||
/// A body's opposite hand: the first mirrored copy made of it, and the matrix that made it. A
|
||||
/// reflection reverses each face's edge walk, so a copy can't be drawn from its source's triangles.
|
||||
/// Two reflections do cancel, though: every later mirrored copy of the body, wherever dragging a
|
||||
/// placement puts it, is a proper rigid move of this one, whose triangles are made once.
|
||||
#[derive(Debug)]
|
||||
pub(super) struct OppositeHand {
|
||||
/// The body it mirrors, to tell a holder whose body was replaced.
|
||||
source: Body,
|
||||
definition: ModelBody,
|
||||
matrix: crate::Mat,
|
||||
}
|
||||
|
||||
impl PlacedMesh {
|
||||
/// Where `copy` (`source` transformed by `matrix`) stands relative to `source`'s opposite
|
||||
/// hand. `None` shows it from its own mesh: a matrix that isn't a reflection of the first.
|
||||
/// A copy of a copy goes by what its source is drawn from, which outlives that source.
|
||||
pub(super) fn mirrored(source: &ModelBody, copy: &Body, matrix: &crate::Mat) -> Option<Self> {
|
||||
let (cell, body, matrix) = match source.mirrored_mesh.as_deref() {
|
||||
Some(drawn) => (&drawn.opposite_hand, &drawn.body, crate::mat_mul(matrix, &drawn.transform)),
|
||||
None => (&source.opposite_hand, &source.body, *matrix),
|
||||
};
|
||||
let hand = cell.get_or_init(|| OppositeHand {
|
||||
source: body.clone(),
|
||||
definition: ModelBody::new(String::new(), copy.clone(), source.feature),
|
||||
matrix,
|
||||
});
|
||||
if !same_triangles(&hand.source, body) {
|
||||
return None;
|
||||
}
|
||||
Self::new(&hand.definition, &crate::mat_mul(&matrix, &crate::mat_inverse(&hand.matrix)?))
|
||||
}
|
||||
|
||||
/// Share the original definition, never a chain of previously placed meshes. A source's
|
||||
/// OnceLock lives with its immutable geometry, so edits get a new source automatically.
|
||||
pub(super) fn new(source: &ModelBody, transform: &crate::Mat) -> Option<Self> {
|
||||
if !proper_rigid(transform) {
|
||||
return None;
|
||||
}
|
||||
Some(match source.mesh_placement.as_deref() {
|
||||
Some(match source.mesh_placement.as_deref().or(source.mirrored_mesh.as_deref()) {
|
||||
Some(original) => Self {
|
||||
body: original.body.clone(),
|
||||
mesh: original.mesh.clone(),
|
||||
plans: original.plans.clone(),
|
||||
plan_meshes: original.plan_meshes.clone(),
|
||||
opposite_hand: original.opposite_hand.clone(),
|
||||
transform: crate::mat_mul(transform, &original.transform),
|
||||
tolerance: original.tolerance,
|
||||
},
|
||||
@@ -41,6 +75,7 @@ impl PlacedMesh {
|
||||
mesh: source.mesh.clone(),
|
||||
plans: source.plan.clone(),
|
||||
plan_meshes: source.plan_mesh.clone(),
|
||||
opposite_hand: source.opposite_hand.clone(),
|
||||
transform: *transform,
|
||||
tolerance: source.display_tol(),
|
||||
},
|
||||
@@ -98,6 +133,29 @@ impl PlacedMesh {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether two bodies mesh to the same triangles: the same shape, split along the same lines.
|
||||
fn same_triangles(a: &Body, b: &Body) -> bool {
|
||||
a.same_shape(b)
|
||||
&& match (a.split_lines(), b.split_lines()) {
|
||||
(Some(x), Some(y)) => Arc::ptr_eq(x, y),
|
||||
(None, None) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
impl ModelBody {
|
||||
/// Keep the meshes `old` holds when this body is the same shape: a feature evaluated again
|
||||
/// that gives the same solids (an import, from its shared file) doesn't mesh them again.
|
||||
pub(super) fn keep_meshes_of(&mut self, old: &ModelBody) {
|
||||
if same_triangles(&self.body, &old.body) {
|
||||
self.mesh = old.mesh.clone();
|
||||
self.plan = old.plan.clone();
|
||||
self.plan_mesh = old.plan_mesh.clone();
|
||||
self.opposite_hand = old.opposite_hand.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The display plan of a definition `body` at quality `q`, made once in `cell`.
|
||||
pub(super) fn definition_plan(
|
||||
body: &Body,
|
||||
|
||||
@@ -68,6 +68,21 @@ fn local_plane(plane: &PlaneRef, depth: usize) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether no feature after the mirror `f` can cut, join or otherwise build on bodies: only
|
||||
/// features that read none, and further mirrors, which copy them. A boolean depends on how a
|
||||
/// copy's revolved faces are turned, so a copy it may reach is made with
|
||||
/// [`solidcraft_kernel::transform_matrix`]; one that is only shown needs no search for that. It
|
||||
/// depends on the design alone, so a design evaluates the same however it was reached.
|
||||
pub(super) fn copies_only_shown(doc: &Document, f: &Feature) -> bool {
|
||||
let mut later = doc.features.iter().skip_while(|g| g.id != f.id).skip(1);
|
||||
let mut components = std::collections::HashMap::with_capacity(doc.features.len());
|
||||
for g in &doc.features {
|
||||
components.entry(g.id).or_insert(g.component);
|
||||
}
|
||||
let copies = |g: &Feature| matches!(&g.kind, FeatureKind::Mirror { features, combine: false, .. } if features.is_empty());
|
||||
later.all(|g| copies(g) || local_geometry(g, &components))
|
||||
}
|
||||
|
||||
pub(super) fn local_geometry(f: &Feature, components: &std::collections::HashMap<u64, u64>) -> bool {
|
||||
// Persistent picked-face/edge attachments can move an otherwise literal input.
|
||||
if !f.face_names.is_empty() || !f.edge_names.is_empty() || !f.edge_refs.is_empty() {
|
||||
|
||||
+260
-33
@@ -148,7 +148,7 @@ fn resolve_uncached(
|
||||
let mesh = b.mesh();
|
||||
match kind {
|
||||
LinkKind::Intersect => section(plane, &mesh, None),
|
||||
_ => silhouette(plane, &mesh),
|
||||
_ => silhouette(plane, &mesh, Some(&b.body)),
|
||||
}
|
||||
}
|
||||
LinkSource::Vertex { body, at } => {
|
||||
@@ -208,7 +208,7 @@ fn resolve_uncached(
|
||||
LinkSource::Intersection { a, b } => {
|
||||
let (ma, fa) = crate::project3d::mesh_of(st, a).ok_or_else(|| lost(&a.describe()))?;
|
||||
let (mb, fb) = crate::project3d::mesh_of(st, b).ok_or_else(|| lost(&b.describe()))?;
|
||||
wires = crate::project3d::mesh_intersection(&ma, fa, &mb, fb);
|
||||
wires = crate::project3d::mesh_intersection(&ma, fa.as_deref(), &mb, fb.as_deref());
|
||||
Vec::new()
|
||||
}
|
||||
LinkSource::Iso { body, at, dir } => {
|
||||
@@ -302,9 +302,78 @@ fn edge_geom(plane: &Plane, kind: LinkKind, exact: Option<Vec<LinkGeom>>, pts: V
|
||||
/// Edges `edges` of `body` (indices into its mesh's `edges`) on `plane` as their exact curves:
|
||||
/// lines, circles and arcs, and the Bézier pieces of a free-form curve. `None` for an edge that
|
||||
/// doesn't lie in a plane parallel to `plane` or isn't read exactly (a mesh body's): it is
|
||||
/// fitted to its polyline instead.
|
||||
/// fitted to its polyline instead. An edge read as spline pieces, or not read, that runs along
|
||||
/// a circle in a plane parallel to `plane` comes in as that circle ([`circular`]): a round's
|
||||
/// edge is a spline that only follows its circle.
|
||||
fn exact_edges(body: &crate::ModelBody, plane: &Plane, edges: &[usize]) -> Vec<Option<Vec<LinkGeom>>> {
|
||||
body.body.planar_edges(plane, edges).into_iter().map(|segs| segs?.iter().map(link_geom).collect()).collect()
|
||||
let mut mesh = None;
|
||||
body.body
|
||||
.planar_edges(plane, edges)
|
||||
.into_iter()
|
||||
.zip(edges)
|
||||
.map(|(segs, e)| match segs {
|
||||
Some(segs) if !segs.iter().any(|s| matches!(s, Seg2::Cubic { .. } | Seg2::Bezier { .. })) => segs.iter().map(link_geom).collect(),
|
||||
Some(segs) => {
|
||||
let samples: Vec<Vec2> =
|
||||
segs.iter().flat_map(|s| (0..=CIRCLE_SAMPLES).map(move |i| s.point_at(f64::from(i) / f64::from(CIRCLE_SAMPLES)))).collect();
|
||||
match circular(&samples) {
|
||||
Some((g, _)) => Some(vec![g]),
|
||||
None => segs.iter().map(link_geom).collect(),
|
||||
}
|
||||
}
|
||||
None if body.body.is_mesh() => None,
|
||||
None => {
|
||||
// Not read: its polyline lies on the curve, but only many points of it show a circle.
|
||||
let pts = mesh.get_or_insert_with(|| body.mesh()).edges.get(*e).filter(|pts| pts.len() > 8)?;
|
||||
let level = plane.height(*pts.first()?);
|
||||
let (g, radius) = circular(&pts.iter().map(|p| plane.to_local(*p)).collect::<Vec<_>>())?;
|
||||
pts.iter().all(|p| (plane.height(*p) - level).abs() <= radius * CIRCLE_TOL).then(|| vec![g])
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Points sampled along each Bézier piece of an edge to see whether it runs along a circle.
|
||||
const CIRCLE_SAMPLES: u16 = 16;
|
||||
/// How far round (radians) a curve turns at least to be taken for a circle: a letter's nearly
|
||||
/// straight stroke lies near a large circle too.
|
||||
const CIRCLE_MIN_TURN: f64 = 0.1;
|
||||
/// How far (a fraction of its radius) a curve may stray from a circle and still be that circle:
|
||||
/// a round's edge keeps to it within 3e-7, a curve that merely looks round (a letter's bowl of
|
||||
/// four cubic spans) only within 3e-4.
|
||||
const CIRCLE_TOL: f64 = 1e-5;
|
||||
|
||||
/// The circle (closing points) or arc through `samples`, taken in order along a curve, with
|
||||
/// its radius: when they all lie on one within [`CIRCLE_TOL`]. The arc ends at the first and
|
||||
/// last sample, so it meets what the curve met.
|
||||
fn circular(samples: &[Vec2]) -> Option<(LinkGeom, f64)> {
|
||||
let (first, last) = (*samples.first()?, *samples.last()?);
|
||||
let (c, radius) = ls_circle(samples)?;
|
||||
let tol = radius * CIRCLE_TOL;
|
||||
if !(radius.is_finite() && tol > 0.0) || samples.iter().any(|p| (p.dist(c) - radius).abs() > tol) {
|
||||
return None;
|
||||
}
|
||||
let mut sweep = 0.0;
|
||||
for w in samples.windows(2) {
|
||||
let (Some(a), Some(b)) = (w.first(), w.get(1)) else { continue };
|
||||
let mut d = (*b - c).angle() - (*a - c).angle();
|
||||
if d > std::f64::consts::PI {
|
||||
d -= std::f64::consts::TAU;
|
||||
} else if d < -std::f64::consts::PI {
|
||||
d += std::f64::consts::TAU;
|
||||
}
|
||||
sweep += d;
|
||||
}
|
||||
let turn = sweep.abs();
|
||||
if turn > std::f64::consts::TAU + 1e-6 || turn < CIRCLE_MIN_TURN {
|
||||
return None;
|
||||
}
|
||||
if first.dist(last) <= tol {
|
||||
return (turn > std::f64::consts::TAU - 1e-6).then_some((LinkGeom::Circle(c, radius), radius));
|
||||
}
|
||||
// Counter-clockwise from `a` to `b`.
|
||||
let (a, b) = if sweep > 0.0 { (first, last) } else { (last, first) };
|
||||
Some((LinkGeom::Arc { c, a, b }, radius))
|
||||
}
|
||||
|
||||
/// `edges` of `body` projected on `plane`: each exactly ([`exact_edges`]) or fitted to its
|
||||
@@ -669,7 +738,7 @@ fn refresh_links_in(
|
||||
*at = p;
|
||||
named_face = Some((body.clone(), index, p));
|
||||
if split {
|
||||
warn.push(format!("projected face `{name}` was split; using the nearest piece"));
|
||||
warn.push(format!("projected face `{}` was split; using the nearest piece", crate::name_lookup::quoted(name)));
|
||||
}
|
||||
} else {
|
||||
sk.set_link_lost(&l.id, true);
|
||||
@@ -1058,36 +1127,171 @@ fn section_of_polyline(plane: &Plane, pts: &[Vec3]) -> Vec<LinkGeom> {
|
||||
}
|
||||
|
||||
/// The outline of a body seen along the sketch normal: mesh edges between triangles facing the
|
||||
/// sketch and triangles facing away (or edge-on), projected and fitted.
|
||||
fn silhouette(plane: &Plane, m: &Mesh) -> Vec<LinkGeom> {
|
||||
/// sketch and triangles facing away (or edge-on), projected and fitted. A face of `body` that
|
||||
/// faces one way all over ([`face_facing`]) does so in every triangle, though they lean a hair
|
||||
/// either way: a round wall seen edge-on joins its two rims at different angles, and thin
|
||||
/// slivers of a narrow chamfer can point the other way.
|
||||
fn silhouette(plane: &Plane, m: &Mesh, body: Option<&solidcraft_kernel::Body>) -> Vec<LinkGeom> {
|
||||
let n = plane.normal();
|
||||
let size = m.bounds().diagonal().max(1e-9);
|
||||
let q = size * 1e-7;
|
||||
let key = |p: Vec3| ((p.x / q).round() as i64, (p.y / q).round() as i64, (p.z / q).round() as i64);
|
||||
// Undirected mesh edge → facing flags of the triangles using it.
|
||||
let mut edges: BTreeMap<((i64, i64, i64), (i64, i64, i64)), (Vec3, Vec3, Vec<bool>)> = BTreeMap::new();
|
||||
for t in &m.triangles {
|
||||
let Some([a, b, c]) = m.tri(t) else { continue };
|
||||
let fnrm = (b - a).cross(c - a);
|
||||
let Some(un) = fnrm.normalized() else { continue };
|
||||
let front = un.dot(n) > 1e-6;
|
||||
for (p, r) in [(a, b), (b, c), (c, a)] {
|
||||
let (kp, kr) = (key(p), key(r));
|
||||
let k = if kp <= kr { (kp, kr) } else { (kr, kp) };
|
||||
edges.entry(k).or_insert_with(|| (p, r, Vec::new())).2.push(front);
|
||||
// Vertices welded within this: the copies of a vertex on the seam between two faces can lie
|
||||
// a few 1e-7 apart, and an edge used by one triangle only is outline.
|
||||
let weld = size * 1e-6;
|
||||
let cell = |p: Vec3| ((p.x / weld).floor() as i64, (p.y / weld).floor() as i64, (p.z / weld).floor() as i64);
|
||||
let mut grid: BTreeMap<(i64, i64, i64), Vec<usize>> = BTreeMap::new();
|
||||
let mut verts: Vec<Vec3> = Vec::new();
|
||||
let mut vertex = |p: Vec3| -> usize {
|
||||
let (x, y, z) = cell(p);
|
||||
for dx in -1..=1 {
|
||||
for dy in -1..=1 {
|
||||
for dz in -1..=1 {
|
||||
let found = grid
|
||||
.get(&(x + dx, y + dy, z + dz))
|
||||
.and_then(|ids| ids.iter().copied().find(|i| verts.get(*i).is_some_and(|v| v.dist(p) <= weld)));
|
||||
if let Some(i) = found {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
verts.push(p);
|
||||
grid.entry((x, y, z)).or_default().push(verts.len() - 1);
|
||||
verts.len() - 1
|
||||
};
|
||||
let tris: Vec<([Vec3; 3], Vec3, Option<u32>)> = m
|
||||
.triangles
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(ti, t)| {
|
||||
let [a, b, c] = m.tri(t)?;
|
||||
Some(([a, b, c], (b - a).cross(c - a), m.tri_face.get(ti).copied()))
|
||||
})
|
||||
.collect();
|
||||
// Each face's area-weighted facing, and whether any triangle of it is in doubt: near
|
||||
// edge-on, or facing against the rest of its face. Only those faces ask what they are.
|
||||
let mut faces: BTreeMap<u32, (f64, bool)> = BTreeMap::new();
|
||||
for (_, w, f) in &tris {
|
||||
if let Some(f) = f {
|
||||
faces.entry(*f).or_default().0 += w.dot(n);
|
||||
}
|
||||
}
|
||||
let segs: Vec<(Vec2, Vec2)> = edges
|
||||
for (_, w, f) in &tris {
|
||||
let (Some(f), Some(un)) = (f, w.normalized()) else { continue };
|
||||
if let Some(face) = faces.get_mut(f) {
|
||||
face.1 |= un.dot(n).abs() < 0.5 || (un.dot(n) > 0.0) != (face.0 > 0.0);
|
||||
}
|
||||
}
|
||||
let facing: BTreeMap<u32, Facing> = match body {
|
||||
Some(body) => faces.iter().filter(|(_, (_, doubt))| *doubt).filter_map(|(f, (sum, _))| Some((*f, face_facing(body, *f, n, *sum)?))).collect(),
|
||||
None => BTreeMap::new(),
|
||||
};
|
||||
// Undirected mesh edge → facing flags of the triangles using it.
|
||||
let ids: Vec<[usize; 3]> = tris.iter().map(|([a, b, c], _, _)| [vertex(*a), vertex(*b), vertex(*c)]).collect();
|
||||
let mut edges: BTreeMap<(usize, usize), (Vec3, Vec3, Vec<(bool, Option<u32>)>)> = BTreeMap::new();
|
||||
for ((_, w, f), [ia, ib, ic]) in tris.iter().zip(ids) {
|
||||
let Some(un) = w.normalized() else { continue };
|
||||
let front = match f.and_then(|f| facing.get(&f)) {
|
||||
Some(Facing::Front) => true,
|
||||
Some(Facing::Away) => false,
|
||||
None => un.dot(n) > 1e-6,
|
||||
};
|
||||
for (p, r) in [(ia, ib), (ib, ic), (ic, ia)] {
|
||||
if p == r {
|
||||
continue;
|
||||
}
|
||||
let (Some(vp), Some(vr)) = (verts.get(p).copied(), verts.get(r).copied()) else { continue };
|
||||
edges.entry((p.min(r), p.max(r))).or_insert((vp, vr, Vec::new())).2.push((front, *f));
|
||||
}
|
||||
}
|
||||
let mut segs: Vec<(Vec2, Vec2)> = edges
|
||||
.values()
|
||||
.filter(|(_, _, f)| {
|
||||
let fronts = f.iter().filter(|x| **x).count();
|
||||
fronts > 0 && fronts < f.len() || (f.len() == 1 && fronts == 1)
|
||||
let fronts = f.iter().filter(|x| x.0).count();
|
||||
fronts > 0 && fronts < f.len()
|
||||
})
|
||||
.map(|(a, b, _)| (plane.to_local(*a), plane.to_local(*b)))
|
||||
.collect();
|
||||
let singles: Vec<(Vec3, Vec3, bool, Option<u32>)> = edges
|
||||
.values()
|
||||
.filter_map(|(a, b, f)| match f.as_slice() {
|
||||
[(front, face)] => Some((*a, *b, *front, *face)),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
segs.extend(open_edges(&singles, size).into_iter().map(|(a, b)| (plane.to_local(a), plane.to_local(b))));
|
||||
fit_segments(&segs, size, true)
|
||||
}
|
||||
|
||||
/// Of the mesh edges used by one triangle each (`a`, `b`, whether the triangle faces the sketch,
|
||||
/// its face), those facing the sketch that are outline: an open sheet's border. Where two faces
|
||||
/// meet, one face's triangles can split the seam at a vertex the other's run straight past, and
|
||||
/// both sides are then used once; a seam whose other side faces the sketch too is no outline.
|
||||
fn open_edges(singles: &[(Vec3, Vec3, bool, Option<u32>)], size: f64) -> Vec<(Vec3, Vec3)> {
|
||||
// The two sides of a seam stray from each other by at most a chord's sag.
|
||||
let seam = size * 2e-3;
|
||||
let cell = seam * 8.0;
|
||||
let at = |p: Vec3| ((p.x / cell).floor() as i64, (p.y / cell).floor() as i64, (p.z / cell).floor() as i64);
|
||||
let mut grid: BTreeMap<(i64, i64, i64), Vec<usize>> = BTreeMap::new();
|
||||
let mut long: Vec<usize> = Vec::new();
|
||||
for (i, (a, b, front, _)) in singles.iter().enumerate() {
|
||||
if !front {
|
||||
continue;
|
||||
}
|
||||
let (lo, hi) = (at(a.min(*b) - Vec3::new(seam, seam, seam)), at(a.max(*b) + Vec3::new(seam, seam, seam)));
|
||||
let cells = (hi.0 - lo.0 + 1).saturating_mul(hi.1 - lo.1 + 1).saturating_mul(hi.2 - lo.2 + 1);
|
||||
if cells > 512 {
|
||||
long.push(i);
|
||||
continue;
|
||||
}
|
||||
for x in lo.0..=hi.0 {
|
||||
for y in lo.1..=hi.1 {
|
||||
for z in lo.2..=hi.2 {
|
||||
grid.entry((x, y, z)).or_default().push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
singles
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(i, (a, b, front, face))| {
|
||||
let m = (*a + *b) * 0.5;
|
||||
*front
|
||||
&& !grid
|
||||
.get(&at(m))
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.chain(&long)
|
||||
.any(|j| singles.get(*j).is_some_and(|(p, q, _, other)| j != i && other != face && m.dist_to_segment(*p, *q) <= seam))
|
||||
})
|
||||
.map(|(_, (a, b, _, _))| (*a, *b))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Which way a whole face of a body faces, seen along a sketch normal.
|
||||
#[derive(Clone, Copy)]
|
||||
enum Facing {
|
||||
Front,
|
||||
/// Away from the sketch or edge-on.
|
||||
Away,
|
||||
}
|
||||
|
||||
/// Which way face `face` of `body` faces seen along `n`, when it faces one way all over: a
|
||||
/// plane (edge-on when it contains `n`), a cylinder about `n` (edge-on), a cone about `n`.
|
||||
/// `sum` is the face's triangles' normals weighted by their areas, dotted with `n`: the side a
|
||||
/// plane or such a cone faces. `None` for any other face: each triangle says.
|
||||
fn face_facing(body: &solidcraft_kernel::Body, face: u32, n: Vec3, sum: f64) -> Option<Facing> {
|
||||
use solidcraft_kernel::FaceSurface;
|
||||
let side = if sum > 0.0 { Facing::Front } else { Facing::Away };
|
||||
match solidcraft_kernel::face_surface(body, face as usize)? {
|
||||
FaceSurface::Plane { normal, .. } if normal.vec().dot(n).abs() < 1e-9 => Some(Facing::Away),
|
||||
FaceSurface::Plane { .. } => Some(side),
|
||||
FaceSurface::Cylinder { axis, .. } if axis.vec().dot(n).abs() > 1.0 - 1e-12 => Some(Facing::Away),
|
||||
FaceSurface::Cone { axis, .. } if axis.vec().dot(n).abs() > 1.0 - 1e-12 => Some(side),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Section of a mesh (or one of its faces) with the sketch plane.
|
||||
pub(crate) fn section(plane: &Plane, m: &Mesh, face: Option<u32>) -> Vec<LinkGeom> {
|
||||
let size = m.bounds().diagonal().max(1e-9);
|
||||
@@ -1339,8 +1543,9 @@ fn snap_tangent_arcs(out: &mut [LinkGeom], tol: f64) {
|
||||
for i in 0..out.len() {
|
||||
let Some(LinkGeom::Arc { c, a, b }) = out.get(i).cloned() else { continue };
|
||||
let r = (c.dist(a) + c.dist(b)) / 2.0;
|
||||
// (arc end, line index, line end index, tangent point, unit normal towards the centre, centre distance)
|
||||
let mut hits: Vec<(usize, usize, usize, Vec2, Vec2, f64)> = Vec::new();
|
||||
// Per arc end: (gap to the line's end, line index, line end index, tangent point, unit
|
||||
// normal towards the centre, centre distance)
|
||||
let mut hits: [Vec<(f64, usize, usize, Vec2, Vec2, f64)>; 2] = [Vec::new(), Vec::new()];
|
||||
for (end, e) in [a, b].into_iter().enumerate() {
|
||||
for (j, g) in out.iter().enumerate() {
|
||||
let LinkGeom::Line(p, q) = g else { continue };
|
||||
@@ -1358,17 +1563,39 @@ fn snap_tangent_arcs(out: &mut [LinkGeom], tol: f64) {
|
||||
if (foot - far).dot(near_pt - far) <= 0.0 || near_pt.dist(foot) > 0.25 * r {
|
||||
continue;
|
||||
}
|
||||
hits.push((end, j, near, foot, n, d));
|
||||
// When this end of the arc already meets another piece of the outline and the line
|
||||
// doesn't end there, the line lies further along (past that piece, as along a
|
||||
// conic fitted with several arcs): moving the end onto it would leave a gap.
|
||||
let gap = near_pt.dist(e);
|
||||
let meets = |g: &LinkGeom| match *g {
|
||||
LinkGeom::Line(s, t) | LinkGeom::Arc { a: s, b: t, .. } => s.dist(e) <= tol || t.dist(e) <= tol,
|
||||
_ => false,
|
||||
};
|
||||
if gap > tol && out.iter().enumerate().any(|(k, g)| k != i && k != j && meets(g)) {
|
||||
continue;
|
||||
}
|
||||
if let Some(h) = hits.get_mut(end) {
|
||||
h.push((gap, j, near, foot, n, d));
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut hits_by_end: [Option<(usize, usize, Vec2, Vec2, f64)>; 2] = [None, None];
|
||||
for (end, j, near, foot, n, d) in hits {
|
||||
if let Some(slot) = hits_by_end.get_mut(end)
|
||||
&& slot.is_none()
|
||||
{
|
||||
*slot = Some((j, near, foot, n, d));
|
||||
}
|
||||
for h in &mut hits {
|
||||
h.sort_by(|x, y| x.0.total_cmp(&y.0));
|
||||
}
|
||||
// Each end takes the line ending nearest it. Both ends on one line would pull the arc down
|
||||
// to a point: the end nearer that line's end keeps it, the other takes its next line.
|
||||
let [ha, hb] = &hits;
|
||||
let (ta, tb) = match (ha.first(), hb.first()) {
|
||||
(Some(x), Some(y)) if x.1 == y.1 => {
|
||||
if x.0 <= y.0 {
|
||||
(Some(x), hb.iter().find(|h| h.1 != x.1))
|
||||
} else {
|
||||
(ha.iter().find(|h| h.1 != y.1), Some(y))
|
||||
}
|
||||
}
|
||||
pair => pair,
|
||||
};
|
||||
let hits_by_end: [Option<(usize, usize, Vec2, Vec2, f64)>; 2] = [ta, tb].map(|h| h.map(|&(_, j, near, foot, n, d)| (j, near, foot, n, d)));
|
||||
let (mut c, mut r) = (c, r);
|
||||
if let [Some((_, _, _, n1, d1)), Some((_, _, _, n2, d2))] = hits_by_end {
|
||||
// One circle tangent to both lines: equal distance to each.
|
||||
|
||||
@@ -12,15 +12,17 @@ use crate::eval::ModelState;
|
||||
/// Most triangle pairs examined by one intersection.
|
||||
const MAX_PAIRS: usize = 20_000_000;
|
||||
|
||||
/// The mesh (and face, for a face source) of a body or face reference.
|
||||
pub(crate) fn mesh_of(st: &ModelState, src: &LinkSource) -> Option<(Arc<Mesh>, Option<u32>)> {
|
||||
/// The mesh of a body or face reference and, for a face, the faces of its surface patch: the B-rep
|
||||
/// pieces joined across hidden seams, as the viewport picks and highlights it (#9009).
|
||||
pub(crate) fn mesh_of(st: &ModelState, src: &LinkSource) -> Option<(Arc<Mesh>, Option<Vec<u32>>)> {
|
||||
match src {
|
||||
LinkSource::Body { body } => Some((st.body(body)?.mesh(), None)),
|
||||
LinkSource::Face { body, at } => {
|
||||
let b = st.body(body).or_else(|| st.bodies.iter().min_by(|x, y| dist_to(&x.mesh(), *at).total_cmp(&dist_to(&y.mesh(), *at))))?;
|
||||
let m = b.mesh();
|
||||
let (f, _, _) = crate::project::nearest_face(&m, *at)?;
|
||||
Some((m, Some(f)))
|
||||
let patch: Vec<u32> = m.surface_patch(f as usize).into_iter().filter_map(|i| u32::try_from(i).ok()).collect();
|
||||
Some((m, Some(patch)))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
@@ -30,8 +32,9 @@ fn dist_to(m: &Mesh, p: Vec3) -> f64 {
|
||||
crate::project::nearest_face(m, p).map(|x| x.2).unwrap_or(f64::INFINITY)
|
||||
}
|
||||
|
||||
fn tris(m: &Mesh, face: Option<u32>) -> Vec<[Vec3; 3]> {
|
||||
m.triangles.iter().zip(&m.tri_face).filter(|(_, f)| face.is_none_or(|x| x == **f)).filter_map(|(t, _)| m.tri(t)).collect()
|
||||
/// The triangles of the faces in `faces` (sorted ascending), or of the whole mesh for `None`.
|
||||
fn tris(m: &Mesh, faces: Option<&[u32]>) -> Vec<[Vec3; 3]> {
|
||||
m.triangles.iter().zip(&m.tri_face).filter(|(_, f)| faces.is_none_or(|fs| fs.binary_search(f).is_ok())).filter_map(|(t, _)| m.tri(t)).collect()
|
||||
}
|
||||
|
||||
/// Ray–triangle hit parameter (Möller–Trumbore), any sign.
|
||||
@@ -60,7 +63,7 @@ fn ray_hit(o: Vec3, d: Vec3, t: &[Vec3; 3]) -> Option<f64> {
|
||||
/// miss the face split the result.
|
||||
pub(crate) fn onto_face(m: &Mesh, f: u32, pts: &[Vec3], dir: Vec3) -> Vec<Vec<Vec3>> {
|
||||
let Some(d) = dir.normalized() else { return Vec::new() };
|
||||
let ts = tris(m, Some(f));
|
||||
let ts = tris(m, Some(std::slice::from_ref(&f)));
|
||||
let size = m.bounds().diagonal().max(1e-9);
|
||||
// Resample so the projected curve follows the surface.
|
||||
let step = size / 200.0;
|
||||
@@ -138,8 +141,8 @@ fn tri_tri(a: &[Vec3; 3], b: &[Vec3; 3]) -> Option<(Vec3, Vec3)> {
|
||||
(pr(hi) - pr(lo) > 1e-12).then_some((lo, hi))
|
||||
}
|
||||
|
||||
/// Where two meshes (or faces of them) meet, as polylines.
|
||||
pub(crate) fn mesh_intersection(ma: &Mesh, fa: Option<u32>, mb: &Mesh, fb: Option<u32>) -> Vec<Vec<Vec3>> {
|
||||
/// Where two meshes (or face patches of them) meet, as polylines.
|
||||
pub(crate) fn mesh_intersection(ma: &Mesh, fa: Option<&[u32]>, mb: &Mesh, fb: Option<&[u32]>) -> Vec<Vec<Vec3>> {
|
||||
let (ta, tb) = (tris(ma, fa), tris(mb, fb));
|
||||
let bbox = |t: &[Vec3; 3]| (t[0].min(t[1]).min(t[2]), t[0].max(t[1]).max(t[2]));
|
||||
// Uniform grid over B.
|
||||
|
||||
@@ -235,6 +235,117 @@ mod tests {
|
||||
let (Some(LinkGeom::Arc { a, .. }), Some(LinkGeom::Line(p, _))) = (out.get(1), out.get(2)) else { panic!("{out:?}") };
|
||||
assert!(a.dist(*p) < 1e-9, "{out:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tangent_snapping_leaves_a_line_further_along_alone() {
|
||||
use solidcraft_geom::Vec2;
|
||||
use solidcraft_sketch::LinkGeom;
|
||||
let v = Vec2::new;
|
||||
let y = |x: f64| 2.0 - (4.0 - x * x).sqrt();
|
||||
// The arc's end at x = 0.5 already meets the next piece; the line along y = 0 ends at
|
||||
// x = 0.3, further along the outline the other way. Snapping onto it would pull the end
|
||||
// off the piece it meets.
|
||||
let mut out = vec![
|
||||
LinkGeom::Arc { c: v(0., 2.), a: v(0.5, y(0.5)), b: v(-1.5, y(-1.5)) },
|
||||
LinkGeom::Line(v(0.3, 0.), v(10., 0.)),
|
||||
LinkGeom::Line(v(0.5, y(0.5)), v(3., 3.)),
|
||||
];
|
||||
let before = out.clone();
|
||||
super::super::snap_tangent_arcs(&mut out, 0.05);
|
||||
assert_eq!(out.get(1), before.get(1), "{out:?}");
|
||||
let (Some(LinkGeom::Arc { a, .. }), Some(LinkGeom::Line(p, _))) = (out.first(), out.get(2)) else { panic!("{out:?}") };
|
||||
assert!(a.dist(*p) < 1e-9, "{out:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tangent_snapping_gives_one_line_to_one_end_of_an_arc() {
|
||||
use solidcraft_geom::Vec2;
|
||||
use solidcraft_sketch::LinkGeom;
|
||||
let v = Vec2::new;
|
||||
let y = |x: f64| 2.0 - (4.0 - x * x).sqrt();
|
||||
// A short arc whose two ends both lie near the end of one line: both snapping onto it
|
||||
// would shrink the arc to the tangent point.
|
||||
let mut out = vec![LinkGeom::Arc { c: v(0., 2.), a: v(-0.2, y(-0.2)), b: v(0.2, y(0.2)) }, LinkGeom::Line(v(-10., 0.), v(0., 0.))];
|
||||
super::super::snap_tangent_arcs(&mut out, 0.05);
|
||||
let Some(LinkGeom::Arc { a, b, .. }) = out.first() else { panic!("{out:?}") };
|
||||
assert!(a.dist(*b) > 0.1, "{out:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tangent_snapping_moves_the_other_end_of_an_arc_to_its_next_line() {
|
||||
use solidcraft_geom::Vec2;
|
||||
use solidcraft_sketch::LinkGeom;
|
||||
let v = Vec2::new;
|
||||
let y = |x: f64| 2.0 - (4.0 - x * x).sqrt();
|
||||
let (a, b) = (v(-0.2, y(-0.2)), v(0.2, y(0.2)));
|
||||
// The line along y = 0 ends nearest both ends of the arc but nearer b, which keeps it; a
|
||||
// takes the next line, tangent to the circle at a and ending a little past it.
|
||||
let back = (v(0., 2.) - a).normalized().map(|n| v(-n.y, n.x)).unwrap_or(Vec2::X);
|
||||
let mut out =
|
||||
vec![LinkGeom::Arc { c: v(0., 2.), a, b }, LinkGeom::Line(v(0.1, 0.), v(10., 0.)), LinkGeom::Line(a + back * 10., a + back * 0.35)];
|
||||
super::super::snap_tangent_arcs(&mut out, 0.05);
|
||||
let (Some(LinkGeom::Arc { a, b, .. }), Some(LinkGeom::Line(p, _)), Some(LinkGeom::Line(_, q))) = (out.first(), out.get(1), out.get(2)) else {
|
||||
panic!("{out:?}")
|
||||
};
|
||||
assert!(b.dist(*p) < 1e-9 && a.dist(*q) < 1e-9 && a.dist(*b) > 0.1, "{out:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tangent_snapping_closes_a_conic_corner_in_any_order() {
|
||||
use solidcraft_geom::Vec2;
|
||||
use solidcraft_sketch::LinkGeom;
|
||||
// A square of side 150 with a conic round of 10 mm legs at each corner, fitted with lines
|
||||
// and arcs the way a silhouette is, then snapped with the pieces in each order.
|
||||
for rho in [0.1, 0.15, 0.5, 0.8, 0.9] {
|
||||
let w = rho / (1. - rho);
|
||||
let conic = |s: Vec2, apex: Vec2, e: Vec2, t: f64| {
|
||||
let (b0, b1, b2) = ((1. - t) * (1. - t), 2. * t * (1. - t) * w, t * t);
|
||||
(s * b0 + apex * b1 + e * b2) * (1. / (b0 + b1 + b2))
|
||||
};
|
||||
let v = Vec2::new;
|
||||
let corners = [
|
||||
(v(-75., 65.), v(-75., 75.), v(-65., 75.)),
|
||||
(v(65., 75.), v(75., 75.), v(75., 65.)),
|
||||
(v(75., -65.), v(75., -75.), v(65., -75.)),
|
||||
(v(-65., -75.), v(-75., -75.), v(-75., -65.)),
|
||||
];
|
||||
let mut chain = Vec::new();
|
||||
for (s, apex, e) in corners {
|
||||
chain.extend((0..=40).map(|k| conic(s, apex, e, f64::from(k) / 40.)));
|
||||
}
|
||||
if let Some(&first) = chain.first() {
|
||||
chain.push(first);
|
||||
}
|
||||
let mut fitted = Vec::new();
|
||||
super::super::fit_chain_with_arcs(&chain, Some(212. * 1e-5), &mut fitted, true);
|
||||
assert!(fitted.iter().any(|g| matches!(g, LinkGeom::Arc { .. })), "{fitted:?}");
|
||||
let ends = |g: &LinkGeom| match *g {
|
||||
LinkGeom::Line(s, t) | LinkGeom::Arc { a: s, b: t, .. } => vec![s, t],
|
||||
_ => Vec::new(),
|
||||
};
|
||||
for k in 0..fitted.len() {
|
||||
let mut out = fitted.clone();
|
||||
out.rotate_left(k);
|
||||
super::super::snap_tangent_arcs(&mut out, 212. * 1e-3);
|
||||
// Every end still meets another piece's end. An end left without a line moves a little
|
||||
// when the arc's radius is set from the lines (well under 0.01 mm here); before, ends
|
||||
// pulled onto lines further along left gaps of 0.25 to 0.55 mm.
|
||||
for (i, g) in out.iter().enumerate() {
|
||||
for e in ends(g) {
|
||||
let gap = out
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(j, _)| *j != i)
|
||||
.flat_map(|(_, h)| ends(h))
|
||||
.map(|x| x.dist(e))
|
||||
.fold(f64::INFINITY, f64::min);
|
||||
assert!(gap < 212. * 1e-3 * 0.02, "rho {rho}, rotation {k}: gap {gap} at {e:?}: {out:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boundaries_touching_at_a_vertex_project() {
|
||||
// Two squares of one face family meet only at the corner (1, 1): four boundary edges meet
|
||||
|
||||
+596
-43
@@ -529,6 +529,10 @@ pub struct SheetFlange {
|
||||
/// (flat coordinates, counter-clockwise).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub exact: Option<Loop2>,
|
||||
/// A fold's bend zone as drawn, when a curved edge of the sheet crosses it (flat coordinates,
|
||||
/// counter-clockwise): `zone` is this instead of straight ends.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub zone_shape: Option<Loop2>,
|
||||
/// Which feature made it: `F<feature>.<k>` (persistent edge names).
|
||||
#[serde(default, skip_serializing_if = "String::is_empty")]
|
||||
pub tag: String,
|
||||
@@ -688,6 +692,10 @@ pub struct SheetPerforation {
|
||||
/// copy, whose holes are the mirror image of its source's.
|
||||
#[serde(skip_serializing_if = "std::ops::Not::not")]
|
||||
pub from_right: bool,
|
||||
/// The field's outline in flat coordinates (see [`SheetBody::perforation_outline`]): holes only inside these
|
||||
/// regions, margin clear of their edges, on a grid that starts from their bounding box. None: the whole flat.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub outline: Option<Vec<Region2>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
@@ -830,6 +838,205 @@ fn rect(a: Vec2, b: Vec2, c: Vec2, d: Vec2) -> Region2 {
|
||||
Region2 { outer: lp, holes: Vec::new() }
|
||||
}
|
||||
|
||||
/// How a fold's bend runs along its line `d`: where its start line starts and ends, how far its far
|
||||
/// edge runs on past those ends, and which ends are cut askew (`SheetFlange::ext`, `tear`).
|
||||
type FoldEnds = (f64, f64, (f64, f64), (bool, bool));
|
||||
|
||||
/// The ends of a fold's zone (`strip`, between the lines at `s0` and `s0 + ba` along `n`) when
|
||||
/// straight edges of the sheet slant across it, so that it folds as a bend with torn ends. Every
|
||||
/// corner must lie on one of the two lines: a curved edge has corners in between, and an S of the
|
||||
/// same area as a straight end would pass on area alone.
|
||||
fn slanted_zone(strip: &[Vec2], n: Vec2, d: Vec2, s0: f64, ba: f64) -> Option<FoldEnds> {
|
||||
let on_line = |q: &Vec2, s: f64| (q.dot(n) - s).abs() <= ZONE_SQUARE_TOL;
|
||||
if !strip.iter().all(|q| on_line(q, s0) || on_line(q, s0 + ba)) {
|
||||
return None;
|
||||
}
|
||||
let span = |s: f64| {
|
||||
let mut along = strip.iter().filter(|q| on_line(q, s)).map(|q| q.dot(d));
|
||||
let first = along.next()?;
|
||||
Some(along.fold((first, first), |(lo, hi), u| (lo.min(u), hi.max(u))))
|
||||
};
|
||||
let ((a0, a1), (e0, e1)) = (span(s0)?, span(s0 + ba)?);
|
||||
let (start, far) = (a1 - a0, e1 - e0);
|
||||
let trapezoid = (start + far) / 2.0 * ba;
|
||||
if start <= ZONE_SQUARE_TOL
|
||||
|| far <= ZONE_SQUARE_TOL
|
||||
|| (trapezoid - signed_area(strip).abs()).abs() > 1e-6 * start.max(far).max(1.0) * ba.max(1.0)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let end = |on: f64| if on.abs() > ZONE_SQUARE_TOL { (on, true) } else { (0.0, false) };
|
||||
let ((on0, torn0), (on1, torn1)) = (end(a0 - e0), end(e1 - a1));
|
||||
Some((a0, a1, (on0, on1), (torn0, torn1)))
|
||||
}
|
||||
|
||||
/// A turn sharper than this (its sine) between two edges of an outline is a corner. Smooth curves
|
||||
/// and their chords turn less: a chord at the sheet's 0.001 mm tolerance turns about 5° on a 1 mm
|
||||
/// radius.
|
||||
const CORNER_TURN: f64 = 0.17;
|
||||
|
||||
/// Where two edges of an outline meet at a corner.
|
||||
fn outline_corners(outline: &Loop2) -> Vec<Vec2> {
|
||||
let segs = &outline.segs;
|
||||
let mut corners = Vec::new();
|
||||
for (i, next) in segs.iter().enumerate() {
|
||||
let Some(prev) = segs.get((i + segs.len() - 1) % segs.len()) else { continue };
|
||||
let (t0, t1) = (prev.end_tangent(), next.start_tangent());
|
||||
if t0.cross(t1).abs() > CORNER_TURN || t0.dot(t1) < 0.0 {
|
||||
corners.push(next.start());
|
||||
}
|
||||
}
|
||||
corners
|
||||
}
|
||||
|
||||
/// `seg` in pieces, cut where it crosses the line `level` along `n`, into `out`. A curve that only
|
||||
/// touches the line stays whole.
|
||||
fn cut_at_level(seg: &Seg2, n: Vec2, level: f64, out: &mut Vec<Seg2>) {
|
||||
const SAMPLES: usize = 64;
|
||||
const MAX_CROSSINGS: usize = 16;
|
||||
const ON_LINE: f64 = 1e-9;
|
||||
let off = |s: &Seg2, t: f64| s.point_at(t).dot(n) - level;
|
||||
let mut rest = *seg;
|
||||
for _ in 0..MAX_CROSSINGS {
|
||||
// The first change of side, skipping samples on the line itself.
|
||||
let (mut last, mut crossing) = (None::<(f64, f64)>, None);
|
||||
for k in 0..=SAMPLES {
|
||||
let t = k as f64 / SAMPLES as f64;
|
||||
let v = off(&rest, t);
|
||||
if v.abs() <= ON_LINE {
|
||||
continue;
|
||||
}
|
||||
match last {
|
||||
Some((was, before)) if before * v < 0.0 => {
|
||||
crossing = Some((was, t, before));
|
||||
break;
|
||||
}
|
||||
_ => last = Some((t, v)),
|
||||
}
|
||||
}
|
||||
let Some((mut lo, mut hi, before)) = crossing else { break };
|
||||
// On the distance itself: the cut lands on the line, and what's left starts on it.
|
||||
for _ in 0..60 {
|
||||
let mid = (lo + hi) / 2.0;
|
||||
if off(&rest, mid) * before > 0.0 {
|
||||
lo = mid;
|
||||
} else {
|
||||
hi = mid;
|
||||
}
|
||||
}
|
||||
let (first, second) = rest.split_at((lo + hi) / 2.0);
|
||||
out.push(first);
|
||||
rest = second;
|
||||
}
|
||||
out.push(rest);
|
||||
}
|
||||
|
||||
/// A panel's outline cut exactly across a fold whose zone a curved edge crosses, each piece
|
||||
/// keeping its exact curves.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
struct CurvedFold {
|
||||
fixed: Loop2,
|
||||
zone: Loop2,
|
||||
moving: Loop2,
|
||||
ends: FoldEnds,
|
||||
}
|
||||
|
||||
/// `outline` cut into the side short of the zone between the lines at `s0` and `s0 + ba` along `n`,
|
||||
/// the zone, and the side past it. It must run from one side through the zone to the other and
|
||||
/// back through the zone, once: a notch, several tabs on the line or a zone that reaches the
|
||||
/// outline's tip is `None`. Both of the zone's ends count as torn: their edges aren't square across.
|
||||
fn curved_fold(outline: &Loop2, n: Vec2, d: Vec2, s0: f64, ba: f64) -> Option<CurvedFold> {
|
||||
const ON_LINE: f64 = 1e-6;
|
||||
if outline.segs.len() > MAX_FLAT_POINTS {
|
||||
return None;
|
||||
}
|
||||
let outline = outline.ccw();
|
||||
let mut pieces = Vec::new();
|
||||
for seg in &outline.segs {
|
||||
let mut near = Vec::new();
|
||||
cut_at_level(seg, n, s0, &mut near);
|
||||
for piece in &near {
|
||||
cut_at_level(piece, n, s0 + ba, &mut pieces);
|
||||
}
|
||||
}
|
||||
// The band each piece lies in: 0 short of the zone, 1 in it, 2 past it. A piece along either
|
||||
// line is an edge square across the bend, not one crossing it.
|
||||
let mut bands = Vec::with_capacity(pieces.len());
|
||||
for piece in &pieces {
|
||||
let v = piece.mid().dot(n);
|
||||
if !v.is_finite() || (v - s0).abs() <= ON_LINE || (v - s0 - ba).abs() <= ON_LINE {
|
||||
return None;
|
||||
}
|
||||
bands.push(if v < s0 {
|
||||
0
|
||||
} else if v > s0 + ba {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
});
|
||||
}
|
||||
// Round the outline from where it enters the side short of the zone.
|
||||
let count = pieces.len();
|
||||
let first = (0..count).find(|&i| bands.get(i) == Some(&0) && bands.get((i + count - 1) % count) != Some(&0))?;
|
||||
let mut runs: Vec<(i32, Vec<Seg2>)> = Vec::new();
|
||||
for k in 0..count {
|
||||
let i = (first + k) % count;
|
||||
let (band, piece) = (*bands.get(i)?, *pieces.get(i)?);
|
||||
match runs.last_mut() {
|
||||
Some((b, run)) if *b == band => run.push(piece),
|
||||
_ => runs.push((band, vec![piece])),
|
||||
}
|
||||
}
|
||||
let [(0, near), (1, there), (2, far), (1, back)] = runs.as_slice() else { return None };
|
||||
let ends_of = |run: &[Seg2]| Some((run.first()?.start(), run.last()?.end()));
|
||||
let ((near0, near1), (there0, there1), (far0, far1), (back0, back1)) = (ends_of(near)?, ends_of(there)?, ends_of(far)?, ends_of(back)?);
|
||||
let on = |q: Vec2, s: f64| (q.dot(n) - s).abs() <= ON_LINE;
|
||||
if !(on(near0, s0) && on(near1, s0) && on(far0, s0 + ba) && on(far1, s0 + ba)) {
|
||||
return None;
|
||||
}
|
||||
let closed = |run: &[Seg2], to: Vec2, from: Vec2| {
|
||||
let mut segs = run.to_vec();
|
||||
segs.push(Seg2::Line { a: to, b: from });
|
||||
segs
|
||||
};
|
||||
let mut zone = closed(there, there1, back0);
|
||||
zone.extend_from_slice(back);
|
||||
zone.push(Seg2::Line { a: back1, b: there0 });
|
||||
let span = |p: Vec2, q: Vec2| (p.dot(d).min(q.dot(d)), p.dot(d).max(q.dot(d)));
|
||||
let ((a0, a1), (e0, e1)) = (span(near0, near1), span(far0, far1));
|
||||
if a1 - a0 <= ON_LINE || e1 - e0 <= ON_LINE {
|
||||
return None;
|
||||
}
|
||||
Some(CurvedFold {
|
||||
fixed: Loop2 { segs: closed(near, near1, near0) },
|
||||
zone: Loop2 { segs: zone },
|
||||
moving: Loop2 { segs: closed(far, far1, far0) },
|
||||
ends: (a0, a1, (a0 - e0, e1 - a1), (true, true)),
|
||||
})
|
||||
}
|
||||
|
||||
/// Two counter-clockwise loops joined along a straight edge they share, running opposite ways:
|
||||
/// every curve of both exactly as it was, which the planar booleans wouldn't hand back.
|
||||
fn join_along_seam(a: &Loop2, b: &Loop2) -> Option<Loop2> {
|
||||
const SAME: f64 = 1e-6;
|
||||
if a.segs.len().saturating_mul(b.segs.len()) > MAX_FLAT_POINTS {
|
||||
return None;
|
||||
}
|
||||
for (i, ea) in a.segs.iter().enumerate() {
|
||||
let Seg2::Line { a: p, b: q } = *ea else { continue };
|
||||
for (j, eb) in b.segs.iter().enumerate() {
|
||||
let Seg2::Line { a: r, b: s } = *eb else { continue };
|
||||
if p.dist(s) <= SAME && q.dist(r) <= SAME {
|
||||
let mut segs = Vec::with_capacity(a.segs.len() + b.segs.len());
|
||||
segs.extend(a.segs.iter().skip(i + 1).chain(a.segs.iter().take(i)).cloned());
|
||||
segs.extend(b.segs.iter().skip(j + 1).chain(b.segs.iter().take(j)).cloned());
|
||||
return Some(Loop2 { segs });
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Round to a 1e-9 mm grid (keeps trimmed edges exactly where the kernel expects them).
|
||||
fn snap(v: Vec2) -> Vec2 {
|
||||
let r = |x: f64| (x * 1e9).round() / 1e9;
|
||||
@@ -1087,6 +1294,9 @@ impl SheetBody {
|
||||
/// Flat region of a flange's bend zone: square across the bend at both ends, but at a torn
|
||||
/// end its far edge ends where the panel's does.
|
||||
pub fn zone(&self, f: &SheetFlange) -> Region2 {
|
||||
if let Some(lp) = &f.zone_shape {
|
||||
return Region2 { outer: lp.ccw(), holes: Vec::new() };
|
||||
}
|
||||
let ba = self.allowance(f);
|
||||
let (u0, u1) = self.zone_span(f);
|
||||
let (a, b) = (f.p + f.d * u0, f.p + f.d * u1);
|
||||
@@ -1103,6 +1313,32 @@ impl SheetBody {
|
||||
(if f.tear.0 { -f.ext.0 } else { u0 }, if f.tear.1 { f.len + f.ext.1 } else { u1 })
|
||||
}
|
||||
|
||||
/// How far a bend's zone reaches along the bend from `p`: a torn end runs askew, and a curved
|
||||
/// edge may bulge past both of its ends.
|
||||
pub(crate) fn zone_reach(&self, f: &SheetFlange) -> (f64, f64) {
|
||||
if let Some(lp) = &f.zone_shape {
|
||||
return lp.polyline(1e-3).iter().map(|q| (*q - f.p).dot(f.d)).fold((f64::MAX, f64::MIN), |(lo, hi), u| (lo.min(u), hi.max(u)));
|
||||
}
|
||||
let ((u0, u1), (e0, e1)) = (self.zone_span(f), self.zone_ends(f));
|
||||
(u0.min(e0), u1.max(e1))
|
||||
}
|
||||
|
||||
/// Where a bend's centre line starts and ends, along the bend from `p`: between the middles
|
||||
/// of the bend's two ends (a torn end is askew), or where a curved edge crosses it.
|
||||
fn centre_line(&self, f: &SheetFlange) -> (f64, f64) {
|
||||
if let Some(lp) = &f.zone_shape {
|
||||
let mut at = Vec::new();
|
||||
for e in &lp.segs {
|
||||
line_hits(f.p + f.n * (self.allowance(f) / 2.0), f.d, e, &mut at);
|
||||
}
|
||||
if let (Some(lo), Some(hi)) = (at.iter().copied().reduce(f64::min), at.iter().copied().reduce(f64::max)) {
|
||||
return (lo, hi);
|
||||
}
|
||||
}
|
||||
let ((u0, u1), (e0, e1)) = (self.zone_span(f), self.zone_ends(f));
|
||||
((u0 + e0) / 2.0, (u1 + e1) / 2.0)
|
||||
}
|
||||
|
||||
/// Folded (in the flat frame, as `fold`), the point of a bend's end (the one at `p`, or the
|
||||
/// other one when `at_end`) at the turn `phi` of the bend and the height `z` in the sheet. A
|
||||
/// torn end runs evenly along the bend while the bend turns, from its start line's end to
|
||||
@@ -1141,7 +1377,9 @@ impl SheetBody {
|
||||
if let Some(shape) = &f.shape
|
||||
&& let Some((lo, hi)) = edge_span(shape, f.p + f.n * self.allowance(f))
|
||||
{
|
||||
return (span.0.max(lo), span.1.min(hi));
|
||||
// A torn end runs askew to where the panel's end starts, so the panel's root doesn't
|
||||
// limit the bend there.
|
||||
return (if f.tear.0 { span.0 } else { span.0.max(lo) }, if f.tear.1 { span.1 } else { span.1.min(hi) });
|
||||
}
|
||||
span
|
||||
}
|
||||
@@ -1925,6 +2163,60 @@ impl SheetBody {
|
||||
Ok(regions)
|
||||
}
|
||||
|
||||
/// The sheet's panels and bend zones. Where a free-form edge crosses a fold, the zone and the
|
||||
/// panels either side are already joined along their seams, so the edge stays the curve it
|
||||
/// was drawn as: merged by the planar booleans it would come back as fitted arcs. A seam
|
||||
/// another feature changed is left to them.
|
||||
fn flat_pieces(&self) -> Result<Vec<Region2>> {
|
||||
let mut owners: Vec<Option<usize>> = Vec::new();
|
||||
let mut slots: Vec<Option<Region2>> = Vec::new();
|
||||
for (pi, r) in self.panels() {
|
||||
owners.push(pi);
|
||||
slots.push(Some(planar_region(&r)?));
|
||||
}
|
||||
let panel_slot = |pi: Option<usize>| owners.iter().position(|o| *o == pi);
|
||||
let panels = slots.len();
|
||||
for f in &self.flanges {
|
||||
slots.push(Some(planar_region(&self.zone(f))?));
|
||||
}
|
||||
// Which slot now holds each: joined pieces live in the one they joined.
|
||||
let mut held_by: Vec<usize> = (0..slots.len()).collect();
|
||||
let free_form = |slots: &[Option<Region2>], at: usize| slots.get(at).and_then(Option::as_ref).is_some_and(|r| !r.outer.is_line_arc());
|
||||
for (i, f) in self.flanges.iter().enumerate() {
|
||||
// A fold's pieces share whole seams; an edge flange's bend needn't span its edge.
|
||||
if f.shape.is_none() {
|
||||
continue;
|
||||
}
|
||||
let zone = panels + i;
|
||||
let sides = [panel_slot(f.parent), panel_slot(Some(i))];
|
||||
if !free_form(&slots, zone) && !sides.iter().flatten().any(|&p| free_form(&slots, held_by.get(p).copied().unwrap_or(p))) {
|
||||
continue;
|
||||
}
|
||||
for side in sides.into_iter().flatten() {
|
||||
let at = |held_by: &[usize], k: usize| held_by.get(k).copied().unwrap_or(k);
|
||||
let (a, b) = (at(&held_by, zone), at(&held_by, side));
|
||||
if a == b {
|
||||
continue;
|
||||
}
|
||||
let (Some(Some(ra)), Some(Some(rb))) = (slots.get(a), slots.get(b)) else { continue };
|
||||
let Some(joined) = join_along_seam(&ra.outer, &rb.outer) else { continue };
|
||||
let holes = ra.holes.iter().chain(&rb.holes).cloned().collect();
|
||||
if let Some(slot) = slots.get_mut(a) {
|
||||
*slot = Some(Region2 { outer: joined, holes });
|
||||
}
|
||||
if let Some(slot) = slots.get_mut(b) {
|
||||
*slot = None;
|
||||
}
|
||||
for k in &mut held_by {
|
||||
if *k == b {
|
||||
*k = a;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(slots.into_iter().flatten().collect())
|
||||
}
|
||||
|
||||
pub(crate) fn flat_material(&self) -> Result<Vec<Region2>> {
|
||||
let mut work = FlatWork::new();
|
||||
// Merge structural pieces before cutting: clipping each panel first introduces
|
||||
@@ -1938,16 +2230,12 @@ impl SheetBody {
|
||||
}
|
||||
let mut regions = Vec::new();
|
||||
let mut panel_points = 0usize;
|
||||
for (_, r) in self.panels() {
|
||||
let r = planar_region(&r)?;
|
||||
for r in self.flat_pieces()? {
|
||||
panel_points = panel_points.saturating_add(region_complexity(std::slice::from_ref(&r))?.1);
|
||||
if panel_points > MAX_FLAT_POINTS {
|
||||
return Err(flat_budget_error());
|
||||
}
|
||||
regions.push(r);
|
||||
}
|
||||
for f in &self.flanges {
|
||||
regions.push(planar_region(&self.zone(f))?);
|
||||
region_complexity(®ions)?;
|
||||
}
|
||||
let regions = material_union(regions, &mut work)?;
|
||||
@@ -2079,9 +2367,7 @@ impl SheetBody {
|
||||
let mut out = Vec::new();
|
||||
for (i, f) in self.flanges.iter().enumerate() {
|
||||
let ba = self.allowance(f);
|
||||
// The centre line runs between the middles of the bend's two ends (a torn end is askew).
|
||||
let ((u0, u1), (e0, e1)) = (self.zone_span(f), self.zone_ends(f));
|
||||
let (u0, u1) = ((u0 + e0) / 2.0, (u1 + e1) / 2.0);
|
||||
let (u0, u1) = self.centre_line(f);
|
||||
let a = f.p + f.n * (ba / 2.0) + f.d * u0;
|
||||
for (s0, s1) in clear_of(a, f.d, u1 - u0, &cuts) {
|
||||
out.push(BendInfo {
|
||||
@@ -2104,9 +2390,45 @@ impl SheetBody {
|
||||
self.flanges.iter().map(|f| self.zone(f)).collect()
|
||||
}
|
||||
|
||||
/// A perforation field's outline (#732) in flat coordinates: closed regions of a sketch in `plane` (world). An
|
||||
/// unfolded sheet takes them as drawn on the flat; a folded one through each panel whose material the plane lies
|
||||
/// in, clipped to that panel, so an outline on one face never reaches a flange in the flat. Refused when the
|
||||
/// plane lies in no panel.
|
||||
pub fn perforation_outline(&self, plane: &Plane, regions: &[Region2]) -> Result<Vec<Region2>> {
|
||||
let on = |pi: Option<usize>| {
|
||||
let w = self.world(pi);
|
||||
let n = apply_vector(&w, Vec3::Z);
|
||||
let z = (plane.origin - apply_point(&w, Vec3::ZERO)).dot(n);
|
||||
n.cross(plane.normal()).len() < 1e-6 && (-LINEAR_TOL..=self.t + LINEAR_TOL).contains(&z)
|
||||
};
|
||||
if self.flat {
|
||||
if !on(None) {
|
||||
return Err(DocError::Invalid("the perforation's outline sketch must lie on the flat sheet".into()));
|
||||
}
|
||||
return regions.iter().map(|r| self.region_to_flat(None, plane, r)).collect();
|
||||
}
|
||||
let mut work = FlatWork::new();
|
||||
let mut out = Vec::new();
|
||||
let mut found = false;
|
||||
for (pi, panel) in self.parallel_panels(plane) {
|
||||
if !on(pi) {
|
||||
continue;
|
||||
}
|
||||
found = true;
|
||||
let mapped = regions.iter().map(|r| self.region_to_flat(pi, plane, r)).collect::<Result<Vec<_>>>()?;
|
||||
out.extend(material_boolean(std::slice::from_ref(&panel), &mapped, kernel::BoolOp::Intersect, &mut work)?);
|
||||
}
|
||||
if !found {
|
||||
return Err(DocError::Invalid("the perforation's outline sketch must lie on a face of the sheet".into()));
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// Hole centres of a perforation field (#732), flat coordinates: a staggered (60°, odd rows shifted half a pitch)
|
||||
/// or square grid from the flat bounding box's lower-left corner plus margin + radius; a hole is kept where it is
|
||||
/// at least margin clear of the material's edges and cut-outs and of every bend zone.
|
||||
/// at least margin clear of the material's edges and cut-outs and of every bend zone. A field with an outline
|
||||
/// keeps its holes inside the outline and margin clear of its edges too, on a grid from the lower-left corner of
|
||||
/// the outline's bounding box (within the flat's).
|
||||
pub fn perforation_holes(&self, p: &SheetPerforation) -> Result<Vec<Vec2>> {
|
||||
let regions = self.flat_regions()?;
|
||||
let zones = self.bend_zones();
|
||||
@@ -2127,6 +2449,20 @@ impl SheetBody {
|
||||
if !(lo.x <= hi.x) || !(p.pitch > 0.0) {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
// The rows and columns span the flat, or the outline's part of it; the bucket grid below stays the flat's.
|
||||
let mut field = Aabb2 { min: lo, max: hi };
|
||||
if let Some(outline) = &p.outline {
|
||||
let n = segs.len();
|
||||
for r in outline {
|
||||
for l in std::iter::once(&r.outer).chain(&r.holes) {
|
||||
segs.extend(l.polyline(1e-3).edges().map(|(&a, &b)| (a, b)));
|
||||
}
|
||||
}
|
||||
field = field.intersection(&segs.get(n..).unwrap_or(&[]).iter().map(|(a, _)| *a).collect::<Aabb2>());
|
||||
if !(field.min.x <= field.max.x && field.min.y <= field.max.y) {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
}
|
||||
// Segments bucketed in a grid, so each hole only checks the edges near it.
|
||||
let cell = (2.0 * clear).max(p.pitch).max(1.0);
|
||||
let (nx, ny) = (((hi.x - lo.x) / cell) as usize + 1, ((hi.y - lo.y) / cell) as usize + 1);
|
||||
@@ -2153,20 +2489,22 @@ impl SheetBody {
|
||||
let dy = if p.square { p.pitch } else { p.pitch * 3f64.sqrt() / 2.0 };
|
||||
let mut out = Vec::new();
|
||||
let mut row = 0usize;
|
||||
let Aabb2 { min: flo, max: fhi } = field;
|
||||
loop {
|
||||
let y = lo.y + clear + row as f64 * dy;
|
||||
if y > hi.y - clear + 1e-9 {
|
||||
let y = flo.y + clear + row as f64 * dy;
|
||||
if y > fhi.y - clear + 1e-9 {
|
||||
break;
|
||||
}
|
||||
let shift = if !p.square && row % 2 == 1 { p.pitch / 2.0 } else { 0.0 };
|
||||
// Steps across the row from its first hole; a mirrored field counts from the right.
|
||||
let mut step = 0.0;
|
||||
while lo.x + clear + shift + step <= hi.x - clear + 1e-9 {
|
||||
let x = if p.from_right { hi.x - clear - shift - step } else { lo.x + clear + shift + step };
|
||||
while flo.x + clear + shift + step <= fhi.x - clear + 1e-9 {
|
||||
let x = if p.from_right { fhi.x - clear - shift - step } else { flo.x + clear + shift + step };
|
||||
let c = Vec2::new(x, y);
|
||||
let (i, j) = idx(x, y);
|
||||
let near = grid.get(j * nx + i).map(Vec::as_slice).unwrap_or(&[]);
|
||||
if regions.iter().any(|r| r.contains(c))
|
||||
&& p.outline.as_ref().is_none_or(|o| o.iter().any(|r| r.contains(c)))
|
||||
&& !zones.iter().any(|z| z.contains(c))
|
||||
&& near.iter().all(|&k| segs.get(k).is_none_or(|&s| dist(c, s) >= clear - 1e-9))
|
||||
{
|
||||
@@ -2287,6 +2625,31 @@ impl SheetBody {
|
||||
best
|
||||
}
|
||||
|
||||
/// Where the base's outline already runs along the bend line (through `o`, along `d`) beside a
|
||||
/// picked edge that spans [ua, ub] along d: the end of the nearest such run before it and the
|
||||
/// start of the nearest after it (none: unbounded). Trimmed back to that line, the picked edge
|
||||
/// merges with those runs, as beside the steps of a stepped outline; the flange stays off them.
|
||||
fn bend_line_runs(&self, o: Vec2, d: Vec2, ua: f64, ub: f64) -> (f64, f64) {
|
||||
let n = Vec2::new(-d.y, d.x);
|
||||
let pts = self.base.outer.ccw().polyline(1e-3);
|
||||
let m = pts.len();
|
||||
let (mut lo, mut hi) = (f64::MIN, f64::MAX);
|
||||
for i in 0..m {
|
||||
let (Some(&p), Some(&q)) = (pts.get(i), pts.get((i + 1) % m)) else { continue };
|
||||
if (p - o).dot(n).abs() > 1e-6 || (q - o).dot(n).abs() > 1e-6 {
|
||||
continue;
|
||||
}
|
||||
let (u0, u1) = (p.dot(d).min(q.dot(d)), p.dot(d).max(q.dot(d)));
|
||||
if u1 <= ua + 1e-9 {
|
||||
lo = lo.max(u1);
|
||||
}
|
||||
if u0 >= ub - 1e-9 {
|
||||
hi = hi.min(u0);
|
||||
}
|
||||
}
|
||||
(lo, hi)
|
||||
}
|
||||
|
||||
/// What `cuts` (the sheet's cut-outs) leave of a panel's edge a→b: the stretch nearest `pick`
|
||||
/// (a world point), as distances from `a` along the edge; of two as near, the first. `None`
|
||||
/// where they have taken the edge whole.
|
||||
@@ -2379,10 +2742,8 @@ impl SheetBody {
|
||||
return Err(DocError::Invalid("not supported yet: a partial side-edge flange beside a cut-out".into()));
|
||||
}
|
||||
let clipped = if side || original.shape.is_some() || original.exact.is_some() {
|
||||
// A panel with curves gives way to its 0.001 mm chords here.
|
||||
let region = self.panel(original);
|
||||
if !region.outer.segs.iter().all(|s| matches!(s, Seg2::Line { .. })) {
|
||||
return Err(DocError::Invalid("not supported yet: a flange setback on a curved flange panel".into()));
|
||||
}
|
||||
let points = clip(®ion.outer.ccw().polyline(1e-3), a + inward * by, inward);
|
||||
if points.len() < 3 {
|
||||
return Err(DocError::Invalid("the flange's setback removes the whole panel".into()));
|
||||
@@ -2632,17 +2993,23 @@ impl SheetBody {
|
||||
let (s0, s1) = self
|
||||
.edge_left(panel, a, b, e.pick, &cuts)
|
||||
.ok_or_else(|| DocError::Invalid(format!("{}a cut-out has taken that edge away; pick an edge the sheet still has", on(k))))?;
|
||||
// Where the base already runs along the bend line beside this edge (a stepped outline):
|
||||
// the flange stays off it, though trimming merges the two.
|
||||
let run = match panel {
|
||||
None => self.bend_line_runs(a - outward * setback, d, a.dot(d), b.dot(d)),
|
||||
Some(_) => (f64::MIN, f64::MAX),
|
||||
};
|
||||
let cut = (s0 > 1e-9, s1 < a.dist(b) - 1e-9);
|
||||
let (a, b) = (if cut.0 { a + d * s0 } else { a }, if cut.1 { a + d * s1 } else { b });
|
||||
found.push((panel, a, b, d, top, setback, leg, e.angle, cut));
|
||||
found.push((panel, a, b, d, top, setback, leg, e.angle, cut, run));
|
||||
}
|
||||
for (panel, a, b, _, _, setback, _, _, cut) in &found {
|
||||
for (panel, a, b, _, _, setback, _, _, cut, _) in &found {
|
||||
self.trim(*panel, *a, *b, *setback, *cut)?;
|
||||
}
|
||||
let mut made = Vec::new();
|
||||
// Whether a cut-out has freed each new bend's ends (start, end) of the base beside them.
|
||||
let mut freed = Vec::new();
|
||||
for (panel, a, b, d, top, setback, leg, angle, cut) in found {
|
||||
for (panel, a, b, d, top, setback, leg, angle, cut, run) in found {
|
||||
let outward = Vec2::new(d.y, -d.x);
|
||||
let line_o = a - outward * setback;
|
||||
// The trimmed edge: along d, on the moved line.
|
||||
@@ -2691,6 +3058,16 @@ impl SheetBody {
|
||||
.ok_or_else(|| DocError::Invalid("the flange's edge was trimmed away".into()))?
|
||||
}
|
||||
};
|
||||
// Not over the base's own edge where it already ran along the bend line (beyond a
|
||||
// step of the outline): the flange stops where the picked edge did.
|
||||
let (start, len) = {
|
||||
let u = start.dot(d);
|
||||
let (u0, u1) = (u.max(run.0), (u + len).min(run.1));
|
||||
if !(u1 - u0 > 1e-9) {
|
||||
return Err(DocError::Invalid("the flange's edge was trimmed away".into()));
|
||||
}
|
||||
(start + d * (u0 - u), u1 - u0)
|
||||
};
|
||||
// Where a cut-out ends the edge, the flange ends with it.
|
||||
let (start, len) = if cut.0 || cut.1 {
|
||||
let u = (start - a).dot(d);
|
||||
@@ -2718,6 +3095,7 @@ impl SheetBody {
|
||||
leg: leg.max(0.0),
|
||||
shape: None,
|
||||
exact: None,
|
||||
zone_shape: None,
|
||||
tag: String::new(),
|
||||
ext: (0.0, 0.0),
|
||||
slope: (0.0, 0.0),
|
||||
@@ -3616,6 +3994,7 @@ impl SheetBody {
|
||||
tear: (false, false),
|
||||
shape: None,
|
||||
exact: None,
|
||||
zone_shape: None,
|
||||
tag: String::new(),
|
||||
});
|
||||
Ok(self.flanges.len() - 1)
|
||||
@@ -3629,7 +4008,8 @@ impl SheetBody {
|
||||
/// flat). On the base the side holding `fixed` (default: the larger side) stays put; on a
|
||||
/// flange the side toward its own bend does. `flip` folds the other way. A `fixed` point that
|
||||
/// touches no material still picks the side it lies on, and sets `warning` (see
|
||||
/// [`FOLD_FIXED_OFF_SHEET`]).
|
||||
/// [`FOLD_FIXED_OFF_SHEET`]). The outline may cross the zone on straight slanted edges or on
|
||||
/// any curve; a sharp corner in the zone is refused, by name.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn add_fold(
|
||||
&mut self,
|
||||
@@ -3684,11 +4064,12 @@ impl SheetBody {
|
||||
let pts = dedup(&r.outer.ccw().polyline(1e-3));
|
||||
let (a2, b2) = (Vec2::new(fa.x, fa.y), Vec2::new(fb.x, fb.y));
|
||||
if pts.contains((a2 + b2) * 0.5) || polys_cross(&pts, &[a2, b2, (a2 + b2) * 0.5]) {
|
||||
found = Some((pi, inv, a2, b2, pts));
|
||||
found = Some((pi, inv, a2, b2, pts, r.outer));
|
||||
break;
|
||||
}
|
||||
}
|
||||
let (panel, inv, a2, b2, pts) = found.ok_or_else(|| DocError::Invalid("the fold line must lie on a flat face of the sheet".into()))?;
|
||||
let (panel, inv, a2, b2, pts, outline) =
|
||||
found.ok_or_else(|| DocError::Invalid("the fold line must lie on a flat face of the sheet".into()))?;
|
||||
let d = (b2 - a2).normalized().ok_or_else(|| DocError::Invalid("the fold line has no length".into()))?;
|
||||
let mut n = Vec2::new(-d.y, d.x);
|
||||
let l0 = a2.dot(n);
|
||||
@@ -3741,6 +4122,7 @@ impl SheetBody {
|
||||
tear: (false, false),
|
||||
shape: None,
|
||||
exact: None,
|
||||
zone_shape: None,
|
||||
tag: String::new(),
|
||||
};
|
||||
let ba = self.allowance(&probe);
|
||||
@@ -3771,26 +4153,46 @@ impl SheetBody {
|
||||
let angle = if flip { -angle } else { angle };
|
||||
return self.fold_tabs(panel, &pts, (n, d), (s0, ba), span, (angle, radius), &strips);
|
||||
}
|
||||
// The bend zone must be a rectangle across the panel (a slanted or curved edge would
|
||||
// run across the bend as a helix, which the sheet's faces can't carry yet).
|
||||
// The bend zone is a rectangle across the panel, or straight edges of the sheet slant
|
||||
// across it, or curved ones do. Those ends fold to helices and curves, as torn ends and
|
||||
// cut-outs across a bend do.
|
||||
let us: Vec<f64> = strip.iter().map(|q| q.dot(d)).collect();
|
||||
let (u0, u1) = (us.iter().cloned().fold(f64::MAX, f64::min), us.iter().cloned().fold(f64::MIN, f64::max));
|
||||
let mut ends: FoldEnds = (u0, u1, (0.0, 0.0), (false, false));
|
||||
let mut zone_shape = None;
|
||||
// An outline with curves is cut exactly, wherever the curves are, so they stay as drawn in
|
||||
// the flat pattern; a cut that doesn't come out whole falls back on the chords.
|
||||
let cut = if outline.segs.iter().all(|e| matches!(e, Seg2::Line { .. })) { None } else { curved_fold(&outline, n, d, s0, ba) };
|
||||
if ((u1 - u0) * ba - signed_area(&strip).abs()).abs() > 1e-6 * (u1 - u0).max(1.0) * ba.max(1.0) {
|
||||
// A corner of the outline inside the zone (e.g. a fold right at a tab's root): name
|
||||
// it and how far the line has to move to clear it.
|
||||
let corners: Vec<(Vec2, f64)> = pts.iter().map(|q| (*q, q.dot(n) - s0)).filter(|(_, s)| *s > 1e-6 && *s < ba - 1e-6).collect();
|
||||
if let Some(&(c, _)) = corners.first() {
|
||||
let on = corners.iter().fold(0.0_f64, |m, (_, s)| m.max(*s));
|
||||
let back = corners.iter().fold(0.0_f64, |m, (_, s)| m.max(ba - *s));
|
||||
let w = apply_point(&self.world(panel), Vec3::new(c.x, c.y, self.t));
|
||||
return Err(DocError::Invalid(format!(
|
||||
"the bend zone ({ba:.3} mm wide) reaches a corner of the sheet's outline at ({:.3}, {:.3}, {:.3}): \
|
||||
move the fold line at least {on:.3} mm toward the side that folds or {back:.3} mm toward the side that stays",
|
||||
w.x, w.y, w.z
|
||||
)));
|
||||
if let Some(slanted) = slanted_zone(&strip, n, d, s0, ba) {
|
||||
ends = slanted;
|
||||
} else {
|
||||
// A corner of the outline inside the zone (e.g. a fold right at a tab's root): name
|
||||
// it and how far the line has to move to clear it.
|
||||
let corners: Vec<(Vec2, f64)> =
|
||||
outline_corners(&outline).into_iter().map(|q| (q, q.dot(n) - s0)).filter(|(_, s)| *s > 1e-6 && *s < ba - 1e-6).collect();
|
||||
if let Some(&(c, _)) = corners.first() {
|
||||
let on = corners.iter().fold(0.0_f64, |m, (_, s)| m.max(*s));
|
||||
let back = corners.iter().fold(0.0_f64, |m, (_, s)| m.max(ba - *s));
|
||||
let w = apply_point(&self.world(panel), Vec3::new(c.x, c.y, self.t));
|
||||
return Err(DocError::Invalid(format!(
|
||||
"the bend zone ({ba:.3} mm wide) reaches a corner of the sheet's outline at ({:.3}, {:.3}, {:.3}): \
|
||||
move the fold line at least {on:.3} mm toward the side that folds or {back:.3} mm toward the side that stays",
|
||||
w.x, w.y, w.z
|
||||
)));
|
||||
}
|
||||
let Some(curved) = &cut else {
|
||||
return Err(DocError::Invalid("not supported yet: folds whose bend zone crosses a slanted or curved edge of the sheet".into()));
|
||||
};
|
||||
ends = curved.ends;
|
||||
zone_shape = Some(curved.zone.clone());
|
||||
}
|
||||
return Err(DocError::Invalid("not supported yet: folds whose bend zone crosses a slanted or curved edge of the sheet".into()));
|
||||
}
|
||||
let (u0, u1, ext, tear) = ends;
|
||||
let (fixed_exact, moving_exact) = match cut {
|
||||
Some(cut) => (Some(cut.fixed), Some(cut.moving)),
|
||||
None => (None, None),
|
||||
};
|
||||
let up = !flip;
|
||||
let leg = moving.iter().map(|q| q.dot(n) - s0 - ba).fold(0.0, f64::max);
|
||||
let p0 = n * s0 + d * u0;
|
||||
@@ -3801,11 +4203,14 @@ impl SheetBody {
|
||||
v.into_iter().map(snap).collect::<Vec<_>>()
|
||||
};
|
||||
match panel {
|
||||
None => self.base = Region2 { outer: Loop2::polygon(&fixed_part).ccw(), holes: self.base.holes.clone() },
|
||||
None => {
|
||||
self.base =
|
||||
Region2 { outer: fixed_exact.clone().unwrap_or_else(|| Loop2::polygon(&fixed_part)).ccw(), holes: self.base.holes.clone() }
|
||||
}
|
||||
Some(j) => {
|
||||
if let Some(f) = self.flanges.get_mut(j) {
|
||||
f.shape = Some(ccw(fixed_part.clone()));
|
||||
f.exact = None;
|
||||
f.exact = fixed_exact;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3818,11 +4223,12 @@ impl SheetBody {
|
||||
angle: if up { angle } else { -angle },
|
||||
radius,
|
||||
leg,
|
||||
ext: (0.0, 0.0),
|
||||
ext,
|
||||
slope: (0.0, 0.0),
|
||||
tear: (false, false),
|
||||
tear,
|
||||
shape: Some(ccw(moving)),
|
||||
exact: None,
|
||||
exact: moving_exact,
|
||||
zone_shape,
|
||||
tag: String::new(),
|
||||
});
|
||||
let i = self.flanges.len() - 1;
|
||||
@@ -3967,6 +4373,7 @@ impl SheetBody {
|
||||
tear: (false, false),
|
||||
shape: Some(ccw(moving)),
|
||||
exact: None,
|
||||
zone_shape: None,
|
||||
tag: String::new(),
|
||||
});
|
||||
}
|
||||
@@ -4723,6 +5130,9 @@ impl SheetBody {
|
||||
if let Some(exact) = &mut f.exact {
|
||||
*exact = flip_x_loop(exact);
|
||||
}
|
||||
if let Some(zone) = &mut f.zone_shape {
|
||||
*zone = flip_x_loop(zone);
|
||||
}
|
||||
}
|
||||
for r in self.holes.iter_mut().chain(self.labels.iter_mut()).chain(self.pockets.iter_mut().map(|p| &mut p.region)) {
|
||||
*r = flip_x_region(r);
|
||||
@@ -4733,6 +5143,9 @@ impl SheetBody {
|
||||
}
|
||||
for p in &mut self.perforations {
|
||||
p.from_right = !p.from_right;
|
||||
for r in p.outline.iter_mut().flatten() {
|
||||
*r = flip_x_region(r);
|
||||
}
|
||||
}
|
||||
for (_, a, b) in &mut self.base_curves {
|
||||
(*a, *b) = (flip_x(*b), flip_x(*a));
|
||||
@@ -4864,6 +5277,7 @@ pub fn contour_flange(body: &str, rule: &str, rv: &RuleValues, plane: &Plane, pt
|
||||
tear: (false, false),
|
||||
shape: None,
|
||||
exact: None,
|
||||
zone_shape: None,
|
||||
tag: String::new(),
|
||||
};
|
||||
let ba = sheet.allowance(&f);
|
||||
@@ -5540,6 +5954,145 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// Any closed curve the sheet's outlines can hold: a circle, conics, cubics, and a mix.
|
||||
fn curved_outlines() -> Vec<(&'static str, Loop2)> {
|
||||
let w = std::f64::consts::FRAC_1_SQRT_2;
|
||||
let v = |x: f64, y: f64| Vec2::new(x, y);
|
||||
let conic = |a: Vec2, apex: Vec2, b: Vec2| Seg2::Conic { a, apex, b, w };
|
||||
let ellipse = vec![
|
||||
conic(v(80.0, 0.0), v(80.0, 40.0), v(0.0, 40.0)),
|
||||
conic(v(0.0, 40.0), v(-80.0, 40.0), v(-80.0, 0.0)),
|
||||
conic(v(-80.0, 0.0), v(-80.0, -40.0), v(0.0, -40.0)),
|
||||
conic(v(0.0, -40.0), v(80.0, -40.0), v(80.0, 0.0)),
|
||||
];
|
||||
let cubic = |p0: Vec2, p1: Vec2, p2: Vec2, p3: Vec2| Seg2::Cubic { p0, p1, p2, p3 };
|
||||
// An uneven blob: its sides aren't symmetric about the fold or each other.
|
||||
let spline = vec![
|
||||
cubic(v(70.0, -5.0), v(75.0, 30.0), v(30.0, 55.0), v(0.0, 40.0)),
|
||||
cubic(v(0.0, 40.0), v(-30.0, 25.0), v(-90.0, 45.0), v(-85.0, 0.0)),
|
||||
cubic(v(-85.0, 0.0), v(-80.0, -45.0), v(-20.0, -60.0), v(0.0, -35.0)),
|
||||
cubic(v(0.0, -35.0), v(20.0, -10.0), v(65.0, -40.0), v(70.0, -5.0)),
|
||||
];
|
||||
// A D: a straight side the fold runs across, and an arc it also crosses.
|
||||
let d_shape = vec![
|
||||
Seg2::Line { a: v(-50.0, -50.0), b: v(0.0, -50.0) },
|
||||
Seg2::Arc { center: v(0.0, 0.0), radius: 50.0, start: -std::f64::consts::FRAC_PI_2, sweep: std::f64::consts::PI },
|
||||
Seg2::Line { a: v(0.0, 50.0), b: v(-50.0, 50.0) },
|
||||
Seg2::Line { a: v(-50.0, 50.0), b: v(-50.0, -50.0) },
|
||||
];
|
||||
vec![
|
||||
("circle", Loop2::circle(v(0.0, 0.0), 50.0)),
|
||||
("ellipse", Loop2 { segs: ellipse }),
|
||||
("spline", Loop2 { segs: spline }),
|
||||
("D", Loop2 { segs: d_shape }),
|
||||
]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_curved_outline_is_cut_exactly_across_a_fold_whatever_the_curve() {
|
||||
let (n, d) = (Vec2::new(1.0, 0.0), Vec2::new(0.0, -1.0));
|
||||
for (name, lp) in curved_outlines() {
|
||||
for (s0, ba) in [(-2.8, 5.6), (-30.0, 12.0), (10.0, 1.5)] {
|
||||
let cut = curved_fold(&lp, n, d, s0, ba).unwrap_or_else(|| panic!("{name} at {s0}"));
|
||||
// The pieces are the outline: nothing lost, nothing doubled, and all closed.
|
||||
let area = |l: &Loop2| l.signed_area();
|
||||
let sum = area(&cut.fixed) + area(&cut.zone) + area(&cut.moving);
|
||||
assert!((sum - lp.signed_area().abs()).abs() < 1e-6, "{name} at {s0}: {sum} vs {}", lp.signed_area());
|
||||
for piece in [&cut.fixed, &cut.zone, &cut.moving] {
|
||||
assert!(piece.is_closed(1e-9) && piece.is_ccw(), "{name} at {s0}: {piece:?}");
|
||||
}
|
||||
// Each lies wholly in its band, on the outline's own curves: sampled densely.
|
||||
let band = |l: &Loop2, lo: f64, hi: f64| l.polyline(1e-3).iter().all(|q| q.dot(n) >= lo - 1e-6 && q.dot(n) <= hi + 1e-6);
|
||||
assert!(band(&cut.fixed, f64::MIN, s0) && band(&cut.zone, s0, s0 + ba) && band(&cut.moving, s0 + ba, f64::MAX), "{name} at {s0}");
|
||||
// A curve is cut, not replaced by chords: the same kind of segment comes back.
|
||||
let curves = |l: &Loop2| l.segs.iter().filter(|e| !matches!(e, Seg2::Line { .. })).count();
|
||||
assert!(curves(&cut.fixed) + curves(&cut.zone) + curves(&cut.moving) >= curves(&lp), "{name} at {s0}");
|
||||
assert!(cut.ends.3 == (true, true), "{name}: both ends are torn");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// What keeps a flat pattern true: unrolling a bend keeps lengths on its neutral surface, so
|
||||
/// a curved edge is as long folded as in the flat there, and only there.
|
||||
#[test]
|
||||
fn a_curved_edge_keeps_its_length_on_the_neutral_surface() {
|
||||
for (name, lp) in curved_outlines() {
|
||||
let mut s = plate(10.0, 10.0);
|
||||
s.base = Region2 { outer: lp, holes: Vec::new() };
|
||||
let t = s.t;
|
||||
let mut warning = None;
|
||||
let (a, b) = (Vec3::new(5.0, -200.0, t), Vec3::new(5.0, 200.0, t));
|
||||
let i =
|
||||
s.add_fold(a, b, std::f64::consts::FRAC_PI_2, 3.0, "centerline", false, None, &mut warning).unwrap_or_else(|e| panic!("{name}: {e}"));
|
||||
let zone = s.flanges[i].zone_shape.clone().unwrap_or_else(|| panic!("{name}: no exact zone"));
|
||||
let neutral = t - s.k * t;
|
||||
let mut edges = 0;
|
||||
for e in zone.segs.iter().filter(|e| !matches!(zone_edge_kind(&s.flanges[i], e), ZoneEdge::Along)) {
|
||||
edges += 1;
|
||||
const N: usize = 400;
|
||||
let at = |k: usize| e.point_at(k as f64 / N as f64);
|
||||
let flat: f64 = (0..N).map(|k| at(k).dist(at(k + 1))).sum();
|
||||
let folded = |z: f64| -> f64 { (0..N).map(|k| s.map3(Piece::Zone(i), at(k), z).dist(s.map3(Piece::Zone(i), at(k + 1), z))).sum() };
|
||||
assert!((folded(neutral) - flat).abs() < 1e-3, "{name}: {} folded vs {flat} flat", folded(neutral));
|
||||
// The test can tell: on the inside of the bend the edge is shorter.
|
||||
assert!(flat - folded(t) > 0.05, "{name}: {} on the inside vs {flat} flat", folded(t));
|
||||
}
|
||||
assert!(edges >= 2, "{name}: {zone:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_fold_across_an_outline_that_doesnt_pass_through_once_is_refused() {
|
||||
let (n, d) = (Vec2::new(1.0, 0.0), Vec2::new(0.0, -1.0));
|
||||
let v = |x: f64, y: f64| Vec2::new(x, y);
|
||||
let circle = Loop2::circle(v(0.0, 0.0), 50.0);
|
||||
// The zone reaches the tip: nothing lies past it, or short of it.
|
||||
assert!(curved_fold(&circle, n, d, 49.0, 5.0).is_none());
|
||||
assert!(curved_fold(&circle, n, d, -54.0, 5.0).is_none());
|
||||
// Wholly off the sheet.
|
||||
assert!(curved_fold(&circle, n, d, 60.0, 5.0).is_none());
|
||||
// Two tabs on the line: the outline crosses the zone four times.
|
||||
let tabs = Loop2::polygon(&[
|
||||
v(-10.0, -30.0),
|
||||
v(10.0, -30.0),
|
||||
v(10.0, -5.0),
|
||||
v(-10.0, -5.0),
|
||||
v(-10.0, 5.0),
|
||||
v(10.0, 5.0),
|
||||
v(10.0, 30.0),
|
||||
v(-10.0, 30.0),
|
||||
v(-20.0, 30.0),
|
||||
v(-20.0, -30.0),
|
||||
]);
|
||||
assert!(curved_fold(&tabs, n, d, -2.0, 4.0).is_none());
|
||||
// An edge square across the bend, along the zone's line.
|
||||
let along = Loop2::polygon(&[v(-10.0, -10.0), v(2.0, -10.0), v(2.0, 10.0), v(-10.0, 10.0)]);
|
||||
assert!(curved_fold(&along, n, d, -2.0, 4.0).is_none());
|
||||
// Numbers that aren't.
|
||||
assert!(curved_fold(&circle, n, d, f64::NAN, 5.0).is_none());
|
||||
assert!(curved_fold(&Loop2 { segs: Vec::new() }, n, d, 0.0, 5.0).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_fold_zone_with_straight_ends_is_told_from_a_curved_or_cornered_one() {
|
||||
let (n, d) = (Vec2::new(1.0, 0.0), Vec2::new(0.0, 1.0));
|
||||
let zone = |pts: &[(f64, f64)]| slanted_zone(&pts.iter().map(|&(x, y)| Vec2::new(x, y)).collect::<Vec<_>>(), n, d, 0.0, 4.0);
|
||||
// Narrowing, widening, leaning one way, and square at one end.
|
||||
assert_eq!(zone(&[(0.0, 0.0), (4.0, 1.0), (4.0, 9.0), (0.0, 10.0)]), Some((0.0, 10.0, (-1.0, -1.0), (true, true))));
|
||||
assert_eq!(zone(&[(0.0, 1.0), (4.0, 0.0), (4.0, 10.0), (0.0, 9.0)]), Some((1.0, 9.0, (1.0, 1.0), (true, true))));
|
||||
assert_eq!(zone(&[(0.0, 0.0), (4.0, 2.0), (4.0, 12.0), (0.0, 10.0)]), Some((0.0, 10.0, (-2.0, 2.0), (true, true))));
|
||||
assert_eq!(zone(&[(0.0, 0.0), (4.0, 0.0), (4.0, 9.0), (0.0, 10.0)]), Some((0.0, 10.0, (0.0, -1.0), (false, true))));
|
||||
// An S across the zone encloses what the straight end would: its corners give it away.
|
||||
assert_eq!(zone(&[(0.0, 0.0), (4.0, 1.0), (4.0, 9.0), (2.0, 10.0), (2.0, 9.0), (0.0, 10.0)]), None);
|
||||
// A curved end, as a polyline.
|
||||
assert_eq!(zone(&[(0.0, 0.0), (4.0, 0.0), (4.0, 9.0), (3.0, 9.8), (1.0, 9.8), (0.0, 9.0)]), None);
|
||||
// Nothing to bend across: the zone comes to a point on either line, or isn't there.
|
||||
assert_eq!(zone(&[(0.0, 0.0), (4.0, 5.0), (0.0, 10.0)]), None);
|
||||
assert_eq!(zone(&[(0.0, 5.0), (4.0, 0.0), (4.0, 10.0)]), None);
|
||||
assert_eq!(zone(&[]), None);
|
||||
assert_eq!(zone(&[(f64::NAN, 0.0), (4.0, 0.0), (4.0, 10.0), (0.0, 10.0)]), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_slanted_flat_edge_folds_to_its_exact_helix() {
|
||||
// A 72° flange, inside: the bend's axis runs along x through y = setback, z = T + R,
|
||||
|
||||
@@ -429,7 +429,8 @@ fn other_skin(b: &Body, mesh: &Mesh, c: &Chamfer, tol: f64) -> Option<Chamfer> {
|
||||
|
||||
/// Face `g`'s outline with each picked chamfer along it taken out: the edge along the chamfer
|
||||
/// moves to where `g`'s plane meets the plane of the face across the chamfer (its corners
|
||||
/// sliding along the edges before and after it), and runs along that face.
|
||||
/// sliding along the edges before and after it, or stepping down to it where the face goes on
|
||||
/// past the chamfer's end), and runs along that face.
|
||||
fn without_chamfers(b: &Body, mut face: Face, g: usize, chamfers: &[Chamfer], tol: f64) -> Result<Face> {
|
||||
for c in chamfers {
|
||||
let other = if c.a == g {
|
||||
@@ -447,29 +448,57 @@ fn without_chamfers(b: &Body, mut face: Face, g: usize, chamfers: &[Chamfer], to
|
||||
let &[k] = along.as_slice() else {
|
||||
return err("not supported yet: a chamfer picked as a bend that runs along a face more than once");
|
||||
};
|
||||
// Its corners slide along the edges before and after it: those must be straight.
|
||||
if [k + m - 1, k + 1].iter().any(|i| face.round.get(i % m).is_some_and(Option::is_some)) {
|
||||
return err("not supported yet: a round edge of a face that ends at a chamfer picked as a bend");
|
||||
}
|
||||
// Each end of the edge slides along the edge on its other side, out to the other plane.
|
||||
let slide = |corner: usize, before: usize| -> Option<Vec3> {
|
||||
let (Some(&p0), Some(&q0)) = (face.pts.get(k), face.pts.get((k + 1) % m)) else { return err("a face of the sheet has no outline") };
|
||||
let Some(e) = (q0 - p0).normalized() else { return err("a face of the sheet has an edge without length") };
|
||||
// Each end of the edge slides along the straight edge on its other side, out to the
|
||||
// other plane.
|
||||
let slide = |corner: usize, before: usize, edge: usize| -> Option<Vec3> {
|
||||
if face.round.get(edge % m).is_some_and(Option::is_some) {
|
||||
return None;
|
||||
}
|
||||
let (p, q) = (*face.pts.get(corner % m)?, *face.pts.get(before % m)?);
|
||||
let d = (p - q).normalized()?;
|
||||
let s = (o - p).dot(n) / d.dot(n);
|
||||
(s.is_finite() && s >= -tol).then_some(p + d * s)
|
||||
(d.cross(e).len() > 1e-6 && s.is_finite() && s >= -tol).then_some(p + d * s)
|
||||
};
|
||||
let (Some(p), Some(q)) = (slide(k, k + m - 1), slide(k + 1, k + 2)) else {
|
||||
return err("a face beside a chamfer picked as a bend does not reach the face across it");
|
||||
// Where that edge is round or runs on along the chamfer's line (the face goes on past
|
||||
// the chamfer's end), the end keeps its corner and steps down square to the edge onto
|
||||
// the other plane: a step the bend's setback takes up again.
|
||||
let w = face.n.cross(e);
|
||||
let drop = |p: Vec3| -> Result<Vec3> {
|
||||
let s = (o - p).dot(n) / w.dot(n);
|
||||
if s.is_finite() { Ok(p + w * s) } else { err("a face beside a chamfer picked as a bend does not reach the face across it") }
|
||||
};
|
||||
if let Some(x) = face.pts.get_mut(k) {
|
||||
*x = p;
|
||||
}
|
||||
if let Some(x) = face.pts.get_mut((k + 1) % m) {
|
||||
*x = q;
|
||||
}
|
||||
if let Some(x) = face.across.get_mut(k) {
|
||||
*x = Some(other);
|
||||
let (next_across, next_round) = (face.across.get((k + 1) % m).copied().flatten(), face.round.get((k + 1) % m).copied().flatten());
|
||||
let start: Vec<(Vec3, Option<usize>)> = match slide(k, k + m - 1, k + m - 1) {
|
||||
Some(p) => vec![(p, Some(other))],
|
||||
None => vec![(p0, None), (drop(p0)?, Some(other))],
|
||||
};
|
||||
let end: Vec<(Vec3, Option<usize>, Option<(Vec3, Vec3)>)> = match slide(k + 1, k + 2, k + 1) {
|
||||
Some(q) => vec![(q, next_across, next_round)],
|
||||
None => vec![(drop(q0)?, None, None), (q0, next_across, next_round)],
|
||||
};
|
||||
let (mut pts, mut across, mut round) = (Vec::with_capacity(m + 2), Vec::with_capacity(m + 2), Vec::with_capacity(m + 2));
|
||||
for i in 0..m {
|
||||
if i == k {
|
||||
for (p, a) in &start {
|
||||
pts.push(*p);
|
||||
across.push(*a);
|
||||
round.push(None);
|
||||
}
|
||||
} else if i == (k + 1) % m {
|
||||
for (p, a, r) in &end {
|
||||
pts.push(*p);
|
||||
across.push(*a);
|
||||
round.push(*r);
|
||||
}
|
||||
} else {
|
||||
pts.push(face.pts.get(i).copied().unwrap_or_default());
|
||||
across.push(face.across.get(i).copied().flatten());
|
||||
round.push(face.round.get(i).copied().flatten());
|
||||
}
|
||||
}
|
||||
(face.pts, face.across, face.round) = (pts, across, round);
|
||||
}
|
||||
Ok(face)
|
||||
}
|
||||
@@ -489,8 +518,10 @@ fn rip_cut(x: &Face, y: &Face, e: Vec3, t: f64, gap: f64) -> Result<f64> {
|
||||
|
||||
/// A counter-clockwise outline with each edge moved in by its own amount. Moved edges meet
|
||||
/// where their lines cross; two edges in one line that move by different amounts are joined by
|
||||
/// a square step. Returns the corners and, per edge, the edge it was (none for a step).
|
||||
fn inset(pts: &[Vec2], by: &[f64]) -> Result<(Vec<Vec2>, Vec<Option<usize>>)> {
|
||||
/// a square step. An edge left shorter than `tol` (a step down to a bend's line that the bend's
|
||||
/// setback takes up) is dropped, with the corner it shares with the edge that moved further.
|
||||
/// Returns the corners and, per edge, the edge it was (none for a step).
|
||||
fn inset(pts: &[Vec2], by: &[f64], tol: f64) -> Result<(Vec<Vec2>, Vec<Option<usize>>)> {
|
||||
let n = pts.len();
|
||||
if n < 3 {
|
||||
return err("a face of the sheet has no outline");
|
||||
@@ -518,6 +549,15 @@ fn inset(pts: &[Vec2], by: &[f64]) -> Result<(Vec<Vec2>, Vec<Option<usize>>)> {
|
||||
}
|
||||
from.push(Some(i));
|
||||
}
|
||||
let moved_by = |from: &[Option<usize>], j: usize| from.get(j).copied().flatten().and_then(|i| by.get(i)).map_or(0.0, |b| b.abs());
|
||||
while out.len() > 3 {
|
||||
let m = out.len();
|
||||
let short = (0..m).find(|j| matches!((out.get(*j), out.get((j + 1) % m)), (Some(a), Some(b)) if a.dist(*b) <= tol));
|
||||
let Some(j) = short else { break };
|
||||
let corner = if moved_by(&from, (j + m - 1) % m) > moved_by(&from, (j + 1) % m) { j } else { (j + 1) % m };
|
||||
out.remove(corner);
|
||||
from.remove(j);
|
||||
}
|
||||
// Every edge still runs the way it did, and the outline is still one simple loop.
|
||||
let m = out.len();
|
||||
let kept = (0..m).all(|j| match (from.get(j).copied().flatten().and_then(moved), out.get(j), out.get((j + 1) % m)) {
|
||||
@@ -899,7 +939,7 @@ pub fn convert_body(
|
||||
}
|
||||
let first = panel.face.pts.first().copied().unwrap_or_default();
|
||||
let face_at = |e: DocError| DocError::Invalid(format!("{e} (the face at {})", at(first)));
|
||||
let (trimmed, from) = inset(&pts, &by).map_err(face_at)?;
|
||||
let (trimmed, from) = inset(&pts, &by, tol).map_err(face_at)?;
|
||||
let exact = drawn(&trimmed, &from, &pts, &round).map_err(face_at)?;
|
||||
match fl {
|
||||
None => sh.base.outer = exact.unwrap_or_else(|| Loop2::polygon(&trimmed.iter().map(|q| snap(*q)).collect::<Vec<_>>())),
|
||||
@@ -1011,6 +1051,7 @@ pub fn convert_body(
|
||||
slope: (0.0, 0.0),
|
||||
shape: None,
|
||||
exact: None,
|
||||
zone_shape: None,
|
||||
tag: String::new(),
|
||||
tear: (false, false),
|
||||
});
|
||||
@@ -1046,21 +1087,21 @@ mod tests {
|
||||
fn edges_move_in_by_their_own_amounts_and_step_along_one_line() {
|
||||
let v = Vec2::new;
|
||||
let square = [v(0.0, 0.0), v(10.0, 0.0), v(10.0, 10.0), v(0.0, 10.0)];
|
||||
let (p, from) = inset(&square, &[1.0, 0.0, 2.0, 0.0]).unwrap();
|
||||
let (p, from) = inset(&square, &[1.0, 0.0, 2.0, 0.0], 1e-6).unwrap();
|
||||
assert_eq!(p, vec![v(0.0, 1.0), v(10.0, 1.0), v(10.0, 8.0), v(0.0, 8.0)]);
|
||||
assert_eq!(from, vec![Some(0), Some(1), Some(2), Some(3)]);
|
||||
// Two edges in one line that move by different amounts: a square step between them,
|
||||
// so 5 x 2 goes from the 10 x 10.
|
||||
let split = [v(0.0, 0.0), v(5.0, 0.0), v(10.0, 0.0), v(10.0, 10.0), v(0.0, 10.0)];
|
||||
let (p, from) = inset(&split, &[0.0, 2.0, 0.0, 0.0, 0.0]).unwrap();
|
||||
let (p, from) = inset(&split, &[0.0, 2.0, 0.0, 0.0, 0.0], 1e-6).unwrap();
|
||||
assert_eq!(p, vec![v(0.0, 0.0), v(5.0, 0.0), v(5.0, 2.0), v(10.0, 2.0), v(10.0, 10.0), v(0.0, 10.0)]);
|
||||
assert_eq!(from, vec![Some(0), None, Some(1), Some(2), Some(3), Some(4)]);
|
||||
// A slanted end keeps its line: the corner slides along it (2 up a 45° edge is 2 along).
|
||||
let slanted = [v(0.0, 0.0), v(10.0, 0.0), v(15.0, 5.0), v(0.0, 5.0)];
|
||||
let (p, _) = inset(&slanted, &[2.0, 0.0, 0.0, 0.0]).unwrap();
|
||||
let (p, _) = inset(&slanted, &[2.0, 0.0, 0.0, 0.0], 1e-6).unwrap();
|
||||
assert!(p[1].dist(v(12.0, 2.0)) < 1e-12 && p[0].dist(v(0.0, 2.0)) < 1e-12, "{p:?}");
|
||||
// Setbacks longer than the face leave nothing of it.
|
||||
assert!(inset(&square, &[6.0, 0.0, 6.0, 0.0]).is_err());
|
||||
assert!(inset(&square, &[f64::NAN, 0.0, 0.0, 0.0]).is_err());
|
||||
assert!(inset(&square, &[6.0, 0.0, 6.0, 0.0], 1e-6).is_err());
|
||||
assert!(inset(&square, &[f64::NAN, 0.0, 0.0, 0.0], 1e-6).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,8 +68,8 @@ impl SheetBody {
|
||||
let Some(inverse) = mat_inverse(&world) else { continue };
|
||||
let q = apply_point(&inverse, p) - o;
|
||||
let u = q.dot(d);
|
||||
let (u0, u1) = self.zone_span(f);
|
||||
if u < u0 - tol || u > u1 + tol {
|
||||
let (lo, hi) = self.zone_reach(f);
|
||||
if u < lo - tol || u > hi + tol {
|
||||
continue;
|
||||
}
|
||||
let radial = q - d * u;
|
||||
|
||||
@@ -159,6 +159,21 @@ fn results_kept_ahead_outlive_the_cache() {
|
||||
assert!(rel(measure(&m.state().bodies[0].body).unwrap().volume, 30000.0) < 1e-9);
|
||||
}
|
||||
|
||||
/// A warning quotes a long generated name by its start and end (#8541: fourteen split edges'
|
||||
/// full names made one chamfer warning 122,701 characters long); a short name stays whole.
|
||||
#[test]
|
||||
fn warnings_quote_long_names_by_their_ends() {
|
||||
let face = |i: usize| format!("Extrude{i}.side(Sketch{i}.l{i})·Fillet{i}.blend(Chamfer{i}.cap)");
|
||||
let long = (0..40).map(face).collect::<Vec<_>>().join("|");
|
||||
let q = crate::name_lookup::quoted(&long);
|
||||
assert!(q.chars().count() <= 80, "{q}");
|
||||
assert!(q.starts_with("Extrude0.side") && q.ends_with(&long[long.len() - 10..]) && q.contains('…'), "{q}");
|
||||
assert_eq!(crate::name_lookup::quoted("Extrude1.side|Box1.top"), "Extrude1.side|Box1.top");
|
||||
// Characters, not bytes: a name of non-ASCII characters is cut between characters.
|
||||
let wide = "Ü".repeat(200);
|
||||
assert_eq!(crate::name_lookup::quoted(&wide).chars().count(), 73);
|
||||
}
|
||||
|
||||
/// A saved model whose feature warning is longer than 65,536 characters is still taken on open
|
||||
/// (#8541): Save writes it, so Open reads it.
|
||||
#[test]
|
||||
@@ -581,6 +596,33 @@ fn feature_inserted_at_a_marker_inside_a_timeline_folder_joins_it() {
|
||||
assert!(Document::from_json(&doc.to_json()).is_ok(), "the inserted feature must leave a reopenable design");
|
||||
}
|
||||
|
||||
/// #9373: a timeline folder that names a feature or joint the design doesn't have, or a feature
|
||||
/// a folder already lists, no longer stops the design opening. Those entries are dropped and
|
||||
/// everything else is kept. Editing a feature inside a folder used to save such entries.
|
||||
#[test]
|
||||
fn a_folder_naming_missing_or_repeated_features_still_opens() {
|
||||
let mut doc = Document::new("stale folder entries");
|
||||
let sketch = || FeatureKind::Sketch { plane: PlaneRef::Origin { name: "XY".into() }, sketch: Sketch::new() };
|
||||
let a = doc.add_feature(sketch(), Some("A")).unwrap();
|
||||
let b = doc.add_feature(sketch(), Some("B")).unwrap();
|
||||
let c = doc.add_feature(sketch(), Some("C")).unwrap();
|
||||
let missing = doc.next_id + 10;
|
||||
doc.timeline_groups.push(TimelineGroup { id: 100, name: "Repeats".into(), parent: None, features: vec![a, b, b], joints: vec![] });
|
||||
doc.timeline_groups.push(TimelineGroup {
|
||||
id: 101,
|
||||
name: "Stale".into(),
|
||||
parent: None,
|
||||
features: vec![b, c, missing, missing, missing],
|
||||
joints: vec![missing, missing],
|
||||
});
|
||||
let opened = Document::from_json(&doc.to_json()).expect("the design opens");
|
||||
assert_eq!(opened.features.iter().map(|f| f.id).collect::<Vec<_>>(), vec![a, b, c]);
|
||||
assert_eq!(opened.timeline_groups[0].features, vec![a, b], "listed once");
|
||||
assert_eq!(opened.timeline_groups[1].features, vec![c], "no missing feature, nor one the first folder holds");
|
||||
assert!(opened.timeline_groups[1].joints.is_empty());
|
||||
assert_eq!(opened.timeline_groups.iter().map(|g| g.name.as_str()).collect::<Vec<_>>(), ["Repeats", "Stale"], "both folders are kept");
|
||||
}
|
||||
|
||||
/// A marker after the last feature leaves every feature active, as no marker does, so a feature
|
||||
/// added then is active too, not added rolled back (#7741: editing the last feature from the
|
||||
/// History runs its command with the marker there).
|
||||
@@ -653,6 +695,28 @@ fn new_names_take_the_first_free_number() {
|
||||
assert_eq!(name(&doc, next), names[0], "the first freed number is taken again");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn construction_plane_name_collisions_leave_the_document_unchanged() {
|
||||
let plane = || FeatureKind::ConstructionPlane { plane: PlaneRef::Origin { name: "XY".into() } };
|
||||
let sketch = || FeatureKind::Sketch { plane: PlaneRef::Origin { name: "XY".into() }, sketch: Sketch::new() };
|
||||
for (first, second) in [(plane(), plane()), (plane(), sketch()), (sketch(), plane())] {
|
||||
let mut doc = Document::new("names");
|
||||
let id = doc.add_feature(first, Some("Mid")).unwrap();
|
||||
// A suppressed feature beyond the marker still owns its name.
|
||||
doc.feature_mut(id).unwrap().suppressed = true;
|
||||
doc.marker = Some(0);
|
||||
let before = doc.clone();
|
||||
for name in ["Mid", " Mid "] {
|
||||
let error = doc.add_feature(second.clone(), Some(name)).unwrap_err().to_string();
|
||||
assert!(error.contains("another feature is called `Mid`"), "{error}");
|
||||
assert_eq!(doc, before, "a refused name must not consume an id or change the timeline");
|
||||
}
|
||||
let other = doc.add_feature(second, Some("Upper")).unwrap();
|
||||
assert_eq!(doc.feature(other).unwrap().name, "Upper");
|
||||
assert_eq!(other, before.next_id);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_boolean_targets_name_overlapping_component() {
|
||||
for (operation, offset) in [(Operation::Cut, 0.0), (Operation::Intersect, 0.0), (Operation::Cut, 40.0)] {
|
||||
@@ -870,3 +934,19 @@ fn browser_data_survives_a_save_and_old_designs_read_unchanged() {
|
||||
assert_eq!(resaved["browser_groups"], groups, "{order}");
|
||||
}
|
||||
}
|
||||
|
||||
/// #10748: verify that type names map to their canonical design-file kind keys.
|
||||
#[test]
|
||||
fn feature_kind_key_and_type_name_mappings() {
|
||||
assert_eq!(FeatureKind::type_name_to_kind_key("HoleFeature"), Some("hole"));
|
||||
assert_eq!(FeatureKind::type_name_to_kind_key("BoxFeature"), Some("box"));
|
||||
assert_eq!(FeatureKind::type_name_to_kind_key("EdgeFlangeFeature"), Some("sheet_flange"));
|
||||
assert_eq!(FeatureKind::type_name_to_kind_key("RectangularPatternFeature"), Some("pattern"));
|
||||
assert_eq!(FeatureKind::type_name_to_kind_key("PathPatternFeature"), Some("pattern"));
|
||||
assert_eq!(FeatureKind::type_name_to_kind_key("CircularPatternFeature"), Some("pattern"));
|
||||
assert_eq!(FeatureKind::type_name_to_kind_key("ExtrudeFeature"), Some("extrude"));
|
||||
assert_eq!(FeatureKind::type_name_to_kind_key("SplitBodyFeature"), Some("split"));
|
||||
assert_eq!(FeatureKind::type_name_to_kind_key("BaseFeature"), Some("import"));
|
||||
assert_eq!(FeatureKind::type_name_to_kind_key("MeshFeature"), Some("mesh_import"));
|
||||
assert_eq!(FeatureKind::type_name_to_kind_key("NonExistent"), None);
|
||||
}
|
||||
|
||||
+18
-4
@@ -335,6 +335,7 @@ impl Walk for PlaneRef {
|
||||
p = base;
|
||||
}
|
||||
PlaneRef::AlongPath { t, .. } => return f(("Distance (0 to 1)", t, Kind::Unitless, ExprRole::Input)),
|
||||
PlaneRef::Tangent { angle: Some(angle), .. } => return f(("Angle", angle, Kind::Angle, ExprRole::Input)),
|
||||
// A plane seen from behind keeps its base's expressions (a sketch made with
|
||||
// `flip` on an offset plane did not follow the offset's parameter).
|
||||
PlaneRef::Flipped { base, .. } | PlaneRef::ThroughPoint { base, .. } => p = base,
|
||||
@@ -361,6 +362,7 @@ impl Walk for PlaneRef {
|
||||
p = base;
|
||||
}
|
||||
PlaneRef::AlongPath { t, .. } => return f(("Distance (0 to 1)", t, Kind::Unitless, ExprRole::Input)),
|
||||
PlaneRef::Tangent { angle: Some(angle), .. } => return f(("Angle", angle, Kind::Angle, ExprRole::Input)),
|
||||
// A plane seen from behind keeps its base's expressions (a sketch made with
|
||||
// `flip` on an offset plane did not follow the offset's parameter).
|
||||
PlaneRef::Flipped { base, .. } | PlaneRef::ThroughPoint { base, .. } => p = base,
|
||||
@@ -406,7 +408,7 @@ impl PlaneRef {
|
||||
g.walk_feature_refs(f, depth + 1);
|
||||
}
|
||||
}
|
||||
Self::Midplane { a, b } | Self::TwoEdges { a, b } | Self::Perpendicular { plane: a, line: b } => {
|
||||
Self::Midplane { a, b } | Self::TwoEdges { a, b } | Self::AxisPoint { axis: a, point: b } | Self::Perpendicular { plane: a, line: b } => {
|
||||
a.walk_feature_refs(f, depth + 1);
|
||||
b.walk_feature_refs(f, depth + 1);
|
||||
}
|
||||
@@ -415,7 +417,13 @@ impl PlaneRef {
|
||||
b.walk_feature_refs(f, depth + 1);
|
||||
c.walk_feature_refs(f, depth + 1);
|
||||
}
|
||||
Self::Tangent { face, .. } | Self::AlongPath { path: face, .. } => face.walk_feature_refs(f, depth + 1),
|
||||
Self::Tangent { face, reference, .. } => {
|
||||
face.walk_feature_refs(f, depth + 1);
|
||||
if let Some(r) = reference {
|
||||
r.walk_feature_refs(f, depth + 1);
|
||||
}
|
||||
}
|
||||
Self::AlongPath { path, .. } => path.walk_feature_refs(f, depth + 1),
|
||||
Self::Origin { .. } | Self::Custom { .. } | Self::Placed { .. } | Self::Face { .. } => {}
|
||||
}
|
||||
}
|
||||
@@ -437,7 +445,7 @@ impl PlaneRef {
|
||||
g.walk_feature_refs_mut(f, depth + 1);
|
||||
}
|
||||
}
|
||||
Self::Midplane { a, b } | Self::TwoEdges { a, b } | Self::Perpendicular { plane: a, line: b } => {
|
||||
Self::Midplane { a, b } | Self::TwoEdges { a, b } | Self::AxisPoint { axis: a, point: b } | Self::Perpendicular { plane: a, line: b } => {
|
||||
a.walk_feature_refs_mut(f, depth + 1);
|
||||
b.walk_feature_refs_mut(f, depth + 1);
|
||||
}
|
||||
@@ -446,7 +454,13 @@ impl PlaneRef {
|
||||
b.walk_feature_refs_mut(f, depth + 1);
|
||||
c.walk_feature_refs_mut(f, depth + 1);
|
||||
}
|
||||
Self::Tangent { face, .. } | Self::AlongPath { path: face, .. } => face.walk_feature_refs_mut(f, depth + 1),
|
||||
Self::Tangent { face, reference, .. } => {
|
||||
face.walk_feature_refs_mut(f, depth + 1);
|
||||
if let Some(r) = reference {
|
||||
r.walk_feature_refs_mut(f, depth + 1);
|
||||
}
|
||||
}
|
||||
Self::AlongPath { path, .. } => path.walk_feature_refs_mut(f, depth + 1),
|
||||
Self::Origin { .. } | Self::Custom { .. } | Self::Placed { .. } | Self::Face { .. } => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ solidcraft-kernel = { workspace = true }
|
||||
solidcraft-doc = { workspace = true }
|
||||
solidcraft-io = { workspace = true }
|
||||
solidcraft-render = { workspace = true }
|
||||
# reference pictures for inspect.compare_picture (PNG and JPEG decode, PNG encode)
|
||||
image = { workspace = true, features = ["jpeg"] }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
@@ -1424,6 +1424,51 @@ mod tests {
|
||||
assert_eq!(s.selection.len(), 2);
|
||||
}
|
||||
|
||||
/// The viewport asks whether each body is shown several times a frame: a lookup reads the
|
||||
/// set of hidden bodies, not the list, and follows every change of the list, one written in
|
||||
/// place included.
|
||||
#[test]
|
||||
fn hidden_body_lookups_do_not_walk_the_hidden_body_list() {
|
||||
use crate::occurrences::visibility_work;
|
||||
|
||||
let mut s = Session::default();
|
||||
let hide_many = |s: &mut Session, n: usize| {
|
||||
s.visibility.hidden_bodies.clear();
|
||||
s.visibility.hidden_bodies.extend((0..n).map(|i| format!("Hidden{i}")));
|
||||
};
|
||||
let walked_by = |s: &Session, name: &str| {
|
||||
visibility_work::take();
|
||||
let visible = s.body_visible(name);
|
||||
(visible, visibility_work::take())
|
||||
};
|
||||
hide_many(&mut s, 10);
|
||||
let (_, few) = walked_by(&s, "Shown");
|
||||
hide_many(&mut s, 10_000);
|
||||
visibility_work::built_take();
|
||||
let (visible, many) = walked_by(&s, "Shown");
|
||||
assert!(visible);
|
||||
assert!(many <= few, "a lookup walked {many} entries of 10,000 hidden bodies and {few} of 10");
|
||||
assert_eq!(visibility_work::built_take(), 10_000, "the hidden bodies are read into the set once");
|
||||
for i in 0..300 {
|
||||
assert!(walked_by(&s, &format!("Body{i}")) == (true, 0));
|
||||
}
|
||||
assert_eq!(visibility_work::built_take(), 0, "and not again for the next lookups");
|
||||
assert!(!s.body_visible("Hidden5000") && !s.body_visible("Hidden9999"));
|
||||
s.visibility.hidden_bodies.retain(|name| name != "Hidden5000");
|
||||
assert!(s.body_visible("Hidden5000"), "shown again in place");
|
||||
s.visibility.hidden_bodies.push("Shown".into());
|
||||
assert!(!s.body_visible("Shown"), "hidden in place");
|
||||
s.visibility.hidden_bodies.clear();
|
||||
assert!(s.body_visible("Shown") && s.body_visible("Hidden0"), "all shown");
|
||||
run(&mut s, "solid.box", json!({"length": 10, "width": 10, "height": 10, "body_name": "Peg"}));
|
||||
run(&mut s, "browser.visibility", json!({"items": ["Peg"], "visible": false}));
|
||||
assert!(!s.body_visible("Peg"));
|
||||
run(&mut s, "edit.undo", json!({}));
|
||||
assert!(s.body_visible("Peg"), "undo shows it again");
|
||||
run(&mut s, "edit.redo", json!({}));
|
||||
assert!(!s.body_visible("Peg"), "redo hides it again");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show_all_bodies_clears_stale_hidden_names() {
|
||||
for folder in ["bodies", "all"] {
|
||||
@@ -1588,10 +1633,18 @@ mod tests {
|
||||
assert_eq!(sorted(names(&s)), ["A", "B", "B (1)", "Cutter"]);
|
||||
run(&mut s, "body.rename", json!({"body": "B (1)", "name": "B2"}));
|
||||
assert_eq!(sorted(names(&s)), ["A", "B", "B2", "Cutter"]);
|
||||
// The new name is the cut's second piece's, stored by the cut, not in the members' slots.
|
||||
let stored = |id: u64| s.doc.features.iter().find(|f| f.id == id).map(|f| f.body_names.clone()).unwrap_or_default();
|
||||
assert!(!stored(2).contains(&"B2".to_string()), "members: {:?}", stored(2));
|
||||
assert_eq!(stored(4), ["B2"], "the cut stores its piece's name");
|
||||
run(&mut s, "file.save_as", json!({"path": saved.to_string_lossy()}));
|
||||
let mut reopened = Session::default();
|
||||
run(&mut reopened, "doc.open", json!({"path": saved.to_string_lossy()}));
|
||||
assert_eq!(sorted(names(&reopened)), ["A", "B", "B2", "Cutter"], "the rename must not move to member A on rebuild");
|
||||
// `solidcraft-cli eval` of the saved file rebuilds it from its features alone, with no
|
||||
// saved model to take the names from (#10438).
|
||||
let rebuilt = Session::new(solidcraft_doc::Document::from_json(&s.doc.to_json()).unwrap());
|
||||
assert_eq!(sorted(names(&rebuilt)), ["A", "B", "B2", "Cutter"], "a rebuild from the features alone keeps the rename too");
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
@@ -1749,7 +1802,9 @@ mod tests {
|
||||
run(&mut s, "solid.box", json!({"length": 5, "width": 5, "height": 5, "corner": [20, 0, 0], "operation": "new", "body_name": "Tab"}));
|
||||
run(&mut s, "solid.combine", json!({"target": "A", "tools": ["Tab"], "operation": "join", "name": "Joined"}));
|
||||
let want = total(&mut s);
|
||||
s.evaluations = 0;
|
||||
run(&mut s, "body.rename", json!({"body": "A", "name": "B"}));
|
||||
assert_eq!(s.evaluations, 0, "renaming primitive participants changes no geometry");
|
||||
assert!(
|
||||
s.model.results.iter().all(|r| r.error.is_none()),
|
||||
"{:?}",
|
||||
@@ -1759,6 +1814,7 @@ mod tests {
|
||||
assert!((total(&mut s) - want).abs() < 1e-6);
|
||||
run(&mut s, "edit.undo", json!({}));
|
||||
assert_eq!(s.doc.find_feature("Hole").unwrap().participants, vec!["A".to_string()]);
|
||||
assert_eq!(s.evaluations, 0);
|
||||
}
|
||||
|
||||
/// #1109: renaming counted bodies (`Body1`, `Body2` of one pattern) leaves the counted bodies
|
||||
@@ -1822,14 +1878,80 @@ mod tests {
|
||||
assert_eq!(names(&s), vec!["Plate".to_string(), "B".to_string()]);
|
||||
assert_eq!(s.evaluations, 0, "undo and redo of a rename evaluate nothing");
|
||||
assert_eq!(meshes(&s), shown);
|
||||
// A body a later feature uses is renamed in that feature too: that is evaluated.
|
||||
// A later feature's body reference follows the rename without rebuilding geometry.
|
||||
run(&mut s, "solid.combine", json!({"target": "Plate", "tools": ["B"], "operation": "join", "keep_tools": true}));
|
||||
let shown = meshes(&s);
|
||||
s.evaluations = 0;
|
||||
run(&mut s, "body.rename", json!({"body": "B", "name": "Tool"}));
|
||||
assert!(s.evaluations > 0);
|
||||
assert_eq!(s.evaluations, 0);
|
||||
assert_eq!(meshes(&s), shown);
|
||||
assert!(names(&s).contains(&"Tool".to_string()));
|
||||
}
|
||||
|
||||
/// #6287: one rename in a timeline of named body references used to rebuild every feature
|
||||
/// and replace every mesh, including the other bodies' meshes.
|
||||
#[test]
|
||||
fn renaming_a_body_with_downstream_references_keeps_every_mesh() {
|
||||
let mut s = Session::default();
|
||||
for i in 0..18 {
|
||||
run(
|
||||
&mut s,
|
||||
"solid.box",
|
||||
json!({"length": 40, "width": 30, "height": 10, "center": [50 * i, 0, 0], "operation": "new", "body_name": format!("Block{i}")}),
|
||||
);
|
||||
}
|
||||
for _ in 0..2 {
|
||||
for i in 0..18 {
|
||||
run(&mut s, "solid.move", json!({"bodies": [format!("Block{i}")], "translate": [0, 5, 0]}));
|
||||
}
|
||||
}
|
||||
assert_eq!(s.doc.features.len(), 54);
|
||||
let meshes = |s: &Session| s.model.state().bodies.iter().map(|b| b.mesh()).collect::<Vec<_>>();
|
||||
let looks = |s: &Session| {
|
||||
s.model.state().bodies.iter().map(|b| (b.name.clone(), (*solidcraft_doc::naming::face_names(b)).clone())).collect::<Vec<_>>()
|
||||
};
|
||||
let shown = meshes(&s);
|
||||
let before = looks(&s);
|
||||
let measurements = run(&mut s, "inspect.measure", json!({}))["total"].clone();
|
||||
assert!((measurements["volume_mm3"].as_f64().unwrap() - 216_000.0).abs() < 1e-6);
|
||||
s.evaluations = 0;
|
||||
run(&mut s, "body.rename", json!({"body": "Block0", "name": "Bracket"}));
|
||||
assert_eq!(s.evaluations, 0, "renaming references must not evaluate the timeline");
|
||||
let after = looks(&s);
|
||||
assert_eq!(after[0].0, "Bracket");
|
||||
let mut fresh = Session::new((*s.doc).clone());
|
||||
fresh.refresh();
|
||||
assert_eq!(looks(&fresh), after, "the same bodies and face names as rebuilding");
|
||||
assert_eq!(run(&mut fresh, "inspect.measure", json!({}))["total"], measurements);
|
||||
run(&mut s, "edit.undo", json!({}));
|
||||
assert_eq!(looks(&s), before);
|
||||
run(&mut s, "edit.redo", json!({}));
|
||||
assert_eq!(looks(&s), after);
|
||||
assert_eq!(s.evaluations, 0, "undo and redo must also retain the evaluated model");
|
||||
assert!(shown.iter().zip(meshes(&s)).all(|(a, b)| std::sync::Arc::ptr_eq(a, &b)), "every body retains its display mesh");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_body_rename_with_changed_dimensions_still_evaluates() {
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "solid.box", json!({"length": 40, "width": 30, "height": 10, "body_name": "Block"}));
|
||||
run(&mut s, "solid.move", json!({"bodies": ["Block"], "translate": [5, 0, 0]}));
|
||||
let before = s.doc.clone();
|
||||
let model = s.model.clone();
|
||||
run(&mut s, "body.rename", json!({"body": "Block", "name": "Bracket"}));
|
||||
// An outside edit can include a rename and a geometry change together. It must not
|
||||
// take the rename shortcut merely because the later Move's body reference matches.
|
||||
let solidcraft_doc::FeatureKind::Box { height, .. } = &mut s.doc_mut().features[0].kind else { panic!("expected the box") };
|
||||
*height = "20".into();
|
||||
s.model = model;
|
||||
s.evaluations = 0;
|
||||
s.refresh_from(&before, false);
|
||||
assert_eq!(s.evaluations, 1);
|
||||
assert_eq!(names(&s), ["Bracket"]);
|
||||
let volume = run(&mut s, "inspect.measure", json!({}))["total"]["volume_mm3"].as_f64().unwrap();
|
||||
assert!((volume - 24_000.0).abs() < 1e-6, "the changed height must be evaluated: {volume}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn renaming_a_body_leaves_other_text_with_its_name_alone() {
|
||||
let mut s = Session::default();
|
||||
|
||||
@@ -150,6 +150,10 @@ fn paste(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
if !internal {
|
||||
notices.push(format!("pasted sketch retains external exact face attachment to {}", anchor.target.body));
|
||||
}
|
||||
if internal {
|
||||
let source = solidcraft_doc::mat_mul(&shift, frame_change.get(&anchor.component).unwrap_or(&solidcraft_doc::IDENTITY));
|
||||
anchor.reframe_placement(&m, &source)?;
|
||||
}
|
||||
anchor.remap_target(&sketches, &bodies)?;
|
||||
if internal {
|
||||
anchor.component = comp;
|
||||
|
||||
@@ -0,0 +1,692 @@
|
||||
//! Compare the model with a reference picture (#1486): agents model to a picture, and an
|
||||
//! overlap score plus per-edge mm offsets tell the agent which edge to move and by how much.
|
||||
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::io::Cursor;
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use solidcraft_doc::canvas::base64_encode;
|
||||
|
||||
use super::CommandSpec;
|
||||
use super::silhouette::{self, Silhouette};
|
||||
use crate::params::bad;
|
||||
use crate::{Result, Session};
|
||||
|
||||
const CMD: &str = "inspect.compare_picture";
|
||||
/// A reference picture is read through the vfs with a byte cap, like `screenshot` output.
|
||||
const MAX_REFERENCE_BYTES: usize = 32 << 20;
|
||||
/// Decoded pixels are capped as well, so a small file cannot unfold into a huge raster.
|
||||
const MAX_DECODED_PIXELS: u64 = 16 << 20;
|
||||
/// The comparison grid each mask is fitted into at most — the picture's and the fitted
|
||||
/// model's alike; keeps the slide search and the memory it needs bounded.
|
||||
const MAX_GRID: usize = 1024;
|
||||
/// Background grows from the border only through steps of at most this per channel, so a
|
||||
/// backdrop's gradual shading belongs to it and the step at the part's outline stops it.
|
||||
const BG_STEP: u16 = 12;
|
||||
/// When the first pass finds no part, the flood retries at this stricter step: a white part
|
||||
/// on a (245, 245, 245) studio backdrop is a step of 10, which BG_STEP lets through.
|
||||
const BG_STEP_STRICT: u16 = 4;
|
||||
/// A picture whose border is mostly transparent: a pixel below this alpha is see-through.
|
||||
const ALPHA_BG: u8 = 128;
|
||||
/// The global retry's backdrop: a pixel within this of the border's main colour is
|
||||
/// background. A light JPEG (white on (245, 245, 245) at quality 75) blurs the step at the
|
||||
/// part's outline below any local limit, so that pass compares to the backdrop's colour.
|
||||
const BG_NEAR: u16 = 8;
|
||||
/// Stations per side for the edge offsets.
|
||||
const STATIONS: usize = 9;
|
||||
|
||||
// For agents modelling to a picture; nothing to show a person, so no toolbar button.
|
||||
pub static COMMANDS: &[CommandSpec] = &[CommandSpec::new(CMD, "Compare Picture", run).noundo().icon("compare").params(
|
||||
"reference: path to a PNG or JPEG the model should match; view?/camera?/bodies?/rows? as inspect.silhouette; \
|
||||
crop_px?: [x, y, w, h] in pixels, or crop_fraction?: [x, y, w, h] as fractions 0..1 of the picture \
|
||||
(to take one view off a board); part_fills_crop?: false — with true the crop box is the part itself \
|
||||
and no background search runs, for a crop that tightly frames the part; \
|
||||
fit?: \"width\" | \"height\" | \"both\" (default \"width\"): one uniform scale, taken from the named \
|
||||
dimension (\"both\" claims both and scales by width, reporting aspect_mismatch when the shapes differ); \
|
||||
overlay?: false leaves the overlay PNG out → overlap (0..1), edges: top|bottom|left|right in mm at nine stations \
|
||||
(positive where the model reaches past the picture, null where a station misses one of the two), the grid and its \
|
||||
mm_per_px, and overlay_png (red model, blue picture) as base64 PNG. The compare is outline only: \
|
||||
holes are not compared — both the picture's and the model's holes are filled",
|
||||
)];
|
||||
|
||||
fn run(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let reference = p
|
||||
.get("reference")
|
||||
.and_then(Value::as_str)
|
||||
.filter(|v| !v.trim().is_empty())
|
||||
.ok_or_else(|| bad(CMD, "`reference` is required: a path to a PNG or JPEG"))?;
|
||||
let fit = match p.get("fit") {
|
||||
None => Fit::Width,
|
||||
Some(Value::String(v)) => match v.as_str() {
|
||||
"width" => Fit::Width,
|
||||
"height" => Fit::Height,
|
||||
"both" => Fit::Both,
|
||||
other => return Err(bad(CMD, format!("`fit` must be \"width\", \"height\" or \"both\", not `{other}`"))),
|
||||
},
|
||||
Some(_) => return Err(bad(CMD, "`fit` must be a string")),
|
||||
};
|
||||
let overlay = p.get("overlay").map(|v| v.as_bool().unwrap_or(false)).unwrap_or(true);
|
||||
let part_fills_crop = p.get("part_fills_crop").and_then(Value::as_bool).unwrap_or(false);
|
||||
|
||||
// The model side: the same outline inspect.silhouette returns, as filled rows.
|
||||
let camera = silhouette::camera(p)?;
|
||||
let rows = silhouette::rows(p)?;
|
||||
let wanted = silhouette::body_names(p)?;
|
||||
let model = silhouette::outline(s, &camera, wanted.as_deref(), rows)?;
|
||||
|
||||
// The picture side: decode, crop, and find the part.
|
||||
let bytes = solidcraft_io::vfs::read(reference, MAX_REFERENCE_BYTES).map_err(|e| bad(CMD, format!("{reference}: {e}")))?;
|
||||
let img = decode(&bytes)?;
|
||||
let img = apply_crop(img, p.get("crop_px"), p.get("crop_fraction"))?;
|
||||
let picture = if part_fills_crop {
|
||||
part_from_box(&img).map_err(|reason| bad(CMD, format!("the crop cannot be the part: {reason}")))?
|
||||
} else {
|
||||
find_part(&img).map_err(|reason| bad(CMD, format!("no part found in the picture: {reason}")))?
|
||||
};
|
||||
|
||||
// Fit the model outline to the picture's scale, then slide it to the best overlap.
|
||||
let (mw, mh, mmpp_x, mmpp_y) = fit_model(&model, picture.width, picture.height, fit);
|
||||
let mut model_scaled = resample(&model.mask, model.width, model.height, mw, mh);
|
||||
// The same hole rule the picture side has: a through-hole in the model counts as its
|
||||
// outline, not as a hole the picture has to match.
|
||||
fill_holes(&mut model_scaled);
|
||||
let best = best_overlap(&model_scaled, &picture);
|
||||
let placed = Placed { dx: best.dx, dy: best.dy, mask: &model_scaled };
|
||||
let aspect_mismatch = (picture.width as f64 * mh as f64) / (picture.height as f64 * mw as f64);
|
||||
|
||||
let mut out = json!({
|
||||
"overlap": round4(best.overlap),
|
||||
"stations": STATIONS,
|
||||
"edges": {
|
||||
"top": edge_offsets(&placed, &picture, Edge::Top, mmpp_y),
|
||||
"bottom": edge_offsets(&placed, &picture, Edge::Bottom, mmpp_y),
|
||||
"left": edge_offsets(&placed, &picture, Edge::Left, mmpp_x),
|
||||
"right": edge_offsets(&placed, &picture, Edge::Right, mmpp_x),
|
||||
},
|
||||
"fit": match fit { Fit::Width => "width", Fit::Height => "height", Fit::Both => "both" },
|
||||
"aspect_mismatch": round4(aspect_mismatch),
|
||||
"mm_per_px": {"x": round4(mmpp_x), "y": round4(mmpp_y)},
|
||||
"model": {"width": model.width, "height": model.height, "box_mm": [[round4(model.lo[0]), round4(model.lo[1])], [round4(model.hi[0]), round4(model.hi[1])]], "bodies": model.bodies},
|
||||
"picture": {"width": picture.width, "height": picture.height, "source": reference},
|
||||
});
|
||||
if overlay {
|
||||
out["overlay_png"] = json!(base64_encode(&overlay_png(&picture, &placed)));
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Fit {
|
||||
Width,
|
||||
Height,
|
||||
Both,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Edge {
|
||||
Top,
|
||||
Bottom,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
/// A picture mask, tightly framed to the found part.
|
||||
struct Part {
|
||||
width: usize,
|
||||
height: usize,
|
||||
mask: Vec<Vec<bool>>,
|
||||
}
|
||||
|
||||
/// A model mask placed relative to the picture: its top-left sits at (dx, dy) in the
|
||||
/// picture's own pixel coordinates, which may be negative.
|
||||
struct Placed<'a> {
|
||||
dx: i64,
|
||||
dy: i64,
|
||||
mask: &'a [Vec<bool>],
|
||||
}
|
||||
|
||||
/// Decode PNG or JPEG to RGBA8, refusing anything else and anything that unfolds too large.
|
||||
fn decode(bytes: &[u8]) -> Result<image::RgbaImage> {
|
||||
let guess = image::ImageReader::new(Cursor::new(bytes)).with_guessed_format().map_err(|e| bad(CMD, format!("cannot read the picture: {e}")))?;
|
||||
if !matches!(guess.format(), Some(image::ImageFormat::Png) | Some(image::ImageFormat::Jpeg)) {
|
||||
return Err(bad(CMD, "the reference must be a PNG or a JPEG"));
|
||||
}
|
||||
let (w, h) = guess.into_dimensions().map_err(|e| bad(CMD, format!("cannot read the picture: {e}")))?;
|
||||
if w as u64 * h as u64 > MAX_DECODED_PIXELS {
|
||||
return Err(bad(CMD, format!("the picture is {w}x{h}: over the {} megapixel limit", MAX_DECODED_PIXELS >> 20)));
|
||||
}
|
||||
let reader = image::ImageReader::new(Cursor::new(bytes)).with_guessed_format().map_err(|e| bad(CMD, format!("cannot read the picture: {e}")))?;
|
||||
let img = reader.decode().map_err(|e| bad(CMD, format!("cannot decode the picture: {e}")))?;
|
||||
Ok(img.to_rgba8())
|
||||
}
|
||||
|
||||
/// Apply `crop_px: [x, y, w, h]` (pixels) or `crop_fraction: [x, y, w, h]` (fractions 0..1
|
||||
/// of the picture). Explicit units: a crop of `[0, 0, 1, 1]` means one pixel, not the whole.
|
||||
fn apply_crop(img: image::RgbaImage, crop_px: Option<&Value>, crop_fraction: Option<&Value>) -> Result<image::RgbaImage> {
|
||||
match (crop_px, crop_fraction) {
|
||||
(Some(_), Some(_)) => Err(bad(CMD, "give `crop_px` or `crop_fraction`, not both")),
|
||||
(None, None) => Ok(img),
|
||||
(Some(v), None) => {
|
||||
let nums = crop_nums(v, "crop_px")?;
|
||||
let (w, h) = img.dimensions();
|
||||
crop_to(img, nums[0].round() as usize, nums[1].round() as usize, nums[2].round() as usize, nums[3].round() as usize, w, h)
|
||||
}
|
||||
(None, Some(v)) => {
|
||||
let nums = crop_nums(v, "crop_fraction")?;
|
||||
if nums.iter().any(|f| *f > 1.0) {
|
||||
return Err(bad(CMD, "`crop_fraction` values must be 0..1"));
|
||||
}
|
||||
let (w, h) = img.dimensions();
|
||||
crop_to(
|
||||
img,
|
||||
(nums[0] * w as f64).round() as usize,
|
||||
(nums[1] * h as f64).round() as usize,
|
||||
(nums[2] * w as f64).round() as usize,
|
||||
(nums[3] * h as f64).round() as usize,
|
||||
w,
|
||||
h,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn crop_nums(v: &Value, name: &str) -> Result<Vec<f64>> {
|
||||
let nums: Vec<f64> = v
|
||||
.as_array()
|
||||
.filter(|a| a.len() == 4)
|
||||
.and_then(|a| a.iter().map(|v| v.as_f64()).collect())
|
||||
.ok_or_else(|| bad(CMD, format!("`{name}` must be [x, y, w, h]")))?;
|
||||
if nums.iter().any(|v| !v.is_finite() || *v < 0.0) {
|
||||
return Err(bad(CMD, format!("`{name}` values must be non-negative")));
|
||||
}
|
||||
Ok(nums)
|
||||
}
|
||||
|
||||
fn crop_to(img: image::RgbaImage, x: usize, y: usize, cw: usize, ch: usize, w: u32, h: u32) -> Result<image::RgbaImage> {
|
||||
if x >= w as usize || y >= h as usize {
|
||||
return Err(bad(CMD, "the crop starts outside the picture"));
|
||||
}
|
||||
let cw = cw.max(1).min(w as usize - x);
|
||||
let ch = ch.max(1).min(h as usize - y);
|
||||
Ok(image::imageops::crop_imm(&img, x as u32, y as u32, cw as u32, ch as u32).to_image())
|
||||
}
|
||||
|
||||
/// Find the part. Alpha decides only when the backdrop really is transparent — most border
|
||||
/// pixels see-through — so one stray translucent pixel cannot switch the read; then any ink
|
||||
/// is part, so a part drawn at a faint alpha survives. Otherwise grow the background in from
|
||||
/// the border through small colour steps only — seeded from the border pixels close to the
|
||||
/// border's main colour, so a part touching the border is not eaten by its own edge — keep
|
||||
/// what the background did not reach, and keep the largest 4-connected piece. On a retry the
|
||||
/// step is stricter, so a white part on a (245, 245, 245) backdrop (a step of 10) still
|
||||
/// separates; on a second retry the backdrop is taken globally — within BG_NEAR of the
|
||||
/// border's main colour — because a light JPEG blurs the step at the part's outline below
|
||||
/// any local limit. Says which happened when no part is found.
|
||||
fn find_part(img: &image::RgbaImage) -> std::result::Result<Part, String> {
|
||||
let (w, h) = (img.width() as usize, img.height() as usize);
|
||||
if w < 2 || h < 2 {
|
||||
return Err("the picture is smaller than 2 x 2 pixels".to_string());
|
||||
}
|
||||
let px = |x: usize, y: usize| -> [u16; 3] {
|
||||
let p = img.get_pixel(x as u32, y as u32).0;
|
||||
[p[0] as u16, p[1] as u16, p[2] as u16]
|
||||
};
|
||||
let step = |a: [u16; 3], b: [u16; 3]| (a[0].abs_diff(b[0])).max(a[1].abs_diff(b[1])).max(a[2].abs_diff(b[2]));
|
||||
|
||||
// Alpha decides only when the border is mostly see-through: a single backdrop pixel at
|
||||
// alpha 254 in an otherwise opaque picture must not turn the whole read into alpha.
|
||||
let mut border = 0usize;
|
||||
let mut see_through = 0usize;
|
||||
for x in 0..w {
|
||||
for y in [0, h - 1] {
|
||||
border += 1;
|
||||
if img.get_pixel(x as u32, y as u32)[3] < ALPHA_BG {
|
||||
see_through += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
for y in 0..h {
|
||||
for x in [0, w - 1] {
|
||||
border += 1;
|
||||
if img.get_pixel(x as u32, y as u32)[3] < ALPHA_BG {
|
||||
see_through += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
let uses_alpha = see_through * 2 > border;
|
||||
|
||||
let (part, too_small): (Vec<bool>, String) = if uses_alpha {
|
||||
let mask: Vec<bool> = img.pixels().map(|p| p[3] > 0).collect();
|
||||
(mask, "the picture is transparent: no pixel holds any ink".to_string())
|
||||
} else {
|
||||
let bg = flood_background(&px, w, h, BG_STEP, &step);
|
||||
let mask: Vec<bool> = (0..w * h).map(|i| !bg[i]).collect();
|
||||
if largest_piece(&mask, w, h).len() >= 16 {
|
||||
(mask, "the picture holds no piece big enough to be a part".to_string())
|
||||
} else {
|
||||
let bg = flood_background(&px, w, h, BG_STEP_STRICT, &step);
|
||||
let mask: Vec<bool> = (0..w * h).map(|i| !bg[i]).collect();
|
||||
if largest_piece(&mask, w, h).len() >= 16 {
|
||||
(mask, "the picture holds no piece big enough to be a part".to_string())
|
||||
} else {
|
||||
// The local step fails on a light JPEG: the codec blurs the 10-level step
|
||||
// at the part's outline into steps of a few levels each, under any limit.
|
||||
// Take the backdrop globally instead: near the border's main colour.
|
||||
let main = border_main_colour(&px, w, h);
|
||||
let mask: Vec<bool> = (0..w * h).map(|i| step(main, px(i % w, i / w)) > BG_NEAR).collect();
|
||||
(mask, "the background grew from the border over the whole picture, even with a stricter colour step: the part's colour is too close to the backdrop's".to_string())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let best = largest_piece(&part, w, h);
|
||||
if best.len() < 16 {
|
||||
return Err(too_small);
|
||||
}
|
||||
// Tight frame, then downscale to the comparison grid at need.
|
||||
let (x0, y0, x1, y1) = frame(&best, w);
|
||||
let (pw, ph) = (x1 - x0 + 1, y1 - y0 + 1);
|
||||
let mut mask = vec![vec![false; pw]; ph];
|
||||
for &i in &best {
|
||||
let (x, y) = (i % w, i / w);
|
||||
mask[y - y0][x - x0] = true;
|
||||
}
|
||||
let (dw, dh) = fit_grid(pw, ph);
|
||||
let mut mask = if (dw, dh) == (pw, ph) { mask } else { resample(&mask, pw, ph, dw, dh) };
|
||||
fill_holes(&mut mask);
|
||||
Ok(Part { width: dw, height: dh, mask })
|
||||
}
|
||||
|
||||
/// The crop box is the part itself (`part_fills_crop`): no background search. This is how
|
||||
/// one view cropped tight off a board says so — a part touching three sides leaves the
|
||||
/// border's main colour the part's own, and the flood would keep the backdrop strip instead.
|
||||
fn part_from_box(img: &image::RgbaImage) -> std::result::Result<Part, String> {
|
||||
let (w, h) = (img.width() as usize, img.height() as usize);
|
||||
if w < 2 || h < 2 {
|
||||
return Err("the crop is smaller than 2 x 2 pixels".to_string());
|
||||
}
|
||||
let (dw, dh) = fit_grid(w, h);
|
||||
Ok(Part { width: dw, height: dh, mask: vec![vec![true; dw]; dh] })
|
||||
}
|
||||
|
||||
/// The border's main colour: the border colour it has most of. A part crossing the border
|
||||
/// is in the border too, but the backdrop is what most of the border shows.
|
||||
fn border_main_colour(px: &impl Fn(usize, usize) -> [u16; 3], w: usize, h: usize) -> [u16; 3] {
|
||||
let mut counts: HashMap<[u16; 3], usize> = HashMap::new();
|
||||
for x in 0..w {
|
||||
for y in [0, h - 1] {
|
||||
*counts.entry(px(x, y)).or_default() += 1;
|
||||
}
|
||||
}
|
||||
for y in 0..h {
|
||||
for x in [0, w - 1] {
|
||||
*counts.entry(px(x, y)).or_default() += 1;
|
||||
}
|
||||
}
|
||||
counts.into_iter().max_by_key(|(_, n)| *n).map(|(c, _)| c).unwrap_or([0; 3])
|
||||
}
|
||||
|
||||
/// The background mask: grown from every border pixel whose colour is close to the border's
|
||||
/// main colour, crossing only steps of at most `limit` per channel.
|
||||
fn flood_background(px: &impl Fn(usize, usize) -> [u16; 3], w: usize, h: usize, limit: u16, step: &impl Fn([u16; 3], [u16; 3]) -> u16) -> Vec<bool> {
|
||||
let main = border_main_colour(px, w, h);
|
||||
|
||||
let mut bg = vec![false; w * h];
|
||||
let mut queue: VecDeque<(usize, usize)> = VecDeque::new();
|
||||
let mut seed = |x: usize, y: usize| {
|
||||
if step(main, px(x, y)) <= limit && !bg[y * w + x] {
|
||||
bg[y * w + x] = true;
|
||||
queue.push_back((x, y));
|
||||
}
|
||||
};
|
||||
for x in 0..w {
|
||||
seed(x, 0);
|
||||
seed(x, h - 1);
|
||||
}
|
||||
for y in 0..h {
|
||||
seed(0, y);
|
||||
seed(w - 1, y);
|
||||
}
|
||||
while let Some((x, y)) = queue.pop_front() {
|
||||
let here = px(x, y);
|
||||
for (nx, ny) in neighbours(x, y, w, h) {
|
||||
if !bg[ny * w + nx] && step(here, px(nx, ny)) <= limit {
|
||||
bg[ny * w + nx] = true;
|
||||
queue.push_back((nx, ny));
|
||||
}
|
||||
}
|
||||
}
|
||||
bg
|
||||
}
|
||||
|
||||
/// The largest 4-connected piece of a mask, as indices into `w`-wide rows.
|
||||
fn largest_piece(mask: &[bool], w: usize, h: usize) -> Vec<usize> {
|
||||
let mut best: Vec<usize> = Vec::new();
|
||||
let mut seen = vec![false; w * h];
|
||||
for start in 0..w * h {
|
||||
if !mask[start] || seen[start] {
|
||||
continue;
|
||||
}
|
||||
let mut piece: Vec<usize> = Vec::new();
|
||||
let mut queue: VecDeque<usize> = VecDeque::new();
|
||||
seen[start] = true;
|
||||
queue.push_back(start);
|
||||
while let Some(i) = queue.pop_front() {
|
||||
piece.push(i);
|
||||
let (x, y) = (i % w, i / w);
|
||||
for (nx, ny) in neighbours(x, y, w, h) {
|
||||
let j = ny * w + nx;
|
||||
if mask[j] && !seen[j] {
|
||||
seen[j] = true;
|
||||
queue.push_back(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
if piece.len() > best.len() {
|
||||
best = piece;
|
||||
}
|
||||
}
|
||||
best
|
||||
}
|
||||
|
||||
/// Fill the holes: pixels the outside never reaches become part — the rule the picture's
|
||||
/// border flood gives the picture side for free, given to the model's mask as well.
|
||||
fn fill_holes(mask: &mut [Vec<bool>]) {
|
||||
let (h, w) = (mask.len(), mask.first().map_or(0, Vec::len));
|
||||
if w < 2 || h < 2 {
|
||||
return;
|
||||
}
|
||||
let mut outside = vec![false; w * h];
|
||||
let mut queue: VecDeque<usize> = VecDeque::new();
|
||||
for x in 0..w {
|
||||
for y in [0, h - 1] {
|
||||
if !mask[y][x] && !outside[y * w + x] {
|
||||
outside[y * w + x] = true;
|
||||
queue.push_back(y * w + x);
|
||||
}
|
||||
}
|
||||
}
|
||||
for y in 0..h {
|
||||
for x in [0, w - 1] {
|
||||
if !mask[y][x] && !outside[y * w + x] {
|
||||
outside[y * w + x] = true;
|
||||
queue.push_back(y * w + x);
|
||||
}
|
||||
}
|
||||
}
|
||||
while let Some(i) = queue.pop_front() {
|
||||
let (x, y) = (i % w, i / w);
|
||||
for (nx, ny) in neighbours(x, y, w, h) {
|
||||
let j = ny * w + nx;
|
||||
if !mask[ny][nx] && !outside[j] {
|
||||
outside[j] = true;
|
||||
queue.push_back(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
if !mask[y][x] && !outside[y * w + x] {
|
||||
mask[y][x] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn neighbours(x: usize, y: usize, w: usize, h: usize) -> impl Iterator<Item = (usize, usize)> {
|
||||
[(x.wrapping_sub(1), y), (x + 1, y), (x, y.wrapping_sub(1)), (x, y + 1)].into_iter().filter(move |(nx, ny)| *nx < w && *ny < h)
|
||||
}
|
||||
|
||||
fn frame(pixels: &[usize], w: usize) -> (usize, usize, usize, usize) {
|
||||
let mut x0 = usize::MAX;
|
||||
let mut y0 = usize::MAX;
|
||||
let mut x1 = 0;
|
||||
let mut y1 = 0;
|
||||
for &i in pixels {
|
||||
let (x, y) = (i % w, i / w);
|
||||
x0 = x0.min(x);
|
||||
y0 = y0.min(y);
|
||||
x1 = x1.max(x);
|
||||
y1 = y1.max(y);
|
||||
}
|
||||
(x0, y0, x1, y1)
|
||||
}
|
||||
|
||||
fn fit_grid(w: usize, h: usize) -> (usize, usize) {
|
||||
let scale = (MAX_GRID as f64 / w.max(h) as f64).min(1.0);
|
||||
(((w as f64 * scale).round() as usize).max(2), ((h as f64 * scale).round() as usize).max(2))
|
||||
}
|
||||
|
||||
/// Nearest-neighbour resample of a mask to (w1, h1).
|
||||
fn resample(mask: &[Vec<bool>], w0: usize, h0: usize, w1: usize, h1: usize) -> Vec<Vec<bool>> {
|
||||
(0..h1)
|
||||
.map(|y| {
|
||||
let row = &mask[((y * h0) / h1.max(1)).min(h0 - 1)];
|
||||
(0..w1).map(|x| row[((x * w0) / w1.max(1)).min(w0 - 1)]).collect()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// The model outline's size in picture pixels and the mm per pixel that follows. One
|
||||
/// uniform scale, taken from the dimension the two share — the axes are never stretched
|
||||
/// apart, which would hide the very mismatch the tool is for. `both` claims both dimensions
|
||||
/// and scales by width; the result's `aspect_mismatch` says how far the shapes are apart.
|
||||
/// The fitted size takes the same MAX_GRID cap as the picture's: a narrow model fitted by
|
||||
/// width against a wide picture would otherwise ask for hundreds of megabytes of mask.
|
||||
fn fit_model(model: &Silhouette, pw: usize, ph: usize, fit: Fit) -> (usize, usize, f64, f64) {
|
||||
let s = match fit {
|
||||
Fit::Width | Fit::Both => pw as f64 / model.width as f64,
|
||||
Fit::Height => ph as f64 / model.height as f64,
|
||||
};
|
||||
let mw = ((model.width as f64 * s).round() as usize).max(2);
|
||||
let mh = ((model.height as f64 * s).round() as usize).max(2);
|
||||
let (mw, mh) = fit_grid(mw, mh);
|
||||
(mw, mh, model.width as f64 * model.mm_per_px / mw as f64, model.height as f64 * model.mm_per_px / mh as f64)
|
||||
}
|
||||
|
||||
struct Best {
|
||||
overlap: f64,
|
||||
dx: i64,
|
||||
dy: i64,
|
||||
}
|
||||
|
||||
/// Slide the model mask over the picture mask to the best intersection-over-union. The
|
||||
/// coarse pass covers the whole range of placements where the two frames can share a
|
||||
/// pixel — a tenth of the picture around the centre is not enough when the heights differ —
|
||||
/// and the fine pass walks every offset around its winner.
|
||||
fn best_overlap(model: &[Vec<bool>], picture: &Part) -> Best {
|
||||
let pic = spans_of(&picture.mask);
|
||||
let mdl = spans_of(model);
|
||||
let model_area: i64 = mdl.iter().flatten().map(|[a, b]| (b - a) as i64).sum();
|
||||
let pic_area: i64 = pic.iter().flatten().map(|[a, b]| (b - a) as i64).sum();
|
||||
|
||||
let (mw, mh) = (model[0].len() as i64, model.len() as i64);
|
||||
let (lo_x, hi_x) = (1 - mw, picture.width as i64);
|
||||
let (lo_y, hi_y) = (1 - mh, picture.height as i64);
|
||||
// About 64 coarse samples per axis, never finer than 4 px, never coarser than 32.
|
||||
let step_x = ((hi_x - lo_x) / 64).clamp(4, 32);
|
||||
let step_y = ((hi_y - lo_y) / 64).clamp(4, 32);
|
||||
|
||||
// The frames' aligned corners: both masks are tightly framed, so the best placement of a
|
||||
// matching picture is at or near here.
|
||||
let (mut bx, mut by) = (0i64, 0i64);
|
||||
for round in 0..2 {
|
||||
// Coarse: the whole range at the step; fine: every offset within one coarse step
|
||||
// of the coarse winner.
|
||||
let (sx, sy, x0, x1, y0, y1) = if round == 0 {
|
||||
(step_x, step_y, lo_x, hi_x, lo_y, hi_y)
|
||||
} else {
|
||||
(1, 1, (bx - step_x).max(lo_x), (bx + step_x).min(hi_x), (by - step_y).max(lo_y), (by + step_y).min(hi_y))
|
||||
};
|
||||
let (mut best_score, mut best_x, mut best_y) = (i64::MIN, bx, by);
|
||||
let mut dy = y0;
|
||||
while dy <= y1 {
|
||||
let mut dx = x0;
|
||||
while dx <= x1 {
|
||||
let hits = intersection(&mdl, &pic, dx, dy);
|
||||
let union = model_area + pic_area - hits;
|
||||
let score = if union <= 0 { i64::MIN } else { hits * 1_000_000 / union };
|
||||
if score > best_score {
|
||||
best_score = score;
|
||||
best_x = dx;
|
||||
best_y = dy;
|
||||
}
|
||||
dx += sx;
|
||||
}
|
||||
dy += sy;
|
||||
}
|
||||
bx = best_x;
|
||||
by = best_y;
|
||||
}
|
||||
|
||||
let hits = intersection(&mdl, &pic, bx, by);
|
||||
let union = model_area + pic_area - hits;
|
||||
Best { overlap: if union <= 0 { 0.0 } else { hits as f64 / union as f64 }, dx: bx, dy: by }
|
||||
}
|
||||
|
||||
fn spans_of(mask: &[Vec<bool>]) -> Vec<Vec<[usize; 2]>> {
|
||||
mask.iter().map(|row| silhouette::spans(row)).collect()
|
||||
}
|
||||
|
||||
/// Pixels the model shares with the picture at model offset (dx, dy), from run-length rows.
|
||||
fn intersection(mdl: &[Vec<[usize; 2]>], pic: &[Vec<[usize; 2]>], dx: i64, dy: i64) -> i64 {
|
||||
let mut hits = 0i64;
|
||||
for (r, spans) in mdl.iter().enumerate() {
|
||||
if spans.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let Some(y) = (dy + r as i64).try_into().ok().and_then(|y: usize| pic.get(y)) else { continue };
|
||||
if y.is_empty() {
|
||||
continue;
|
||||
}
|
||||
for &[a, b] in spans {
|
||||
let (mut lo, hi) = ((a as i64 + dx).max(0), b as i64 + dx);
|
||||
if hi <= lo {
|
||||
continue;
|
||||
}
|
||||
for &[pa, pb] in y {
|
||||
let (s, e) = (lo.max(pa as i64), hi.min(pb as i64));
|
||||
if e > s {
|
||||
hits += e - s;
|
||||
}
|
||||
lo = lo.max(pb as i64);
|
||||
if lo >= hi || pb as i64 >= hi {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
hits
|
||||
}
|
||||
|
||||
/// The model's edge against the picture's, in mm, at nine stations along one side; positive
|
||||
/// where the model reaches past the picture, null where a station misses one of the two.
|
||||
fn edge_offsets(model: &Placed, picture: &Part, side: Edge, mmpp: f64) -> Vec<Value> {
|
||||
(1..=STATIONS)
|
||||
.map(|i| {
|
||||
let station = match side {
|
||||
Edge::Top | Edge::Bottom => (picture.width as u64 * i as u64 / (STATIONS as u64 + 1)) as usize,
|
||||
Edge::Left | Edge::Right => (picture.height as u64 * i as u64 / (STATIONS as u64 + 1)) as usize,
|
||||
};
|
||||
let offset = match side {
|
||||
Edge::Top | Edge::Bottom => {
|
||||
let pic = column_edge(&picture.mask, station, matches!(side, Edge::Top));
|
||||
let mdl = (station as i64 - model.dx)
|
||||
.try_into()
|
||||
.ok()
|
||||
.and_then(|mx: usize| column_edge(model.mask, mx, matches!(side, Edge::Top)))
|
||||
.map(|e| e as i64 + model.dy);
|
||||
match (mdl, pic) {
|
||||
(Some(m), Some(p)) => Some(match side {
|
||||
Edge::Top => (p as f64 - m as f64) * mmpp,
|
||||
_ => (m as f64 - p as f64) * mmpp,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
Edge::Left | Edge::Right => {
|
||||
let pic = row_edge(&picture.mask, station, matches!(side, Edge::Left));
|
||||
let mdl = (station as i64 - model.dy)
|
||||
.try_into()
|
||||
.ok()
|
||||
.and_then(|my: usize| row_edge(model.mask, my, matches!(side, Edge::Left)))
|
||||
.map(|e| e as i64 + model.dx);
|
||||
match (mdl, pic) {
|
||||
(Some(m), Some(p)) => Some(match side {
|
||||
Edge::Left => (p as f64 - m as f64) * mmpp,
|
||||
_ => (m as f64 - p as f64) * mmpp,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
};
|
||||
offset.map(|mm| json!(round3(mm))).unwrap_or(Value::Null)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// The first (or last, when `first` is false) row holding column `x`; None if none does.
|
||||
fn column_edge(mask: &[Vec<bool>], x: usize, first: bool) -> Option<usize> {
|
||||
let mut found = None;
|
||||
for (y, row) in mask.iter().enumerate() {
|
||||
if row.get(x).copied().unwrap_or(false) {
|
||||
if first {
|
||||
return Some(y);
|
||||
}
|
||||
found = Some(y);
|
||||
}
|
||||
}
|
||||
found
|
||||
}
|
||||
|
||||
/// The first (or last) column set in row `y`; None if the row is empty or missing.
|
||||
fn row_edge(mask: &[Vec<bool>], y: usize, first: bool) -> Option<usize> {
|
||||
let row = mask.get(y)?;
|
||||
if first { row.iter().position(|&on| on) } else { row.iter().rposition(|&on| on) }
|
||||
}
|
||||
|
||||
/// The overlay PNG: the picture in blue, the model in red, on a canvas that holds both.
|
||||
fn overlay_png(picture: &Part, model: &Placed) -> Vec<u8> {
|
||||
// A margin so a model that hangs off the picture still fits the canvas.
|
||||
let m = 8i64;
|
||||
let (x0, y0) = (0.min(model.dx) - m, 0.min(model.dy) - m);
|
||||
let (x1, y1) = (
|
||||
(picture.width as i64).max(model.dx + model.mask.first().map_or(0, |r| r.len()) as i64) + m,
|
||||
(picture.height as i64).max(model.dy + model.mask.len() as i64) + m,
|
||||
);
|
||||
let (w, h) = ((x1 - x0).max(1) as u32, (y1 - y0).max(1) as u32);
|
||||
let mut img = image::RgbaImage::from_pixel(w, h, image::Rgba([250, 250, 250, 255]));
|
||||
for (y, row) in picture.mask.iter().enumerate() {
|
||||
for (x, &on) in row.iter().enumerate() {
|
||||
if on {
|
||||
img.put_pixel((x as i64 - x0) as u32, (y as i64 - y0) as u32, image::Rgba([70, 110, 230, 255]));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (r, row) in model.mask.iter().enumerate() {
|
||||
for (c, &on) in row.iter().enumerate() {
|
||||
if !on {
|
||||
continue;
|
||||
}
|
||||
let (x, y) = (model.dx + c as i64 - x0, model.dy + r as i64 - y0);
|
||||
if (0..w as i64).contains(&x) && (0..h as i64).contains(&y) {
|
||||
img.put_pixel(x as u32, y as u32, image::Rgba([235, 60, 60, 255]));
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut out = Cursor::new(Vec::new());
|
||||
// Encoding a mask-sized PNG cannot realistically fail; if it ever does, no overlay.
|
||||
let enc = image::codecs::png::PngEncoder::new(&mut out);
|
||||
use image::ImageEncoder as _;
|
||||
if enc.write_image(img.as_raw(), img.width(), img.height(), image::ExtendedColorType::Rgba8).is_err() {
|
||||
return Vec::new();
|
||||
}
|
||||
out.into_inner()
|
||||
}
|
||||
|
||||
fn round4(v: f64) -> f64 {
|
||||
if v.abs() < 1e12 { (v * 10_000.0).round() / 10_000.0 } else { v }
|
||||
}
|
||||
|
||||
fn round3(v: f64) -> f64 {
|
||||
if v.abs() < 1e12 { (v * 1_000.0).round() / 1_000.0 } else { v }
|
||||
}
|
||||
@@ -0,0 +1,490 @@
|
||||
//! Tests for `inspect.compare_picture` (#1486): synthetic reference pictures drawn in code,
|
||||
//! written to the temp dir and read back through the vfs like any other reference.
|
||||
|
||||
use std::io::Cursor;
|
||||
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::super::Session;
|
||||
use image::ImageEncoder as _;
|
||||
|
||||
fn run(s: &mut Session, cmd: &str, params: Value) -> Value {
|
||||
s.execute(cmd, ¶ms).unwrap_or_else(|e| panic!("{cmd}: {e}"))
|
||||
}
|
||||
|
||||
fn box_body(s: &mut Session, name: &str, x1: f64, y1: f64, z: f64) {
|
||||
let sketch = format!("{name}Sketch");
|
||||
run(s, "sketch.create", json!({"plane": "XY", "name": sketch}));
|
||||
run(s, "sketch.rectangle.two_point", json!({"p0": [0.0, 0.0], "p1": [x1, y1]}));
|
||||
run(s, "sketch.finish", json!({}));
|
||||
run(s, "solid.extrude", json!({"sketch": sketch, "distance": z, "operation": "new_body", "body_name": name}));
|
||||
}
|
||||
|
||||
/// A PNG whose pixels come from `paint`.
|
||||
fn png(w: usize, h: usize, paint: impl Fn(usize, usize) -> [u8; 3]) -> Vec<u8> {
|
||||
png_rgba(w, h, |x, y| {
|
||||
let [r, g, b] = paint(x, y);
|
||||
[r, g, b, 255]
|
||||
})
|
||||
}
|
||||
|
||||
/// A PNG with alpha, whose pixels come from `paint`.
|
||||
fn png_rgba(w: usize, h: usize, paint: impl Fn(usize, usize) -> [u8; 4]) -> Vec<u8> {
|
||||
let mut img = image::RgbaImage::new(w as u32, h as u32);
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
img.put_pixel(x as u32, y as u32, image::Rgba(paint(x, y)));
|
||||
}
|
||||
}
|
||||
let mut out = Cursor::new(Vec::new());
|
||||
image::codecs::png::PngEncoder::new(&mut out).write_image(img.as_raw(), w as u32, h as u32, image::ExtendedColorType::Rgba8).unwrap();
|
||||
out.into_inner()
|
||||
}
|
||||
|
||||
fn jpeg(w: usize, h: usize, paint: impl Fn(usize, usize) -> [u8; 3]) -> Vec<u8> {
|
||||
jpeg_q(w, h, 90, paint)
|
||||
}
|
||||
|
||||
fn jpeg_q(w: usize, h: usize, q: u8, paint: impl Fn(usize, usize) -> [u8; 3]) -> Vec<u8> {
|
||||
let mut img = image::RgbaImage::new(w as u32, h as u32);
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
let [r, g, b] = paint(x, y);
|
||||
img.put_pixel(x as u32, y as u32, image::Rgba([r, g, b, 255]));
|
||||
}
|
||||
}
|
||||
let rgb = image::DynamicImage::ImageRgba8(img).to_rgb8();
|
||||
let mut out = Cursor::new(Vec::new());
|
||||
image::codecs::jpeg::JpegEncoder::new_with_quality(&mut out, q)
|
||||
.write_image(rgb.as_raw(), w as u32, h as u32, image::ExtendedColorType::Rgb8)
|
||||
.unwrap();
|
||||
out.into_inner()
|
||||
}
|
||||
|
||||
/// Write a reference into the temp dir and return its path.
|
||||
fn reference(name: &str, bytes: &[u8]) -> String {
|
||||
let path = std::env::temp_dir().join(format!("solidcraft-compare-picture-{}-{name}", std::process::id()));
|
||||
std::fs::write(&path, bytes).unwrap();
|
||||
path.to_string_lossy().into_owned()
|
||||
}
|
||||
|
||||
/// A white rectangle on a grey backdrop: `x0..x1, y0..y1` in pixels.
|
||||
fn rect_picture(w: usize, h: usize, x0: usize, y0: usize, x1: usize, y1: usize) -> Vec<u8> {
|
||||
png(w, h, |x, y| if (x0..x1).contains(&x) && (y0..y1).contains(&y) { [255, 255, 255] } else { [200, 200, 200] })
|
||||
}
|
||||
|
||||
fn edges(v: &Value) -> Vec<Vec<Option<f64>>> {
|
||||
["top", "bottom", "left", "right"]
|
||||
.iter()
|
||||
.map(|side| v["edges"][side].as_array().unwrap().iter().map(|e| e.as_f64()).collect::<Vec<Option<f64>>>())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn near(v: Option<f64>, want: f64, tol: f64) -> bool {
|
||||
v.is_some_and(|e| (e - want).abs() <= tol)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_matching_picture_scores_high_with_flat_edges() {
|
||||
let mut s = Session::default();
|
||||
box_body(&mut s, "Box", 20.0, 10.0, 30.0); // front view: 20 wide, 30 tall
|
||||
let path = reference("match.png", &rect_picture(200, 280, 20, 20, 180, 260));
|
||||
let out = run(&mut s, "inspect.compare_picture", json!({"reference": path, "view": "front"}));
|
||||
assert!(out["overlap"].as_f64().unwrap() > 0.95, "overlap {}", out["overlap"]);
|
||||
for side in edges(&out) {
|
||||
for e in side {
|
||||
assert!(near(e, 0.0, 0.6), "edge {e:?}");
|
||||
}
|
||||
}
|
||||
assert!(!out["overlay_png"].as_str().unwrap().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_step_in_the_bottom_edge_shows_up_in_mm() {
|
||||
let mut s = Session::default();
|
||||
box_body(&mut s, "Box", 20.0, 10.0, 30.0);
|
||||
// The aligned picture: a plain part 160 x 240 px — the model's own shape at 8 px per mm.
|
||||
let aligned_path = reference("aligned.png", &rect_picture(200, 280, 20, 20, 180, 260));
|
||||
let aligned = run(&mut s, "inspect.compare_picture", json!({"reference": aligned_path, "view": "front"}));
|
||||
// The stepped picture: over its right half the part's bottom edge sits 35 px higher.
|
||||
// The width fit gives 8 px per mm, so 35 px is 4.375 mm of the model reaching past
|
||||
// the picture.
|
||||
let stepped_path = reference(
|
||||
"stepped.png",
|
||||
&png(200, 280, |x, y| {
|
||||
let bottom = if x >= 100 { 225 } else { 260 };
|
||||
if (20..180).contains(&x) && (20..bottom).contains(&y) { [255, 255, 255] } else { [200, 200, 200] }
|
||||
}),
|
||||
);
|
||||
let stepped = run(&mut s, "inspect.compare_picture", json!({"reference": stepped_path, "view": "front"}));
|
||||
let flat = edges(&aligned)[1].clone();
|
||||
let step = edges(&stepped)[1].clone();
|
||||
for e in flat.iter() {
|
||||
assert!(near(*e, 0.0, 0.6), "flat bottom {e:?}");
|
||||
}
|
||||
// Stations sit at local x = 16..144 px; the notch starts at local x = 80 (global 100),
|
||||
// so stations 5..9 are over it.
|
||||
for e in step.iter().take(4) {
|
||||
assert!(near(*e, 0.0, 0.6), "step bottom left {e:?}");
|
||||
}
|
||||
for e in step.iter().skip(4) {
|
||||
assert!(near(*e, 4.375, 0.6), "step bottom right {e:?}");
|
||||
}
|
||||
assert!(stepped["overlap"].as_f64().unwrap() < aligned["overlap"].as_f64().unwrap());
|
||||
}
|
||||
|
||||
/// #1486's example, pinned: `examples/bracket.json` as it is (upright 30 mm) against the
|
||||
/// same bracket at 40 mm, both from the front, scaled by the 40 mm arm they share.
|
||||
/// The issue's prototype numbers: overlap 0.865, bottom −9.9 mm at the upright (6 % across),
|
||||
/// 0.0 mm everywhere else.
|
||||
#[test]
|
||||
fn the_issue_bracket_pins_overlap_and_the_short_upright() {
|
||||
let mut s = Session::default();
|
||||
// The bracket profile of examples/bracket.json: a 40 x 6 arm with a 6-wide upright to
|
||||
// 30 mm, on XZ so the front view shows the upright hanging below the arm.
|
||||
run(&mut s, "sketch.create", json!({"plane": "XZ", "name": "Profile"}));
|
||||
run(&mut s, "sketch.line", json!({"points": [[0, 0], [40, 0], [40, 6], [6, 6], [6, 30], [0, 30]], "closed": true}));
|
||||
run(&mut s, "sketch.finish", json!({}));
|
||||
run(&mut s, "solid.extrude", json!({"distance": 20.0, "direction": "negative", "body_name": "Bracket"}));
|
||||
// The same bracket with the upright at 40 mm, at 20 px per mm: an 800 x 800 part, the
|
||||
// 800 x 120 arm on top and the 120-wide upright down to 800, on a grey margin.
|
||||
let path = reference(
|
||||
"bracket.png",
|
||||
&png(840, 840, |x, y| {
|
||||
let part = ((20..820).contains(&x) && (20..140).contains(&y)) || ((20..140).contains(&x) && (20..820).contains(&y));
|
||||
if part { [255, 255, 255] } else { [200, 200, 200] }
|
||||
}),
|
||||
);
|
||||
let out = run(&mut s, "inspect.compare_picture", json!({"reference": path, "view": "front", "rows": 480, "overlay": false}));
|
||||
let overlap = out["overlap"].as_f64().unwrap();
|
||||
assert!((overlap - 0.865).abs() <= 0.01, "overlap {overlap}");
|
||||
let e = edges(&out);
|
||||
// The upright is the first station (x = 80 of 800): the model is 10 mm short there.
|
||||
assert!(near(e[1][0], -10.0, 0.2), "bottom at the upright {:?}", e[1][0]);
|
||||
for side in 1..9 {
|
||||
assert!(near(e[1][side], 0.0, 0.1), "bottom elsewhere {:?}", e[1]);
|
||||
}
|
||||
for i in 0..9 {
|
||||
assert!(near(e[0][i], 0.0, 0.1), "top {:?}", e[0]);
|
||||
// Below the model's 30 mm upright the model has no row to answer with: those
|
||||
// side stations are null, as documented.
|
||||
for side in [e[2].clone(), e[3].clone()] {
|
||||
match side[i] {
|
||||
None => {}
|
||||
Some(v) => assert!(v.abs() <= 0.1, "side {side:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_gradient_backdrop_still_separates_from_the_part() {
|
||||
let mut s = Session::default();
|
||||
box_body(&mut s, "Box", 20.0, 10.0, 30.0);
|
||||
// A backdrop that shifts one step per row: the border flood follows it; the step at the
|
||||
// part's outline (to white) stops it.
|
||||
let path = reference(
|
||||
"gradient.png",
|
||||
&png(200, 300, |x, y| if (20..180).contains(&x) && (20..280).contains(&y) { [255, 255, 255] } else { [200 + (y / 30) as u8, 200, 200] }),
|
||||
);
|
||||
let out = run(&mut s, "inspect.compare_picture", json!({"reference": path, "view": "front"}));
|
||||
assert!(out["overlap"].as_f64().unwrap() > 0.9, "overlap {}", out["overlap"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_jpeg_reference_works_too() {
|
||||
let mut s = Session::default();
|
||||
box_body(&mut s, "Box", 20.0, 10.0, 30.0);
|
||||
let path =
|
||||
reference("match.jpg", &jpeg(200, 300, |x, y| if (20..180).contains(&x) && (20..280).contains(&y) { [255, 255, 255] } else { [40, 40, 40] }));
|
||||
let out = run(&mut s, "inspect.compare_picture", json!({"reference": path, "view": "front"}));
|
||||
assert!(out["overlap"].as_f64().unwrap() > 0.9, "overlap {}", out["overlap"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crop_takes_one_part_off_the_board() {
|
||||
let mut s = Session::default();
|
||||
box_body(&mut s, "Box", 20.0, 10.0, 30.0);
|
||||
// Two parts side by side; the crop keeps the left one, a plain rectangle that matches
|
||||
// the model. The right part is bigger overall but L-shaped, so without the crop the
|
||||
// largest piece wins and the score drops.
|
||||
let path = reference(
|
||||
"two.png",
|
||||
&png(400, 300, |x, y| {
|
||||
let left = (20..180).contains(&x) && (20..260).contains(&y);
|
||||
let right = (210..390).contains(&x) && (5..295).contains(&y) && !((300..390).contains(&x) && (100..200).contains(&y));
|
||||
if left || right { [255, 255, 255] } else { [200, 200, 200] }
|
||||
}),
|
||||
);
|
||||
let out = run(&mut s, "inspect.compare_picture", json!({"reference": path.clone(), "view": "front", "crop_fraction": [0.0, 0.0, 0.5, 1.0]}));
|
||||
// The picture size is the found part's tight frame: the left rectangle is 160 x 240 px.
|
||||
assert_eq!(out["picture"]["width"], 160);
|
||||
assert!(out["overlap"].as_f64().unwrap() > 0.95, "overlap {}", out["overlap"]);
|
||||
let whole = run(&mut s, "inspect.compare_picture", json!({"reference": path, "view": "front"}));
|
||||
assert!(whole["overlap"].as_f64().unwrap() < out["overlap"].as_f64().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crop_px_has_explicit_units() {
|
||||
let mut s = Session::default();
|
||||
box_body(&mut s, "Box", 20.0, 10.0, 30.0);
|
||||
let path = reference("px.png", &rect_picture(200, 280, 20, 20, 180, 260));
|
||||
// Pixels mean pixels: [10, 10, 180, 260] keeps the part with a margin, and a crop of
|
||||
// one pixel is one pixel, not the whole picture.
|
||||
let out = run(&mut s, "inspect.compare_picture", json!({"reference": path.clone(), "view": "front", "crop_px": [10, 10, 180, 260]}));
|
||||
assert!(out["overlap"].as_f64().unwrap() > 0.95, "overlap {}", out["overlap"]);
|
||||
let dot = reference("dot.png", &rect_picture(200, 280, 100, 100, 101, 101));
|
||||
assert!(
|
||||
s.execute("inspect.compare_picture", &json!({"reference": dot, "crop_px": [0, 0, 1, 1]})).unwrap_err().to_string().contains("no part found")
|
||||
);
|
||||
// Giving both spellings is refused, and so is a fraction above 1.
|
||||
assert!(
|
||||
s.execute("inspect.compare_picture", &json!({"reference": path, "crop_px": [0, 0, 10, 10], "crop_fraction": [0.0, 0.0, 0.1, 0.1]}))
|
||||
.unwrap_err()
|
||||
.to_string()
|
||||
.contains("not both")
|
||||
);
|
||||
assert!(
|
||||
s.execute("inspect.compare_picture", &json!({"reference": path, "crop_fraction": [0.0, 0.0, 1.5, 1.0]}))
|
||||
.unwrap_err()
|
||||
.to_string()
|
||||
.contains("0..1")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fit_both_never_stretches_the_axes_apart() {
|
||||
let mut s = Session::default();
|
||||
box_body(&mut s, "Box", 20.0, 10.0, 30.0); // front view: 20 wide, 30 tall
|
||||
// A 20 x 20 mm square picture of a 20 x 30 mm model: `both` used to stretch the model
|
||||
// to the square and report a perfect 1.000 with every edge at 0.0 mm. One uniform
|
||||
// scale (from the width they share) leaves the 10 mm of overhang visible instead.
|
||||
let path = reference("square.png", &rect_picture(220, 220, 10, 10, 210, 210));
|
||||
let both = run(&mut s, "inspect.compare_picture", json!({"reference": path.clone(), "view": "front", "fit": "both", "overlay": false}));
|
||||
assert!((both["overlap"].as_f64().unwrap() - 200.0 / 300.0).abs() < 0.02, "overlap {}", both["overlap"]);
|
||||
assert!((both["aspect_mismatch"].as_f64().unwrap() - 1.5).abs() < 0.01, "aspect_mismatch {}", both["aspect_mismatch"]);
|
||||
// And `width` is the default: leaving `fit` out answers the same as naming it.
|
||||
let plain = run(&mut s, "inspect.compare_picture", json!({"reference": path, "view": "front", "overlay": false}));
|
||||
assert_eq!(plain["overlap"], both["overlap"]);
|
||||
assert_eq!(plain["edges"], both["edges"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fit_width_keeps_the_models_aspect() {
|
||||
let mut s = Session::default();
|
||||
box_body(&mut s, "Box", 20.0, 10.0, 30.0);
|
||||
// The picture part is 160 x 160 px: fit=width scales the model uniformly to 160 x 240,
|
||||
// so it hangs 80 px (10 mm) over the picture's height in total, wherever the slide puts
|
||||
// it. The overlap of a contained model is 160/240 wherever it sits.
|
||||
let path = reference("short.png", &rect_picture(200, 220, 20, 20, 180, 180));
|
||||
let out = run(&mut s, "inspect.compare_picture", json!({"reference": path, "view": "front", "fit": "width"}));
|
||||
assert!((out["overlap"].as_f64().unwrap() - 160.0 / 240.0).abs() < 0.05, "overlap {}", out["overlap"]);
|
||||
let bottom: Vec<Option<f64>> = out["edges"]["bottom"].as_array().unwrap().iter().map(|e| e.as_f64()).collect();
|
||||
let top: Vec<Option<f64>> = out["edges"]["top"].as_array().unwrap().iter().map(|e| e.as_f64()).collect();
|
||||
for i in 0..9 {
|
||||
// The overhang splits between top and bottom but always totals the extra 10 mm.
|
||||
assert!(bottom[i].unwrap() >= 0.0 && top[i].unwrap() >= 0.0, "bottom {:?} top {:?}", bottom[i], top[i]);
|
||||
assert!((bottom[i].unwrap() + top[i].unwrap() - 10.0).abs() < 0.6, "bottom {:?} top {:?}", bottom[i], top[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_narrow_model_fits_the_grid_not_the_memory() {
|
||||
let mut s = Session::default();
|
||||
// A 2 mm x 1000 mm sliver seen from the front: fitted by width against a 1024-wide
|
||||
// picture it used to become 1024 x 524 288 booleans. The fitted model now takes the
|
||||
// same MAX_GRID cap as the picture, so the comparison just runs.
|
||||
box_body(&mut s, "Sliver", 2.0, 1.0, 1000.0);
|
||||
let path = reference("sliver.png", &rect_picture(1030, 1010, 3, 5, 1027, 1005));
|
||||
let out = run(&mut s, "inspect.compare_picture", json!({"reference": path, "view": "front", "overlay": false}));
|
||||
// The shapes are nothing alike (the sliver is 1:500, the picture 1024:1000), and the
|
||||
// capped model grid keeps the overlap small — what matters is that it ran at all.
|
||||
assert!(out["overlap"].as_f64().unwrap() < 0.1, "overlap {}", out["overlap"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ordinary_pictures_keep_their_part() {
|
||||
let mut s = Session::default();
|
||||
box_body(&mut s, "Box", 20.0, 10.0, 30.0);
|
||||
// A part touching the picture's border: only border pixels close to the border's main
|
||||
// colour seed the background, so the part's own border pixels do not flood it.
|
||||
let border = reference("border.png", &rect_picture(200, 280, 0, 20, 160, 260));
|
||||
let out = run(&mut s, "inspect.compare_picture", json!({"reference": border, "view": "front"}));
|
||||
assert!(out["overlap"].as_f64().unwrap() > 0.95, "border overlap {}", out["overlap"]);
|
||||
// A white part on a (245, 245, 245) studio backdrop: the step of 10 is below BG_STEP,
|
||||
// but the stricter retry separates it.
|
||||
let studio = reference(
|
||||
"studio.png",
|
||||
&png(200, 280, |x, y| if (20..180).contains(&x) && (20..260).contains(&y) { [255, 255, 255] } else { [245, 245, 245] }),
|
||||
);
|
||||
let out = run(&mut s, "inspect.compare_picture", json!({"reference": studio, "view": "front"}));
|
||||
assert!(out["overlap"].as_f64().unwrap() > 0.95, "studio overlap {}", out["overlap"]);
|
||||
// A part on a transparent background: alpha below 128 is background.
|
||||
let alpha = reference(
|
||||
"alpha.png",
|
||||
&png_rgba(200, 280, |x, y| if (20..180).contains(&x) && (20..260).contains(&y) { [90, 90, 90, 255] } else { [0, 0, 0, 0] }),
|
||||
);
|
||||
let out = run(&mut s, "inspect.compare_picture", json!({"reference": alpha, "view": "front"}));
|
||||
assert!(out["overlap"].as_f64().unwrap() > 0.95, "alpha overlap {}", out["overlap"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_through_hole_counts_the_same_on_both_sides() {
|
||||
let mut s = Session::default();
|
||||
// A 200 x 200 mm plate with a 100 x 100 mm through-hole, against a picture of itself
|
||||
// at 1 px per mm, seen from the top (the hole goes through the plate's faces). The
|
||||
// picture's hole is filled by the border flood; the model's hole now gets the same
|
||||
// rule, so the plate scores ~1.0 instead of 0.75.
|
||||
run(&mut s, "sketch.create", json!({"plane": "XY", "name": "Plate"}));
|
||||
run(&mut s, "sketch.rectangle.two_point", json!({"p0": [0.0, 0.0], "p1": [200.0, 200.0]}));
|
||||
run(&mut s, "sketch.finish", json!({}));
|
||||
run(&mut s, "solid.extrude", json!({"sketch": "Plate", "distance": 10.0, "operation": "new_body", "body_name": "Plate"}));
|
||||
run(&mut s, "sketch.create", json!({"plane": "XY", "name": "Hole"}));
|
||||
run(&mut s, "sketch.rectangle.two_point", json!({"p0": [50.0, 50.0], "p1": [150.0, 150.0]}));
|
||||
run(&mut s, "sketch.finish", json!({}));
|
||||
run(&mut s, "solid.extrude", json!({"sketch": "Hole", "distance": 20.0, "operation": "cut"}));
|
||||
let path = reference(
|
||||
"plate.png",
|
||||
&png(220, 220, |x, y| {
|
||||
let plate = (10..210).contains(&x) && (10..210).contains(&y);
|
||||
let hole = (60..160).contains(&x) && (60..160).contains(&y);
|
||||
if plate && !hole { [255, 255, 255] } else { [200, 200, 200] }
|
||||
}),
|
||||
);
|
||||
let out = run(&mut s, "inspect.compare_picture", json!({"reference": path, "view": "top", "overlay": false}));
|
||||
assert!(out["overlap"].as_f64().unwrap() > 0.98, "overlap {}", out["overlap"]);
|
||||
for side in edges(&out) {
|
||||
for e in side {
|
||||
assert!(near(e, 0.0, 0.6), "edge {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hostile_and_empty_references_fail_cleanly() {
|
||||
let mut s = Session::default();
|
||||
box_body(&mut s, "Box", 20.0, 10.0, 30.0);
|
||||
assert!(s.execute("inspect.compare_picture", &json!({})).is_err());
|
||||
// Not an image at all.
|
||||
let text = reference("not.png", b"just text, not a picture");
|
||||
assert!(s.execute("inspect.compare_picture", &json!({"reference": text})).unwrap_err().to_string().contains("PNG or a JPEG"));
|
||||
// A uniform picture: the background eats everything, and says so.
|
||||
let flat = reference("flat.png", &png(64, 64, |_, _| [200, 200, 200]));
|
||||
assert!(s.execute("inspect.compare_picture", &json!({"reference": flat})).unwrap_err().to_string().contains("no part found"));
|
||||
// A fully transparent picture: alpha says there is nothing there.
|
||||
let clear = reference("clear.png", &png_rgba(64, 64, |_, _| [0, 0, 0, 0]));
|
||||
assert!(s.execute("inspect.compare_picture", &json!({"reference": clear})).unwrap_err().to_string().contains("transparent"));
|
||||
// A missing file.
|
||||
assert!(s.execute("inspect.compare_picture", &json!({"reference": "no-such-file.png"})).is_err());
|
||||
// A bad fit name.
|
||||
let path = reference("match2.png", &rect_picture(200, 280, 20, 20, 180, 260));
|
||||
assert!(s.execute("inspect.compare_picture", &json!({"reference": path, "fit": "diagonal"})).is_err());
|
||||
// overlay: false leaves the PNG out.
|
||||
let out = run(&mut s, "inspect.compare_picture", json!({"reference": path, "view": "front", "overlay": false}));
|
||||
assert!(out.get("overlay_png").is_none());
|
||||
}
|
||||
|
||||
/// Reviewer re-test case: a 60 x 60 opaque PNG with a white 40 x 40 square and a single
|
||||
/// backdrop pixel at alpha 254 used to be read as all part — 1.000 against the 40 x 40
|
||||
/// square at 0.067 mm/px instead of 0.1. Alpha now decides only when the border is mostly
|
||||
/// see-through, and a part drawn at alpha 100 on a transparent backdrop is still the part.
|
||||
#[test]
|
||||
fn one_translucent_pixel_does_not_switch_to_alpha() {
|
||||
let mut s = Session::default();
|
||||
box_body(&mut s, "Square", 4.0, 1.0, 4.0); // front view: 4 x 4 mm
|
||||
let path = reference(
|
||||
"speck.png",
|
||||
&png_rgba(60, 60, |x, y| {
|
||||
let alpha = if x == 5 && y == 55 { 254 } else { 255 };
|
||||
let c = if (10..50).contains(&x) && (10..50).contains(&y) { 255u8 } else { 128 };
|
||||
[c, c, c, alpha]
|
||||
}),
|
||||
);
|
||||
let out = run(&mut s, "inspect.compare_picture", json!({"reference": path, "view": "front", "overlay": false}));
|
||||
// The part is the 40 x 40 square: 4 mm over 40 px is 0.1 mm/px, not 4/60 = 0.067.
|
||||
let mmpp = out["mm_per_px"]["x"].as_f64().unwrap();
|
||||
assert!((mmpp - 0.1).abs() < 0.01, "mm_per_px {mmpp}");
|
||||
assert!(out["overlap"].as_f64().unwrap() > 0.9, "overlap {}", out["overlap"]);
|
||||
// A part drawn at alpha 100 on a transparent backdrop is the part, not the background.
|
||||
let faint = reference(
|
||||
"faint.png",
|
||||
&png_rgba(60, 60, |x, y| if (10..50).contains(&x) && (10..50).contains(&y) { [255, 255, 255, 100] } else { [0, 0, 0, 0] }),
|
||||
);
|
||||
let out = run(&mut s, "inspect.compare_picture", json!({"reference": faint, "view": "front", "overlay": false}));
|
||||
assert!(out["overlap"].as_f64().unwrap() > 0.9, "faint overlap {}", out["overlap"]);
|
||||
let mmpp = out["mm_per_px"]["x"].as_f64().unwrap();
|
||||
assert!((mmpp - 0.1).abs() < 0.01, "faint mm_per_px {mmpp}");
|
||||
}
|
||||
|
||||
/// Reviewer re-test case: one view cropped off a board — the part touches three sides and
|
||||
/// takes 270 of the 400 border samples, so the border's main colour is the part's own and
|
||||
/// the flood eats it: the 15 px backdrop strip becomes "the part" and an exact model scores
|
||||
/// 0.18. `part_fills_crop: true` says the crop box is the part: no flood, exact model ~1.0.
|
||||
#[test]
|
||||
fn a_tight_crop_can_declare_the_box_is_the_part() {
|
||||
let mut s = Session::default();
|
||||
box_body(&mut s, "Box", 17.0, 10.0, 20.0); // front view: 17 wide, 20 tall
|
||||
// The tight crop: the white part fills x 0..85 of a 100 x 100 box, touching three
|
||||
// sides — 270 of the border samples — with a 15 px backdrop strip down the right.
|
||||
let path = reference("tight.png", &png(100, 100, |x, _| if x < 85 { [255, 255, 255] } else { [200, 200, 200] }));
|
||||
let flood = run(&mut s, "inspect.compare_picture", json!({"reference": path.clone(), "view": "front", "overlay": false}));
|
||||
assert!(flood["overlap"].as_f64().unwrap() < 0.5, "the flood keeps the strip: overlap {}", flood["overlap"]);
|
||||
let out = run(
|
||||
&mut s,
|
||||
"inspect.compare_picture",
|
||||
json!({"reference": path, "view": "front", "crop_px": [0, 0, 85, 100], "part_fills_crop": true, "overlay": false}),
|
||||
);
|
||||
assert!(out["overlap"].as_f64().unwrap() > 0.95, "overlap {}", out["overlap"]);
|
||||
// 17 mm over the 85 px the part fills: 0.2 mm/px.
|
||||
assert!((out["mm_per_px"]["x"].as_f64().unwrap() - 0.2).abs() < 0.01, "mm_per_px {}", out["mm_per_px"]);
|
||||
// A crop of a 2 x 2 box is the part; a 1 px box is refused.
|
||||
let dot = reference("tight-dot.png", &png(4, 4, |_, _| [255, 255, 255]));
|
||||
assert!(
|
||||
s.execute("inspect.compare_picture", &json!({"reference": dot, "part_fills_crop": true, "crop_px": [0, 0, 1, 1]}))
|
||||
.unwrap_err()
|
||||
.to_string()
|
||||
.contains("2 x 2")
|
||||
);
|
||||
}
|
||||
|
||||
/// Reviewer re-test case: holes are filled on both sides, so a model missing a 100 x 100
|
||||
/// through-hole scores ~1.000 against a picture that shows the hole. That is the contract —
|
||||
/// outline only — and the command description must say so, so an agent does not read 1.000
|
||||
/// as "matches".
|
||||
#[test]
|
||||
fn the_compare_is_outline_only_holes_are_not_compared() {
|
||||
assert!(
|
||||
crate::cmd::compare_picture::COMMANDS[0].params.contains("holes are not compared"),
|
||||
"the command description must say the compare is outline only"
|
||||
);
|
||||
let mut s = Session::default();
|
||||
// A solid 200 x 200 mm plate against a picture of a plate with a 100 x 100 through-hole.
|
||||
box_body(&mut s, "Plate", 200.0, 10.0, 200.0);
|
||||
let path = reference(
|
||||
"hole-only.png",
|
||||
&png(220, 220, |x, y| {
|
||||
let plate = (10..210).contains(&x) && (10..210).contains(&y);
|
||||
let hole = (60..160).contains(&x) && (60..160).contains(&y);
|
||||
if plate && !hole { [255, 255, 255] } else { [200, 200, 200] }
|
||||
}),
|
||||
);
|
||||
let out = run(&mut s, "inspect.compare_picture", json!({"reference": path, "view": "front", "overlay": false}));
|
||||
// The outlines match: the hole is not compared, as the description says.
|
||||
assert!(out["overlap"].as_f64().unwrap() > 0.98, "overlap {}", out["overlap"]);
|
||||
}
|
||||
|
||||
/// Reviewer re-test case: a white part on (245, 245, 245) saved as JPEG at quality 75 gave
|
||||
/// "no part" even on the stricter retry — the codec blurs the 10-level step at the part's
|
||||
/// outline into steps of a few levels each, under any local limit. The third pass takes the
|
||||
/// backdrop globally: near the border's main colour.
|
||||
#[test]
|
||||
fn a_light_jpeg_keeps_its_part() {
|
||||
let mut s = Session::default();
|
||||
box_body(&mut s, "Box", 20.0, 10.0, 30.0);
|
||||
let path = reference(
|
||||
"light.jpg",
|
||||
&jpeg_q(200, 300, 75, |x, y| if (20..180).contains(&x) && (20..280).contains(&y) { [255, 255, 255] } else { [245, 245, 245] }),
|
||||
);
|
||||
let out = run(&mut s, "inspect.compare_picture", json!({"reference": path, "view": "front", "overlay": false}));
|
||||
assert!(out["overlap"].as_f64().unwrap() > 0.9, "overlap {}", out["overlap"]);
|
||||
}
|
||||
@@ -12,8 +12,8 @@ use solidcraft_geom::Vec3;
|
||||
use super::CommandSpec;
|
||||
use crate::params::{bad, bool_, str_, string_list, vec3};
|
||||
use crate::schema::component::{
|
||||
EditInPlaceParams, EditInPlaceResult, FinishEditParams, FinishEditResult, MoveFeaturesParams, MoveFeaturesResult, OccurrenceAlignParams,
|
||||
OccurrenceAlignResult, ReparentItemsParams, ReparentItemsResult,
|
||||
EditInPlaceParams, EditInPlaceResult, FinishEditParams, FinishEditResult, MoveFeaturesParams, MoveFeaturesResult, MoveSketchesToPartsParams,
|
||||
MoveSketchesToPartsResult, OccurrenceAlignParams, OccurrenceAlignResult, ReparentItemsParams, ReparentItemsResult,
|
||||
};
|
||||
use crate::schema::{TypedContract, output_schema_for};
|
||||
use crate::{Result, Session};
|
||||
@@ -21,6 +21,7 @@ use crate::{Result, Session};
|
||||
const EDIT_IN_PLACE_CONTRACT: TypedContract<EditInPlaceParams> = TypedContract::new();
|
||||
const FINISH_EDIT_CONTRACT: TypedContract<FinishEditParams> = TypedContract::new();
|
||||
const MOVE_FEATURES_CONTRACT: TypedContract<MoveFeaturesParams> = TypedContract::new();
|
||||
const MOVE_SKETCHES_TO_PARTS_CONTRACT: TypedContract<MoveSketchesToPartsParams> = TypedContract::new();
|
||||
const REPARENT_ITEMS_CONTRACT: TypedContract<ReparentItemsParams> = TypedContract::new();
|
||||
|
||||
#[path = "component_align.rs"]
|
||||
@@ -41,9 +42,15 @@ pub(super) mod properties;
|
||||
mod reparent;
|
||||
#[path = "component_reparent_items.rs"]
|
||||
mod reparent_items;
|
||||
#[path = "component_sketches_to_parts.rs"]
|
||||
mod sketches_to_parts;
|
||||
#[path = "component_transfer.rs"]
|
||||
mod transfer;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "component_history_tests.rs"]
|
||||
#[allow(clippy::unwrap_used, clippy::expect_used)]
|
||||
mod history_tests;
|
||||
#[cfg(test)]
|
||||
#[path = "occurrence_tests.rs"]
|
||||
#[allow(clippy::unwrap_used, clippy::expect_used)]
|
||||
@@ -53,6 +60,12 @@ pub static COMMANDS: &[CommandSpec] = &[
|
||||
CommandSpec::new("component.cycle_color", "Cycle Component Color", cycle_color)
|
||||
.icon("colored_components").doc_only()
|
||||
.params("component: id or name; saves the next identification colour and enables Component Colors; shared by every instance, separate from appearances"),
|
||||
CommandSpec::new("component.hide_history", "Hide Component History", hide_history)
|
||||
.icon("eye").doc_only()
|
||||
.params("component: id or name; keeps the component's features in the design but leaves them out of the History bar (#10393)"),
|
||||
CommandSpec::new("component.show_history", "Show Component History", show_history)
|
||||
.icon("eye").doc_only()
|
||||
.params("component: id or name; puts a component's features back in the History bar after Hide Component History (#10393)"),
|
||||
CommandSpec::new("component.create", "New Component", new_component)
|
||||
.at("SOLID", "ASSEMBLE")
|
||||
.icon("component")
|
||||
@@ -122,7 +135,21 @@ pub static COMMANDS: &[CommandSpec] = &[
|
||||
),
|
||||
CommandSpec::new("component.move_sketches", "Move Sketches to Component", move_sketches)
|
||||
.icon("move")
|
||||
.params("sketches: [ids or names], component; the sketches keep their own coordinates (component.move_features keeps them where they are in the world)"),
|
||||
.params("sketches: [ids or names], component; the sketches keep their own coordinates (component.move_features keeps them where they are in the world), except that a sketch on an exactly attached face stays where it is on the face"),
|
||||
CommandSpec::new(
|
||||
"component.move_sketches.to_parts",
|
||||
"Move Sketches to Their Parts",
|
||||
crate::checked!("component.move_sketches.to_parts", sketches_to_parts::to_parts),
|
||||
)
|
||||
.icon("move")
|
||||
.contract(&MOVE_SKETCHES_TO_PARTS_CONTRACT)
|
||||
.output(output_schema_for::<MoveSketchesToPartsResult>)
|
||||
.params(
|
||||
"sketches?: [ids or names] (default: every sketch in the design) → moved, stayed, sketches [{sketch, name, component, part}], \
|
||||
kept [{sketch, name, code, reason}]. Each sketch whose plane is an exactly attached face of a part's body goes to that part, as \
|
||||
component.move_sketches moves it, and stays where it is on the face; the others (on an origin, construction or fixed \
|
||||
plane, on a body of the design itself, or already in their part) stay where they are. One undo step",
|
||||
),
|
||||
CommandSpec::new("component.move_features", "Move Features to Component", crate::checked!("component.move_features", move_features::move_features))
|
||||
.icon("move")
|
||||
.contract(&MOVE_FEATURES_CONTRACT)
|
||||
@@ -520,6 +547,7 @@ fn move_sketches(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
return Err(bad(cmd, "`sketches` must list sketches"));
|
||||
}
|
||||
for id in &ids {
|
||||
sketches_to_parts::reauthor(s, *id, to, cmd)?;
|
||||
if let Some(f) = s.doc_mut().feature_mut(*id) {
|
||||
f.component = to;
|
||||
}
|
||||
@@ -789,3 +817,21 @@ fn cycle_color(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
s.colored_components = Some(());
|
||||
Ok(json!({"component": id, "color": next}))
|
||||
}
|
||||
|
||||
/// Mark a component's features out of the History bar, or back in (#10393). The features still
|
||||
/// rebuild; only the History bar leaves them out.
|
||||
fn set_history_hidden(s: &mut Session, p: &Value, hidden: bool) -> Result<Value> {
|
||||
let cmd = if hidden { "component.hide_history" } else { "component.show_history" };
|
||||
let id = component(s, p, "component", cmd)?;
|
||||
let c = s.doc_mut().components.iter_mut().find(|c| c.id == id).ok_or_else(|| bad(cmd, "choose a component below the design root"))?;
|
||||
c.history_hidden = hidden;
|
||||
Ok(json!({"component": id, "history_hidden": hidden}))
|
||||
}
|
||||
|
||||
fn hide_history(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
set_history_hidden(s, p, true)
|
||||
}
|
||||
|
||||
fn show_history(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
set_history_hidden(s, p, false)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Per-component History hiding (#10393) through the same commands the Browser menu runs.
|
||||
use crate::Session;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn hide_and_show_history_mark_the_component_flag_and_undo() {
|
||||
let mut s = Session::default();
|
||||
s.execute("component.create", &json!({ "name": "Copy" })).unwrap();
|
||||
s.execute("component.activate", &json!({ "component": "root" })).unwrap();
|
||||
assert_eq!(s.execute("component.hide_history", &json!({ "component": "Copy" })).unwrap()["history_hidden"], json!(true));
|
||||
assert!(s.doc.components.iter().find(|c| c.name == "Copy").unwrap().history_hidden);
|
||||
assert_eq!(s.execute("component.show_history", &json!({ "component": "Copy" })).unwrap()["history_hidden"], json!(false));
|
||||
assert!(!s.doc.components.iter().find(|c| c.name == "Copy").unwrap().history_hidden);
|
||||
// Undo of the show puts the flag back; undo of the hide takes it away.
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
assert!(s.doc.components.iter().find(|c| c.name == "Copy").unwrap().history_hidden);
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
assert!(!s.doc.components.iter().find(|c| c.name == "Copy").unwrap().history_hidden);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hide_history_needs_a_real_component_and_survives_a_save_round_trip() {
|
||||
let mut s = Session::default();
|
||||
s.execute("component.create", &json!({ "name": "Copy" })).unwrap();
|
||||
assert!(s.execute("component.hide_history", &json!({ "component": "root" })).is_err());
|
||||
s.execute("component.hide_history", &json!({ "component": "Copy" })).unwrap();
|
||||
// The flag rides in the design JSON and comes back on read.
|
||||
let json = s.doc.to_json();
|
||||
let read = solidcraft_doc::Document::from_json(&json).unwrap();
|
||||
assert!(read.components.iter().find(|c| c.name == "Copy").unwrap().history_hidden);
|
||||
// A design without the flag reads as shown, as always.
|
||||
let plain = solidcraft_doc::Document::from_json(&Session::default().doc.to_json()).unwrap();
|
||||
assert!(!plain.components.iter().any(|c| c.history_hidden));
|
||||
}
|
||||
@@ -31,7 +31,8 @@ mod relink_tests;
|
||||
pub static COMMANDS: &[CommandSpec] = &[
|
||||
CommandSpec::new("component.insert_design", "Insert Design", insert_design).at("SOLID", "ASSEMBLE").icon("component").params(
|
||||
"path: a .sc design → a new component linked to it, holding its solid bodies as placed in that design and a copy of its design properties; \
|
||||
name? (default: the design's name); parent?: component id or name (default: the active one); translate?: [x,y,z]; \
|
||||
name? (default: the design's name); parent?: component id or name (default: the active one); \
|
||||
translate?: [x,y,z] in mm from the destination parent's origin (default [0,0,0], also with reuse); \
|
||||
reuse?: bool (default false): insert a free occurrence, reusing an existing link to the same file when available",
|
||||
),
|
||||
CommandSpec::new("component.update_links", "Update Links", update_links).icon("update").params(
|
||||
@@ -41,7 +42,7 @@ pub static COMMANDS: &[CommandSpec] = &[
|
||||
joints whose faces are gone or that no longer solve)",
|
||||
),
|
||||
CommandSpec::new("component.relink", "Relink Source", relink).icon("update").params(
|
||||
"component: id or name of a linked component; path: a saved .sc design — replaces the source and linked bodies for all its occurrences, preserving identities and placements; returns path, previous_path, warnings, joint_errors and joint_conflicts",
|
||||
"component: id or name of a linked component; path: a saved design (.sc or .json) — replaces the source and linked bodies for all its occurrences, preserving identities and placements; returns path, previous_path, warnings, joint_errors and joint_conflicts",
|
||||
),
|
||||
CommandSpec::new("component.break_link", "Break Link", break_link)
|
||||
.icon("break")
|
||||
@@ -506,8 +507,13 @@ fn insert_design(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
if p.get("reuse").and_then(Value::as_bool).unwrap_or(false) {
|
||||
let existing = s.doc.links.iter().find(|(_, link)| absolute(&resolve(s.path.as_deref(), &link.path)) == file).map(|(id, _)| *id);
|
||||
if let Some(component) = existing {
|
||||
let parent = p.get("parent").cloned().unwrap_or_else(|| json!(s.active_component));
|
||||
return super::component::copy_occurrence(s, &json!({"component": component, "parent": parent, "translate": translate}));
|
||||
let parent = if p.get("parent").is_some() { component_key(s, p, "parent", cmd)? } else { s.active_component };
|
||||
// Reuse the definition, not another placement's captured or pending move.
|
||||
// Insert Design places from the parent origin whether the link is new or reused.
|
||||
let mut transform = solidcraft_doc::IDENTITY;
|
||||
transform[3] = [translate.x, translate.y, translate.z, 1.0];
|
||||
let occurrence = s.doc_mut().add_occurrence(component, parent, transform)?;
|
||||
return Ok(json!({"occurrence": occurrence, "component": component}));
|
||||
}
|
||||
}
|
||||
let ((design_name, sf, faces, metadata, properties), read) = linked_bodies_read(&file, s.path.as_deref())?;
|
||||
@@ -721,8 +727,9 @@ fn relink(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
}
|
||||
let given = str_(p, "path").filter(|x| !x.trim().is_empty() && x.len() < 4096).ok_or_else(|| bad(cmd, "`path` is required"))?;
|
||||
let file = absolute(Path::new(given));
|
||||
if !solidcraft_io::is_design_container_path(&file) {
|
||||
return Err(bad(cmd, "choose a saved .sc design"));
|
||||
// Any design Insert Design takes: a .sc container (or a legacy one) or a plain JSON design.
|
||||
if !solidcraft_io::is_design_path(given) {
|
||||
return Err(bad(cmd, "choose a saved design (.sc or .json)"));
|
||||
}
|
||||
// A network or device path is judged from its text before the disk is asked anything.
|
||||
link_refused(s.path.as_deref(), given).map_err(|e| bad(cmd, e.to_string()))?;
|
||||
|
||||
@@ -93,6 +93,15 @@ pub(super) fn move_features(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
FeatureKind::Sketch { plane, .. } => {
|
||||
if let Some(anchor) = crate::cmd::sketch::face::anchor_mut(plane) {
|
||||
let moved_body = moved_bodies.contains(&anchor.target.body);
|
||||
let author_delta = if authored_here {
|
||||
let old = s.doc.feature(f.id).ok_or_else(|| bad(cmd, "attached sketch no longer exists"))?;
|
||||
mat_mul(&to_dest, &s.doc.component_transform(old.component))
|
||||
} else {
|
||||
solidcraft_doc::IDENTITY
|
||||
};
|
||||
let source_delta =
|
||||
if moved_body { mat_mul(&to_dest, &s.doc.component_transform(anchor.component)) } else { solidcraft_doc::IDENTITY };
|
||||
anchor.reframe_placement(&author_delta, &source_delta)?;
|
||||
rewrite(&anchor.target.body, &mut anchor.path, &mut anchor.author_path);
|
||||
if moved_body {
|
||||
anchor.component = dest;
|
||||
|
||||
@@ -161,6 +161,90 @@ fn reused_design_insertion_is_free_even_before_a_link_exists() {
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reused_design_insertion_uses_the_requested_position_and_survives_undo_and_reopen() {
|
||||
let dir = folder("reuse-position");
|
||||
let file = dir.join("part.sc");
|
||||
part(&file, 30);
|
||||
for first_reuses in [false, true] {
|
||||
let mut s = Session::default();
|
||||
let first = run(&mut s, "component.insert_design", json!({"path": file, "translate": [20, 0, 0], "reuse": first_reuses}));
|
||||
let before = s.doc.to_json();
|
||||
let undo = s.undo.len();
|
||||
let second = run(&mut s, "component.insert_design", json!({"path": file, "translate": [40, 5, 7], "reuse": true}));
|
||||
assert_eq!(first["component"], second["component"]);
|
||||
assert_eq!(s.doc.links.len(), 1);
|
||||
assert_eq!(s.doc.features.len(), 1);
|
||||
assert_eq!(s.undo.len(), undo + 1);
|
||||
let occurrence = second["occurrence"].as_u64().unwrap();
|
||||
assert_linked_bounds(&mut s, occurrence, [40.0, 5.0, 7.0], [50.0, 25.0, 37.0]);
|
||||
assert_linked_bounds(&mut s, first["occurrence"].as_u64().unwrap(), [20.0, 0.0, 0.0], [30.0, 20.0, 30.0]);
|
||||
assert!(!s.doc.occurrences.iter().find(|o| o.id == occurrence).unwrap().grounded);
|
||||
run(&mut s, "edit.undo", json!({}));
|
||||
assert_eq!(s.doc.to_json(), before);
|
||||
run(&mut s, "edit.redo", json!({}));
|
||||
assert_linked_bounds(&mut s, occurrence, [40.0, 5.0, 7.0], [50.0, 25.0, 37.0]);
|
||||
let assembly = dir.join("assembly.sc");
|
||||
run(&mut s, "file.save", json!({"path": assembly}));
|
||||
let mut cold = Session::default();
|
||||
run(&mut cold, "doc.open", json!({"path": assembly}));
|
||||
assert_linked_bounds(&mut cold, occurrence, [40.0, 5.0, 7.0], [50.0, 25.0, 37.0]);
|
||||
let at_origin = run(&mut cold, "component.insert_design", json!({"path": file, "reuse": true}))["occurrence"].as_u64().unwrap();
|
||||
assert_linked_bounds(&mut cold, at_origin, [0.0, 0.0, 0.0], [10.0, 20.0, 30.0]);
|
||||
}
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
fn assert_linked_bounds(s: &mut Session, occurrence: u64, min: [f64; 3], max: [f64; 3]) {
|
||||
let p = run(s, "occurrence.properties", json!({"occurrence": occurrence}));
|
||||
assert_eq!(p["body_count"], 1);
|
||||
assert!((p["total"]["volume_mm3"].as_f64().unwrap() - 6000.0).abs() < 1e-6, "{p}");
|
||||
for (key, expected) in [("min", min), ("max", max)] {
|
||||
for (axis, expected) in expected.into_iter().enumerate() {
|
||||
assert!((p["total"]["bbox"][key][axis].as_f64().unwrap() - expected).abs() < 1e-6, "{key}[{axis}] != {expected}: {p}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reused_design_insertion_ignores_existing_moves_and_uses_the_destination_parent() {
|
||||
let dir = folder("reuse-parent");
|
||||
let file = dir.join("part.sc");
|
||||
part(&file, 30);
|
||||
for capture in [false, true] {
|
||||
let mut s = Session::default();
|
||||
let first = run(&mut s, "component.insert_design", json!({"path": file, "translate": [20, 0, 0]}));
|
||||
run(
|
||||
&mut s,
|
||||
"occurrence.move",
|
||||
json!({"occurrence": first["occurrence"], "translate": [0, 10, 0], "axis": [0, 0, 1], "angle": "90 deg", "capture": capture, "move_grounded": true}),
|
||||
);
|
||||
let original = s.doc.occurrences.clone();
|
||||
let pending = s.pending_moves.clone();
|
||||
let parent = run(&mut s, "component.create", json!({"name": "Parent", "activate": false}));
|
||||
run(&mut s, "occurrence.move", json!({"occurrence": parent["occurrence"], "translate": [100, 0, 0], "axis": [0, 0, 1], "angle": "90 deg"}));
|
||||
for explicit_parent in [true, false] {
|
||||
let mut params = json!({"path": file, "translate": [40, 5, 7], "reuse": true});
|
||||
if explicit_parent {
|
||||
params["parent"] = json!("Parent");
|
||||
} else {
|
||||
run(&mut s, "component.activate", json!({"component": parent["component"]}));
|
||||
}
|
||||
let inserted = run(&mut s, "component.insert_design", params);
|
||||
assert_eq!(inserted["component"], first["component"]);
|
||||
assert_linked_bounds(&mut s, inserted["occurrence"].as_u64().unwrap(), [75.0, 40.0, 7.0], [95.0, 50.0, 37.0]);
|
||||
assert_eq!(s.doc.occurrences.first(), original.first());
|
||||
assert_eq!(s.pending_moves, pending);
|
||||
}
|
||||
let before = s.doc.to_json();
|
||||
for parent in [json!("missing"), first["component"].clone()] {
|
||||
assert!(s.execute("component.insert_design", &json!({"path": file, "parent": parent, "reuse": true})).is_err());
|
||||
assert_eq!(s.doc.to_json(), before);
|
||||
}
|
||||
}
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_design_properties_update_density_without_geometry_and_keep_edited_cards() {
|
||||
let dir = folder("properties-density");
|
||||
@@ -509,6 +593,24 @@ fn relink_rejects_bad_sources_and_targets_without_touching_the_design_or_undo()
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
/// Relink takes any design Insert Design takes: a plain JSON design too, and back to a container.
|
||||
#[test]
|
||||
fn relink_takes_a_json_design_as_insert_design_does() {
|
||||
let dir = folder("json");
|
||||
let (container, plain) = (dir.join("source.sc"), dir.join("source.json"));
|
||||
part(&container, 30);
|
||||
part(&plain, 45);
|
||||
assert!(!solidcraft_io::is_design_container(&std::fs::read(&plain).unwrap()), "file.save writes a .json design as plain JSON");
|
||||
let mut s = Session::default();
|
||||
let component = run(&mut s, "component.insert_design", json!({"path": container}))["component"].as_u64().unwrap();
|
||||
let to_json = run(&mut s, "component.relink", json!({"component": component, "path": plain}));
|
||||
assert_eq!(to_json["geometry_changed"], true);
|
||||
assert!((volume(&mut s) - 9000.0).abs() < 1e-5);
|
||||
run(&mut s, "component.relink", json!({"component": component, "path": container}));
|
||||
assert!((volume(&mut s) - 6000.0).abs() < 1e-5);
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn relink_keeps_joint_definitions_and_reports_solver_diagnostics() {
|
||||
let dir = folder("joints");
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user