Release 0.30.0

This commit is contained in:
bherbruck
2026-11-01 21:55:18 -05:00
449 changed files with 30971 additions and 1889 deletions
+4 -3
View File
@@ -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:
+124
View File
@@ -630,6 +630,130 @@ 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 |
## Code adapted from sibling projects
+7 -6
View File
@@ -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)
Generated
+24 -17
View File
@@ -4208,7 +4208,7 @@ dependencies = [
[[package]]
name = "solidcraft"
version = "0.29.0"
version = "0.30.0"
dependencies = [
"block2 0.6.2",
"eframe",
@@ -4245,11 +4245,11 @@ dependencies = [
[[package]]
name = "solidcraft-3dx-client"
version = "0.29.0"
version = "0.30.0"
[[package]]
name = "solidcraft-cli"
version = "0.29.0"
version = "0.30.0"
dependencies = [
"image",
"log",
@@ -4261,7 +4261,7 @@ dependencies = [
[[package]]
name = "solidcraft-derive"
version = "0.29.0"
version = "0.30.0"
dependencies = [
"proc-macro2",
"quote",
@@ -4270,7 +4270,7 @@ dependencies = [
[[package]]
name = "solidcraft-doc"
version = "0.29.0"
version = "0.30.0"
dependencies = [
"log",
"serde",
@@ -4287,7 +4287,7 @@ dependencies = [
[[package]]
name = "solidcraft-engine"
version = "0.29.0"
version = "0.30.0"
dependencies = [
"image",
"log",
@@ -4308,7 +4308,7 @@ dependencies = [
[[package]]
name = "solidcraft-geom"
version = "0.29.0"
version = "0.30.0"
dependencies = [
"serde",
"serde_json",
@@ -4317,7 +4317,7 @@ dependencies = [
[[package]]
name = "solidcraft-gpu"
version = "0.29.0"
version = "0.30.0"
dependencies = [
"image",
"log",
@@ -4337,7 +4337,7 @@ dependencies = [
[[package]]
name = "solidcraft-io"
version = "0.29.0"
version = "0.30.0"
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.0"
dependencies = [
"log",
"proptest",
@@ -4381,8 +4382,9 @@ dependencies = [
[[package]]
name = "solidcraft-mcp"
version = "0.29.0"
version = "0.30.0"
dependencies = [
"image",
"serde_json",
"solidcraft-engine",
"solidcraft-gpu",
@@ -4390,7 +4392,7 @@ dependencies = [
[[package]]
name = "solidcraft-render"
version = "0.29.0"
version = "0.30.0"
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.0"
dependencies = [
"harfrust",
"serde",
@@ -4417,14 +4420,18 @@ dependencies = [
[[package]]
name = "solidcraft-space-mouse"
version = "0.29.0"
version = "0.30.0"
dependencies = [
"serde",
]
[[package]]
name = "solidcraft-testkit"
version = "0.30.0"
[[package]]
name = "solidcraft-ui-egui"
version = "0.29.0"
version = "0.30.0"
dependencies = [
"arboard",
"egui",
@@ -4455,7 +4462,7 @@ dependencies = [
[[package]]
name = "solidcraft-web"
version = "0.29.0"
version = "0.30.0"
dependencies = [
"eframe",
"egui",
@@ -6010,7 +6017,7 @@ checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7"
[[package]]
name = "xtask"
version = "0.29.0"
version = "0.30.0"
dependencies = [
"proc-macro2",
"serde",
+3 -1
View File
@@ -5,7 +5,7 @@ members = ["crates/*", "apps/*", "xtask"]
exclude = ["vendor"]
[workspace.package]
version = "0.29.0"
version = "0.30.0"
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"
+27 -8
View File
@@ -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);
}
}
+4 -3
View File
@@ -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" => {
@@ -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
@@ -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

+89 -8
View File
@@ -556,6 +556,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 +794,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 +860,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 +869,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 +877,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 +888,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 +937,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 +959,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 +1050,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 +1216,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 +1267,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 +1302,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 +1314,7 @@
"color.linear": "عرض قيم اللون في النطاق الخطي 0-1",
"color.recent": "الألوان الأخيرة",
"color.recent_help": "تظهر هنا الألوان التي تختارها في أي حقل لون",
"color.red": "أحمر",
"color.saturation": "التشبع (بالمئة)",
"color.saturation_value": "التشبع والقيمة",
"color.selected": "اللون المحدد",
@@ -1296,6 +1322,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 +1372,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 +1459,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 +1582,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 +1610,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 +1655,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 +1833,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 +1937,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 أثناء سحب التدوير لالتقاط اتجاه العرض واللف.",
@@ -2121,6 +2158,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 +2172,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": "الشريط المخطط هو المنطقة الميتة: تُتجاهل الحركة داخلها.",
@@ -2191,8 +2232,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": "التسميات",
@@ -2250,6 +2293,8 @@
"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": "ملف التعريف النشط",
@@ -2347,7 +2392,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 +2592,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 +2706,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 +3462,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": "التقادم",
@@ -4075,11 +4135,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 +4185,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 +4330,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": "إزالة",
@@ -4398,9 +4468,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 +4738,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 +4746,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,6 +4756,7 @@
"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": "السجل",
@@ -4701,6 +4776,7 @@
"ui.insert_design.saved_file": "في {name} تغييرات غير محفوظة، لذا أُدرج ملفه المحفوظ. احفظه، ثم استخدم تحديث الروابط لإدخال التغييرات.",
"ui.insert_design.unsaved": "تغييرات غير محفوظة",
"ui.inspect": "فحص",
"ui.instances": "النسخ",
"ui.intersect": "تقاطع",
"ui.isolate": "عزل",
"ui.join": "ضم",
@@ -4851,9 +4927,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 +5041,7 @@
"ui.showDimension": "إظهار البُعد",
"ui.showOrigin": "إظهار نقطة الأصل",
"ui.showPlanes": "إظهار المستويات",
"ui.show_component_history": "إظهار سجله",
"ui.show_constraints": "القيود",
"ui.show_construction_geometries": "الهندسة المساعدة",
"ui.show_dimensions": "الأبعاد",
@@ -5081,12 +5158,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",
@@ -5188,6 +5268,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
View File
@@ -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
View File
@@ -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
View File
@@ -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",
+83 -2
View File
@@ -556,6 +556,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 +794,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 +868,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 +886,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 +935,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 +1047,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 +1213,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 +1264,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 +1299,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 +1311,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 +1319,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 +1369,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 +1456,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 +1579,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 +1833,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 +1934,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 +1945,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.",
@@ -2120,6 +2155,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 +2169,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.",
@@ -2190,6 +2229,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",
@@ -2247,6 +2288,8 @@
"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",
@@ -2306,6 +2349,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 +2587,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 +2701,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 +3457,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 +3912,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",
@@ -4069,11 +4130,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 +4180,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 +4325,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",
@@ -4389,9 +4463,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",
@@ -4692,6 +4768,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 +4919,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",
@@ -5072,12 +5149,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",
@@ -5179,6 +5259,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."
}
+1
View File
@@ -249,6 +249,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.",
+83 -2
View File
@@ -556,6 +556,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 +794,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 +860,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 +869,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 +877,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 +888,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 +937,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 +959,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 +1050,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 +1216,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 +1267,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 +1302,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 +1314,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 +1322,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 +1372,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 +1459,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 +1582,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 +1836,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 +1937,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.",
@@ -2121,6 +2158,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 +2172,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.",
@@ -2191,6 +2232,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",
@@ -2250,6 +2293,8 @@
"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",
@@ -2547,6 +2592,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 +2706,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 +3462,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",
@@ -4075,11 +4135,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 +4185,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",
@@ -4398,9 +4468,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 +4738,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 +4746,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,6 +4756,7 @@
"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",
@@ -4701,6 +4776,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 +4927,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 +5041,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",
@@ -5081,12 +5158,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",
@@ -5188,6 +5268,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."
}
+3 -1
View File
@@ -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",
@@ -4357,6 +4358,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 +4506,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",
+3 -1
View File
@@ -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",
+3 -1
View File
@@ -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",
@@ -4657,6 +4658,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 +4807,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
View File
@@ -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",
+83 -2
View File
@@ -556,6 +556,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 +794,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 +860,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 +869,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 +877,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 +888,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 +937,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 +959,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 +1050,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 +1216,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 +1267,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 +1302,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 +1314,7 @@
"color.linear": "ערכי הצבע מוצגים במרחב לינארי 0-1",
"color.recent": "צבעים אחרונים",
"color.recent_help": "צבעים שבוחרים בכל שדה צבע מופיעים כאן",
"color.red": "אדום",
"color.saturation": "רוויה (אחוזים)",
"color.saturation_value": "רוויה ובהירות",
"color.selected": "הצבע שנבחר",
@@ -1296,6 +1322,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 +1372,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 +1459,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 +1582,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 +1836,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 +1937,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 בזמן גרירת סיבוב כדי להצמיד את כיוון התצוגה ואת הגלגול.",
@@ -2121,6 +2158,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 +2172,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": "הפס המקווקו הוא האזור המת: תנועה בתוכו לא נלקחת בחשבון.",
@@ -2191,6 +2232,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": "השארת תיבות דו־שיח וכלים פתוחים במעבר בין עיצובים",
@@ -2250,6 +2293,8 @@
"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": "פרופיל פעיל",
@@ -2547,6 +2592,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 +2706,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 +3462,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": "הזדקנות",
@@ -4075,11 +4135,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 +4185,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": "התמונה הבאה",
@@ -4398,9 +4468,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 +4738,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 +4746,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,6 +4756,7 @@
"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": "ציר הזמן",
@@ -4701,6 +4776,7 @@
"ui.insert_design.saved_file": "ב-{name} יש שינויים שלא נשמרו, ולכן נוסף הקובץ השמור שלו. שמרו אותו ואז בחרו עדכון קישורים כדי להכניס את השינויים.",
"ui.insert_design.unsaved": "שינויים שלא נשמרו",
"ui.inspect": "בדיקה",
"ui.instances": "מופעים",
"ui.intersect": "חיתוך משותף",
"ui.isolate": "בידוד",
"ui.join": "איחוד",
@@ -4851,9 +4927,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 +5041,7 @@
"ui.showDimension": "הצגת מידה",
"ui.showOrigin": "הצגת ראשית",
"ui.showPlanes": "הצגת מישורים",
"ui.show_component_history": "הצג את ההיסטוריה שלו",
"ui.show_constraints": "אילוצים",
"ui.show_construction_geometries": "גאומטריית עזר",
"ui.show_dimensions": "מידות",
@@ -5081,12 +5158,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",
@@ -5188,6 +5268,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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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",
+83 -2
View File
@@ -556,6 +556,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 +794,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 +860,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 +869,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 +877,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 +888,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 +937,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 +959,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 +1050,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 +1216,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 +1267,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 +1302,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 +1314,7 @@
"color.linear": "0〜1の線形空間で色の値を表示",
"color.recent": "最近使った色",
"color.recent_help": "どの色フィールドで選んだ色もここに表示されます",
"color.red": "赤",
"color.saturation": "彩度(パーセント)",
"color.saturation_value": "彩度と明度",
"color.selected": "選択した色",
@@ -1296,6 +1322,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 +1372,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 +1459,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 +1582,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 +1836,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 +1937,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を押すと、視線方向とロールがスナップします。",
@@ -2121,6 +2158,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 +2172,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": "斜線の帯はデッドゾーンです。この範囲内の動きは無視されます。",
@@ -2191,6 +2232,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": "デザインを切り替えてもダイアログとツールを開いたままにする",
@@ -2250,6 +2293,8 @@
"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": "使用中のプロファイル",
@@ -2547,6 +2592,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 +2706,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 +3462,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": "エイジング",
@@ -4075,11 +4135,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 +4185,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": "次の画像",
@@ -4398,9 +4468,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 +4738,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 +4746,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,6 +4756,7 @@
"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": "履歴",
@@ -4701,6 +4776,7 @@
"ui.insert_design.saved_file": "{name} に保存されていない変更があるため、保存済みのファイルを挿入しました。保存してから、リンクを更新で変更を取り込んでください。",
"ui.insert_design.unsaved": "未保存の変更",
"ui.inspect": "検査",
"ui.instances": "インスタンス",
"ui.intersect": "交差",
"ui.isolate": "分離表示",
"ui.join": "結合",
@@ -4851,9 +4927,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 +5041,7 @@
"ui.showDimension": "寸法を表示",
"ui.showOrigin": "原点を表示",
"ui.showPlanes": "平面を表示",
"ui.show_component_history": "このコンポーネントの履歴を表示",
"ui.show_constraints": "拘束",
"ui.show_construction_geometries": "構築ジオメトリ",
"ui.show_dimensions": "寸法",
@@ -5081,12 +5158,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",
@@ -5188,6 +5268,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に進みます。"
}
+52 -1
View File
@@ -476,6 +476,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 +759,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 +906,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 +1097,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 +1115,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 +1175,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 +1220,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 +1323,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 +1365,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 +1553,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 +1567,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": "빗금 친 영역이 데드존입니다. 그 안의 움직임은 무시됩니다.",
@@ -1633,6 +1655,8 @@
"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.project_options_dialog": "Project 옵션 대화상자",
@@ -1823,6 +1847,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 +1942,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 +2417,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": "검사",
@@ -2929,6 +2968,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 +3010,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": "다음 이미지",
@@ -3200,9 +3248,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 +3539,7 @@
"ui.insert_design.saved_file": "{name}에 저장되지 않은 변경 사항이 있어 저장된 파일을 삽입했습니다. 저장한 다음 링크 업데이트로 변경 사항을 가져오세요.",
"ui.insert_design.unsaved": "저장되지 않은 변경 사항",
"ui.inspect": "검사",
"ui.instances": "인스턴스",
"ui.intersect": "교차",
"ui.isolate": "분리 표시",
"ui.join": "접합",
@@ -3616,9 +3667,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": "프로파일",
+70 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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",
+3 -1
View File
@@ -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",
+2 -1
View File
@@ -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
View File
@@ -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",
+70 -1
View File
@@ -787,6 +787,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 +861,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 +1188,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 +1281,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 +1334,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.",
@@ -1875,6 +1891,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 +2109,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 +2125,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 +2142,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 +2157,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 +2166,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 +2203,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) открываются только с перечисленных здесь серверов или после того, как вы разрешите сервер по запросу.",
@@ -2218,6 +2251,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 +2280,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_recent_snap": "Автопривязка к недавним центрам и серединам",
"prefs.sketch_recent_snap_note": "При рисовании курсор привязывается к месту пересечения направляющих двух последних центров окружностей или середин прямых отрезков. Выкл.: эти направляющие остаются только визуальными, а курсор не смещается.",
"prefs.sketch_selection": "Эскиз и выделение",
@@ -2267,6 +2304,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": "файл темы",
@@ -3717,6 +3772,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 +4173,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 +4339,7 @@
"ui.dark": "Тёмная",
"ui.default_modelling_orientation": "Ориентация моделирования по умолчанию",
"ui.delete": "Удалить",
"ui.delete_select_first": "Сначала выберите, что удалить: операции, тела, компоненты или геометрию эскиза",
"ui.depth": "Глубина",
"ui.design": "Дизайн",
"ui.design_properties.author": "Автор",
@@ -4372,6 +4432,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 +4443,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 +4620,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 +4716,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 +4753,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 +4981,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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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",
+83 -2
View File
@@ -556,6 +556,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 +794,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 +860,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 +869,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 +877,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 +888,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 +937,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 +959,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 +1050,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 +1216,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 +1267,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 +1302,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 +1314,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 +1322,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 +1372,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 +1459,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 +1582,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 +1836,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 +1937,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.",
@@ -2121,6 +2158,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 +2172,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.",
@@ -2191,6 +2232,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ế",
@@ -2250,6 +2293,8 @@
"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",
@@ -2547,6 +2592,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 +2706,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 +3462,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",
@@ -4075,11 +4135,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 +4185,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",
@@ -4398,9 +4468,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 +4738,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 +4746,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,6 +4756,7 @@
"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ử",
@@ -4701,6 +4776,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 +4927,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 +5041,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",
@@ -5081,12 +5158,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",
@@ -5188,6 +5268,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."
}
+72 -1
View File
@@ -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",
+83 -2
View File
@@ -556,6 +556,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 +794,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 +860,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 +869,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 +877,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 +888,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 +937,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 +959,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 +1050,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 +1216,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 +1267,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 +1302,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 +1314,7 @@
"color.linear": "以 0–1 线性空间显示颜色值",
"color.recent": "最近使用的颜色",
"color.recent_help": "在任意颜色字段中选择的颜色会显示在这里",
"color.red": "红色",
"color.saturation": "饱和度(百分比)",
"color.saturation_value": "饱和度和明度",
"color.selected": "所选颜色",
@@ -1296,6 +1322,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 +1372,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 +1459,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 +1582,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 +1836,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 +1937,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,可吸附视图方向和画面旋转角度。",
@@ -2121,6 +2158,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 +2172,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": "斜线区域为死区:此范围内的移动将被忽略。",
@@ -2191,6 +2232,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": "切换设计时保持对话框和工具打开",
@@ -2250,6 +2293,8 @@
"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": "当前配置文件",
@@ -2547,6 +2592,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 +2706,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 +3462,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": "老化",
@@ -4075,11 +4135,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 +4185,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": "下一张图片",
@@ -4398,9 +4468,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 +4738,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 +4746,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,6 +4756,7 @@
"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": "历史",
@@ -4701,6 +4776,7 @@
"ui.insert_design.saved_file": "{name} 有未保存的更改,因此插入的是其已保存的文件。请先保存,再使用“更新链接”导入这些更改。",
"ui.insert_design.unsaved": "未保存的更改",
"ui.inspect": "检查",
"ui.instances": "实例数",
"ui.intersect": "相交",
"ui.isolate": "隔离",
"ui.join": "连接",
@@ -4851,9 +4927,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 +5041,7 @@
"ui.showDimension": "显示尺寸",
"ui.showOrigin": "显示原点",
"ui.showPlanes": "显示平面",
"ui.show_component_history": "显示其历史记录",
"ui.show_constraints": "约束",
"ui.show_construction_geometries": "构造几何图元",
"ui.show_dimensions": "尺寸",
@@ -5081,12 +5158,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",
@@ -5188,6 +5268,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中继续。"
}
+42
View File
@@ -226,6 +226,28 @@ 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_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),
pref_well: Color32::from_rgb(238, 241, 245),
pref_note: Color32::from_rgb(184, 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 +367,26 @@ 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.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;
+14
View File
@@ -208,6 +208,7 @@ fn map_plane_ref(m: &Mat, p: &mut PlaneRef) {
}
PlaneRef::Midplane { .. }
| PlaneRef::TwoEdges { .. }
| PlaneRef::AxisPoint { .. }
| PlaneRef::ThreePoints { .. }
| PlaneRef::AlongPath { .. }
| PlaneRef::Perpendicular { .. } => {
@@ -418,6 +419,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
+4 -2
View File
@@ -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,
+1 -1
View File
@@ -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)?;
}
+11 -1
View File
@@ -620,6 +620,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 {
@@ -768,7 +778,7 @@ 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()],
+209 -5
View File
@@ -97,6 +97,8 @@ 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.
@@ -2019,7 +2021,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 +2032,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 +2083,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.
@@ -2412,6 +2433,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 +3325,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 +3357,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 +3661,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 +3765,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 +4174,7 @@ impl Document {
}
PlaneRef::Midplane { .. }
| PlaneRef::TwoEdges { .. }
| PlaneRef::AxisPoint { .. }
| PlaneRef::ThreePoints { .. }
| PlaneRef::Tangent { .. }
| PlaneRef::AlongPath { .. }
@@ -4173,6 +4372,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
+101 -22
View File
@@ -1213,6 +1213,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)
}
@@ -1958,13 +1960,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 +1970,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 {
@@ -4282,7 +4310,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 +4320,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 +5014,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 +5076,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();
@@ -6624,7 +6676,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);
@@ -7187,10 +7249,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
};
@@ -8198,6 +8260,7 @@ fn plane_reads_geometry(plane: &PlaneRef) -> bool {
PlaneRef::ThroughPoint { .. }
| PlaneRef::Midplane { .. }
| PlaneRef::TwoEdges { .. }
| PlaneRef::AxisPoint { .. }
| PlaneRef::ThreePoints { .. }
| PlaneRef::Tangent { .. }
| PlaneRef::AlongPath { .. }
@@ -8575,7 +8638,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,7 +8664,24 @@ 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);
@@ -8678,14 +8761,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 +8949,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"]
+234 -2
View File
@@ -12,20 +12,24 @@ 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.
#[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,
}
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 }) };
}
pub(super) fn count(f: impl FnOnce(&mut Counts)) {
@@ -52,6 +56,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 +174,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 +885,201 @@ 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);
}
+4 -1
View File
@@ -802,7 +802,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,
+83 -8
View File
@@ -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();
+1
View File
@@ -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"}),
@@ -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.
+1 -1
View File
@@ -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) => {
+3 -3
View File
@@ -87,9 +87,9 @@ 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,
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;
+6 -2
View File
@@ -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;
}
}
}
+6 -2
View File
@@ -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)
+20 -4
View File
@@ -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
}
+259 -32
View File
@@ -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 } => {
@@ -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.
+111
View File
@@ -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
+53
View File
@@ -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]
@@ -653,6 +668,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 +907,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);
}
+2 -2
View File
@@ -406,7 +406,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);
}
@@ -437,7 +437,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);
}
+1
View File
@@ -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 }
+124 -2
View File
@@ -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();
+4
View File
@@ -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;
+692
View File
@@ -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, &params).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"]);
}
+49 -3
View File
@@ -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));
}
+13 -6
View File
@@ -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");
@@ -0,0 +1,139 @@
//! Sketches and the component that owns them: a face-attached sketch moved to another component
//! keeps its place on the face, and `component.move_sketches.to_parts` (#10218) moves sketches
//! to the part whose face they are drawn on.
use serde_json::{Value, json};
use solidcraft_doc::{EmbossFace, FeatureKind, PlaneRef, apply_point, apply_vector, mat_inverse, mat_mul};
use solidcraft_geom::Plane;
use crate::params::{bad, name_or_id_list};
use crate::schema::component::{MoveSketchesToPartsResult, SketchKeptInPlace, SketchMovedToPart};
use crate::{Result, Session};
/// The face a sketch's plane is built on, beneath an offset, flip or angle.
fn face_mut(mut plane: &mut PlaneRef) -> Option<&mut PlaneRef> {
for _ in 0..64 {
match plane {
PlaneRef::Offset { base, .. } | PlaneRef::Flipped { base, .. } | PlaneRef::AtAngle { base, .. } | PlaneRef::ThroughPoint { base, .. } => {
plane = base
}
PlaneRef::Face { .. } => return Some(plane),
_ => return None,
}
}
None
}
/// Make the face-attached sketch `id`, about to belong to `to`, authored in a placement of `to`:
/// the placement its face is in when `to` owns the face, the design for the design, else `to`'s
/// first. The picked point, the repair plane and an angle's axis are carried into that placement,
/// so the sketch stays where it is on the face. Other sketches keep their own coordinates.
pub(super) fn reauthor(s: &mut Session, id: u64, to: u64, cmd: &str) -> Result<()> {
let Some(feature) = s.doc.feature(id) else { return Ok(()) };
let from = feature.component;
let FeatureKind::Sketch { plane, .. } = &feature.kind else { return Ok(()) };
let Some(anchor) = solidcraft_doc::face_anchor::plane_face_anchor(plane) else { return Ok(()) };
if from == to {
return Ok(());
}
let doc = s.placed_document();
let path = if to == anchor.component {
anchor.path.clone()
} else if to == 0 {
Vec::new()
} else {
doc.first_path(to)
};
let new_frame = EmbossFace::component_frame(&doc, to, &path)?;
let inverse = mat_inverse(&new_frame).ok_or_else(|| bad(cmd, "the component's placement can't be inverted"))?;
// A sketch whose old placement no longer resolves only gets the new one: its face anchor
// places it, and the picked point and repair plane are a guide.
let relative = anchor.author_frame(&doc, from).ok().map(|old| mat_mul(&inverse, &old));
let Some(feature) = s.doc_mut().feature_mut(id) else { return Ok(()) };
let FeatureKind::Sketch { plane, .. } = &mut feature.kind else { return Ok(()) };
if let (Some(relative), PlaneRef::AtAngle { axis_origin, axis_dir, .. }) = (relative, &mut *plane) {
*axis_origin = apply_point(&relative, *axis_origin);
*axis_dir = apply_vector(&relative, *axis_dir);
}
let Some(PlaneRef::Face { plane: picked, at, anchor: Some(anchor), .. }) = face_mut(plane) else { return Ok(()) };
anchor.author_path = path;
if let Some(relative) = relative {
*at = apply_point(&relative, *at);
if let Some(moved) = Plane::new(apply_point(&relative, picked.origin), apply_vector(&relative, picked.x), apply_vector(&relative, picked.y)) {
*picked = moved;
}
}
Ok(())
}
/// Why a sketch stays where it is (a code and its text), or the component whose face it is on.
fn part_of(s: &Session, owner: u64, plane: &PlaneRef) -> std::result::Result<u64, (&'static str, &'static str)> {
let mut base = plane;
for _ in 0..64 {
match base {
PlaneRef::Offset { base: inner, .. }
| PlaneRef::Flipped { base: inner, .. }
| PlaneRef::AtAngle { base: inner, .. }
| PlaneRef::ThroughPoint { base: inner, .. } => base = inner,
_ => break,
}
}
let PlaneRef::Face { anchor, .. } = base else { return Err(("not_on_face", "not on a face")) };
let Some(anchor) = anchor else {
return Err(("no_exact_attachment", "its face has no exact attachment (a sheet metal panel, or picked before exact attachments)"));
};
if anchor.component == 0 {
return Err(("design_body", "on a body of the design itself, not of a part"));
}
if s.doc.components.iter().all(|component| component.id != anchor.component) {
return Err(("part_missing", "its face's part is no longer in the design"));
}
if anchor.component == owner {
return Err(("already_in_part", "already in its part"));
}
Ok(anchor.component)
}
/// `component.move_sketches.to_parts`: each sketch given (every sketch when none are) whose plane
/// is a face of a part's body goes to that part, as `component.move_sketches` moves it. One undo
/// step; the reply lists what moved and what stayed, and why.
pub(super) fn to_parts(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "component.move_sketches.to_parts";
let ids: Vec<u64> = if p.get("sketches").is_some() {
let keys = name_or_id_list(cmd, p, "sketches")?;
let mut ids = Vec::with_capacity(keys.len().min(10_000));
for key in &keys {
match s.doc.find_feature(key) {
Some(f) if matches!(f.kind, FeatureKind::Sketch { .. }) => {
if !ids.contains(&f.id) {
ids.push(f.id);
}
}
_ => return Err(bad(cmd, format!("no sketch `{key}`"))),
}
}
ids
} else {
s.doc.features.iter().filter(|f| matches!(f.kind, FeatureKind::Sketch { .. })).map(|f| f.id).collect()
};
let mut result = MoveSketchesToPartsResult::default();
let mut moves = Vec::new();
for id in ids {
let Some(f) = s.doc.feature(id) else { continue };
let FeatureKind::Sketch { plane, .. } = &f.kind else { continue };
match part_of(s, f.component, plane) {
Ok(component) => {
let part = s.doc.components.iter().find(|c| c.id == component).map(|c| c.name.clone()).unwrap_or_default();
result.sketches.push(SketchMovedToPart { sketch: id, name: f.name.clone(), component, part });
moves.push((id, component));
}
Err((code, reason)) => {
result.kept.push(SketchKeptInPlace { sketch: id, name: f.name.clone(), code: code.to_string(), reason: reason.to_string() })
}
}
}
for (id, component) in moves {
super::move_sketches(s, &json!({"sketches": [id.to_string()], "component": component}))?;
}
(result.moved, result.stayed) = (result.sketches.len(), result.kept.len());
serde_json::to_value(result).map_err(|e| bad(cmd, e.to_string()))
}
+22 -2
View File
@@ -16,8 +16,8 @@ use super::features::{add_feature, check_expr, plane_param, sketch_id};
use crate::params::{bad, req_expr, str_, vec3};
use crate::schema::construct::{
AxisEdgeParams, AxisNormalToFaceParams, AxisThroughCylinderParams, AxisTwoPlanesParams, AxisTwoPointsParams, MidplaneParams,
PlaneAlongPathParams, PlanePerpendicularParams, PlaneTangentParams, PlaneThreePointsParams, PlaneTwoEdgesParams, PointAlongPathParams,
PointCenterParams, PointEdgePlaneParams, PointThreePlanesParams, PointTwoEdgesParams, PointVertexParams,
PlaneAlongPathParams, PlaneAxisPointParams, PlanePerpendicularParams, PlaneTangentParams, PlaneThreePointsParams, PlaneTwoEdgesParams,
PointAlongPathParams, PointCenterParams, PointEdgePlaneParams, PointThreePlanesParams, PointTwoEdgesParams, PointVertexParams,
};
use crate::schema::features::FeatureResult;
use crate::schema::{TypedContract, output_schema_for};
@@ -39,6 +39,7 @@ const POINT_EDGE_PLANE_CONTRACT: TypedContract<PointEdgePlaneParams> = TypedCont
const POINT_ALONG_PATH_CONTRACT: TypedContract<PointAlongPathParams> = TypedContract::new();
const PLANE_MIDPLANE_CONTRACT: TypedContract<MidplaneParams> = TypedContract::new();
const PLANE_TWO_EDGES_CONTRACT: TypedContract<PlaneTwoEdgesParams> = TypedContract::new();
const PLANE_AXIS_POINT_CONTRACT: TypedContract<PlaneAxisPointParams> = TypedContract::new();
const PLANE_THREE_POINTS_CONTRACT: TypedContract<PlaneThreePointsParams> = TypedContract::new();
const PLANE_TANGENT_CONTRACT: TypedContract<PlaneTangentParams> = TypedContract::new();
const PLANE_ALONG_PATH_CONTRACT: TypedContract<PlaneAlongPathParams> = TypedContract::new();
@@ -123,6 +124,12 @@ pub static COMMANDS: &[CommandSpec] = &[
.contract(&PLANE_TWO_EDGES_CONTRACT)
.output(output_schema_for::<FeatureResult>)
.params("a, b: straight edge or line refs in one plane; name?"),
CommandSpec::new("construct.plane.axis_point", "Plane Through Axis And Point", crate::checked!("construct.plane.axis_point", plane_axis_point))
.at("SOLID", "CONSTRUCT")
.icon("plane_axis_point")
.contract(&PLANE_AXIS_POINT_CONTRACT)
.output(output_schema_for::<FeatureResult>)
.params("axis: origin axis, construction axis, straight edge or sketch line ref; point: off-axis point ref; name?"),
CommandSpec::new("construct.plane.three_points", "Plane Through Three Points", crate::checked!("construct.plane.three_points", plane_points))
.at("SOLID", "CONSTRUCT")
.icon("plane_three_points")
@@ -374,6 +381,19 @@ fn plane_edges(s: &mut Session, p: &Value) -> Result<Value> {
plane(s, p, PlaneRef::TwoEdges { a: Box::new(a), b: Box::new(b) })
}
fn plane_axis_point(s: &mut Session, p: &Value) -> Result<Value> {
let kind = plane_axis_point_kind(s, p)?;
add_feature(s, p, kind)
}
/// Shared parser for the constructor and its world-to-authoring-frame boundary.
pub(crate) fn plane_axis_point_kind(s: &Session, p: &Value) -> Result<FeatureKind> {
let cmd = "construct.plane.axis_point";
let axis = geo(s, p.get("axis"), cmd, "axis")?;
let point = geo(s, p.get("point"), cmd, "point")?;
Ok(FeatureKind::ConstructionPlane { plane: PlaneRef::AxisPoint { axis: Box::new(axis), point: Box::new(point) } })
}
fn plane_points(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "construct.plane.three_points";
let (a, b, c) = (geo(s, p.get("a"), cmd, "a")?, geo(s, p.get("b"), cmd, "b")?, geo(s, p.get("c"), cmd, "c")?);
+20 -1
View File
@@ -10,6 +10,14 @@ use crate::params::{bad, bool_, name_or_id_values, num, str_};
use crate::{EngineError, Result, Sel, Session, Snapshot};
pub static COMMANDS: &[CommandSpec] = &[
CommandSpec::new("ui.assistant", "Toggle Assistant", crate::checked!("ui.assistant", assistant))
.icon("assistant")
.key("Ctrl+Shift+A")
.noundo()
.doc_only()
.contract(&ASSISTANT_CONTRACT)
.output(crate::schema::output_schema_for::<crate::schema::edit::AssistantResult>)
.params("Show or hide Assistant in the app. Headless calls are refused without changing the design"),
CommandSpec::new("edit.undo", "Undo", undo).icon("undo").key("Ctrl+Z").noundo().doc_only(),
CommandSpec::new("edit.redo", "Redo", redo).icon("redo").key("Ctrl+Y").noundo().doc_only(),
CommandSpec::new("edit.repeat", "Repeat Last Command", crate::checked!("edit.repeat", repeat))
@@ -119,6 +127,12 @@ fn redo(s: &mut Session, _p: &Value) -> Result<Value> {
Ok(json!({"redone": label}))
}
const ASSISTANT_CONTRACT: crate::schema::TypedContract<crate::schema::edit::AssistantParams> = crate::schema::TypedContract::new();
fn assistant(_s: &mut Session, _p: &Value) -> Result<Value> {
Err(EngineError::Other("ui.assistant shows or hides Assistant in the app; no assistant panel exists in a headless session".into()))
}
const REPEAT_CONTRACT: crate::schema::TypedContract<crate::schema::edit::RepeatParams> = crate::schema::TypedContract::new();
/// Repeat Last Command (#7077) starts a tool, which only the app has: it starts the last one in
@@ -1123,7 +1137,7 @@ fn inspect_feature(s: &mut Session, p: &Value) -> Result<Value> {
let rolled_back = s.doc.features.iter().position(|x| x.id == id).is_some_and(|i| i >= marker);
let r = s.model.result(id);
Ok(json!({
"id": f.id, "name": f.name, "type": f.kind.type_name(), "suppressed": f.suppressed, "rolled_back": rolled_back,
"id": f.id, "name": f.name, "type": f.kind.kind_key(), "type_name": f.kind.type_name(), "suppressed": f.suppressed, "rolled_back": rolled_back,
"definition": def, "omitted": omitted, "inputs": inputs,
"error": r.and_then(|r| r.error.clone()), "warning": r.and_then(|r| r.warning.clone()),
"boolean_diagnostics": r.map(|r| &r.boolean_diagnostics),
@@ -1737,6 +1751,11 @@ fn redefine(s: &mut Session, p: &Value) -> Result<Value> {
}
trial.doc_mut().marker = Some(idx + 1);
trial.refresh();
// The edited feature keeps its name. Its temporary replacement needs a fresh one while
// the original remains in the trial as a placeholder (including construction planes).
if let Some(parameters) = params.as_object_mut() {
parameters.remove("name");
}
let r = spec.run_nested(&mut trial, &params)?;
let made = r.get("feature").and_then(Value::as_u64).ok_or_else(|| bad(cmd, format!("`{cid}` did not make a feature")))?;
let mut new = trial.doc.feature(made).cloned().ok_or_else(|| bad(cmd, format!("`{cid}` did not make a feature")))?;
+33
View File
@@ -1628,3 +1628,36 @@ fn timeline_joint_folders_reject_hostile_joint_references_atomically() {
}
}
}
/// A reference image is shared, not copied, by every edit after it: each undo state holds the
/// design as it was, and a copied image of up to 16 MiB per state would hold gigabytes.
#[test]
fn edits_after_a_canvas_share_its_image_with_every_undo_state() {
let mut s = Session::default();
let mut png = b"\x89PNG\r\n\x1a\n\0\0\0\rIHDR".to_vec();
png.extend([0, 0, 0, 4, 0, 0, 0, 2, 8, 6, 0, 0, 0, 0, 0, 0, 0]);
png.resize(64 << 10, 0);
run(&mut s, "canvas.insert", json!({"data": solidcraft_doc::canvas::base64_encode(&png), "plane": "XY", "width": 40}));
for i in 0..20 {
run(&mut s, "parameters.add", json!({"name": format!("p{i}"), "expression": "10 mm"}));
}
let image = s.doc.canvases[0].data.as_ptr();
let copies = s.undo.iter().filter_map(|u| u.doc.canvases.first()).filter(|c| c.data.as_ptr() != image).count();
assert_eq!(copies, 0, "undo states holding their own copy of the image");
for _ in 0..20 {
run(&mut s, "edit.undo", json!({}));
}
assert_eq!(s.doc.canvases[0].data.as_ptr(), image, "undo brings back the same image");
assert_eq!(s.doc.canvases[0].bytes().as_deref(), Some(png.as_slice()));
}
#[test]
fn assistant_toggle_is_refused_headlessly_without_changing_the_document() {
let mut s = Session::default();
let before = (s.doc.to_json(), s.undo.len());
let error = s.execute("ui.assistant", &json!({})).unwrap_err().to_string();
assert!(error.contains("headless session"), "{error}");
assert_eq!((s.doc.to_json(), s.undo.len()), before);
let spec = crate::find_command("ui.assistant").unwrap();
assert_eq!((spec.shortcut, spec.tab, spec.undoable), (Some("Ctrl+Shift+A"), "", false));
}
+102
View File
@@ -67,6 +67,108 @@ fn generated_face_profiles_keep_exact_links_live_after_edit_and_reopen() {
}
}
}
#[test]
fn a_component_extruded_from_another_components_face_moves_independently() {
let mut s = Session::default();
let reference = run(&mut s, "component.create", json!({"name":"Reference","grounded":false}));
run(&mut s, "solid.box", json!({"length":20,"width":20,"height":20,"name":"Base","body_name":"Stock"}));
run(&mut s, "component.activate", json!({"component":0}));
let part = run(&mut s, "component.create", json!({"name":"Part"}));
run(&mut s, "solid.extrude", json!({"face":[10,10,20],"distance":10,"operation":"new","body_name":"Pad"}));
run(&mut s, "component.activate", json!({"component":0}));
let measure = |s: &mut Session, center: [f64; 3], volume: f64| {
assert!(s.model.results.iter().all(|r| r.error.is_none()), "{:?}", s.model.results);
let got = run(s, "inspect.center_of_mass", json!({"bodies":["Pad"]}));
for (i, want) in center.iter().enumerate() {
assert!((got["center"][i].as_f64().unwrap() - want).abs() < 1e-6, "{got}; expected {center:?}");
}
assert!((got["volume"].as_f64().unwrap() - volume).abs() < 1e-6, "{got}");
};
measure(&mut s, [10., 10., 25.], 4000.);
run(&mut s, "occurrence.move", json!({"occurrence":part["occurrence"],"translate":[40,0,0],"capture":true}));
measure(&mut s, [50., 10., 25.], 4000.);
run(&mut s, "edit.undo", json!({}));
measure(&mut s, [10., 10., 25.], 4000.);
run(&mut s, "edit.redo", json!({}));
measure(&mut s, [50., 10., 25.], 4000.);
// Moving the reference is an assembly placement, not an edit to its shape.
run(&mut s, "occurrence.move", json!({"occurrence":reference["occurrence"],"translate":[0,50,0],"capture":true}));
measure(&mut s, [50., 10., 25.], 4000.);
// Both the attached plane and its projected outline still follow source dimensions.
run(&mut s, "timeline.edit", json!({"feature":"Base","set":{"length":"30","height":"25"}}));
measure(&mut s, [55., 10., 30.], 6000.);
let mut reopened = Session::new(Document::from_json(&s.doc.to_json()).unwrap());
measure(&mut reopened, [55., 10., 30.], 6000.);
run(&mut reopened, "occurrence.move", json!({"occurrence":part["occurrence"],"translate":[0,0,10],"capture":true}));
measure(&mut reopened, [55., 10., 40.], 6000.);
run(&mut reopened, "timeline.copy", json!({"features":["Base","Sketch1","Extrude1"]}));
run(&mut reopened, "timeline.paste", json!({"translate":[0,0,50]}));
let copy = run(&mut reopened, "inspect.center_of_mass", json!({"bodies":["Pad (2)"]}));
for (i, want) in [55., 10., 90.].iter().enumerate() {
assert!((copy["center"][i].as_f64().unwrap() - want).abs() < 1e-6, "{copy}");
}
assert!((copy["volume"].as_f64().unwrap() - 6000.).abs() < 1e-6);
// Reorganizing the feature definitions must preserve the captured context too.
run(&mut reopened, "component.move_features", json!({"features":["Base","Sketch1","Extrude1"],"component":0}));
measure(&mut reopened, [55., 10., 40.], 6000.);
}
#[test]
fn a_cross_component_face_profile_keeps_its_rotated_creation_context() {
let mut s = Session::default();
let source = run(&mut s, "component.create", json!({"name":"Reference","grounded":false}));
run(&mut s, "solid.box", json!({"length":20,"width":20,"height":20,"body_name":"Stock"}));
run(&mut s, "occurrence.move", json!({"occurrence":source["occurrence"],"translate":[30,20,0],"angle":"90 deg"}));
let part = run(&mut s, "component.create", json!({"name":"Part","parent":0}));
run(&mut s, "occurrence.move", json!({"occurrence":part["occurrence"],"translate":[-20,10,0],"angle":"-90 deg","capture":false}));
run(&mut s, "solid.extrude", json!({"face":[20,30,20],"distance":10,"operation":"new","body_name":"Pad"}));
assert!(s.pending_moves.is_empty(), "face capture records the author's pending placement");
run(&mut s, "component.activate", json!({"component":0}));
let center = |s: &mut Session, want: [f64; 3]| {
let got = run(s, "inspect.center_of_mass", json!({"bodies":["Pad"]}));
for (i, want) in want.iter().enumerate() {
assert!((got["center"][i].as_f64().unwrap() - want).abs() < 1e-6, "{got}");
}
assert!((got["volume"].as_f64().unwrap() - 4000.).abs() < 1e-6);
};
center(&mut s, [20., 30., 25.]);
run(&mut s, "occurrence.move", json!({"occurrence":part["occurrence"],"translate":[50,0,5],"angle":"90 deg","capture":false}));
center(&mut s, [20., 20., 30.]);
run(&mut s, "component.capture_position", json!({}));
center(&mut s, [20., 20., 30.]);
let mut reopened = Session::new(Document::from_json(&s.doc.to_json()).unwrap());
center(&mut reopened, [20., 20., 30.]);
}
#[test]
fn moving_the_original_face_derived_component_does_not_move_its_copies_backwards() {
let mut s = Session::default();
run(&mut s, "component.create", json!({"name":"Reference"}));
run(&mut s, "solid.box", json!({"length":20,"width":20,"height":20}));
let part = run(&mut s, "component.create", json!({"name":"Part","parent":0}));
run(&mut s, "solid.extrude", json!({"face":[10,10,20],"distance":10,"operation":"new","body_name":"Pad"}));
run(&mut s, "component.activate", json!({"component":0}));
let copy = run(&mut s, "occurrence.copy", json!({"component":part["component"],"translate":[0,40,0]}));
run(&mut s, "occurrence.copy", json!({"component":part["component"],"translate":[0,80,0]}));
run(&mut s, "occurrence.move", json!({"occurrence":copy["occurrence"],"translate":[10,0,0],"capture":true}));
let centers = |s: &mut Session, original: [f64; 3]| {
for (body, want) in [("Pad", original), ("Pad (Part:2)", [20., 50., 25.]), ("Pad (Part:3)", [10., 90., 25.])] {
let got = run(s, "inspect.center_of_mass", json!({"bodies":[body]}));
for (i, want) in want.iter().enumerate() {
assert!((got["center"][i].as_f64().unwrap() - want).abs() < 1e-6, "{body}: {got}");
}
assert!((got["volume"].as_f64().unwrap() - 4000.).abs() < 1e-6, "{got}");
}
};
centers(&mut s, [10., 10., 25.]);
run(&mut s, "occurrence.move", json!({"occurrence":part["occurrence"],"translate":[40,0,0],"capture":true}));
centers(&mut s, [50., 10., 25.]);
let mut reopened = Session::new(Document::from_json(&s.doc.to_json()).unwrap());
centers(&mut reopened, [50., 10., 25.]);
run(&mut reopened, "occurrence.move", json!({"occurrence":part["occurrence"],"angle":"90 deg","capture":true}));
centers(&mut reopened, [-10., 50., 25.]);
}
#[test]
fn internal_capture_does_not_bypass_missing_ambiguous_or_invalid_path_picks() {
let mut s = Session::default();
+9 -8
View File
@@ -1591,8 +1591,8 @@ pub(crate) fn edge_ref_form(e: &Value) -> std::result::Result<EdgeRefForm<'_>, &
Ok(EdgeRefForm::Index { body, index })
}
/// Press Pull: a face moves along its normal (an extrude that joins outward or cuts inward); an
/// edge is rounded (a fillet).
/// Press Pull: a face moves along its normal (an extrude that joins outward or cuts inward); a
/// fillet's or chamfer's face changes that feature's size; an edge is rounded (a fillet).
fn press_pull(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "solid.press_pull";
let distance = req_expr(cmd, p, "distance")?;
@@ -1604,10 +1604,9 @@ fn press_pull(s: &mut Session, p: &Value) -> Result<Value> {
if d.abs() < 1e-9 {
return Err(bad(cmd, "the distance must not be zero"));
}
// A fillet's or chamfer's face: its size changes by the distance (the feature is edited).
if let Some(fp) = vec3(face)
&& super::face::face_is_planar(s, fp) == Some(false)
{
// A fillet's or chamfer's face: its size changes by the distance (the feature is edited). A
// chamfer's face is flat, so this is asked before whether the face is planar (#4986).
if let Some(fp) = vec3(face) {
let name = solidcraft_doc::naming::face_names_at(&s.model.state(), &[fp]).into_iter().next().unwrap_or_default();
let fid = solidcraft_doc::persistent_name::made_by(&name);
if solidcraft_doc::persistent_name::is_blend(&name) {
@@ -1625,8 +1624,10 @@ fn press_pull(s: &mut Session, p: &Value) -> Result<Value> {
return Ok(json!({"feature": f.id, "edited": f.name, key: new, "recomputed": out.get("recomputed"), "restored": out.get("restored")}));
}
// Another curved face moves along its normal.
let r = super::find_command("solid.offset_face").ok_or_else(|| bad(cmd, "solid.offset_face"))?;
return (r.run)(s, &json!({"faces": [face], "distance": distance}));
if super::face::face_is_planar(s, fp) == Some(false) {
let r = super::find_command("solid.offset_face").ok_or_else(|| bad(cmd, "solid.offset_face"))?;
return (r.run)(s, &json!({"faces": [face], "distance": distance}));
}
}
// Pushed in: the extrude cuts the other way by as much (a bare number stays one, so it keeps
// being read as a bare number typed into the extrude).
+240 -11
View File
@@ -9,6 +9,9 @@ mod easyprint;
#[path = "file_import.rs"]
pub(crate) mod import;
#[path = "file_open_steps.rs"]
pub(crate) mod open_steps;
#[cfg(not(target_arch = "wasm32"))]
#[path = "file_send.rs"]
mod send;
@@ -58,6 +61,39 @@ pub static COMMANDS: &[CommandSpec] = &[
.icon("export")
.noundo()
.params("path; format?: stl|stla|obj|step|3mf|3mf-tagged|urdf|json (default from extension; 3mf-tagged: 3MF whose triangles carry their B-rep face, at full precision, for meshing and analysis tools; docs/formats/3mf-face-tags.md; obj: the colours of the bodies go in a material library (`.mtl`) written next to the mesh file; urdf: the whole assembly as a robot, with STL meshes written next to it; docs/urdf.md); bodies?: [names] (JSON and URDF export the complete design); urdf only: mesh_prefix? (before each mesh file name, e.g. package://robot/meshes/), effort? (default 1000), velocity? (default 10); quality?: standard|fine|finest (STL, OBJ and 3MF: how closely the mesh follows curved faces, a chord tolerance of 2e-4, 5e-5 or 1e-5 of each body's size; default standard); objects?: body|component (3MF only: one object per body, the default, or one object for each placed component with two or more bodies, with those bodies as its parts, so a slicer loads them as one object with parts)"),
// One command per format, so each File › Export item can have a key of its own; started
// from a key or command search, the app opens the save picker (the mesh dialog for STL and
// 3MF). Each takes `file.export`'s parameters for its format, without `format`.
CommandSpec::new("file.export_step", "Export STEP", crate::checked!("file.export_step", export_step))
.icon("export")
.noundo()
.contract(&EXPORT_STEP_CONTRACT)
.output(crate::schema::output_schema_for::<crate::schema::file::ExportResult>)
.params("path; bodies?: [names] (default: every body; a design with components then exports as a STEP assembly) → {path, bytes, format}: STEP, whatever the path's extension"),
CommandSpec::new("file.export_stl", "Export STL", crate::checked!("file.export_stl", export_stl))
.icon("mesh_export")
.noundo()
.contract(&EXPORT_STL_CONTRACT)
.output(crate::schema::output_schema_for::<crate::schema::file::ExportResult>)
.params("path; bodies?: [names] (default: every body); quality?: standard|fine|finest (default standard) → {path, bytes, format}: binary STL, whatever the path's extension"),
CommandSpec::new("file.export_3mf", "Export 3MF", crate::checked!("file.export_3mf", export_3mf))
.icon("mesh_export")
.noundo()
.contract(&EXPORT_3MF_CONTRACT)
.output(crate::schema::output_schema_for::<crate::schema::file::ExportResult>)
.params("path; bodies?: [names] (default: every body); quality?: standard|fine|finest (default standard); objects?: body|component (as file.export) → {path, bytes, format}: 3MF, whatever the path's extension"),
CommandSpec::new("file.export_obj", "Export OBJ", crate::checked!("file.export_obj", export_obj_cmd))
.icon("export")
.noundo()
.contract(&EXPORT_OBJ_CONTRACT)
.output(crate::schema::output_schema_for::<crate::schema::file::ExportResult>)
.params("path; bodies?: [names] (default: every body); quality?: standard|fine|finest (default standard) → {path, bytes, format, files}: OBJ, with the colours of the bodies in a material library (`.mtl`) next to it"),
CommandSpec::new("file.export_urdf", "Export URDF", crate::checked!("file.export_urdf", export_urdf_cmd))
.icon("export")
.noundo()
.contract(&EXPORT_URDF_CONTRACT)
.output(crate::schema::output_schema_for::<crate::schema::file::UrdfExportResult>)
.params("path; mesh_prefix?; effort? (default 1000); velocity? (default 10) → {path, format, files, links, joints, warnings}: the whole assembly as a robot, with STL meshes written next to it; docs/urdf.md"),
CommandSpec::new("file.save_mesh", "Save As Mesh", save_mesh)
.at("INSERT", "FILES")
.icon("mesh_export")
@@ -86,6 +122,12 @@ fn new_doc(s: &mut Session, p: &Value) -> Result<Value> {
Ok(json!({"name": name}))
}
const EXPORT_STEP_CONTRACT: crate::schema::TypedContract<crate::schema::file::ExportStepParams> = crate::schema::TypedContract::new();
const EXPORT_STL_CONTRACT: crate::schema::TypedContract<crate::schema::file::ExportMeshParams> = crate::schema::TypedContract::new();
const EXPORT_3MF_CONTRACT: crate::schema::TypedContract<crate::schema::file::Export3mfParams> = crate::schema::TypedContract::new();
const EXPORT_OBJ_CONTRACT: crate::schema::TypedContract<crate::schema::file::ExportMeshParams> = crate::schema::TypedContract::new();
const EXPORT_URDF_CONTRACT: crate::schema::TypedContract<crate::schema::file::ExportUrdfParams> = crate::schema::TypedContract::new();
fn path_arg<'a>(p: &'a Value, cmd: &str) -> Result<&'a str> {
str_(p, "path").filter(|x| !x.trim().is_empty() && x.len() < 4096).ok_or_else(|| bad(cmd, "`path` is required"))
}
@@ -339,11 +381,29 @@ fn open(s: &mut Session, p: &Value) -> Result<Value> {
let read_only = bool_(p, "read_only").unwrap_or(false);
// Stamped before it is read: a write while it is being read is a change too.
let disk = solidcraft_io::design_lock::stamp(path);
let (doc, saved) = read_design(s, path)?;
opened_design(s, Session::opened_at(doc, saved.as_ref(), Some(path)), path, read_only, disk)
}
/// Read the design file at `path` for `doc.open`, under its file's name if it has none of its own.
fn read_design(s: &mut Session, path: &str) -> Result<(Document, Option<solidcraft_doc::SavedModel>)> {
let (mut doc, saved) = solidcraft_io::read_design_file(path, s.open_preview.take()).map_err(|e| EngineError::Other(format!("{path}: {e}")))?;
if let Some(stem) = default_name_for(&doc.name, path) {
doc.name = stem;
}
s.replace_design(Session::opened_at(doc, saved.as_ref(), Some(path)));
Ok((doc, saved))
}
/// `doc.open` of a design: `opened`, the design read from `path` and evaluated, takes `s`'s
/// place, and the reply says what it is.
fn opened_design(
s: &mut Session,
opened: Session,
path: &str,
read_only: bool,
disk: Option<solidcraft_io::design_lock::FileStamp>,
) -> Result<Value> {
s.replace_design(opened);
s.disk = disk;
s.read_only = read_only;
let errors = s.model.results.iter().filter(|r| r.error.is_some()).count();
@@ -475,7 +535,12 @@ impl Session {
/// them. Absolute in-memory targets survive Save As, undo, redo and recovery unchanged.
pub fn opened_at(mut doc: Document, saved: Option<&solidcraft_doc::SavedModel>, path: Option<&str>) -> Self {
super::component_link::resolve_design_links(&mut doc, path);
let mut session = Self::opened(doc, saved);
Self::opened(doc, saved).located(path)
}
/// This session, just opened, as the design in the file at `path`.
pub(crate) fn located(self, path: Option<&str>) -> Self {
let mut session = self;
session.path = path.map(str::to_owned);
// A design saved before body names were pinned (#6403) is pinned now, as its first
// command would pin it: that command pinned them as part of itself, so undoing it went
@@ -674,6 +739,7 @@ fn component_parts(s: &Session) -> Vec<(String, Vec<String>)> {
fn export_to(
s: &Session,
cmd: &str,
path: &str,
format: Format,
bodies: &[String],
@@ -688,7 +754,7 @@ fn export_to(
// A design with components exports as a STEP assembly; otherwise bodies as placed.
let bytes = if format == Format::Design {
if !bodies.is_empty() {
return Err(bad("file.export", "design JSON exports the complete document; omit bodies"));
return Err(bad(cmd, "design JSON exports the complete document; omit bodies"));
}
solidcraft_io::write_design(&s.design())
} else if format == Format::Step && bodies.is_empty() && !s.doc.occurrences.is_empty() {
@@ -727,11 +793,44 @@ fn export(s: &mut Session, p: &Value) -> Result<Value> {
let path = path_arg(p, cmd)?;
let format = Format::from_name(str_(p, "format").unwrap_or(path))?;
if format == Format::Urdf {
return export_urdf(s, path, p);
if !string_list(cmd, p, "bodies")?.is_empty() {
return Err(bad(cmd, "urdf exports the whole assembly; omit bodies"));
}
return export_urdf(s, cmd, path, p);
}
let quality = mesh_quality(p, cmd)?;
let parts = mesh_parts(s, p, cmd, format)?;
export_to(s, path, format, &string_list(cmd, p, "bodies")?, &parts, quality)
export_to(s, cmd, path, format, &string_list(cmd, p, "bodies")?, &parts, quality)
}
fn export_step(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "file.export_step";
let path = path_arg(p, cmd)?;
export_to(s, cmd, path, Format::Step, &string_list(cmd, p, "bodies")?, &[], solidcraft_io::MeshQuality::default())
}
fn export_stl(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "file.export_stl";
let path = path_arg(p, cmd)?;
export_to(s, cmd, path, Format::StlBinary, &string_list(cmd, p, "bodies")?, &[], mesh_quality(p, cmd)?)
}
fn export_3mf(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "file.export_3mf";
let path = path_arg(p, cmd)?;
let parts = mesh_parts(s, p, cmd, Format::ThreeMf)?;
export_to(s, cmd, path, Format::ThreeMf, &string_list(cmd, p, "bodies")?, &parts, mesh_quality(p, cmd)?)
}
fn export_obj_cmd(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "file.export_obj";
let path = path_arg(p, cmd)?;
export_to(s, cmd, path, Format::Obj, &string_list(cmd, p, "bodies")?, &[], mesh_quality(p, cmd)?)
}
fn export_urdf_cmd(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "file.export_urdf";
export_urdf(s, cmd, path_arg(p, cmd)?, p)
}
/// The OBJ export: the mesh file and the material library beside it, with the colours of the
@@ -762,11 +861,7 @@ fn export_obj(s: &Session, path: &str, bodies: &[String], quality: solidcraft_io
/// The assembly as a URDF and its meshes next to it (docs/urdf.md). Every path is checked before
/// anything is written; the meshes go first and the `.urdf` last.
fn export_urdf(s: &Session, path: &str, p: &Value) -> Result<Value> {
let cmd = "file.export";
if !string_list(cmd, p, "bodies")?.is_empty() {
return Err(bad(cmd, "urdf exports the whole assembly; omit bodies"));
}
fn export_urdf(s: &Session, cmd: &str, path: &str, p: &Value) -> Result<Value> {
let positive = |k: &str| match p.get(k) {
None | Some(Value::Null) => Ok(None),
Some(_) => num(p, k).filter(|x| *x > 0.0).map(Some).ok_or_else(|| bad(cmd, format!("`{k}` must be a positive number"))),
@@ -861,7 +956,7 @@ fn save_mesh(s: &mut Session, p: &Value) -> Result<Value> {
},
);
}
export_to(s, path, format, &string_list(cmd, p, "bodies")?, &parts, quality)
export_to(s, cmd, path, format, &string_list(cmd, p, "bodies")?, &parts, quality)
}
/// The browser build can't start a program.
@@ -1024,6 +1119,43 @@ mod tests {
let _ = std::fs::remove_dir_all(&dir);
}
/// Each format's own export command (one key each in Keyboard Shortcuts) writes that format
/// whatever the path's extension, as `file.export` does with `format`, and says so.
#[test]
fn each_format_has_an_export_command_of_its_own() {
let dir = std::env::temp_dir().join(format!("solidcraft-export-per-format-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let at = |n: &str| dir.join(n).to_string_lossy().to_string();
let mut s = Session::default();
s.execute("solid.box", &json!({"length": 20, "width": 10, "height": 5, "body_name": "Block"})).unwrap();
for (cmd, format, signature) in [
("file.export_step", "Step", &b"ISO-10303-21;"[..]),
("file.export_3mf", "ThreeMf", &b"PK"[..]),
("file.export_obj", "Obj", &b""[..]),
("file.export_stl", "StlBinary", &b""[..]),
] {
// A name with another format's extension: the command, not the name, picks the format.
let path = at(&format!("{format}.txt"));
let r = s.execute(cmd, &json!({"path": path})).unwrap();
assert_eq!((r["path"].as_str(), r["format"].as_str()), (Some(path.as_str()), Some(format)), "{cmd}: {r}");
let bytes = std::fs::read(&path).unwrap();
assert_eq!(r["bytes"].as_u64(), Some(bytes.len() as u64), "{cmd}");
assert!(bytes.starts_with(signature), "{cmd}");
}
// The triangles `file.export` writes (the 80-byte header holds the file's name): a box is
// 12 triangles, 84 + 50 bytes each.
s.execute("file.export", &json!({"path": at("box.stl")})).unwrap();
assert_eq!(std::fs::read(at("StlBinary.txt")).unwrap()[80..], std::fs::read(at("box.stl")).unwrap()[80..]);
assert_eq!(std::fs::metadata(at("box.stl")).unwrap().len(), 84 + 50 * 12);
let obj = std::fs::read_to_string(at("Obj.txt")).unwrap();
assert_eq!(obj.lines().filter(|l| l.starts_with("f ")).count(), 12, "{obj}");
let r = s.execute("file.export_urdf", &json!({"path": at("robot.urdf")})).unwrap();
assert_eq!(r["format"], "Urdf");
assert!(std::fs::read_to_string(at("robot.urdf")).unwrap().contains("<robot"));
let _ = std::fs::remove_dir_all(&dir);
}
/// #4747: a mesh export's `quality` follows curved faces more closely. A 3 mm hole in a
/// 200 mm plate gets more triangles at fine and more again at finest, in STL and 3MF alike
/// (Save As Mesh too); standard is what the export always gave; anything else is refused.
@@ -1399,6 +1531,41 @@ mod tests {
assert!(d.rejected.is_empty());
}
/// The model a save keeps writes each distinct sheet-metal body once, and every state names
/// the records of exactly the sheets it holds (found by the sheet's text, not by building its
/// record again for every state, which took most of a large sheet-metal design's save).
#[test]
fn a_saved_model_writes_each_sheet_once_for_the_states_that_hold_it() {
let mut s = Session::new(Document::new("Sheets"));
let sheet_rule = json!({"name": "AL2", "thickness": "2 mm", "k_factor": "0.5", "bend_radius": "1 mm", "active": true});
s.execute("sheet.manage_rules", &sheet_rule).unwrap();
for (i, x) in [0.0, 200.0, 400.0].into_iter().enumerate() {
s.execute("sketch.create", &json!({"plane": "XY", "name": format!("Base{i}")})).unwrap();
s.execute("sketch.rectangle.two_point", &json!({"p0": [x, 0], "p1": [x + 150.0, 100]})).unwrap();
s.execute("sketch.finish", &json!({})).unwrap();
s.execute("sheet.flange", &json!({"sketch": format!("Base{i}")})).unwrap();
s.execute("sheet.flange", &json!({"edges": [[x + 75.0, 0, 2]], "height": 20})).unwrap();
}
let saved = saved_geometry(&s, &s.doc).unwrap();
let records = &saved.records;
let sheets = records["sheets"].as_array().unwrap();
let texts: std::collections::HashSet<String> = sheets.iter().map(Value::to_string).collect();
assert_eq!(texts.len(), sheets.len(), "each sheet once");
let mut held = std::collections::HashSet::new();
for (r, feature) in s.model.results.iter().zip(records["features"].as_array().unwrap()) {
let state = &records["states"][feature["state"].as_u64().unwrap() as usize];
let named = state["sheets"].as_array().unwrap();
assert_eq!(named.len(), r.state.sheets.len());
for (i, sheet) in named.iter().zip(&r.state.sheets) {
let written = &sheets[i.as_u64().unwrap() as usize];
assert_eq!(*written, serde_json::to_value(sheet).unwrap(), "{}: the record of its own sheet", r.name);
held.insert(written.to_string());
}
}
assert_eq!(held, texts, "every record written is one a state holds");
assert!(sheets.len() >= 6, "each part's base, then with its flange: {}", sheets.len());
}
/// A design whose saved model would give a body back with other topology opens with the
/// bodies it was saved with. A pattern copy cut by an automatic extrude came back from the
/// native codec with two loose vertices more (10 faces, 18 edges, 12 vertices instead of 10);
@@ -1855,6 +2022,68 @@ mod tests {
std::fs::remove_dir_all(dir).unwrap();
}
/// #10198: a design opened a feature at a time ([`OpenSteps`]) is the design `doc.open`
/// opens: the same features, results, bodies and history marker, with its file and name. Its
/// import is offered before the step that evaluates it, and other files are left to `doc.open`.
#[test]
fn a_design_opened_a_feature_at_a_time_is_the_one_doc_open_opens() {
use super::open_steps::OpenSteps;
let dir = std::env::temp_dir().join(format!("solidcraft-open-steps-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("steps.sc").to_string_lossy().to_string();
let step = concat!(env!("CARGO_MANIFEST_DIR"), "/../../tests/step-conformance/cylinder_seam.step");
let mut s = Session::default();
s.execute("solid.box", &json!({"length":10,"width":10,"height":10})).unwrap();
s.execute("file.insert_step", &json!({"path": step})).unwrap();
s.execute("component.activate", &json!({"component": "root"})).unwrap();
s.execute("solid.box", &json!({"length":5,"width":5,"height":5,"corner":[40,0,0]})).unwrap();
s.execute("solid.box", &json!({"length":5,"width":5,"height":5,"corner":[60,0,0]})).unwrap();
let features = s.doc.features.len();
s.execute("timeline.rollback", &json!({"position": features - 1})).unwrap();
s.execute("file.save", &json!({"path": path, "cache": false})).unwrap();
let mut whole = Session::default();
let opened = whole.execute("doc.open", &json!({"path": path, "read_only": true})).unwrap();
let mut steps = OpenSteps::start(&json!({"path": path, "read_only": true})).unwrap().expect("a design is opened in steps");
assert_eq!(steps.path(), path);
assert_eq!(steps.progress(), (0, features - 1), "the saved marker is where evaluation stops");
let mut imports = 0;
let mut n = 0;
while !steps.done() {
if let Some(text) = steps.next_import() {
assert!(text.contains("ISO-10303-21"));
imports += 1;
}
steps.step();
n += 1;
assert_eq!(steps.progress().0, n);
}
assert_eq!((imports, n), (1, features - 1));
assert!(!steps.step(), "nothing is left to evaluate");
let mut stepped = Session::default();
let reply = steps.finish(&mut stepped).unwrap();
assert_eq!(reply, opened);
assert_eq!(stepped.doc, whole.doc);
assert_eq!(stepped.doc.marker, Some(features - 1));
assert_eq!((stepped.path.as_deref(), stepped.read_only), (Some(path.as_str()), true));
let summary = |s: &Session| -> Vec<(u64, bool, bool)> { s.model.results.iter().map(|r| (r.id, r.skipped, r.error.is_some())).collect() };
assert_eq!(summary(&stepped), summary(&whole));
let bodies =
|s: &Session| -> Vec<(String, usize)> { s.world_state().bodies.iter().map(|b| (b.name.clone(), b.mesh().triangles.len())).collect() };
assert_eq!(bodies(&stepped), bodies(&whole));
assert_eq!(stepped.world_state().bodies.len(), 3, "the box, the cylinder and the box before the marker");
assert!(stepped.model.is_current(&stepped.doc));
// Other files, and STEP-only options, are opened by `doc.open`.
for p in [json!({"path": step}), json!({"path": "part.stl"}), json!({"path": path, "up": "y"}), json!({})] {
assert!(OpenSteps::start(&p).unwrap().is_none(), "{p}");
}
assert!(OpenSteps::start(&json!({"path": dir.join("missing.sc").to_string_lossy()})).is_err());
std::fs::remove_dir_all(dir).unwrap();
}
/// Every option the format registry offers is a parameter its owning command documents,
/// so the registry and the commands can't drift apart.
#[test]
+125
View File
@@ -0,0 +1,125 @@
//! Opening a design a feature at a time, for a caller that must keep doing other work while a
//! large design is read: the web app has one thread, which has to keep drawing frames (and offer
//! Cancel) meanwhile (#10198). [`OpenSteps::start`] reads the file, [`OpenSteps::step`] evaluates
//! the next feature of its timeline, and [`OpenSteps::finish`] puts the design in the session's
//! place as `doc.open` does. The result is the same design, evaluated the same way: each step
//! moves a copy's history marker one feature on, so the evaluation reuses every result before it.
use std::sync::Arc;
use serde_json::Value;
use solidcraft_doc::{Document, FeatureKind, Model, Visibility};
use super::{opened_design, read_design};
use crate::params::bool_;
use crate::{Result, Session};
/// A design file being opened ([`OpenSteps::start`]).
pub struct OpenSteps {
path: String,
read_only: bool,
disk: Option<solidcraft_io::design_lock::FileStamp>,
/// The design as the session will hold it, its history marker moved to `next` meanwhile.
doc: Arc<Document>,
/// The history marker the design was saved with.
marker: Option<usize>,
visibility: Visibility,
model: Model,
/// Features evaluated so far, and where evaluation stops (the saved marker, or the end).
next: usize,
stop: usize,
/// The model came from the file and needs no evaluation.
adopted: bool,
}
impl OpenSteps {
/// Read the design file `doc.open` would open with parameters `p`, without evaluating it.
/// `None` when `p` names a file `doc.open` reads in another way (a CAD, mesh or drawing file,
/// or a point cloud), or asks for something only those take: run `doc.open` for it.
pub fn start(p: &Value) -> Result<Option<OpenSteps>> {
let Some(path) = p.get("path").and_then(Value::as_str) else { return Ok(None) };
let other = solidcraft_io::is_step_path(path)
|| solidcraft_io::is_mesh_path(path)
|| solidcraft_io::is_point_cloud_path(path)
|| super::super::drawing_import_command(path).is_some()
|| ["names", "up"].iter().any(|k| p.get(k).is_some_and(|v| !v.is_null()));
if other || path.trim().is_empty() || path.len() >= 4096 {
return Ok(None);
}
let read_only = bool_(p, "read_only").unwrap_or(false);
// Stamped before it is read, as `doc.open` does.
let disk = solidcraft_io::design_lock::stamp(path);
let (mut doc, saved) = read_design(&mut Session::default(), path)?;
super::super::component_link::resolve_design_links(&mut doc, Some(path));
// What `Session::opened` does before it evaluates.
solidcraft_io::vfs::install_checks();
let visibility = std::mem::take(&mut doc.visibility);
let doc = Arc::new(doc);
let marker = doc.marker;
let stop = marker.unwrap_or(usize::MAX).min(doc.features.len());
let adopted = Session::saved_model(&doc, saved.as_ref());
let steps = OpenSteps {
path: path.to_string(),
read_only,
disk,
doc,
marker,
visibility,
next: if adopted.is_some() { stop } else { 0 },
stop,
adopted: adopted.is_some(),
model: adopted.unwrap_or_default(),
};
Ok(Some(steps))
}
/// The file being opened.
pub fn path(&self) -> &str {
&self.path
}
/// Features evaluated so far, and how many there are to evaluate.
pub fn progress(&self) -> (usize, usize) {
(self.next, self.stop)
}
/// Whether every feature has been evaluated ([`Self::finish`] is next).
pub fn done(&self) -> bool {
self.next >= self.stop
}
/// The STEP text the next feature imports, when it is an import that is not suppressed: a
/// caller can read it in parts first (`kernel::StepReading`), and while it holds that
/// reading's result the step finds the file read.
pub fn next_import(&self) -> Option<Arc<str>> {
let f = self.doc.features.get(self.next).filter(|_| !self.done())?;
match &f.kind {
FeatureKind::Import { step, .. } if !f.suppressed => Some(step.clone()),
_ => None,
}
}
/// Evaluate the next feature. False once there is none left.
pub fn step(&mut self) -> bool {
if self.done() {
return false;
}
self.next += 1;
Arc::make_mut(&mut self.doc).marker = Some(self.next);
self.model.evaluate(&self.doc);
!self.done()
}
/// Put the design in `s`'s place as `doc.open` does, and answer as it does. Features not
/// yet evaluated are evaluated now.
pub fn finish(mut self, s: &mut Session) -> Result<Value> {
Arc::make_mut(&mut self.doc).marker = self.marker;
if !self.adopted {
// Every result is current: this takes them all and keeps the placement proof
// `Session::opened` keeps.
self.model.evaluate_shared(&self.doc);
}
let opened = Session::with_model(self.doc, self.visibility, self.model).located(Some(&self.path));
opened_design(s, opened, &self.path, self.read_only, self.disk)
}
}
+8 -4
View File
@@ -27,7 +27,7 @@ pub static COMMANDS: &[CommandSpec] = &[
.icon("measure")
.key("I")
.noundo()
.params("bodies?: [names] (default all) → closed per body; volume_mm3, center_of_mass, density_g_cm3 and mass_g only for closed, consistently oriented bodies; area, bbox and counts for all; total.open_bodies_skipped counts bodies excluded from volume/mass totals; or items: [1-2 selections] (face/edge: type, body, point; index optional) → length/area/volume; diameter, radius, circumference or arc_length/angle_deg of circles, arcs and cylinders; slot sizes; min (distance_mm, from, to), max, center_distance (centre to centre, or from an axis) and angle"),
.params("bodies?: [names] (default all); names_only?: bool (each world body's name and its definition's, nothing measured) → closed per body; volume_mm3, center_of_mass, density_g_cm3 and mass_g only for closed, consistently oriented bodies; area, bbox and counts for all; total.open_bodies_skipped counts bodies excluded from volume/mass totals; or items: [1-2 selections] (face/edge: type, body, point; index optional) → length/area/volume; diameter, radius, circumference or arc_length/angle_deg of circles, arcs and cylinders; slot sizes; min (distance_mm, from, to), max, center_distance (centre to centre, or from an axis) and angle"),
CommandSpec::new("document.inspect", "Inspect Design", inspect).icon("inspect").noundo().params("measure?: bool (include body measurements)"),
CommandSpec::new("document.revision", "Design Revision", revision).icon("inspect").noundo().params(
"→ document_id (this open design's id: kept while it stays open, another for each design started or opened), \
@@ -123,6 +123,13 @@ fn measure(s: &mut Session, p: &Value) -> Result<Value> {
let want = string_list(cmd, p, "bodies")?;
let st = s.world_state();
let selected: Vec<_> = st.bodies.iter().filter(|b| want.is_empty() || want.contains(&b.name)).collect();
if !want.is_empty() && selected.len() != want.len() {
return Err(bad(cmd, "unknown body name"));
}
if bool_(p, "names_only").unwrap_or(false) {
let bodies: Vec<_> = selected.iter().map(|b| json!({"name": b.name, "definition": b.definition_name()})).collect();
return Ok(json!({"body_count": bodies.len(), "bodies": bodies}));
}
// Each body is measured on its own: on all cores, in body order.
let mut bodies: Vec<Value> = solidcraft_kernel::map_on_threads(&selected, 16, |b| measure_json(b))
.into_iter()
@@ -140,9 +147,6 @@ fn measure(s: &mut Session, p: &Value) -> Result<Value> {
b["mass_g"] = json!(v * rho / 1000.0);
}
}
if !want.is_empty() && bodies.len() != want.len() {
return Err(bad(cmd, "unknown body name"));
}
// Sheet metal keeps the edges where bends meet flat faces (so bends stay visible): count its
// B-rep faces as they are, without merging coplanar neighbours.
let sheets = s.model.state().sheets.clone();
+151 -1
View File
@@ -286,6 +286,31 @@ fn describe(s: &Session, sel: &Sel) -> Option<Item> {
let st = s.world_state();
let plain = |geo, dir, json| Some(Item { geo, dir, json, shape: Shape::Other, slot: None });
match sel {
Sel::Plane { name } => {
let (point, normal) = plane_named(s, name)?;
Some(Item {
geo: Geo::Points(vec![point]),
dir: Some(normal),
json: json!({"type": "plane", "point": point, "normal": normal}),
shape: Shape::Plane { point, normal: UnitVec3::new(normal)? },
slot: None,
})
}
Sel::Axis { name } => {
let (origin, dir) = axis_named(s, name)?;
let (a, b) = (origin - dir * 1e6, origin + dir * 1e6);
Some(Item {
geo: Geo::Points(vec![origin, a, b]),
dir: Some(dir),
json: json!({"type": "axis", "origin": origin, "direction": dir}),
shape: Shape::Line { a, b },
slot: None,
})
}
Sel::ConstructionPoint { name } => {
let p = crate::view::construction_points(s).into_iter().find(|(_, n, _)| n == name).map(|(_, _, p)| p)?;
Some(Item { geo: Geo::Points(vec![p]), dir: None, json: json!({"type": "point", "position": p}), shape: Shape::Point(p), slot: None })
}
Sel::Vertex { point, .. } => Some(Item {
geo: Geo::Points(vec![*point]),
dir: None,
@@ -328,7 +353,8 @@ fn describe(s: &Session, sel: &Sel) -> Option<Item> {
let planar = normals.first().copied().filter(|n0| normals.iter().all(|n| n.dot(*n0) > 1.0 - 1e-6));
let mut json = json!({"type": "face", "area_mm2": area, "planar": planar.is_some()});
let verts: Vec<Vec3> = tris.iter().flat_map(|t| t.iter().copied()).collect();
let shape = match solidcraft_kernel::face_surface(&b.body, *index) {
let surface = solidcraft_kernel::face_surface(&b.body, *index);
let shape = match surface {
Some(solidcraft_kernel::FaceSurface::Cylinder { origin, axis, radius }) => {
super::measure_shape::cylinder(origin, axis, radius, &verts).unwrap_or(Shape::Other)
}
@@ -340,6 +366,7 @@ fn describe(s: &Session, sel: &Sel) -> Option<Item> {
_ => Shape::Other,
};
add_round(&mut json, &shape);
super::measure_shape::add_curved(&mut json, surface);
let slot = if matches!(shape, Shape::Round { .. }) && !shape.full() { face_slot(b, &m, &shape, &verts) } else { None };
if let Some(sl) = &slot {
json["slot"] = sl.json();
@@ -397,6 +424,122 @@ fn sample<T: Copy>(v: &[T], max: usize) -> Vec<T> {
}
/// Closest pair of points between two pieces of geometry.
/// An origin plane (XY, XZ, YZ) or a construction plane's point and normal.
fn plane_named(s: &Session, name: &str) -> Option<(Vec3, Vec3)> {
match name {
"XY" => Some((Vec3::ZERO, Vec3::Z)),
"XZ" => Some((Vec3::ZERO, Vec3::Y)),
"YZ" => Some((Vec3::ZERO, Vec3::X)),
_ => crate::view::construction_planes(s).into_iter().find(|(_, n, _)| n == name).map(|(_, _, p)| (p.origin, p.x.cross(p.y))),
}
}
/// An origin axis (X, Y, Z) or a construction axis's point and unit direction.
fn axis_named(s: &Session, name: &str) -> Option<(Vec3, Vec3)> {
match name {
"X" => Some((Vec3::ZERO, Vec3::X)),
"Y" => Some((Vec3::ZERO, Vec3::Y)),
"Z" => Some((Vec3::ZERO, Vec3::Z)),
_ => crate::view::construction_axes(s).into_iter().find(|(_, n, _, _)| n == name).map(|(_, _, origin, dir)| (origin, dir)),
}
}
/// The closest points of two exact shapes, where the sampled points would be wrong: a plane or
/// an axis runs on, so a point set of it is not the object.
fn exact_min(a: &Shape, b: &Shape) -> Option<(f64, Vec3, Vec3)> {
use Shape::*;
let to_plane = |p: Vec3, point: Vec3, normal: UnitVec3| {
let n = normal.vec();
let h = (p - point).dot(n);
(h.abs(), p, p - n * h)
};
let to_line = |p: Vec3, a: Vec3, b: Vec3| {
let d = b - a;
let t = if d.len() > 1e-12 { ((p - a).dot(d) / d.dot(d)).clamp(0.0, 1.0) } else { 0.0 };
let q = a + d * t;
(p.dist(q), p, q)
};
match (a, b) {
(Point(p), Plane { point, normal }) => Some(to_plane(*p, *point, *normal)),
(Plane { point, normal }, Point(p)) => {
let (d, f, t) = to_plane(*p, *point, *normal);
Some((d, t, f))
}
(Point(p), Line { a: la, b: lb }) => Some(to_line(*p, *la, *lb)),
(Line { a: la, b: lb }, Point(p)) => {
let (d, f, t) = to_line(*p, *la, *lb);
Some((d, t, f))
}
(Point(p), Point(q)) => Some((p.dist(*q), *p, *q)),
(Plane { point: p1, normal: n1 }, Plane { point: p2, normal: n2 }) => {
let (v1, v2) = (n1.vec(), n2.vec());
if v1.cross(v2).len() < 1e-9 {
let h = (*p2 - *p1).dot(v1);
Some((h.abs(), *p1, *p2 - v1 * h))
} else {
// They meet: a point on the line of intersection has distance zero.
let dir = v1.cross(v2);
let t = -(*p1 - *p2).dot(v2) / dir.dot(v2);
let p = *p1 + dir * t;
Some((0.0, p, p))
}
}
(Plane { point, normal }, Line { a: la, b: lb }) => {
let d = *lb - *la;
let den = d.dot(normal.vec());
if den.abs() < 1e-9 {
let (dist, _, q) = {
let h = (*la - *point).dot(normal.vec());
(h.abs(), *la, *la - normal.vec() * h)
};
Some((dist, q, *la))
} else {
let t = (*point - *la).dot(normal.vec()) / den;
let p = *la + d * t;
Some((0.0, p, p))
}
}
(Line { a: la, b: lb }, Plane { point, normal }) => {
let d = *lb - *la;
let den = d.dot(normal.vec());
if den.abs() < 1e-9 {
let h = (*la - *point).dot(normal.vec());
Some((h.abs(), *la, *la - normal.vec() * h))
} else {
let t = (*point - *la).dot(normal.vec()) / den;
let p = *la + d * t;
Some((0.0, p, p))
}
}
(Line { a: a1, b: b1 }, Line { a: a2, b: b2 }) => {
let (d1, d2) = (*b1 - *a1, *b2 - *a2);
let n = d1.cross(d2);
if n.len() < 1e-9 {
let (dist, _, q) = {
let dd = d1;
let t = if dd.len() > 1e-12 { ((*a2 - *a1).dot(dd) / dd.dot(dd)).clamp(0.0, 1.0) } else { 0.0 };
let q = *a1 + dd * t;
((*a2).dist(q), *a2, q)
};
Some((dist, q, *a2))
} else {
let w = *a1 - *a2;
let a = d1.dot(d1);
let b = d1.dot(d2);
let c = d2.dot(d2);
let d = d1.dot(w);
let e = d2.dot(w);
let den = a * c - b * b;
let (t, s) = if den.abs() < 1e-12 { (0.0, e / c) } else { ((b * e - c * d) / den, (a * e - b * d) / den) };
let p = *a1 + d1 * t;
let q = *a2 + d2 * s;
Some((p.dist(q), p, q))
}
}
_ => None,
}
}
fn closest(a: &Geo, b: &Geo) -> Option<(Vec3, Vec3)> {
let pts_to_tris = |pts: &[Vec3], tris: &[[Vec3; 3]]| -> Option<(Vec3, Vec3)> {
let pts = sample(pts, MAX_POINTS);
@@ -555,6 +698,13 @@ pub(crate) fn measure_items(s: &Session, items: &Value) -> Result<Value> {
out["delta"] = json!(q - p);
out["min"] = json!(d);
}
if let Some((d, p, q)) = exact_min(&a.shape, &b.shape) {
out["distance_mm"] = json!(d);
out["from"] = json!(p);
out["to"] = json!(q);
out["delta"] = json!(q - p);
out["min"] = json!(d);
}
if let (Some((p, q)), Some(d)) = (far, max) {
out["max"] = json!(d);
out["max_from"] = json!(p);
+21
View File
@@ -68,6 +68,27 @@ pub(crate) fn add_round(v: &mut Value, shape: &Shape) {
});
}
/// The radius of a toroidal or spherical face, added to its description: a fillet round a
/// circular edge is a torus whose tube radius is the fillet's (`major_radius` is the ring
/// through the middle of the rolling ball), a ball or a spherical corner patch a sphere. These
/// faces have no axis to measure from, so they stay out of [`Shape`].
pub(crate) fn add_curved(v: &mut Value, surface: Option<solidcraft_kernel::FaceSurface>) {
use solidcraft_kernel::FaceSurface;
let (radius, kind) = match surface {
Some(FaceSurface::Torus { minor, major, .. }) if minor.is_finite() && minor > 0.0 => {
if major.is_finite() {
v["major_radius"] = json!(major.abs());
}
(minor, "torus")
}
Some(FaceSurface::Sphere { radius, .. }) if radius.is_finite() && radius > 0.0 => (radius, "sphere"),
_ => return,
};
v["diameter"] = json!(2.0 * radius);
v["radius"] = json!(radius);
v["surface"] = json!(kind);
}
/// Cylinder `axis` through `origin` of radius `radius` over the points of its face: the
/// round shape, centred level with the middle of the face, with its extent and coverage.
pub(crate) fn cylinder(origin: Vec3, axis: UnitVec3, radius: f64, pts: &[Vec3]) -> Option<Shape> {
+17 -3
View File
@@ -26,6 +26,9 @@ pub(crate) mod clearance;
mod clipboard;
mod clipboard_foreign;
mod compare;
mod compare_picture;
#[cfg(test)]
mod compare_picture_tests;
mod component;
mod component_clone;
mod component_clone_assembly;
@@ -37,16 +40,20 @@ mod face;
mod features;
mod features_more;
mod join_pieces;
pub(crate) use construct::{GeoForm, GeoFormError, REF as GEO_REF_FORMS, SKETCH_PART, geo_form, is_model_axis};
pub(crate) use construct::{GeoForm, GeoFormError, REF as GEO_REF_FORMS, SKETCH_PART, geo_form, is_model_axis, plane_axis_point_kind};
pub(crate) use face::{FaceRefForm, face_ref_form};
pub(crate) use features::profiles as feature_profiles;
pub(crate) use features::{EdgeRefForm, PLANE_FORMS, axis_dir_form, axis_line_object, edge_ref_form, explicit_plane, plane_name_problem, sketch_key};
pub use features::{auto_operation, extrude_reach, shown_scope};
pub use file::open_steps::OpenSteps;
pub use join_pieces::{Addition, addition};
pub use sketch::dimension_roles;
pub(crate) use sketch::face::face_pick_form;
pub use sketch_construction::{ConstructionLine, construction_line_choice, construction_line_mode, construction_line_solutions};
pub use sketch_create::tangent_arc_preview;
pub use sketch_construction::{
ConstructionLine, construction_line_choice, construction_line_mode, construction_line_solutions, construction_line_takes_length,
construction_segment,
};
pub use sketch_create::{tangent_arc_preview, tangent_arc_starts_at};
pub use sketch_import::drawing_import_command;
pub use sketch_modify::{extend_arc_preview, extend_preview, trim_piece};
pub use view_dxf::sketch::exportable as view_dxf_exportable;
@@ -242,6 +249,12 @@ impl CommandSpec {
other => other,
}
}
/// The refusal this command gives when it is not available in this session: none when it
/// is. It is the engine's own wording (`EngineError::Disabled`), so a caller that only has
/// the message can tell it from a failure of the command itself.
pub fn disabled_refusal(&self, s: &Session) -> Option<String> {
(self.enabled)(s).err().map(|why| crate::EngineError::Disabled(self.id.to_string(), why).to_string())
}
pub fn info(&self, s: &Session) -> CommandInfo {
let e = (self.enabled)(s);
let (label, icon) = match self.id {
@@ -342,6 +355,7 @@ pub fn command_specs() -> Vec<&'static CommandSpec> {
v.extend(metadata::COMMANDS.iter());
v.extend(inspect::COMMANDS.iter());
v.extend(compare::COMMANDS.iter());
v.extend(compare_picture::COMMANDS.iter());
v.extend(silhouette::COMMANDS.iter());
v.extend(materials::COMMANDS.iter());
v.extend(material_properties::COMMANDS.iter());
+38 -1
View File
@@ -624,7 +624,8 @@ fn hole_picks(p: &Value, cmd: &str) -> Result<Vec<Vec3>> {
/// A point on the cylindrical wall of the hole at `a`, a little in from the picked rim, and the
/// point on the hole's axis level with it (the same for every pick on one rim).
fn hole_wall(s: &Session, a: Vec3) -> Option<(Vec3, Vec3)> {
let st = s.model.state();
// Picks are in world coordinates, including rims on already placed standard parts.
let st = s.world_state();
let dirs = [Vec3::Z * -1.0, Vec3::Z, Vec3::X, Vec3::X * -1.0, Vec3::Y, Vec3::Y * -1.0];
for step in [0.2, 0.5, 1.0, 2.0] {
for b in &st.bodies {
@@ -859,6 +860,42 @@ mod tests {
assert!(s.model.state().threads.iter().any(|t| t.designation == "M8" && (t.pitch - 1.25).abs() < 1e-9));
}
#[test]
fn a_screw_seats_on_a_translated_washer() {
for z in [11.6, 11.2] {
let mut s = Session::default();
run(&mut s, "solid.box", json!({"length":60,"width":60,"height":10,"body_name":"Plate"}));
run(&mut s, "solid.hole", json!({"position":[19.6,33.948,10],"diameter":8.4}));
let washer = run(&mut s, "parts.insert", json!({"family":"washer","size":"M8","at":[23.8,33.948,10]}));
let before = s.doc.clone();
let undo = s.undo.len();
// Both the top bore rim and the wall are world picks on the seated washer (#9648).
let screw = run(&mut s, "parts.fastener", json!({"family":"socket_head_cap_screw","size":"M8","length":20,"at":[23.8,33.948,z]}));
let joint = s.doc.assembly.joints.iter().find(|j| Some(j.id) == screw["joint"].as_u64()).unwrap();
assert_eq!(Some(joint.a.occurrence), washer["occurrence"].as_u64());
let (lo, hi) = bbox(&s, "SHCS M8x20");
assert!((lo.z + 8.4).abs() < 1e-6 && (hi.z - 19.6).abs() < 1e-6, "{lo:?} {hi:?}");
assert!(((lo.x + hi.x) / 2.0 - 19.6).abs() < 1e-6 && ((lo.y + hi.y) / 2.0 - 33.948).abs() < 1e-6, "{lo:?} {hi:?}");
assert_eq!(s.undo.len(), undo + 1);
run(&mut s, "edit.undo", json!({}));
assert_eq!(s.doc, before);
}
}
#[test]
fn a_screw_seats_on_a_rotated_washer() {
let mut s = Session::default();
let washer = run(&mut s, "parts.insert", json!({"family":"washer","size":"M8","point":[40,50,60],"direction":[1,0,0]}));
run(&mut s, "occurrence.ground", json!({"occurrence":washer["occurrence"],"grounded":true}));
let screw = run(&mut s, "parts.insert", json!({"family":"socket_head_cap_screw","size":"M8","length":20,"at":[41.6,54.2,60]}));
let joint = s.doc.assembly.joints.iter().find(|j| Some(j.id) == screw["joint"].as_u64()).unwrap();
assert_eq!(Some(joint.a.occurrence), washer["occurrence"].as_u64());
// The seating face is X = 41.6: the 20 mm shank runs along -X and the 8 mm head along +X.
let (lo, hi) = bbox(&s, "SHCS M8x20");
assert!(lo.dist(solidcraft_geom::Vec3::new(21.6, 43.5, 53.5)) < 1e-6, "{lo:?}");
assert!(hi.dist(solidcraft_geom::Vec3::new(49.6, 56.5, 66.5)) < 1e-6, "{hi:?}");
}
/// Several holes in one insert: one component placed and seated once per hole, in one undo
/// step (#9341); two picks of one rim are one hole; a pick off every body fails the whole
/// insert.
+6 -6
View File
@@ -68,7 +68,7 @@ pub static COMMANDS: &[CommandSpec] = &[
CommandSpec::new("render.optics.unbind","Remove Optical Binding",optical_bindings::unbind).icon("appearance").doc_only()
.params("name: saved setup; target: exact saved definition/feature/component/occurrence_path; expected_revision. Removes only that optical assignment in one undo without a device or file access."),
CommandSpec::new("render.result.regrade", "Regrade Linear Master", jobs::regrade).icon("render_image").noundo().doc_only()
.params("job: exact completed JobId; presentation: {exposure_ev?,tone_map?,finishing?,backdrop?,background?,transparent_background?}. Queues cancellable nondestructive PNG presentation from that retained native FLOAT32 master, not from PNG. Captured geometry/camera/lighting and source EXR remain unchanged; no path tracing, denoising, external EXR import or automatic file writes. Returns a distinct job with source identity and original-master digest. Existing frame retention and export rules still apply."),
.params("job?: exact completed JobId (default selected result); presentation: {exposure_ev?,tone_map?,finishing?,backdrop?,background?,transparent_background?}. Queues cancellable nondestructive PNG presentation from that retained native FLOAT32 master, not from PNG. Captured geometry/camera/lighting and source EXR remain unchanged; no path tracing, denoising, external EXR import or automatic file writes. Returns a distinct job with source identity and original-master digest. Existing frame retention and export rules still apply."),
CommandSpec::new("render.camera.set", "Set Render Camera", set_camera).icon("camera").noundo().doc_only()
.params("camera: complete Render Camera snapshot (target, yaw, pitch, distance, fov; roll defaults to 0 and up to z). Validates the same finite mm/radian bounds as saved render intent, preserves explicit roll/up and changes only transient view state. No model evaluation, setup save, renderer, dirty flag or undo step. Design view.apply retains its separate up-axis policy."),
CommandSpec::new("render.camera.get", "Inspect Render Camera", get_camera).icon("camera").noundo()
@@ -102,7 +102,7 @@ CommandSpec::new("render.library.surface.bind", "Save Surface Binding", surface_
CommandSpec::new("render.library.surface.unbind", "Remove Surface Binding", surface_bindings::unbind).icon("appearance").doc_only()
.params("name: setup, target: saved definition/feature/component/occurrence_path, set?: setup patch. Removes only that explicit binding in one undo; no device or current geometry required."),
CommandSpec::new("render.result.pin", "Retain Displayed Render Result", jobs::pin).icon("inspect").noundo().doc_only()
.params("job: completed JobId, or null to release the displayed-result pin. Retains at most one completed result independently of a newer completion; preserves exact export bytes and original host memory accounting. No design/undo change; preview or foreign jobs refuse."),
.params("job?: completed JobId (default selected result), or null to clear selection. Selects within the eight retained completed results; oldest is evicted first. Preserves exact export bytes and original host memory accounting. No design/undo change; preview or foreign jobs refuse."),
CommandSpec::new("render.library.targets", "Find Render Library Targets", library::targets).icon("list").noundo()
.params("query?: exact-name substring; offset?:0; limit?:128 (1..128). Returns a bounded page of captured body ordinals/names, revision and scene bounds. Settles the model through ordinary command handling; never returns a large mesh payload."),
CommandSpec::new("render.library.list", "Browse Render Library", library::list).icon("list").noundo().doc_only()
@@ -128,13 +128,13 @@ CommandSpec::new("render.preflight", "Check Render Request", jobs::preflight).ic
CommandSpec::new("render.start", "Render Image", jobs::start).key("R").noundo().doc_only().at("RENDER", "IMAGE").icon("render_image")
.params("Same request as render.preflight. Captures an immutable product scene and queues a bounded image job. Returns its session-scoped job identity, captured scene_generation and progress. Does not change the design. See docs/render-jobs.md"),
CommandSpec::new("render.status", "Render Progress", jobs::status).icon("list").noundo().doc_only()
.params("job: {session, serial, generation}. Non-blocking progress, error, optional CPU preview metadata {frame_sequence,samples_per_pixel,frame}, and completed frame metadata, optional sampling allocation/work counts and retained-master presentation provenance; no image bytes and no rendering in this call. Preview is separate from the exportable final image."),
.params("job?: {session, serial, generation}. Non-blocking progress, errors and captured frame metadata with queue:{running,waiting,job,shot,error}. Without job returns queue only. Advances an explicitly started render.batch through render.start as jobs finish; never pumps render work. Preview is separate from the exportable final image."),
CommandSpec::new("render.cancel", "Cancel Render", jobs::cancel).icon("delete").noundo().doc_only()
.params("job: {session, serial, generation}. Requests cooperative cancellation; completed jobs remain completed. Late cancelled frames cannot be published."),
.params("job?: {session, serial, generation}, default the active render.batch job. Requests cooperative cancellation and drops its waiting shots; completed jobs remain completed. Late cancelled frames cannot be published."),
CommandSpec::new("render.result.copy", "Copy Render Image", jobs::copy).icon("copy").noundo().doc_only()
.params("job: completed JobId. Validates the retained PNG for app clipboard copying (max 4096 per side, 4194304 pixels); headless returns frame metadata only. Never copies a provisional image or starts a job."),
.params("job?: completed JobId (default selected result). Validates the retained PNG for app clipboard copying (max 4096 per side, 4194304 pixels); headless returns frame metadata only. Never copies a provisional image or starts a job."),
CommandSpec::new("render.export", "Export Render Image", jobs::export).icon("export").noundo().doc_only()
.params("job: {session, serial, generation}, path: destination matching format?: png (default) or exr, overwrite?: false. EXR requires an opt-in raw linear master retained by that completed GPU job; never converts PNG pixels. Exports a retained completed image atomically; an existing destination is preserved on failure. See docs/render-jobs.md"),
.params("job?: {session, serial, generation} (default selected result), path: destination matching format?: png (default) or exr, overwrite?: false. EXR requires an opt-in raw linear master retained by that completed GPU job; never converts PNG pixels. Exports a retained completed image atomically; an existing destination is preserved on failure. See docs/render-jobs.md"),
CommandSpec::new("render.backends", "Render Backends", backends).icon("list")
.noundo()
.doc_only()
+14 -1
View File
@@ -1052,6 +1052,14 @@ fn completed_png_content_identity_matches_status_and_exact_retained_export() {
finish(&host);
let newer = s.execute("render.status", &json!({"job":replacement})).unwrap();
assert_ne!(newer["frame"]["png_sha256"], metadata["png_sha256"]);
let still_retained = dir.join("still-retained.png");
s.execute("render.export", &json!({"job":id,"path":still_retained})).unwrap();
assert_eq!(std::fs::read(still_retained).unwrap(), bytes);
for _ in 0..7 {
let next = start(&mut s);
finish(&host);
s.execute("render.status", &json!({"job":next})).unwrap();
}
let evicted = dir.join("evicted.png");
assert!(s.execute("render.export", &json!({"job":id,"path":evicted})).is_err());
assert!(!evicted.exists());
@@ -1281,7 +1289,12 @@ fn output_viewer_copy_validates_completed_job_without_mutation_or_pixel_payloads
assert_eq!(copied["frame"]["job"], json!(job));
assert_eq!(copied["frame"]["width"], 16);
assert!(copied.get("png").is_none());
for params in [json!({}), json!(null), json!({"job":job,"extra":true}), json!({"job":{"session":0,"serial":0,"generation":0}})] {
for params in [json!({}), json!(null)] {
assert_eq!(s.execute("render.result.copy", &params).unwrap(), copied, "an omitted job uses the selected result");
}
for params in
[json!({"job":1e300}), json!({"job":"selected"}), json!({"job":job,"extra":true}), json!({"job":{"session":0,"serial":0,"generation":0}})]
{
assert!(s.execute("render.result.copy", &params).is_err());
}
assert!(Arc::ptr_eq(&document, &s.doc));
+42 -21
View File
@@ -675,7 +675,12 @@ pub(super) fn start(s: &mut Session, params: &Value) -> Result<Value> {
p.scene,
p.request,
p.replace,
crate::render_jobs::Execution { executor: p.executor, options: p.options, environment: p.environment },
crate::render_jobs::Execution {
executor: p.executor,
options: p.options,
environment: p.environment,
shot: params.get("setup").and_then(Value::as_str).map(str::to_owned),
},
)
.map_err(job_error)?;
let mut status = json!(status);
@@ -688,24 +693,32 @@ pub(super) fn start(s: &mut Session, params: &Value) -> Result<Value> {
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct Regrade {
job: JobId,
job: Option<JobId>,
presentation: solidcraft_render::job::presentation::Presentation,
}
pub(super) fn regrade(s: &mut Session, params: &Value) -> Result<Value> {
let input: Regrade = serde_json::from_value(params.clone()).map_err(|e| bad("render.result.regrade", e.to_string()))?;
input.presentation.validate().map_err(job_error)?;
Ok(json!(s.render_jobs.regrade(input.job, input.presentation).map_err(job_error)?))
let job = result_source(s, input.job, "render.result.regrade")?;
Ok(json!(s.render_jobs.regrade(job, input.presentation).map_err(job_error)?))
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct Job {
job: JobId,
fn result_source(s: &mut Session, job: Option<JobId>, command: &str) -> Result<JobId> {
s.render_jobs.collect().map_err(job_error)?;
job.or_else(|| s.render_selected_result()).ok_or_else(|| bad(command, "no completed result is selected"))
}
pub(super) fn status(s: &mut Session, params: &Value) -> Result<Value> {
let input: Job = serde_json::from_value(params.clone()).map_err(|e| bad("render.status", e.to_string()))?;
Ok(json!(s.render_jobs.status(input.job).map_err(job_error)?))
let input: ResultJob = serde_json::from_value(params.clone()).map_err(|e| bad("render.status", e.to_string()))?;
s.advance_render_batch()?;
let Some(job) = input.job else {
return Ok(json!({"queue":s.render_queue_state()}));
};
let mut reply = json!(s.render_jobs.status(job).map_err(job_error)?);
if let Some(object) = reply.as_object_mut() {
object.insert("queue".into(), json!(s.render_queue_state()));
}
Ok(reply)
}
#[derive(Deserialize)]
@@ -714,18 +727,23 @@ struct Pin {
job: Option<JobId>,
}
pub(super) fn pin(s: &mut Session, params: &Value) -> Result<Value> {
if params.get("job").is_none() {
return Err(bad("render.result.pin", "job or explicit null is required"));
}
let input: Pin = serde_json::from_value(params.clone()).map_err(|e| bad("render.result.pin", e.to_string()))?;
Ok(json!({"frame":s.render_jobs.pin(input.job).map_err(job_error)?}))
let job = if params.get("job").is_some_and(Value::is_null) { None } else { Some(result_source(s, input.job, "render.result.pin")?) };
Ok(json!({"frame":s.render_jobs.pin(job).map_err(job_error)?}))
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct ResultJob {
job: Option<JobId>,
}
pub(super) fn copy(s: &mut Session, params: &Value) -> Result<Value> {
let input: Job = serde_json::from_value(params.clone()).map_err(|e| bad("render.result.copy", e.to_string()))?;
let input: ResultJob = serde_json::from_value(params.clone()).map_err(|e| bad("render.result.copy", e.to_string()))?;
let job = result_source(s, input.job, "render.result.copy")?;
let frame = s
.render_jobs
.handle(input.job)
.handle(job)
.map_err(job_error)?
.frame()
.map_err(job_error)?
@@ -738,12 +756,14 @@ pub(super) fn copy(s: &mut Session, params: &Value) -> Result<Value> {
}
pub(super) fn cancel(s: &mut Session, params: &Value) -> Result<Value> {
let input: Job = serde_json::from_value(params.clone()).map_err(|e| bad("render.cancel", e.to_string()))?;
let status = s.render_jobs.status(input.job).map_err(job_error)?;
let input: ResultJob = serde_json::from_value(params.clone()).map_err(|e| bad("render.cancel", e.to_string()))?;
let job = input.job.or(s.render_queue_state().job).ok_or_else(|| bad("render.cancel", "job is required without an active shot queue"))?;
let status = s.render_jobs.status(job).map_err(job_error)?;
s.cancel_render_batch(job);
if status.progress.phase.terminal() {
return Ok(json!(status));
}
Ok(json!(s.render_jobs.handle(input.job).map_err(job_error)?.cancel().map_err(job_error)?))
Ok(json!(s.render_jobs.handle(job).map_err(job_error)?.cancel().map_err(job_error)?))
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Deserialize)]
@@ -766,7 +786,7 @@ impl ExportFormat {
struct Export {
#[serde(default)]
format: ExportFormat,
job: JobId,
job: Option<JobId>,
path: String,
#[serde(default)]
overwrite: bool,
@@ -784,7 +804,8 @@ pub(super) fn export(s: &mut Session, params: &Value) -> Result<Value> {
// Atomic native writers are also used for the app's own files and do not
// apply a request's path scope. Check the caller's destination explicitly.
solidcraft_io::vfs::check(&input.path).map_err(|e| bad("render.export", e.to_string()))?;
let frame = s.render_jobs.frame(input.job).map_err(job_error)?;
let job = result_source(s, input.job, "render.export")?;
let frame = s.render_jobs.frame(job).map_err(job_error)?;
let bytes = match input.format {
ExportFormat::Png => frame.png(),
ExportFormat::Exr => frame
@@ -797,7 +818,7 @@ pub(super) fn export(s: &mut Session, params: &Value) -> Result<Value> {
if input.overwrite { solidcraft_io::write_atomic(path, bytes, false) } else { solidcraft_io::safe_write::create_atomic(path, bytes) };
result.map_err(|e| bad("render.export", format!("could not export completed {}: {e}", input.format.extension())))?;
Ok(
json!({"job":input.job,"path":input.path,"bytes":bytes.len(),"format":input.format.extension(),"encoding":if input.format==ExportFormat::Exr {"scene_linear_srgb_d65"} else {"srgb"},"alpha":if input.format==ExportFormat::Exr {"coverage_premultiplied"} else {"straight"},"frame":frame.info,"linear_master":frame.linear_master_info(),"presentation":frame.presentation()}),
json!({"job":job,"path":input.path,"bytes":bytes.len(),"format":input.format.extension(),"encoding":if input.format==ExportFormat::Exr {"scene_linear_srgb_d65"} else {"srgb"},"alpha":if input.format==ExportFormat::Exr {"coverage_premultiplied"} else {"straight"},"frame":frame.info,"linear_master":frame.linear_master_info(),"presentation":frame.presentation()}),
)
}
+484 -19
View File
@@ -1,7 +1,10 @@
//! Planned Render interactions, with explicit non-mutating refusals.
//! Transient Render view requests and explicit refusals for planned interactions.
use super::CommandSpec;
use crate::schema::render_lights::{MuteParams, SoloParams};
use crate::schema::{TypedContract, output_schema_for, render_ui::CheckReport};
use crate::schema::{
TypedContract, output_schema_for,
render_ui::{BatchParams, BatchResult, CheckReport, CompareDivider, CompareParams, CompareSide, ResultIndex},
};
use crate::{Result, Session, params::bad};
use serde::Deserialize;
use serde_json::{Value, json};
@@ -33,19 +36,10 @@ fn mute(_: &mut crate::Session, params: &serde_json::Value) -> crate::Result<ser
crate::render_lights::parse_id(&input.id).map_err(|e| crate::params::bad(command, e))?;
serde_json::to_value(input).map_err(|e| crate::params::bad(command, e.to_string()))
}
macro_rules! waiting {
($id:literal, $label:literal, $tab:literal, $panel:literal, $icon:literal, $reason:literal) => {
CommandSpec::new($id, $label, |_, _| Err(crate::EngineError::Other(concat!("not supported yet: ", $reason).into())))
.enabled(|_| Err(concat!("not supported yet: ", $reason).into()))
.planned("5493")
.noundo()
.doc_only()
.at($tab, $panel)
.icon($icon)
.params("No supported parameters. Always refuses without changing the design or history.")
};
}
const RESULT_INDEX: TypedContract<ResultIndex> = TypedContract::new();
const COMPARE: TypedContract<CompareParams> = TypedContract::new();
const DIVIDER: TypedContract<CompareDivider> = TypedContract::new();
const BATCH: TypedContract<BatchParams> = TypedContract::new();
const CHECK_REPORT: TypedContract<CheckReport> = TypedContract::new();
@@ -57,6 +51,16 @@ pub static COMMANDS: &[CommandSpec] = &[
.noundo()
.doc_only()
.params("text: nonempty report, at most 65536 bytes. Validated clipboard request; headless echo only, never renders or edits a design."),
CommandSpec::new("render.result.select", "Select Render Result", crate::checked!("render.result.select", select_result))
.icon("render_image").noundo().doc_only()
.enabled(results_ready)
.contract(&RESULT_INDEX).output(output_schema_for::<ResultIndex>)
.params("index: integer 0..7, oldest first; must be retained. Selects a completed result as transient per-document view state; echoes index."),
CommandSpec::new("render.result.discard", "Discard Render Result", crate::checked!("render.result.discard", discard_result))
.icon("delete").noundo().doc_only()
.enabled(results_ready)
.contract(&RESULT_INDEX).output(output_schema_for::<ResultIndex>)
.params("index: integer 0..7, oldest first; must be retained. Drops only that transient result, selecting the newest remaining result if necessary; echoes index."),
CommandSpec::new("render.output.size", "Set Render Output Size", output_size)
.icon("settings")
.noundo()
@@ -88,8 +92,20 @@ pub static COMMANDS: &[CommandSpec] = &[
.noundo().doc_only().icon("render_scene").at("RENDER SCENE","LIGHTS")
.contract(&MUTE).output(crate::schema::output_schema_for::<MuteParams>)
.params("id: kind:slot light address; mute:bool. Validates and echoes per-document UI view state, applied to live preview and captured render copies only. Never changes saved settings or history."),
waiting!("render.compare", "Compare Images", "RENDER", "RESULT", "compare", "retained image comparison"),
waiting!("render.batch", "Render All Shots", "RENDER", "BATCH", "render_image", "the saved-shot render queue"),
CommandSpec::new("render.compare", "Compare Images", crate::checked!("render.compare", compare))
.icon("compare").noundo().doc_only().at("RENDER", "RESULT")
.enabled(results_ready)
.contract(&COMPARE).output(output_schema_for::<CompareParams>)
.params("left, right: live or retained result index 0..7 (oldest first), distinct and retained; OR off:true. Validated per-document comparison view request; headless echo, no rendering or design/history change."),
CommandSpec::new("render.compare.divider", "Move Render Comparison Divider", crate::checked!("render.compare.divider", divider))
.icon("compare").noundo().doc_only()
.contract(&DIVIDER).output(output_schema_for::<CompareDivider>)
.params("position: finite number, clamped to 0.05..0.95. Transient comparison divider request; headless echo only."),
CommandSpec::new("render.batch", "Render All Shots", crate::checked!("render.batch", batch))
.icon("render_image").noundo().doc_only().at("RENDER", "BATCH")
.enabled(|s| s.render_batch_ready())
.contract(&BATCH).output(output_schema_for::<BatchResult>)
.params("request?: render.start parameters, shared across all saved shots except camera. Default: first saved setup in name order. Captures up to 32 shot cameras and queues sequentially through render.start; returns {queued:n}. Refuses active renders, no shots or stale geometry. render.status (also with {}) and the UI tick advance the queue; Cancel on the active job drops waiting shots. No design/history/revision change."),
];
fn copy_check(_: &mut Session, params: &Value) -> Result<Value> {
@@ -100,6 +116,110 @@ fn copy_check(_: &mut Session, params: &Value) -> Result<Value> {
Ok(json!({"text":report.text}))
}
fn compare(s: &mut Session, params: &Value) -> Result<Value> {
let input: CompareParams = serde_json::from_value(params.clone()).map_err(|e| bad("render.compare", e.to_string()))?;
match input {
CompareParams::Off(off) if off.off => {
s.render_jobs.set_comparison(None);
Ok(json!({"off":true}))
}
CompareParams::Pair(pair) => {
if pair.left == pair.right {
return Err(bad("render.compare", "comparison sides must differ"));
}
let results = s.render_retained_results()?;
for side in [pair.left, pair.right] {
if let CompareSide::Result(index) = side
&& (index > 7 || results.get(index as usize).is_none())
{
return Err(bad("render.compare", "retained result index is out of bounds"));
}
}
let resolve = |side| match side {
CompareSide::Live(_) => None,
CompareSide::Result(index) => results.get(index as usize).map(|result| result.frame.job),
};
s.render_jobs.set_comparison(Some([resolve(pair.left), resolve(pair.right)]));
Ok(json!({"left":pair.left,"right":pair.right}))
}
_ => Err(bad("render.compare", "off must be true")),
}
}
fn divider(_: &mut Session, params: &Value) -> Result<Value> {
let input: CompareDivider = serde_json::from_value(params.clone()).map_err(|e| bad("render.compare.divider", e.to_string()))?;
if !input.position.is_finite() {
return Err(bad("render.compare.divider", "position must be finite"));
}
Ok(json!({"position":input.position.clamp(0.05,0.95)}))
}
fn batch(s: &mut Session, params: &Value) -> Result<Value> {
let input: BatchParams = serde_json::from_value(params.clone()).map_err(|e| bad("render.batch", e.to_string()))?;
s.render_batch_ready().map_err(|reason| bad("render.batch", reason))?;
if s.model.is_stale() {
return Err(bad("render.batch", "finish the modelling gesture before rendering shots"));
}
for name in s.doc.render.setups.keys() {
solidcraft_doc::render_settings::RenderSettings::validate_name(name).map_err(|error| bad("render.batch", error.to_string()))?;
}
let common = if let Some(request) = input.request {
Value::Object(request.into_iter().collect())
} else {
let setup = s.doc.render.setups.values().next().ok_or_else(|| bad("render.batch", "no saved shots"))?;
json!({"settings":setup})
};
let mut request = common.as_object().cloned().ok_or_else(|| bad("render.batch", "request must be an object"))?;
let settings = if let Some(name) = request.get("setup") {
let name = name.as_str().ok_or_else(|| bad("render.batch", "setup must be a saved name"))?;
if request.contains_key("settings") {
return Err(bad("render.batch", "choose either setup or settings"));
}
s.doc.render.setups.get(name).cloned().ok_or_else(|| bad("render.batch", "unknown saved setup"))?
} else if let Some(settings) = request.get("settings") {
serde_json::from_value(settings.clone()).map_err(|e| bad("render.batch", e.to_string()))?
} else {
s.doc.render.setups.values().next().cloned().ok_or_else(|| bad("render.batch", "no saved shots"))?
};
request.remove("setup");
// A batch never replaces an active job; malformed replace values must still refuse.
if request.get("replace").is_some_and(|value| value != &json!(false)) {
return Err(bad("render.batch", "replace must be false or omitted"));
}
request.insert("settings".into(), json!(settings));
let request = std::sync::Arc::new(Value::Object(request));
let shots = s
.doc
.render
.setups
.iter()
.map(|(name, shot)| crate::render_jobs::BatchShot { name: name.clone(), camera: shot.camera, request: request.clone() })
.collect();
let queued = s.start_render_batch(shots)?;
Ok(json!({"queued":queued}))
}
fn results_ready(s: &Session) -> std::result::Result<(), String> {
if s.render_jobs.has_results() { Ok(()) } else { Err("no completed render results are retained".into()) }
}
fn indexed_result(s: &mut Session, params: &Value, command: &str, discard: bool) -> Result<Value> {
let input: ResultIndex = serde_json::from_value(params.clone()).map_err(|e| bad(command, e.to_string()))?;
if input.index > 7 {
return Err(bad(command, "index must be in 0..7"));
}
s.render_jobs.result_index(input.index as usize, discard).map_err(crate::render_jobs::job_error)?;
Ok(json!({"index":input.index}))
}
fn select_result(s: &mut Session, params: &Value) -> Result<Value> {
indexed_result(s, params, "render.result.select", false)
}
fn discard_result(s: &mut Session, params: &Value) -> Result<Value> {
indexed_result(s, params, "render.result.discard", true)
}
#[cfg(test)]
#[test]
fn render_lights_remaining_planned_commands_refuse_without_mutation() {
@@ -149,17 +269,21 @@ fn output_size(_: &mut Session, params: &Value) -> Result<Value> {
struct ResultView {
view: String,
}
fn result_view(_: &mut Session, params: &Value) -> Result<Value> {
fn result_view(s: &mut Session, params: &Value) -> Result<Value> {
let input: ResultView = serde_json::from_value(params.clone()).map_err(|e| bad("render.result.view", e.to_string()))?;
if !matches!(input.view.as_str(), "model" | "image" | "fit" | "actual") {
return Err(bad("render.result.view", "view must be model, image, fit or actual"));
}
if input.view == "model" {
s.render_jobs.set_comparison(None);
}
Ok(json!({"view":input.view}))
}
fn close_result(_: &mut Session, params: &Value) -> Result<Value> {
fn close_result(s: &mut Session, params: &Value) -> Result<Value> {
if !params.as_object().is_some_and(|p| p.is_empty()) {
return Err(bad("render.result.close", "expected no parameters"));
}
s.render_jobs.set_comparison(None);
Ok(json!({"view":"model"}))
}
@@ -210,3 +334,344 @@ fn output_sections_copy_report_validates_without_design_history_or_job_changes()
assert!(std::sync::Arc::ptr_eq(&doc, &s.doc));
assert_eq!(before, (s.revision, s.undo.len(), s.redo.len(), s.evaluations, s.is_dirty()));
}
#[cfg(test)]
fn complete_result(s: &mut Session, shot: &str) -> solidcraft_render::job::JobId {
let reply = s.execute("render.start", &json!({"setup":shot})).unwrap();
let job = serde_json::from_value(reply["job"].clone()).unwrap();
let host = s.render_job_host().unwrap().clone();
for _ in 0..1000 {
host.pump(4096).unwrap();
if s.execute("render.status", &json!({"job":job})).unwrap()["progress"]["phase"] == "completed" {
return job;
}
}
panic!("bounded result fixture did not complete");
}
#[cfg(test)]
#[test]
fn retained_results_cap_order_metadata_and_selection_never_edit_a_design() {
let mut s = Session::default();
s.set_render_job_host(Some(solidcraft_render::job::CpuHost::cooperative())).unwrap();
s.execute("render.settings.set", &json!({"name":"Hero","set":{"width":16,"height":8}})).unwrap();
s.execute("render.settings.set", &json!({"name":"Hero","set":{"width":32}})).unwrap();
s.execute("edit.undo", &json!({})).unwrap();
assert_eq!(s.redo.len(), 1, "view commands must preserve an available redo step");
let document = s.doc.clone();
let before = (s.undo.len(), s.redo.len(), s.revision, s.evaluations, s.is_dirty());
let jobs: Vec<_> = (0..10).map(|_| complete_result(&mut s, "Hero")).collect();
let results = s.render_retained_results().unwrap();
assert_eq!(results.len(), 8);
assert_eq!(results.iter().map(|result| result.frame.job).collect::<Vec<_>>(), jobs[2..]);
for result in &results {
assert_eq!(result.shot.as_deref(), Some("Hero"));
assert_eq!((result.frame.width, result.frame.height), (16, 8));
assert!(result.seconds.is_finite() && result.seconds >= 0.0);
assert_eq!(result.frame.scene_generation, s.revision);
assert!(!result.stale);
}
assert!(s.render_job_handle(jobs[0]).is_err());
assert!(s.render_job_handle(jobs[1]).is_err());
assert_eq!(s.render_selected_result(), Some(jobs[9]));
assert_eq!(s.execute("render.result.select", &json!({"index":0})).unwrap(), json!({"index":0}));
assert_eq!(s.render_selected_result(), Some(jobs[2]));
assert_eq!(s.execute("render.result.copy", &json!({})).unwrap()["frame"]["job"], json!(jobs[2]));
assert_eq!(s.execute("render.result.pin", &json!({})).unwrap()["frame"]["job"], json!(jobs[2]));
s.execute("render.result.discard", &json!({"index":0})).unwrap();
assert_eq!(s.render_selected_result(), Some(jobs[9]));
assert!(s.render_job_handle(jobs[2]).is_err());
s.execute("render.result.select", &json!({"index":2})).unwrap();
s.execute("render.result.discard", &json!({"index":0})).unwrap();
assert_eq!(s.render_selected_result(), Some(jobs[5]), "discarding an earlier row preserves the selected identity");
while !s.render_retained_results().unwrap().is_empty() {
s.execute("render.result.discard", &json!({"index":0})).unwrap();
}
assert!(s.render_selected_result().is_none());
assert!(s.execute("render.result.copy", &json!({})).is_err());
assert!(std::sync::Arc::ptr_eq(&document, &s.doc));
assert_eq!(before, (s.undo.len(), s.redo.len(), s.revision, s.evaluations, s.is_dirty()));
}
#[cfg(test)]
#[test]
fn retained_result_requests_refuse_hostile_and_missing_indices_atomically() {
let mut s = Session::default();
s.set_render_job_host(Some(solidcraft_render::job::CpuHost::cooperative())).unwrap();
s.execute("render.settings.set", &json!({"name":"Front","set":{"width":8,"height":8}})).unwrap();
let job = complete_result(&mut s, "Front");
let document = s.doc.clone();
let before = (s.undo.len(), s.redo.len(), s.revision, s.evaluations, s.is_dirty());
let retained = s.render_job_host().unwrap().admitted_bytes();
for mode in [crate::schema::ParamCheck::Off, crate::schema::ParamCheck::Strict] {
s.param_check = mode;
for command in ["render.result.select", "render.result.discard"] {
let spec = super::find_command(command).unwrap();
assert!(spec.contract.is_some() && spec.output.is_some());
for request in [
json!(null),
json!([]),
json!({}),
json!({"index":-1}),
json!({"index":0.5}),
json!({"index":1e300}),
json!({"index":u64::MAX}),
json!({"index":"0"}),
json!({"index":null}),
json!({"index":true}),
json!({"index":1}),
json!({"index":8}),
json!({"index":0,"extra":true}),
] {
assert!(s.execute(command, &request).is_err(), "{command}: {request}");
assert_eq!(s.render_retained_results().unwrap().len(), 1);
assert_eq!(s.render_selected_result(), Some(job));
assert_eq!(s.render_job_host().unwrap().admitted_bytes(), retained);
assert!(std::sync::Arc::ptr_eq(&document, &s.doc));
assert_eq!(before, (s.undo.len(), s.redo.len(), s.revision, s.evaluations, s.is_dirty()));
}
}
}
s.execute("render.settings.set", &json!({"name":"Front","set":{"width":16}})).unwrap();
assert!(s.render_retained_results().unwrap()[0].stale);
s.execute("edit.undo", &json!({})).unwrap();
assert!(s.render_retained_results().unwrap()[0].stale, "undo does not recapture pixels");
assert_eq!(s.render_retained_results().unwrap()[0].frame.scene_generation, before.2);
let mut other = Session::default();
assert!(other.render_retained_results().unwrap().is_empty());
assert!(other.execute("render.result.select", &json!({"index":0})).is_err());
assert!(other.render_job_handle(job).is_err());
}
#[cfg(test)]
#[test]
fn render_results_compare_validates_both_sides_and_clamps_only_finite_dividers_without_mutation() {
let mut s = Session::default();
s.set_render_job_host(Some(solidcraft_render::job::CpuHost::cooperative())).unwrap();
s.execute("render.settings.set", &json!({"name":"Hero","set":{"width":8,"height":8}})).unwrap();
complete_result(&mut s, "Hero");
complete_result(&mut s, "Hero");
s.execute("render.settings.set", &json!({"name":"Hero","set":{"width":16}})).unwrap();
s.execute("edit.undo", &json!({})).unwrap();
let document = s.doc.clone();
let before = (s.revision, s.undo.len(), s.redo.len(), s.evaluations, s.is_dirty());
let selected = s.selection.clone();
for mode in [crate::schema::ParamCheck::Off, crate::schema::ParamCheck::Strict] {
s.param_check = mode;
for request in [json!({"left":"live","right":0}), json!({"left":1,"right":0}), json!({"left":0,"right":"live"}), json!({"off":true})] {
assert_eq!(s.execute("render.compare", &request).unwrap(), request);
}
for request in [
json!({}),
json!([]),
json!({"off":false}),
json!({"off":true,"left":0}),
json!({"left":"live","right":"live"}),
json!({"left":0,"right":0}),
json!({"left":"unknown","right":0}),
json!({"left":"live","right":2}),
json!({"left":8,"right":0}),
json!({"left":-1,"right":0}),
json!({"left":0.5,"right":0}),
json!({"left":1e300,"right":"live"}),
json!({"left":null,"right":0}),
json!({"left":"0","right":1}),
json!({"left":true,"right":0}),
json!({"left":"live","right":0,"extra":true}),
] {
assert!(s.execute("render.compare", &request).is_err(), "{request}");
}
for (position, expected) in [(-1e300, 0.05), (0.0, 0.05), (0.25, 0.25), (1.0, 0.95), (1e300, 0.95)] {
assert_eq!(s.execute("render.compare.divider", &json!({"position":position})).unwrap(), json!({"position":expected}));
}
for request in [json!({}), json!({"position":null}), json!({"position":"NaN"}), json!({"position":true}), json!({"position":0.5,"extra":1})] {
assert!(s.execute("render.compare.divider", &request).is_err());
}
assert!(std::sync::Arc::ptr_eq(&document, &s.doc));
assert_eq!(before, (s.revision, s.undo.len(), s.redo.len(), s.evaluations, s.is_dirty()));
assert_eq!(s.selection, selected);
assert_eq!(s.render_retained_results().unwrap().len(), 2);
}
let mut empty = Session::default();
assert!(empty.execute("render.compare", &json!({"left":"live","right":0})).is_err());
assert!(empty.execute("render.compare", &json!({"off":true})).is_err());
for id in ["render.compare", "render.compare.divider", "render.batch"] {
let command = super::find_command(id).unwrap();
assert!(command.contract.is_some() && command.output.is_some() && command.planned.is_none());
assert!(!command.undoable && !command.reads_model);
}
}
#[cfg(test)]
fn batch_fixture(count: usize) -> Session {
let mut s = Session::default();
s.set_render_job_host(Some(solidcraft_render::job::CpuHost::cooperative())).unwrap();
for index in 0..count {
let camera = solidcraft_render::Camera { yaw: index as f64 * 0.2, roll: index as f64 * 0.01, ..Default::default() };
s.execute("render.settings.set", &json!({"name":format!("Shot {index:02}"),"set":{"camera":camera,"width":8+index,"height":8}})).unwrap();
}
s
}
#[cfg(test)]
#[test]
fn render_results_batch_is_sequential_uses_snapshot_cameras_and_one_setup_without_design_or_history_changes() {
let mut s = batch_fixture(3);
s.execute("render.settings.set", &json!({"name":"Shot 00","set":{"width":64}})).unwrap();
s.execute("edit.undo", &json!({})).unwrap();
let document = s.doc.clone();
let before = (s.revision, s.undo.len(), s.redo.len(), s.evaluations, s.is_dirty());
let cameras: Vec<_> = s.doc.render.setups.values().map(|setup| setup.camera).collect();
s.param_check = crate::schema::ParamCheck::Strict;
assert_eq!(
s.execute("render.batch", &json!({"request":{"settings":{"width":12,"height":10,"background":[7,8,9]}}})).unwrap(),
json!({"queued":3})
);
assert_eq!((s.render_queue_state().running, s.render_queue_state().waiting), (1, 2));
assert!(s.execute("render.batch", &json!({})).is_err());
let host = s.render_job_host().unwrap().clone();
let mut jobs = Vec::new();
for (index, camera) in cameras.iter().enumerate() {
let queue = s.render_queue_state();
assert_eq!((queue.running, queue.waiting), (1, 2 - index));
let job = queue.job.unwrap();
let status = s.render_job_handle(job).unwrap().status().unwrap();
assert_eq!(status.request.camera, *camera);
assert_eq!((status.request.width, status.request.height, status.request.background), (12, 10, [7, 8, 9]));
assert_eq!(queue.shot, Some(format!("Shot {index:02}")));
jobs.push(job);
for _ in 0..100 {
if !host.pump(4096).unwrap() {
break;
}
}
let reply = s.execute("render.status", &json!({"job":job})).unwrap();
assert_eq!(reply["progress"]["phase"], "completed");
}
assert_eq!((s.render_queue_state().running, s.render_queue_state().waiting), (0, 0));
assert!(s.render_queue_state().job.is_none());
let results = s.render_retained_results().unwrap();
assert_eq!(results.iter().map(|result| result.frame.job).collect::<Vec<_>>(), jobs);
assert_eq!(results.iter().map(|result| result.shot.clone().unwrap()).collect::<Vec<_>>(), ["Shot 00", "Shot 01", "Shot 02"]);
assert!(std::sync::Arc::ptr_eq(&document, &s.doc));
assert_eq!(before, (s.revision, s.undo.len(), s.redo.len(), s.evaluations, s.is_dirty()));
}
#[cfg(test)]
#[test]
fn render_results_batch_caps_at_32_and_cancel_discards_every_waiting_shot() {
let mut s = batch_fixture(32);
let document = s.doc.clone();
let before = (s.revision, s.undo.len(), s.redo.len(), s.evaluations, s.is_dirty());
assert_eq!(s.execute("render.batch", &json!({})).unwrap(), json!({"queued":32}));
let queue = s.render_queue_state();
assert_eq!((queue.running, queue.waiting), (1, 31));
let job = queue.job.unwrap();
assert!(s.execute("render.start", &json!({"settings":{"width":8,"height":8}})).is_err());
s.execute("render.cancel", &json!({})).unwrap();
assert_eq!(s.render_queue_state().waiting, 0);
assert!(s.render_queue_state().job.is_none());
s.render_job_host().unwrap().pump(4096).unwrap();
assert!(s.render_job_handle(job).unwrap().frame().unwrap().is_none());
for _ in 0..3 {
assert_eq!(s.execute("render.status", &json!({})).unwrap()["queue"]["waiting"], 0);
}
assert!(s.render_retained_results().unwrap().is_empty());
let next = s.execute("render.start", &json!({"settings":{"width":8,"height":8}})).unwrap();
assert_eq!(next["job"]["serial"], 2, "Cancel must not start any waiting shot");
s.execute("render.cancel", &json!({"job":next["job"]})).unwrap();
assert!(std::sync::Arc::ptr_eq(&document, &s.doc));
assert_eq!(before, (s.revision, s.undo.len(), s.redo.len(), s.evaluations, s.is_dirty()));
std::sync::Arc::make_mut(&mut std::sync::Arc::make_mut(&mut s.doc).render).setups.insert("Over limit".into(), Default::default());
assert!(s.execute("render.batch", &json!({})).unwrap_err().to_string().contains("32"));
assert_eq!(s.render_queue_state().waiting, 0);
}
#[cfg(test)]
#[test]
fn render_results_batch_refuses_missing_shots_busy_hostile_and_failed_requests_without_queuing() {
let mut empty = batch_fixture(0);
assert!(empty.execute("render.batch", &json!({})).is_err());
let mut s = batch_fixture(2);
let document = s.doc.clone();
let before = (s.revision, s.undo.len(), s.redo.len(), s.evaluations, s.is_dirty());
for request in [
json!([]),
json!({"request":1e300}),
json!({"request":[]}),
json!({"request":{"bogus":true}}),
json!({"request":{"replace":true}}),
json!({"request":{"settings":{"width":0}}}),
json!({"extra":true}),
] {
assert!(s.execute("render.batch", &request).is_err(), "{request}");
assert_eq!((s.render_queue_state().running, s.render_queue_state().waiting), (0, 0));
assert!(s.render_queue_state().job.is_none());
}
let job = s.execute("render.start", &json!({"settings":{"width":8,"height":8}})).unwrap()["job"].clone();
assert!(s.execute("render.batch", &json!({})).is_err());
assert_eq!((s.render_queue_state().running, s.render_queue_state().waiting), (1, 0));
s.execute("render.cancel", &json!({"job":job})).unwrap();
assert!(std::sync::Arc::ptr_eq(&document, &s.doc));
assert_eq!(before, (s.revision, s.undo.len(), s.redo.len(), s.evaluations, s.is_dirty()));
s.set_render_job_host(None).unwrap();
assert!(s.execute("render.batch", &json!({})).is_err());
assert_eq!((s.render_queue_state().running, s.render_queue_state().waiting), (0, 0));
assert!(s.render_queue_state().error.is_some());
assert!(std::sync::Arc::ptr_eq(&document, &s.doc));
assert_eq!(before, (s.revision, s.undo.len(), s.redo.len(), s.evaluations, s.is_dirty()));
}
#[cfg(test)]
#[test]
fn render_results_batch_cameras_and_common_setup_are_captured_before_later_shot_edits() {
let mut s = batch_fixture(2);
let original = s.doc.render.setups.get("Shot 01").unwrap().camera;
s.execute("render.batch", &json!({"request":{"setup":"Shot 00"}})).unwrap();
let first = s.render_queue_state().job.unwrap();
let changed = solidcraft_render::Camera { yaw: 2.0, ..Default::default() };
s.execute("render.settings.set", &json!({"name":"Shot 01","set":{"camera":changed}})).unwrap();
s.execute("render.settings.set", &json!({"name":"Shot 00","set":{"width":64}})).unwrap();
let document = s.doc.clone();
let before = (s.revision, s.undo.len(), s.redo.len(), s.evaluations);
let host = s.render_job_host().unwrap().clone();
for _ in 0..100 {
if !host.pump(4096).unwrap() {
break;
}
}
let reply = s.execute("render.status", &json!({})).unwrap();
let next: solidcraft_render::job::JobId = serde_json::from_value(reply["queue"]["job"].clone()).unwrap();
assert_ne!(next, first);
let request = s.render_job_handle(next).unwrap().status().unwrap().request;
assert_eq!(request.camera, original);
assert_eq!(request.width, 8);
assert!(std::sync::Arc::ptr_eq(&document, &s.doc));
assert_eq!(before, (s.revision, s.undo.len(), s.redo.len(), s.evaluations));
s.execute("render.cancel", &json!({"job":next})).unwrap();
}
#[cfg(test)]
#[test]
fn render_results_compare_keeps_copy_on_the_visible_result_until_a_source_is_evicted() {
let mut s = batch_fixture(1);
let first = complete_result(&mut s, "Shot 00");
complete_result(&mut s, "Shot 00");
s.execute("render.compare", &json!({"left":"live","right":0})).unwrap();
assert_eq!(s.render_comparison(), Some([None, Some(first)]));
assert_eq!(s.render_selected_result(), Some(first));
let document = s.doc.clone();
let before = (s.revision, s.undo.len(), s.redo.len(), s.evaluations);
let mut newest = first;
for index in 0..7 {
newest = complete_result(&mut s, "Shot 00");
if index < 6 {
assert_eq!(s.execute("render.result.copy", &json!({})).unwrap()["frame"]["job"], json!(first));
assert_eq!(s.render_comparison(), Some([None, Some(first)]));
}
}
assert!(s.render_comparison().is_none());
assert_eq!(s.render_selected_result(), Some(newest));
assert!(std::sync::Arc::ptr_eq(&document, &s.doc));
assert_eq!(before, (s.revision, s.undo.len(), s.redo.len(), s.evaluations));
}
+13
View File
@@ -36,6 +36,19 @@ fn add(s: &mut Session, p: &Value) -> Result<Value> {
.and_then(Value::as_object)
.ok_or_else(|| bad(cmd, "`feature` must be a feature object, as a design file or timeline.inspect has it"))?;
let mut raw = raw.clone();
if let Some(Value::Object(def)) = raw.remove("definition") {
for (k, v) in def {
raw.entry(k).or_insert(v);
}
}
for meta in ["omitted", "inputs", "error", "warning", "boolean_diagnostics", "rolled_back", "type_name"] {
raw.remove(meta);
}
if let Some(kind_str) = raw.get("type").and_then(Value::as_str)
&& let Some(canonical) = FeatureKind::type_name_to_kind_key(kind_str)
{
raw.insert("type".into(), json!(canonical));
}
resolve_legacy_names(&mut raw, &s.doc);
let feature = recorded_feature(cmd, &raw)?;
let params = step_params(cmd, p.get("params"))?;
+28
View File
@@ -243,6 +243,34 @@ fn a_recorded_feature_that_does_not_fit_is_refused_and_changes_nothing() {
assert!(s.doc.feature(r["feature"].as_u64().unwrap()).is_some_and(|f| !f.param_names.is_empty()), "its inputs get parameter names");
}
/// #10748: `timeline.inspect` reports a feature's design variant name in `type` and display name
/// in `type_name`; `timeline.add` accepts `timeline.inspect` results and inspect type names.
#[test]
fn timeline_add_accepts_inspect_result_and_type_names() {
let mut s = Session::default();
run(&mut s, "solid.box", json!({"length": 20, "width": 10, "height": 5, "name": "Block"}));
let mut inspected = run(&mut s, "timeline.inspect", json!({"feature": "Block"}));
assert_eq!(inspected["type"], "box", "inspect type is the design file variant name");
assert_eq!(inspected["type_name"], "BoxFeature", "inspect type_name is the display type");
// 1. timeline.add accepts the full inspected object directly in a new session
let mut s2 = Session::default();
let added = run(&mut s2, "timeline.add", json!({"feature": inspected.clone()}));
assert!(added["error"].is_null(), "{added}");
assert_eq!(s2.doc.features.len(), 1);
// 2. timeline.add with id 0 allocates a new id in the current session
inspected["id"] = json!(0);
let added2 = run(&mut s, "timeline.add", json!({"feature": inspected}));
assert!(added2["error"].is_null(), "{added2}");
assert_eq!(s.doc.features.len(), 2);
// 3. timeline.add accepts the inspect type name (e.g. BoxFeature, HoleFeature)
let added_by_type_name =
run(&mut s, "timeline.add", json!({"feature": {"type": "BoxFeature", "length": "5", "width": "5", "height": "5", "corner": [50, 0, 0]}}));
assert!(added_by_type_name["error"].is_null(), "{added_by_type_name}");
}
#[test]
fn the_script_is_written_to_a_file_that_replays() {
let mut s = Session::default();
+54 -23
View File
@@ -20,19 +20,50 @@ fn run(s: &mut Session, p: &Value) -> Result<Value> {
let camera = camera(p)?;
let rows = rows(p)?;
let wanted = body_names(p)?;
let out = outline(s, &camera, wanted.as_deref(), rows)?;
let mask: Vec<Vec<[usize; 2]>> = out.mask.iter().map(|row| spans(row)).collect();
Ok(json!({
"width": out.width,
"height": out.height,
"mm_per_px": round4(out.mm_per_px),
"box_mm": [[round4(out.lo[0]), round4(out.lo[1])], [round4(out.hi[0]), round4(out.hi[1])]],
"right": [clean(out.right.x), clean(out.right.y), clean(out.right.z)],
"up": [clean(out.up.x), clean(out.up.y), clean(out.up.z)],
"projection": if out.perspective { "perspective" } else { "parallel" },
"bodies": out.bodies,
"triangles": out.triangles,
"mask": mask,
}))
}
/// A projected, tightly framed silhouette: the pixel grid, its scale and placement, and the
/// filled mask as one row of booleans per pixel row. `inspect.silhouette` renders this as
/// run-length rows; `inspect.compare_picture` uses the filled rows directly.
pub(crate) struct Silhouette {
pub width: usize,
pub height: usize,
pub mm_per_px: f64,
/// The view-plane box the mask frames, in mm (u along `right`, v along `up`).
pub lo: [f64; 2],
pub hi: [f64; 2],
pub right: Vec3,
pub up: Vec3,
pub perspective: bool,
pub bodies: usize,
pub triangles: usize,
pub mask: Vec<Vec<bool>>,
}
pub(crate) fn outline(s: &Session, camera: &Camera, wanted: Option<&[String]>, rows: usize) -> Result<Silhouette> {
let state = s.world_state();
if let Some(names) = &wanted {
if let Some(names) = wanted {
for name in names {
if !state.bodies.iter().any(|b| b.name == *name) {
return Err(bad(CMD, format!("unknown body name `{name}`")));
}
}
}
let bodies: Vec<_> = state
.bodies
.iter()
.filter(|b| wanted.as_ref().map_or_else(|| s.body_visible(&b.name), |names| names.iter().any(|name| name == &b.name)))
.collect();
let bodies: Vec<_> = state.bodies.iter().filter(|b| wanted.map_or_else(|| s.body_visible(&b.name), |names| names.contains(&b.name))).collect();
if bodies.is_empty() {
return Err(bad(CMD, "nothing visible to outline"));
}
@@ -133,22 +164,22 @@ fn run(s: &mut Session, p: &Value) -> Result<Value> {
}
}
let mask: Vec<Vec<[usize; 2]>> = covered.chunks_exact(width).map(spans).collect();
Ok(json!({
"width": width,
"height": height,
"mm_per_px": round4(mm_per_px),
"box_mm": [[round4(lo[0]), round4(lo[1])], [round4(hi[0]), round4(hi[1])]],
"right": [clean(right.x), clean(right.y), clean(right.z)],
"up": [clean(up.x), clean(up.y), clean(up.z)],
"projection": if perspective { "perspective" } else { "parallel" },
"bodies": bodies.len(),
"triangles": triangle_count,
"mask": mask,
}))
Ok(Silhouette {
width,
height,
mm_per_px,
lo,
hi,
right,
up,
perspective,
bodies: bodies.len(),
triangles: triangle_count,
mask: covered.chunks_exact(width).map(|row| row.to_vec()).collect(),
})
}
fn camera(p: &Value) -> Result<Camera> {
pub(crate) fn camera(p: &Value) -> Result<Camera> {
if p.get("view").is_some() && p.get("camera").is_some() {
return Err(bad(CMD, "give `view` or `camera`, not both"));
}
@@ -171,7 +202,7 @@ fn camera(p: &Value) -> Result<Camera> {
Ok(camera)
}
fn body_names(p: &Value) -> Result<Option<Vec<String>>> {
pub(crate) fn body_names(p: &Value) -> Result<Option<Vec<String>>> {
match p.get("bodies") {
None => Ok(None),
Some(Value::String(name)) => Ok(Some(vec![name.clone()])),
@@ -184,7 +215,7 @@ fn body_names(p: &Value) -> Result<Option<Vec<String>>> {
}
}
fn rows(p: &Value) -> Result<usize> {
pub(crate) fn rows(p: &Value) -> Result<usize> {
match p.get("rows") {
None => Ok(240),
Some(v) => {
@@ -209,7 +240,7 @@ fn edge(a: [f64; 2], b: [f64; 2], p: [f64; 2]) -> f64 {
(b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0])
}
fn spans(row: &[bool]) -> Vec<[usize; 2]> {
pub(crate) fn spans(row: &[bool]) -> Vec<[usize; 2]> {
let mut out = Vec::new();
let mut start = None;
for (x, &on) in row.iter().enumerate() {
+51 -14
View File
@@ -37,7 +37,7 @@ pub static COMMANDS: &[CommandSpec] = &[
CommandSpec::new("sketch.create", "Create Sketch", create_sketch)
.at("SOLID", "CREATE")
.icon("sketch")
.params("plane: XY|XZ|YZ | construction plane name or id | {origin?: [x,y,z] numbers in mm, default [0,0,0] (not an expression), x_dir, y_dir} (in world coordinates unless frame says otherwise; a named plane is the active component's own) | {face: [world x,y,z], body?: exact world body, index?: face index, path?: complete occurrence ids, fixed?: bool}; new face attachments require a unique certified source (a flat face of a sheet metal body attaches to its panel instead, in the flat pattern's coordinates), fixed:true explicitly freezes the selected plane; offset?: expr, flip?: bool | \"x\" | \"y\" (not on a face: sketch the plane from its other side, so the normal and the default extrude direction are reversed; a half turn about the plane's own x or y axis, true picks the one keeping the sketch's y axis upward; offset still moves along the unflipped normal), frame?: world (default) | component (which coordinates an explicit plane is in; the reply's `frame` says which, with a `note` when the active component is moved or turned), project_edges?: bool, activate_part?: bool (default false; on a face of a body of another component than the active one: activate that component at the picked placement first, as component.activate would, and make the sketch there; it stays active afterwards and the reply's `activated` names it), name?"),
.params("plane: XY|XZ|YZ (sketch x, y along world +X, +Y on XY; +X, -Z on XZ; +Y, +Z on YZ) | construction plane name or id | {origin?: [x,y,z] numbers in mm, default [0,0,0] (not an expression), x_dir, y_dir} (in world coordinates unless frame says otherwise; a named plane is the active component's own) | {face: [world x,y,z], body?: exact world body, index?: face index, path?: complete occurrence ids, fixed?: bool}; new face attachments require a unique certified source (a flat face of a sheet metal body attaches to its panel instead, in the flat pattern's coordinates), fixed:true explicitly freezes the selected plane; offset?: expr, flip?: bool | \"x\" | \"y\" (not on a face: sketch the plane from its other side, so the normal and the default extrude direction are reversed; a half turn about the plane's own x or y axis, true picks the one keeping the sketch's y axis upward; offset still moves along the unflipped normal), frame?: world (default) | component (which coordinates an explicit plane is in; the reply's `frame` says which, with a `note` when the active component is moved or turned), project_edges?: bool, activate_part?: bool (default false; on a face of a body of another component than the active one: activate that component at the picked placement first, as component.activate would, and make the sketch there; it stays active afterwards and the reply's `activated` names it), name?"),
CommandSpec::new("sketch.edit", "Edit Sketch", edit_sketch).noundo().icon("sketch").params("sketch: id or name"),
CommandSpec::new("sketch.finish", "Finish Sketch", finish_sketch).at("SKETCH", "FINISH SKETCH").icon("finish").enabled(in_sketch).noundo(),
CommandSpec::new("sketch.cancel", "Cancel Sketch", cancel_sketch)
@@ -139,7 +139,7 @@ pub static COMMANDS: &[CommandSpec] = &[
.at("SKETCH", "CONSTRAINTS")
.icon("c_coincident")
.enabled(in_sketch)
.params(concat!("a, b: points, a point and a curve or sketch axis (x / y: the sketch's own axes, unless a curve has that id; an axis becomes a linked construction line on it, reported in created), or two distinct lines (joins their closest endpoints), or two circles/arcs (the second lies on the first: concentric and equal, `constraints` lists both)", model_operand_doc!())),
.params(concat!("a, b: points (or a line's middle as {midpoint: line}, which becomes a point tied there), a point and a curve or sketch axis (x / y: the sketch's own axes, unless a curve has that id; an axis becomes a linked construction line on it, reported in created), or two distinct lines (joins their closest endpoints), or two circles/arcs (the second lies on the first: concentric and equal, `constraints` lists both)", model_operand_doc!())),
CommandSpec::new("sketch.constraint.tangent", "Tangent", c_tangent)
.at("SKETCH", "CONSTRAINTS")
.icon("c_tangent")
@@ -174,7 +174,7 @@ pub static COMMANDS: &[CommandSpec] = &[
.at("SKETCH", "CONSTRAINTS")
.icon("c_concentric")
.enabled(in_sketch)
.params(concat!("a, b: circles/arcs", model_operand_doc!())),
.params(concat!("a, b: two circles/arcs, or a circle/arc and a sketch point (including origin), in either order; the point and the curve's centre become coincident", model_operand_doc!())),
CommandSpec::new("sketch.constraint.collinear", "Collinear", c_collinear)
.at("SKETCH", "CONSTRAINTS")
.icon("c_collinear")
@@ -325,7 +325,7 @@ pub(super) fn edit_solved<T>(
};
sketch_store::checked(&sk)?;
sketch_store::write(&mut doc, id, sk)?;
*s.doc_mut() = doc;
s.set_doc(doc);
Ok((out, rep))
}
@@ -472,6 +472,9 @@ const CONNECT_TOL: f64 = 1e-6;
/// New points (index `first` on) that land on an existing point, the origin included, get a
/// coincident constraint to it, as when a click snaps there; `connect: false` leaves them free.
/// A point placed by parameter expressions is never tied: it follows its expressions, and a
/// Coincident to where the expressions happened to come to at draw time conflicts with the
/// next parameter change (#10703).
fn connect_points(sk: &mut Sketch, first: usize, p: &Value) -> Result<Vec<String>> {
if !bool_(p, "connect").unwrap_or(true) {
return Ok(Vec::new());
@@ -481,6 +484,9 @@ fn connect_points(sk: &mut Sketch, first: usize, p: &Value) -> Result<Vec<String
old.sort_by(|a, b| a.0.x.total_cmp(&b.0.x));
let mut ties = Vec::new();
for q in first..sk.points.len() {
if sk.points.get(q).is_some_and(|pt| pt.exprs.is_some()) {
continue;
}
let Some(at) = sk.point(q) else { continue };
let from = old.partition_point(|(o, _)| o.x < at.x - CONNECT_TOL);
let hit =
@@ -2250,17 +2256,38 @@ fn c_coincident(s: &mut Session, p: &Value) -> Result<Value> {
if let Some((a, b)) = line_points {
return Ok(ConstraintKind::Coincident { p: a, q: b });
}
// A line's middle, `{midpoint: line}` as a selected midpoint is sent (#10311): the point
// tied there by a Midpoint constraint stands for it, made as Horizontal/Vertical makes it.
let middle = |sk: &mut Sketch, key: &str| -> Result<Option<usize>> {
let Some(line) = p.get(key).and_then(|v| v.get("midpoint")) else { return Ok(None) };
let l = curve_ref(sk, Some(line), cmd, "midpoint")?;
AlignmentTarget::Midpoint(l).materialize(sk, cmd).map(Some)
};
let (a_mid, b_mid) = (middle(sk, "a")?, middle(sk, "b")?);
let aref = p.get("a").and_then(Value::as_str).unwrap_or_default();
let bref = p.get("b").and_then(Value::as_str).ok_or_else(|| bad(cmd, "`b` must be a point or curve"))?;
let bref = p.get("b").and_then(Value::as_str);
let b_point = match (b_mid, bref) {
(Some(b), _) => Some(b),
(None, Some(r)) => sk.resolve_point(r),
(None, None) => return Err(bad(cmd, "`b` must be a point or curve")),
};
let bref = bref.unwrap_or_default();
// A curve (or sketch axis) picked first and a point second: the point goes onto it.
if sk.resolve_point(aref).is_none()
&& let Some(q) = sk.resolve_point(bref)
if a_mid.is_none()
&& sk.resolve_point(aref).is_none()
&& let Some(q) = b_point
&& let Some(c) = curve_or_axis(sk, aref)?
{
return Ok(ConstraintKind::PointOnCurve { p: q, c });
}
let a = point_ref(sk, p.get("a"), cmd, "a")?;
if let Some(b) = sk.resolve_point(bref) {
let a = match a_mid {
Some(a) => a,
None => point_ref(sk, p.get("a"), cmd, "a")?,
};
if let Some(b) = b_point {
if a == b && (a_mid.is_some() || b_mid.is_some()) {
return Err(bad(cmd, "that point is already the line's midpoint"));
}
return Ok(ConstraintKind::Coincident { p: a, q: b });
}
let c = curve_or_axis(sk, bref)?.ok_or_else(|| bad(cmd, format!("unknown entity `{bref}`")))?;
@@ -2433,11 +2460,21 @@ fn c_perpendicular(s: &mut Session, p: &Value) -> Result<Value> {
})
}
fn c_concentric(s: &mut Session, p: &Value) -> Result<Value> {
let p = &super::sketch_project::project_operands(s, p, "sketch.constraint.concentric", &["a", "b"])?;
constrain(s, p, "sketch.constraint.concentric", |sk| {
two_curves(sk, p, "sketch.constraint.concentric")
.and_then(|ab| two_different("sketch.constraint.concentric", ab))
.map(|(a, b)| ConstraintKind::Concentric { a, b })
let cmd = "sketch.constraint.concentric";
let p = &super::sketch_project::project_operands(s, p, cmd, &["a", "b"])?;
constrain(s, p, cmd, |sk| {
let centre = |key: &str| {
let curve = sk.curves.get(sk.curve_index(p.get(key)?.as_str()?)?)?;
match curve.kind {
CurveKind::Circle { c, .. } | CurveKind::Arc { c, .. } => Some(c),
_ => None,
}
};
let point = |key: &str| sk.resolve_point(p.get(key)?.as_str()?);
if let Some((p, q)) = centre("a").zip(point("b")).or_else(|| centre("b").zip(point("a"))) {
return Ok(ConstraintKind::Coincident { p, q });
}
two_curves(sk, p, cmd).and_then(|ab| two_different(cmd, ab)).map(|(a, b)| ConstraintKind::Concentric { a, b })
})
}
fn c_collinear(s: &mut Session, p: &Value) -> Result<Value> {
+1 -1
View File
@@ -67,7 +67,7 @@ fn make(s: &mut Session, p: &Value, cmd: &str, decal: bool) -> Result<Value> {
id,
name: name.clone(),
format: format.into(),
data: base64_encode(&bytes),
data: base64_encode(&bytes).into(),
pixels,
plane,
center,
@@ -25,6 +25,59 @@ fn sketch(s: &Session) -> solidcraft_sketch::Sketch {
s.model.state().sketch(s.active_sketch.unwrap()).unwrap().sketch.clone()
}
#[test]
fn concentric_centres_a_circle_or_arc_on_a_point_in_either_order() {
for arc in [false, true] {
for origin in [false, true] {
for reverse in [false, true] {
let mut s = new_sketch();
let made = if arc {
run(&mut s, "sketch.arc.center_point", json!({"center": [20, 10], "start": [25, 10], "end": [20, 15]}))
} else {
run(&mut s, "sketch.circle.center", json!({"center": [20, 10], "radius": 5}))
};
let curve = ids(&made["curves"])[0].clone();
let (point, expected) = if origin {
("origin", Vec2::new(0.0, 0.0))
} else {
run(&mut s, "sketch.point", json!({"point": [-7, 4], "id": "target"}));
run(&mut s, "sketch.constraint.fix", json!({"entity": "target"}));
("target", Vec2::new(-7.0, 4.0))
};
let before = s.doc.to_json();
let undo = s.undo.len();
let params = if reverse { json!({"a": point, "b": curve}) } else { json!({"a": curve, "b": point}) };
let result = run(&mut s, "sketch.constraint.concentric", params);
let sk = sketch(&s);
let index = sk.curve_index(&curve).unwrap();
assert!(sk.center(index).unwrap().dist(expected) < 1e-6, "arc={arc}, origin={origin}, reverse={reverse}");
assert!((sk.radius(index).unwrap() - 5.0).abs() < 1e-6);
assert!(matches!(
sk.constraints.iter().find(|c| c.id == result["constraint"].as_str().unwrap()).unwrap().kind,
ConstraintKind::Coincident { .. }
));
assert_eq!(s.undo.len(), undo + 1);
run(&mut s, "edit.undo", json!({}));
assert_eq!(s.doc.to_json(), before);
}
}
}
}
#[test]
fn concentric_rejects_invalid_point_pairs_without_changing_the_sketch() {
let mut s = new_sketch();
run(&mut s, "sketch.circle.center", json!({"center": [20, 10], "radius": 5, "id": "circle"}));
run(&mut s, "sketch.line", json!({"points": [[10, 10], [20, 20]], "ids": ["line"]}));
let before = s.doc.to_json();
let undo = s.undo.len();
for (a, b) in [("line", "origin"), ("origin", "line"), ("origin", "circle.center"), ("circle", "circle.center"), ("circle", "missing")] {
assert!(s.execute("sketch.constraint.concentric", &json!({"a": a, "b": b})).is_err(), "{a}, {b}");
assert_eq!(s.doc.to_json(), before);
assert_eq!(s.undo.len(), undo);
}
}
#[test]
fn redundant_dimensions_are_refused_driven_ones_measure() {
let mut s = new_sketch();
@@ -803,6 +856,61 @@ fn midpoint_alignment_rejects_invalid_or_conflicting_references_atomically() {
}
}
/// #10311: Coincident takes a line's middle as `{midpoint: line}`, first or second. With another
/// line the middle lies on it, with a point it lands on the point; a point tied to the middle by
/// a Midpoint constraint stands for it, in one undo step.
#[test]
fn coincident_takes_a_line_midpoint_either_way_round() {
for other in ["l2", "target"] {
for reverse in [false, true] {
let mut s = new_sketch();
run(&mut s, "sketch.line", json!({"points":[[10,10],[30,20]],"infer":false,"connect":false}));
run(&mut s, "sketch.line", json!({"points":[[0,40],[60,40]],"infer":false,"connect":false}));
run(&mut s, "sketch.point", json!({"point":[24,50],"id":"target"}));
run(&mut s, "sketch.constraint.fix", json!({"entities":["l2","target"]}));
let before = s.doc.to_json();
let undo = s.undo.len();
let middle = json!({"midpoint":"l1"});
let (a, b) = if reverse { (json!(other), middle) } else { (middle, json!(other)) };
let result = run(&mut s, "sketch.constraint.coincident", json!({"a":a,"b":b}));
assert_eq!(result["created"]["points"].as_array().unwrap().len(), 1, "{other} {reverse}: {result}");
assert_eq!(s.undo.len(), undo + 1);
let sk = sketch(&s);
let mid = line_midpoint(&sk, "l1");
if other == "l2" {
assert!((mid.y - 40.0).abs() < 1e-6, "{other} {reverse}: middle at {mid:?}");
} else {
assert!(mid.dist(Vec2::new(24.0, 50.0)) < 1e-6, "{other} {reverse}: middle at {mid:?}");
}
assert!(sk.constraints.iter().any(|c| matches!(c.kind, ConstraintKind::Midpoint { .. })));
run(&mut s, "edit.undo", json!({}));
assert_eq!(s.doc.to_json(), before);
}
}
}
/// A middle Coincident can't take (not a line, an unknown curve, the same middle twice, or the
/// point already tied there) is refused and leaves the sketch as it was.
#[test]
fn coincident_refuses_a_midpoint_it_cannot_take_atomically() {
let mut s = new_sketch();
run(&mut s, "sketch.line", json!({"points":[[10,10],[30,20]],"infer":false,"connect":false}));
run(&mut s, "sketch.circle.center", json!({"center":[40,50],"radius":3}));
let tied = run(&mut s, "sketch.point", json!({"point":"mid:l1"}))["point"].as_str().unwrap().to_owned();
for (a, b) in [
(json!({"midpoint":"c1"}), json!("origin")),
(json!({"midpoint":"missing"}), json!("origin")),
(json!({"midpoint":"l1"}), json!({"midpoint":"l1"})),
(json!({"midpoint":"l1"}), json!(tied)),
] {
let before = s.doc.to_json();
let undo = s.undo.len();
assert!(s.execute("sketch.constraint.coincident", &json!({"a":a,"b":b})).is_err(), "{a} {b}");
assert_eq!(s.doc.to_json(), before, "{a} {b}");
assert_eq!(s.undo.len(), undo);
}
}
fn line_midpoint(sk: &solidcraft_sketch::Sketch, id: &str) -> Vec2 {
// Read the curve's current endpoints: after a corner cut an original endpoint
// id can still name the retained construction side's corner.
+52 -6
View File
@@ -1,4 +1,5 @@
//! Fast infinite reference lines. Preview and creation use the same analytic solutions.
//! Fast reference lines, infinite or with a length (#10258). Preview and creation use the same
//! analytic solutions.
use serde_json::{Value, json};
use solidcraft_geom::{Seg2, Vec2};
@@ -23,7 +24,7 @@ macro_rules! variant {
.enabled(in_sketch)
.contract(&CONTRACT)
.output(output_schema_for::<ConstructionLineResult>)
.params(concat!("Infinite construction line, ", $mode, ". at: point; through?: second point; curve?, curve2?: sketch references; angle?: degrees from sketch X; near?: solution selector; solution?: zero-based solution index. Tangents take circles/arcs, or an open spline/conic endpoint for tangent_at.", created_doc!()))
.params(concat!("Construction line, ", $mode, ". at: point; through?: second point; curve?, curve2?: sketch references; angle?: degrees from sketch X; near?: solution selector, and the side a line with a length runs towards; solution?: zero-based solution index; length?: a segment of this length from `at`, held by a length dimension, instead of an infinite line (horizontal, vertical, angle, parallel, perpendicular); construction?: false makes that segment a normal line. Tangents take circles/arcs, or an open spline/conic endpoint for tangent_at.", created_doc!()))
};
}
@@ -54,6 +55,19 @@ impl ConstructionLine {
}
}
/// The methods that take a `length`: the ones whose line starts at `at` with a known direction.
pub fn construction_line_takes_length(mode: &str) -> bool {
matches!(mode, "horizontal" | "vertical" | "angle" | "parallel" | "perpendicular")
}
/// The two ends of a construction line cut to `length` from its start: along its direction, or
/// the other way when `near` lies behind the start. The preview draws exactly what is created.
pub fn construction_segment(line: ConstructionLine, length: f64, near: Option<Vec2>) -> (Vec2, Vec2) {
let behind = near.is_some_and(|q| (q - line.start).dot(line.direction) < 0.0);
let direction = if behind { -line.direction } else { line.direction };
(line.start, line.start + direction * length)
}
/// The method belonging to an interactive command. The generic command defaults to two points.
pub fn construction_line_mode(id: &str) -> Option<&str> {
if id == CMD { Some("free") } else { id.strip_prefix("sketch.construction_line.") }
@@ -277,6 +291,29 @@ fn run(s: &mut Session, p: &Value, default_mode: &str) -> Result<Value> {
} else {
None
};
let length = match p.get("length") {
None | Some(Value::Null) => None,
Some(_) if !construction_line_takes_length(mode) => {
return Err(bad(CMD, "`length` is for the horizontal, vertical, angle, parallel and perpendicular methods"));
}
Some(_) => {
let e = expr(&p, "length").ok_or_else(|| bad(CMD, "`length` must be a length or expression"))?;
let e = doc.with_design_unit(e.as_str(), solidcraft_doc::expr::Kind::Length);
let value = doc.eval(&e, solidcraft_doc::expr::Kind::Length).map_err(|e| bad(CMD, format!("length: {e}")))?;
if !(value > 1e-9 && value < 1e8) {
return Err(bad(CMD, "length must be positive"));
}
Some((e, value))
}
};
let construction = match p.get("construction") {
None | Some(Value::Null) => true,
Some(Value::Bool(value)) => *value,
Some(_) => return Err(bad(CMD, "`construction` must be a boolean")),
};
if !construction && length.is_none() {
return Err(bad(CMD, "only a line with a `length` can be a normal line: an infinite line is construction geometry"));
}
let solutions = construction_line_solutions(sk, &params, mode)?;
let choice = construction_line_choice(&solutions, &params)?;
let chosen = solutions.get(choice).ok_or_else(|| bad(CMD, "no solution"))?;
@@ -294,11 +331,15 @@ fn run(s: &mut Session, p: &Value, default_mode: &str) -> Result<Value> {
} else {
at.idx()
};
let end = through.as_ref().map_or(chosen.start + chosen.direction * 10.0, |q| q.pos());
let l = sk.add_line(chosen.start, end, start_ref, through.as_ref().and_then(|q| q.idx()), None)?;
let near = p.get("near").and_then(vec2);
let (start, end) = match &length {
Some((_, value)) => construction_segment(*chosen, *value, near),
None => (chosen.start, through.as_ref().map_or(chosen.start + chosen.direction * 10.0, |q| q.pos())),
};
let l = sk.add_line(start, end, start_ref, through.as_ref().and_then(|q| q.idx()), None)?;
if let Some(c) = sk.curves.get_mut(l) {
c.infinite = true;
c.construction = true;
c.infinite = length.is_none();
c.construction = construction;
}
let mut cons = Vec::new();
let kind = match (mode, reference) {
@@ -314,6 +355,11 @@ fn run(s: &mut Session, p: &Value, default_mode: &str) -> Result<Value> {
let parameter = angle_expression.map(|e| doc.new_model_param(&e, "deg"));
cons.push(sk.add_constraint(k, parameter)?);
}
if let Some((e, value)) = &length {
let unit = solidcraft_doc::expr::length_unit_or_mm(&doc.units).to_string();
let parameter = doc.new_model_param(e, &unit);
cons.push(sk.add_constraint(ConstraintKind::Length { l, value: *value }, Some(parameter))?);
}
if let ("tangent_two", Some(b)) = (mode, second) {
cons.push(add_c(sk, ConstraintKind::Tangent { a: l, b })?);
}
+55 -12
View File
@@ -16,7 +16,7 @@ use super::sketch::{add_c, circumcircle, edit, ids_of, mark_construction, req_pa
use super::sketch_store;
use super::{CommandSpec, in_sketch};
use crate::params::{bad, num, req_vec2, str_, string_list};
use crate::schema::sketch_create::{SplineReverseParams, SplineReverseResult};
use crate::schema::sketch_create::{SplineRemovePointParams, SplineRemovePointResult, SplineReverseParams, SplineReverseResult};
use crate::schema::{TypedContract, output_schema_for};
use crate::{EngineError, Result, Session};
@@ -29,6 +29,7 @@ mod text_move_tests;
mod text_expr_tests;
const SPLINE_REVERSE_CONTRACT: TypedContract<SplineReverseParams> = TypedContract::new();
const SPLINE_REMOVE_POINT_CONTRACT: TypedContract<SplineRemovePointParams> = TypedContract::new();
pub static COMMANDS: &[CommandSpec] = &[
CommandSpec::new("sketch.line.midpoint", "Midpoint Line", midpoint_line)
@@ -88,6 +89,12 @@ pub static COMMANDS: &[CommandSpec] = &[
.icon("spline")
.enabled(in_sketch)
.params("curve: fit point spline id, at: [x,y]: a fit point is inserted where the curve passes nearest to it, between its neighbours (the curve still runs through it and all its old points; constraints on them stay); end?: start|end: `at` becomes the spline's new first or last point instead, and the curve runs on to it (refused when that end is joined to other geometry). Control point splines take no new points yet. Returns point: the new point's id"),
CommandSpec::new("sketch.spline.remove_point", "Remove Spline Point", crate::checked!("sketch.spline.remove_point", spline_remove_point))
.icon("spline")
.enabled(in_sketch)
.contract(&SPLINE_REMOVE_POINT_CONTRACT)
.output(output_schema_for::<SplineRemovePointResult>)
.params("curve: spline id, point: one of its points (a point id, or the curve's start or end such as s1.end): the spline runs through (or over) its other points only, and the point goes with the constraints on it unless another curve uses it. Fit point, control point and closed splines. Refused for a closed spline's closing point, for an end joined to other geometry or held by a constraint, when fewer than 2 points would be left (3 on a closed spline), and for a projected, offset or patterned spline. Returns curve, point: the point's id, deleted: whether the point went too"),
CommandSpec::new("sketch.spline.reverse", "Reverse Spline Direction", crate::checked!("sketch.spline.reverse", spline_reverse))
.icon("spline")
.enabled(in_sketch)
@@ -711,6 +718,22 @@ fn spline_insert_point(s: &mut Session, p: &Value) -> Result<Value> {
Ok(json!({"point": point, "curve": p.get("curve"), "sketch": info}))
}
/// One point taken out of a spline (#10783).
fn spline_remove_point(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.spline.remove_point";
let id = str_(p, "curve").ok_or_else(|| bad(cmd, "`curve` must be a spline id"))?.to_string();
let point = str_(p, "point").ok_or_else(|| bad(cmd, "`point` must be one of the spline's points"))?.to_string();
let ((point, deleted), info) = edit(s, p, cmd, false, |sk, _| {
let c = sk.curve_index(&id).ok_or_else(|| bad(cmd, format!("unknown curve `{id}`")))?;
let at = sk.resolve_point(&point).ok_or_else(|| bad(cmd, format!("unknown sketch point `{point}`")))?;
let name = sk.points.get(at).map_or(point.clone(), |q| q.id.clone());
let deleted = sk.remove_spline_point(c, at).map_err(|e| bad(cmd, e.to_string()))?;
Ok((name, deleted))
})?;
let reply = SplineRemovePointResult { curve: id, point, deleted, sketch: info, param_warnings: Vec::new() };
serde_json::to_value(reply).map_err(|e| EngineError::Other(e.to_string()))
}
fn spline_reverse(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.spline.reverse";
let id = str_(p, "curve").ok_or_else(|| bad(cmd, "`curve` must be a spline id"))?.to_string();
@@ -850,6 +873,27 @@ pub fn tangent_arc_preview(sk: &Sketch, curve: &str, start: &str, end: Vec2) ->
Some(out)
}
/// The picked point, then any other point at the same place: a trimmed arc's end is its own
/// point, lying on the end of the curve that trimmed it, which is the one a click picks (#1485).
fn tangent_start_candidates(sk: &Sketch, picked: usize) -> Vec<usize> {
let at = sk.point(picked).unwrap_or_default();
let tol = 1e-6 * at.len().max(1.0);
std::iter::once(picked).chain((0..sk.points.len()).filter(|&q| q != picked && sk.point(q).is_some_and(|x| x.dist(at) <= tol))).collect()
}
/// The first drawn curve (and which of the candidate points) a tangent arc can leave from.
fn tangent_start_curve(sk: &Sketch, candidates: &[usize]) -> Option<(usize, usize)> {
candidates
.iter()
.find_map(|&q| sk.curves.iter().enumerate().find(|(i, c)| !c.construction && leaving_dir(sk, *i, q).is_some()).map(|(i, _)| (i, q)))
}
/// Whether `sketch.arc.tangent` can start at sketch point `start` (an id such as `l1.end`): the
/// end of a drawn line, arc or spline there. A tool asks before taking the click (#10653).
pub fn tangent_arc_starts_at(sk: &Sketch, start: &str) -> bool {
sk.resolve_point(start).is_some_and(|picked| tangent_start_curve(sk, &tangent_start_candidates(sk, picked)).is_some())
}
fn arc_tangent(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "sketch.arc.tangent";
// An id ends the arc on that point, so a chain can close; coordinates make a new point.
@@ -864,14 +908,16 @@ fn arc_tangent(s: &mut Session, p: &Value) -> Result<Value> {
(None, Some(at)) => (at, None),
(None, None) => return Err(bad(cmd, "missing `end`")),
};
// A snapped place that is no point yet (`on:l3:…` on a line, `mid:l1` at its middle: a
// click between a line's ends) is no end to start from: said so, not as an unknown point
// (#10653). Point ids (`l1.end`, `p3`, `origin`) have no colon.
let not_an_end = || bad(cmd, "`start` must be the end point of a line or arc");
if str_(p, "start").is_some_and(|start| start.contains(':')) {
return Err(not_an_end());
}
let st = req_parg(sk, doc, p, "start", cmd)?;
let picked = st.idx().ok_or_else(|| bad(cmd, "`start` must be the end point of a line or arc"))?;
// The picked point, then any other point at the same place: a trimmed arc's end is its own
// point, lying on the end of the curve that trimmed it, which is the one a click picks (#1485).
let at = st.pos();
let tol = 1e-6 * at.len().max(1.0);
let candidates: Vec<usize> =
std::iter::once(picked).chain((0..sk.points.len()).filter(|&q| q != picked && sk.point(q).is_some_and(|x| x.dist(at) <= tol))).collect();
let picked = st.idx().ok_or_else(not_an_end)?;
let candidates = tangent_start_candidates(sk, picked);
let (ci, pi) = match str_(p, "curve") {
Some(id) => {
let ci = sk.curve_index(id).ok_or_else(|| bad(cmd, format!("unknown curve `{id}`")))?;
@@ -882,10 +928,7 @@ fn arc_tangent(s: &mut Session, p: &Value) -> Result<Value> {
.ok_or_else(|| bad(cmd, "`start` is not an end of that curve"))?;
(ci, pi)
}
None => candidates
.iter()
.find_map(|&q| sk.curves.iter().enumerate().find(|(i, c)| !c.construction && leaving_dir(sk, *i, q).is_some()).map(|(i, _)| (i, q)))
.ok_or_else(|| bad(cmd, "no line or arc ends at `start`"))?,
None => tangent_start_curve(sk, &candidates).ok_or_else(|| bad(cmd, "no line or arc ends at `start`"))?,
};
let t = leaving_dir(sk, ci, pi).ok_or_else(|| bad(cmd, "`start` is not an end of that curve"))?;
let s0 = st.pos();
@@ -31,6 +31,7 @@ pub fn dimension_roles(creator: &str) -> Option<&'static [&'static str]> {
// A circle is dimensioned by its diameter unless a radius is given.
"sketch.circle.center" | "sketch.circle.two_point" | "sketch.circle.three_point" => &["diameter", "radius"],
"sketch.arc.center_point" => &["radius", "sweep"],
"sketch.ellipse" => &["major_radius", "minor_radius"],
"sketch.slot.center_to_center" | "sketch.slot.overall" | "sketch.slot.center_point" => &["length", "width"],
"sketch.slot.arc_center" => &["radius", "sweep", "width"],
"sketch.slot.arc_three_point" => &["width"],
@@ -152,6 +153,9 @@ pub(super) fn draw(s: &mut Session, p: &Value) -> Result<Value> {
(c, "width" | "height") if c.starts_with("sketch.rectangle.") => (vec![curve(usize::from(role == "height"))?], "length"),
(c, "radius" | "diameter") if c.starts_with("sketch.circle.") => (vec![curve(0)?], role),
("sketch.arc.center_point", "radius" | "sweep") => (vec![curve(0)?], role),
// `sketch.dimension` gives an ellipse its major radius as `major`, else its minor one.
("sketch.ellipse", "major_radius") => (vec![curve(0)?], "major"),
("sketch.ellipse", "minor_radius") => (vec![curve(0)?], "radius"),
("sketch.slot.center_to_center" | "sketch.slot.center_point", "length") => (vec![curve(0)?], "length"),
("sketch.slot.overall", "length") => (vec![curve(1)?, curve(3)?], "overall_length"),
(c, "width") if c.starts_with("sketch.slot.") => (vec![curve(1)?], "diameter"),

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