Release 0.28.0
@@ -84,6 +84,8 @@ Editable drawing code, native projects and shipped exports are collected under `
|
||||
| docs/book/src/generated/tool-preview.png | SolidCraft contributors | rendered by `cargo xtask book` (`solidcraft-cli snapshot docs/book/scripts/tool-preview.json`; gitignored) | MIT OR Apache-2.0 |
|
||||
| assets/fonts/liberation-sans/LiberationSans-Regular.ttf | Red Hat, Inc. (Liberation Fonts 2.x; digitized data Google) | https://github.com/liberationfonts (Debian fonts-liberation), unmodified; licence text in assets/fonts/liberation-sans/OFL.txt; used for sketch Text outlines | SIL OFL 1.1 |
|
||||
| assets/fonts/biz-udp-gothic/BIZUDPGothic-Regular.ttf | The BIZ UDGothic Project Authors | https://github.com/googlefonts/morisawa-biz-ud-gothic (Google Fonts), unmodified; licence text in assets/fonts/biz-udp-gothic/OFL.txt; used for web UI Japanese font | SIL OFL 1.1 |
|
||||
| assets/fonts/craft-ui-sc/CraftUI-SC.otf | Adobe / Google Inc. (Noto Sans SC); SolidCraft subset | https://github.com/notofonts/noto-cjk Sans/SubsetOTF/SC Regular, glyph-subset for assets/locales/zh.json and renamed Craft UI SC (OFL reserved-name); licence text in assets/fonts/craft-ui-sc/OFL.txt; web UI Simplified Chinese | SIL OFL 1.1 |
|
||||
| assets/fonts/craft-ui-tc/CraftUI-TC.otf | Adobe / Google Inc. (Noto Sans TC); SolidCraft subset | https://github.com/notofonts/noto-cjk Sans/SubsetOTF/TC Regular, glyph-subset for assets/locales/zh-TW.json and renamed Craft UI TC (OFL reserved-name); licence text in assets/fonts/craft-ui-tc/OFL.txt; web UI Traditional Chinese | SIL OFL 1.1 |
|
||||
| tests/step-conformance/cone_point.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
|
||||
| tests/step-conformance/cone_point_vertex.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
|
||||
| tests/step-conformance/cylinder.step | step-conformance contributors | generated from formulas by https://github.com/dan335/step-conformance (corpus/generated, 96f30bb); import regression fixture | CC0-1.0 |
|
||||
@@ -125,6 +127,8 @@ Editable drawing code, native projects and shipped exports are collected under `
|
||||
| assets/appearances/library.rs | SolidCraft contributors | Original SolidCraft resource data or drawing code, collected without redesign | MIT OR Apache-2.0 |
|
||||
| assets/appearances/opaque_finishes.rs | SolidCraft contributors | original illustrative opaque finish values; no external dataset, textures or measured engineering data | MIT OR Apache-2.0 |
|
||||
| assets/fonts/biz-udp-gothic/OFL.txt | The BIZ UDGothic Project Authors | Unmodified accompanying font licence | SIL OFL 1.1 |
|
||||
| assets/fonts/craft-ui-sc/OFL.txt | Adobe / Google Inc. (Noto CJK); SolidCraft header | Accompanying OFL for CraftUI-SC.otf | SIL OFL 1.1 |
|
||||
| assets/fonts/craft-ui-tc/OFL.txt | Adobe / Google Inc. (Noto CJK); SolidCraft header | Accompanying OFL for CraftUI-TC.otf | SIL OFL 1.1 |
|
||||
| assets/fonts/ibm-plex-mono/OFL.txt | IBM Corp. | Unmodified accompanying font licence | SIL OFL 1.1 |
|
||||
| assets/fonts/ibm-plex-sans/OFL.txt | IBM Corp. | Unmodified accompanying font licence | SIL OFL 1.1 |
|
||||
| assets/fonts/liberation-sans/OFL.txt | Red Hat, Inc.; Liberation Fonts contributors | Unmodified accompanying font licence | SIL OFL 1.1 |
|
||||
@@ -611,6 +615,25 @@ Original SolidCraft drawings, Open for owner review; no runtime assignments.
|
||||
| assets/locales/pending/render-wear.json | SolidCraft contributors | Original UI labels and translations | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/model/equation_curve.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
|
||||
| assets/locales/pending/sketch-recent-snap.json | SolidCraft contributors | Original preference messages for the recent centre and midpoint snap | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/model/construction_line.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/model/construction_line_horizontal.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/model/construction_line_vertical.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/model/construction_line_angle.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/model/construction_line_parallel.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/model/construction_line_perpendicular.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/model/construction_line_tangent_external.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/model/construction_line_tangent_at.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/model/construction_line_tangent_two.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/model/construction_line_bisector.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
|
||||
| docs/book/src/generated/equation-circle.png | SolidCraft contributors | rendered by `cargo xtask book` (`solidcraft-cli snapshot docs/book/scripts/equation-circle.json`; gitignored) | MIT OR Apache-2.0 |
|
||||
| docs/book/src/generated/equation-flower.png | SolidCraft contributors | rendered by `cargo xtask book` (`solidcraft-cli snapshot docs/book/scripts/equation-flower.json`; gitignored) | MIT OR Apache-2.0 |
|
||||
| docs/book/src/generated/equation-involute.png | SolidCraft contributors | rendered by `cargo xtask book` (`solidcraft-cli snapshot docs/book/scripts/equation-involute.json`; gitignored) | MIT OR Apache-2.0 |
|
||||
| docs/book/src/generated/equation-line.png | SolidCraft contributors | rendered by `cargo xtask book` (`solidcraft-cli snapshot docs/book/scripts/equation-line.json`; gitignored) | MIT OR Apache-2.0 |
|
||||
| docs/book/src/generated/equation-parabola.png | SolidCraft contributors | rendered by `cargo xtask book` (`solidcraft-cli snapshot docs/book/scripts/equation-parabola.json`; gitignored) | MIT OR Apache-2.0 |
|
||||
| docs/book/src/generated/equation-spiral.png | SolidCraft contributors | rendered by `cargo xtask book` (`solidcraft-cli snapshot docs/book/scripts/equation-spiral.json`; gitignored) | MIT OR Apache-2.0 |
|
||||
| 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 |
|
||||
|
||||
## Code adapted from sibling projects
|
||||
|
||||
@@ -670,6 +693,7 @@ copied into SolidCraft.
|
||||
|
||||
| Directory | Source | Licence | Changes |
|
||||
|---|---|---|---|
|
||||
| vendor/accesskit_macos | accesskit_macos 0.26.3, https://github.com/AccessKit/accesskit (The AccessKit contributors) | MIT OR Apache-2.0 | the subclassing adapter and its tests from 0.27.2: dropping the adapter keeps AppKit's key-value observers (vendor/README.md) |
|
||||
| vendor/truck-meshalgo | truck-meshalgo 0.4.0, https://github.com/ricosjp/truck (RICOS Co. Ltd.) | Apache-2.0 | meshing: bounded parameter searches, collapsed-boundary projection, subdivision and provisional fixed-grid previews (vendor/README.md) |
|
||||
| vendor/truck-stepio | truck-stepio 0.3.0, https://github.com/ricosjp/truck (RICOS Co. Ltd.) | Apache-2.0 | STEP writer: intersection-curve surfaces written at unique ids (vendor/README.md) |
|
||||
| vendor/truck-shapeops | truck-shapeops 0.4.0, https://github.com/ricosjp/truck (RICOS Co. Ltd.) | Apache-2.0 | robust meshing and nearest-parameter fallback in booleans; degenerate intersection-curve rejection; face and edge history of booleans; trace macro; corner choice at vertices several intersection curves end on; reuse of an operand's meshes (vendor/README.md) |
|
||||
|
||||
@@ -60,8 +60,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "accesskit_macos"
|
||||
version = "0.26.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce02dc63b43f0c9296af9ac946312a2dc8814427d7a64d2d600971dac55b6076"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_consumer 0.38.0",
|
||||
@@ -4210,7 +4208,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft"
|
||||
version = "0.27.1"
|
||||
version = "0.28.0"
|
||||
dependencies = [
|
||||
"block2 0.6.2",
|
||||
"eframe",
|
||||
@@ -4242,15 +4240,16 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"winit",
|
||||
"x11rb",
|
||||
"zbus 5.19.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-3dx-client"
|
||||
version = "0.27.1"
|
||||
version = "0.28.0"
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-cli"
|
||||
version = "0.27.1"
|
||||
version = "0.28.0"
|
||||
dependencies = [
|
||||
"image",
|
||||
"log",
|
||||
@@ -4262,7 +4261,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-derive"
|
||||
version = "0.27.1"
|
||||
version = "0.28.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4271,7 +4270,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-doc"
|
||||
version = "0.27.1"
|
||||
version = "0.28.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
@@ -4288,7 +4287,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-engine"
|
||||
version = "0.27.1"
|
||||
version = "0.28.0"
|
||||
dependencies = [
|
||||
"image",
|
||||
"log",
|
||||
@@ -4309,7 +4308,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-geom"
|
||||
version = "0.27.1"
|
||||
version = "0.28.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4318,7 +4317,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-gpu"
|
||||
version = "0.27.1"
|
||||
version = "0.28.0"
|
||||
dependencies = [
|
||||
"image",
|
||||
"log",
|
||||
@@ -4338,7 +4337,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-io"
|
||||
version = "0.27.1"
|
||||
version = "0.28.0"
|
||||
dependencies = [
|
||||
"cadmpeg-core",
|
||||
"cadmpeg-ir",
|
||||
@@ -4362,7 +4361,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-kernel"
|
||||
version = "0.27.1"
|
||||
version = "0.28.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"proptest",
|
||||
@@ -4382,7 +4381,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-mcp"
|
||||
version = "0.27.1"
|
||||
version = "0.28.0"
|
||||
dependencies = [
|
||||
"serde_json",
|
||||
"solidcraft-engine",
|
||||
@@ -4391,7 +4390,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-render"
|
||||
version = "0.27.1"
|
||||
version = "0.28.0"
|
||||
dependencies = [
|
||||
"image",
|
||||
"moxcms",
|
||||
@@ -4405,7 +4404,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-sketch"
|
||||
version = "0.27.1"
|
||||
version = "0.28.0"
|
||||
dependencies = [
|
||||
"harfrust",
|
||||
"serde",
|
||||
@@ -4418,14 +4417,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-space-mouse"
|
||||
version = "0.27.1"
|
||||
version = "0.28.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-ui-egui"
|
||||
version = "0.27.1"
|
||||
version = "0.28.0"
|
||||
dependencies = [
|
||||
"arboard",
|
||||
"egui",
|
||||
@@ -4455,7 +4454,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-web"
|
||||
version = "0.27.1"
|
||||
version = "0.28.0"
|
||||
dependencies = [
|
||||
"eframe",
|
||||
"egui",
|
||||
@@ -5998,7 +5997,7 @@ checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7"
|
||||
|
||||
[[package]]
|
||||
name = "xtask"
|
||||
version = "0.27.1"
|
||||
version = "0.28.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"serde",
|
||||
|
||||
@@ -5,7 +5,7 @@ members = ["crates/*", "apps/*", "xtask"]
|
||||
exclude = ["vendor"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.27.1"
|
||||
version = "0.28.0"
|
||||
edition = "2024"
|
||||
license = "MIT OR Apache-2.0"
|
||||
rust-version = "1.95"
|
||||
@@ -165,6 +165,9 @@ egui-wgpu = { path = "vendor/egui-wgpu" }
|
||||
# winit 0.30 never binds and eframe 0.36 still requires winit 0.30.
|
||||
winit = { path = "vendor/winit" }
|
||||
|
||||
# macOS: dropping the accessibility adapter leaves AppKit's key-value observers intact (#7535).
|
||||
accesskit_macos = { path = "vendor/accesskit_macos" }
|
||||
|
||||
# Mutation testing (cargo-mutants, docs/mutation-testing.md): the ci profile with incremental
|
||||
# builds, since every mutant rebuilds the crates above the one it changes.
|
||||
[profile.mutants]
|
||||
|
||||
@@ -16799,6 +16799,14 @@ Bundled fonts (SIL Open Font License, Version 1.1)
|
||||
Copyright 2022 The BIZ UDGothic Project Authors (https://github.com/googlefonts/morisawa-biz-ud-mincho)
|
||||
Source: https://github.com/googlefonts/morisawa-biz-ud-gothic
|
||||
|
||||
- CraftUI-SC.otf (assets/fonts/craft-ui-sc/CraftUI-SC.otf)
|
||||
Copyright 2014-2021 Adobe / Google Inc. (Noto Sans SC subset for SolidCraft web zh)
|
||||
Source: https://github.com/notofonts/noto-cjk (Sans SubsetOTF/SC)
|
||||
|
||||
- CraftUI-TC.otf (assets/fonts/craft-ui-tc/CraftUI-TC.otf)
|
||||
Copyright 2014-2021 Adobe / Google Inc. (Noto Sans TC subset for SolidCraft web zh-TW)
|
||||
Source: https://github.com/notofonts/noto-cjk (Sans SubsetOTF/TC)
|
||||
|
||||
- IBMPlexSans-Variable.ttf (assets/fonts/ibm-plex-sans/IBMPlexSans-Variable.ttf)
|
||||
Copyright 2017 IBM Corp. with Reserved Font Name "Plex"
|
||||
Source: https://github.com/IBM/plex
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
<link data-trunk rel="rust" data-bin="solidcraft-web" data-wasm-opt="z" data-weak-refs />
|
||||
<link data-trunk rel="copy-file" href="../../assets/fonts/biz-udp-gothic/BIZUDPGothic-Regular.ttf" data-target-path="fonts" />
|
||||
<link data-trunk rel="copy-file" href="../../assets/fonts/biz-udp-gothic/OFL.txt" data-target-path="fonts" />
|
||||
<link data-trunk rel="copy-file" href="../../assets/fonts/craft-ui-sc/CraftUI-SC.otf" data-target-path="fonts" />
|
||||
<link data-trunk rel="copy-file" href="../../assets/fonts/craft-ui-sc/OFL.txt" data-target-path="fonts/craft-ui-sc" />
|
||||
<link data-trunk rel="copy-file" href="../../assets/fonts/craft-ui-tc/CraftUI-TC.otf" data-target-path="fonts" />
|
||||
<link data-trunk rel="copy-file" href="../../assets/fonts/craft-ui-tc/OFL.txt" data-target-path="fonts/craft-ui-tc" />
|
||||
<link data-trunk rel="css" href="../../assets/styles/web/loading.css" />
|
||||
<link data-trunk rel="copy-file" href="../../assets/styles/web/loading-logo.svg" />
|
||||
<!-- Installable as an app (manifest.webmanifest); the icons are the Linux exports of the app mark. -->
|
||||
|
||||
@@ -273,8 +273,10 @@ mod web {
|
||||
}
|
||||
|
||||
const JAPANESE_FONT_PATH: &str = "./fonts/BIZUDPGothic-Regular.ttf";
|
||||
const CHINESE_SC_FONT_PATH: &str = "./fonts/CraftUI-SC.otf";
|
||||
const CHINESE_TC_FONT_PATH: &str = "./fonts/CraftUI-TC.otf";
|
||||
|
||||
type FontLoadResult = Rc<std::cell::RefCell<Option<Result<Vec<u8>, String>>>>;
|
||||
type FontLoadResult = Rc<std::cell::RefCell<Option<Result<(String, Vec<u8>), String>>>>;
|
||||
|
||||
struct Shell {
|
||||
app: SolveApp,
|
||||
@@ -292,11 +294,17 @@ mod web {
|
||||
rename_focus: bool,
|
||||
/// The start page's recent designs were filled from the stored ones.
|
||||
recents_synced: bool,
|
||||
/// On-demand CJK font: fetched only when a CJK language is selected.
|
||||
/// On-demand script fonts: fetched only when a CJK language is selected.
|
||||
font: solidcraft_ui_egui::fonts::CjkFontLoader,
|
||||
font_result: FontLoadResult,
|
||||
/// The fetched CJK font's bytes: kept so a language change can re-order the fallbacks.
|
||||
/// The fetched Japanese font's bytes: kept so a language change can re-order the fallbacks.
|
||||
cjk_bytes: Option<Vec<u8>>,
|
||||
/// The fetched Chinese font's bytes (Simplified or Traditional for this session).
|
||||
chinese_bytes: Option<Vec<u8>>,
|
||||
/// Which Chinese face `chinese_bytes` is: `"zh"` or `"zh-TW"`.
|
||||
chinese_kind: Option<&'static str>,
|
||||
/// Kind requested by the in-flight fetch (`"ja"`, `"zh"`, or `"zh-TW"`).
|
||||
font_fetch_kind: Option<&'static str>,
|
||||
/// The language the fonts were last set up for (a CJK UI leads with the CJK face).
|
||||
font_language: String,
|
||||
space_mouse: SpaceMouse,
|
||||
@@ -674,32 +682,60 @@ mod web {
|
||||
files::autosave(&self.files, None);
|
||||
}
|
||||
|
||||
/// Fetch and install the CJK font when a CJK language is selected. If that fails, this
|
||||
/// Whether the shell already has the on-demand face the live language needs.
|
||||
fn script_font_ready(&self) -> bool {
|
||||
match solidcraft_ui_egui::fonts::web_script_font_kind(&self.app.preferences.language) {
|
||||
Some("zh") => self.chinese_bytes.is_some() && self.chinese_kind == Some("zh"),
|
||||
Some("zh-TW") => self.chinese_bytes.is_some() && self.chinese_kind == Some("zh-TW"),
|
||||
Some("ja") => self.cjk_bytes.is_some(),
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch and install a script font when a CJK language is selected. If that fails, this
|
||||
/// session falls back to English (no tofu); the saved language preference is kept.
|
||||
fn tick_font(&mut self, ctx: &egui::Context) {
|
||||
let done = self.font_result.borrow_mut().take();
|
||||
if let Some(res) = done {
|
||||
let kind = self.font_fetch_kind.take();
|
||||
let installed = match res {
|
||||
Ok(bytes) => {
|
||||
self.cjk_bytes = Some(bytes);
|
||||
Ok((path, bytes)) => {
|
||||
match kind {
|
||||
Some("zh") => {
|
||||
self.chinese_bytes = Some(bytes);
|
||||
self.chinese_kind = Some("zh");
|
||||
}
|
||||
Some("zh-TW") => {
|
||||
self.chinese_bytes = Some(bytes);
|
||||
self.chinese_kind = Some("zh-TW");
|
||||
}
|
||||
_ => {
|
||||
self.cjk_bytes = Some(bytes);
|
||||
}
|
||||
}
|
||||
log::info!("loaded web script font from {path}");
|
||||
true
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!("failed to fetch CJK font from {JAPANESE_FONT_PATH}: {err}");
|
||||
let path = match kind {
|
||||
Some("zh") => CHINESE_SC_FONT_PATH,
|
||||
Some("zh-TW") => CHINESE_TC_FONT_PATH,
|
||||
_ => JAPANESE_FONT_PATH,
|
||||
};
|
||||
log::warn!("failed to fetch script font from {path}: {err}");
|
||||
false
|
||||
}
|
||||
};
|
||||
if installed {
|
||||
log::info!("loaded web CJK font from {JAPANESE_FONT_PATH}");
|
||||
} else if solidcraft_ui_egui::fonts::needs_cjk_font(&self.app.preferences.language) {
|
||||
log::warn!("CJK font unavailable; showing English for this session");
|
||||
if !installed && solidcraft_ui_egui::fonts::needs_cjk_font(&self.app.preferences.language) {
|
||||
log::warn!("script font unavailable; showing English for this session");
|
||||
}
|
||||
self.font.finished(installed, &mut self.app.preferences.language);
|
||||
self.apply_fonts(ctx);
|
||||
ctx.request_repaint();
|
||||
}
|
||||
if self.font.tick(&mut self.app.preferences.language) {
|
||||
self.fetch_font(ctx);
|
||||
let ready = self.script_font_ready();
|
||||
if self.font.tick(&mut self.app.preferences.language, ready) {
|
||||
self.fetch_font(ctx, solidcraft_ui_egui::fonts::web_script_font_kind(&self.app.preferences.language).unwrap_or("ja"));
|
||||
}
|
||||
// The CJK face leads only a CJK UI: its Cyrillic glyphs are full-width, so switching
|
||||
// to Ukrainian (or English) must put the Latin face back in front.
|
||||
@@ -708,31 +744,42 @@ mod web {
|
||||
}
|
||||
}
|
||||
|
||||
/// Set up the UI fonts for the live language, keeping the fetched CJK font as a fallback
|
||||
/// (it leads only for a CJK UI, see [`solidcraft_ui_egui::fonts::install_cjk_font_for`]).
|
||||
/// Set up the UI fonts for the live language, keeping fetched script fonts as fallbacks
|
||||
/// (Chinese leads a Chinese UI; see [`solidcraft_ui_egui::fonts::install_web_script_fonts`]).
|
||||
fn apply_fonts(&mut self, ctx: &egui::Context) {
|
||||
match self.cjk_bytes.clone() {
|
||||
Some(bytes) => {
|
||||
solidcraft_ui_egui::fonts::install_cjk_font_for(ctx, bytes, &self.app.preferences.language);
|
||||
}
|
||||
None => solidcraft_ui_egui::fonts::setup_fonts(ctx),
|
||||
if self.cjk_bytes.is_none() && self.chinese_bytes.is_none() {
|
||||
solidcraft_ui_egui::fonts::setup_fonts(ctx);
|
||||
} else {
|
||||
solidcraft_ui_egui::fonts::install_web_script_fonts(
|
||||
ctx,
|
||||
self.cjk_bytes.clone(),
|
||||
self.chinese_bytes.clone(),
|
||||
&self.app.preferences.language,
|
||||
);
|
||||
}
|
||||
self.font_language.clone_from(&self.app.preferences.language);
|
||||
}
|
||||
|
||||
/// CJK text on screen with no CJK font yet (an English UI showing a Japanese name):
|
||||
/// fetch the font, so the text does not stay tofu (#720).
|
||||
/// CJK text on screen with no Japanese font yet (an English UI showing a Japanese name):
|
||||
/// fetch the Japanese font, so the text does not stay tofu (#720).
|
||||
fn watch_text_for_font(&mut self, ctx: &egui::Context) {
|
||||
if self.font.watching_text() && solidcraft_ui_egui::fonts::painted_cjk(ctx) && self.font.text_needs_font() {
|
||||
self.fetch_font(ctx);
|
||||
self.fetch_font(ctx, "ja");
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_font(&self, ctx: &egui::Context) {
|
||||
fn fetch_font(&mut self, ctx: &egui::Context, kind: &'static str) {
|
||||
let path = match kind {
|
||||
"zh" => CHINESE_SC_FONT_PATH,
|
||||
"zh-TW" => CHINESE_TC_FONT_PATH,
|
||||
_ => JAPANESE_FONT_PATH,
|
||||
};
|
||||
self.font_fetch_kind = Some(kind);
|
||||
let slot = self.font_result.clone();
|
||||
let ctx = ctx.clone();
|
||||
let path = path.to_owned();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
let res = fetch_font_bytes(JAPANESE_FONT_PATH).await;
|
||||
let res = fetch_font_bytes(&path).await.map(|bytes| (path, bytes));
|
||||
*slot.borrow_mut() = Some(res);
|
||||
ctx.request_repaint();
|
||||
});
|
||||
@@ -962,6 +1009,9 @@ mod web {
|
||||
font: Default::default(),
|
||||
font_result: Rc::new(std::cell::RefCell::new(None)),
|
||||
cjk_bytes: None,
|
||||
chinese_bytes: None,
|
||||
chinese_kind: None,
|
||||
font_fetch_kind: None,
|
||||
font_language,
|
||||
space_mouse: SpaceMouse::default(),
|
||||
wheel_gestures: solidcraft_ui_egui::WebWheelGestures::default(),
|
||||
|
||||
@@ -104,5 +104,10 @@ winit = { version = "0.30", default-features = false, features = ["x11"] }
|
||||
raw-window-handle = "0.6"
|
||||
x11rb = { version = "0.13", default-features = false }
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
# The desktop's dark or light preference through the XDG Desktop Portal (src/desktop_theme.rs,
|
||||
# #7944); already in the tree through eframe's accessibility support (accesskit_unix).
|
||||
zbus = { version = "5", default-features = false, features = ["async-io", "blocking-api"] }
|
||||
|
||||
[build-dependencies]
|
||||
embed-resource = "3.0.11"
|
||||
|
||||
@@ -413,11 +413,37 @@ impl Lines {
|
||||
}
|
||||
}
|
||||
|
||||
/// A subscribed connection's seat on the watch: it leaves exactly once, whichever thread sees
|
||||
/// the connection's end first — the connection's own thread at the end of the stream, or the
|
||||
/// events thread at the first write a closed client refuses. The seat used to wait for the
|
||||
/// events thread alone, and that thread only leaves its wait when the design moves, so the
|
||||
/// count could outlive the connection by a second or more (#8315).
|
||||
struct Seat(Mutex<Option<Subscriber>>);
|
||||
|
||||
impl Seat {
|
||||
fn new(counted: Subscriber) -> Self {
|
||||
Seat(Mutex::new(Some(counted)))
|
||||
}
|
||||
|
||||
/// Leave the watch now: the seat goes once, whoever gets here first.
|
||||
fn release(&self) {
|
||||
let counted = self.0.lock().unwrap_or_else(PoisonError::into_inner).take();
|
||||
drop(counted);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Seat {
|
||||
fn drop(&mut self) {
|
||||
self.release();
|
||||
}
|
||||
}
|
||||
|
||||
/// A connection's `control.subscribe` (#6985): its `design.changed` lines, sent from a thread of
|
||||
/// their own until the connection ends.
|
||||
struct Subscription {
|
||||
lines: Arc<Lines>,
|
||||
stop: Arc<AtomicBool>,
|
||||
seat: Arc<Seat>,
|
||||
}
|
||||
|
||||
impl Subscription {
|
||||
@@ -433,15 +459,17 @@ impl Subscription {
|
||||
) -> Option<(Self, std::io::Result<()>)> {
|
||||
let lines = Arc::new(Lines { socket: out.try_clone().ok()?, turn: Mutex::new(()) });
|
||||
let stop = Arc::new(AtomicBool::new(false));
|
||||
let seat = Arc::new(Seat::new(counted));
|
||||
let first = lines.turn.lock().unwrap_or_else(PoisonError::into_inner);
|
||||
let (l, s) = (lines.clone(), stop.clone());
|
||||
let (l, s, watch_seat) = (lines.clone(), stop.clone(), Arc::clone(&seat));
|
||||
// The connection's seat on the watch rides the events thread: it leaves when the thread
|
||||
// does, so the next failed write to a closed client retires it even while the connection's
|
||||
// own thread is slow to see the end of the stream (#7226).
|
||||
std::thread::Builder::new().stack_size(64 * 1024).spawn(move || send_events(design, &l, &s, seen, counted)).ok()?;
|
||||
// own thread is slow to see the end of the stream (#7226). The connection's own thread
|
||||
// leaves it too, so a closed connection stops counting at once (#8315).
|
||||
std::thread::Builder::new().stack_size(64 * 1024).spawn(move || send_events(design, &l, &s, seen, watch_seat)).ok()?;
|
||||
let sent = send(&mut &lines.socket, reply);
|
||||
drop(first);
|
||||
Some((Subscription { lines, stop }, sent))
|
||||
Some((Subscription { lines, stop, seat }, sent))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,6 +482,9 @@ fn design_of(reply: &Value) -> Option<(String, u64)> {
|
||||
impl Drop for Subscription {
|
||||
fn drop(&mut self) {
|
||||
self.stop.store(true, Ordering::Relaxed);
|
||||
// The seat on the watch goes now: the connection is over, and nothing counts it any more
|
||||
// (#8315).
|
||||
self.seat.release();
|
||||
// The socket closes now, not when the thread next looks; a line blocked on a client that
|
||||
// stopped reading ends too.
|
||||
let _ = self.lines.socket.shutdown(std::net::Shutdown::Both);
|
||||
@@ -461,10 +492,10 @@ impl Drop for Subscription {
|
||||
}
|
||||
|
||||
/// The `design.changed` lines of a subscribed connection, each time the design differs from the
|
||||
/// last one it was told of (`seen`), until `stop`. Owns the connection's seat on the watch
|
||||
/// (`counted`), so the seat leaves exactly when this thread does: on `stop`, or on the first
|
||||
/// write a closed client refuses (#7226).
|
||||
fn send_events(design: &DesignWatch, lines: &Lines, stop: &AtomicBool, mut seen: (String, u64), _counted: Subscriber) {
|
||||
/// last one it was told of (`seen`), until `stop`. Holds the connection's seat on the watch
|
||||
/// (`seat`), which it leaves when the thread ends: on `stop`, or on the first write a closed
|
||||
/// client refuses (#7226).
|
||||
fn send_events(design: &DesignWatch, lines: &Lines, stop: &AtomicBool, mut seen: (String, u64), seat: Arc<Seat>) {
|
||||
while !stop.load(Ordering::Relaxed) {
|
||||
let Some(now) = design.wait(&seen, stop, Duration::from_secs(1)).filter(|now| *now != seen) else { continue };
|
||||
if lines.send(&json!({"event": "design.changed", "params": {"document_id": now.0, "revision": now.1}})).is_err() {
|
||||
@@ -473,6 +504,17 @@ fn send_events(design: &DesignWatch, lines: &Lines, stop: &AtomicBool, mut seen:
|
||||
seen = now;
|
||||
std::thread::sleep(EVENT_GAP);
|
||||
}
|
||||
// The client is gone: it is not counted on the watch any more, whatever the connection's own
|
||||
// thread still has to notice (#8315).
|
||||
seat.release();
|
||||
}
|
||||
|
||||
/// What this app's control channel does beyond the methods every version has, for a client to
|
||||
/// find out before it relies on it (an older app answers `control.capabilities` with "unknown
|
||||
/// method"). `param_check`: the levels `engine.execute`, `engine.batch` and `engine.script` take
|
||||
/// as a request's own parameter check (#9184).
|
||||
fn capabilities() -> Value {
|
||||
json!({"param_check": solidcraft_engine::schema::ParamCheck::ALL.map(|l| l.name())})
|
||||
}
|
||||
|
||||
/// Write `reply` on the connection: through its subscription once it has one.
|
||||
@@ -535,15 +577,20 @@ fn serve_with(
|
||||
let id = msg.get("id").cloned().unwrap_or(Value::Null);
|
||||
let method = msg.get("method").and_then(Value::as_str).unwrap_or("").to_string();
|
||||
let params = msg.get("params").cloned().unwrap_or(json!({}));
|
||||
// A request that timed out may have finished since (#1618). `control.running` only
|
||||
// looks, so it leaves that reply where it is.
|
||||
let late = pending.as_ref().filter(|_| method != "control.running").and_then(|p| match p.reply.try_recv() {
|
||||
// A request that timed out may have finished since (#1618). `control.running` and
|
||||
// `control.capabilities` only look, so they leave that reply where it is.
|
||||
let late = pending.as_ref().filter(|_| !matches!(method.as_str(), "control.running" | "control.capabilities")).and_then(|p| match p
|
||||
.reply
|
||||
.try_recv()
|
||||
{
|
||||
Ok(reply) => Some(reply),
|
||||
Err(TryRecvError::Disconnected) => Some(json!({"ok": false, "error": "the app ended the request without a reply"})),
|
||||
Err(TryRecvError::Empty) => None,
|
||||
});
|
||||
let mut r = if method == "control.running" {
|
||||
json!({"ok": true, "result": running.result(connection)})
|
||||
} else if method == "control.capabilities" {
|
||||
json!({"ok": true, "result": capabilities()})
|
||||
} else if method == "control.pending" {
|
||||
match (pending.take(), late) {
|
||||
(None, _) => json!({"ok": true, "result": {"pending": null}}),
|
||||
@@ -750,6 +797,9 @@ mod tests {
|
||||
let mut watcher = open();
|
||||
let (r, rows) = running(&mut watcher, &|_| true);
|
||||
assert!(r["ok"] == true && rows.is_empty(), "idle: {r}");
|
||||
// What the app's control channel does beyond the methods every version has (#9184).
|
||||
let r = watcher(json!({"id": 0, "method": "control.capabilities", "token": token}));
|
||||
assert_eq!((&r["ok"], &r["result"]["param_check"]), (&json!(true), &json!(["off", "warn", "strict"])), "{r}");
|
||||
|
||||
let mut export = open();
|
||||
let path = "/private/part.step";
|
||||
@@ -781,6 +831,9 @@ mod tests {
|
||||
let own: Vec<&Value> = r["result"]["running"].as_array().unwrap().iter().filter(|row| row["self"] == true).collect();
|
||||
assert_eq!(own.len(), 1, "{r}");
|
||||
assert_eq!(own[0]["command"], "file.export", "{r}");
|
||||
// So may it ask what the app can do: answered at once, not refused as busy.
|
||||
let r = export(json!({"id": 8, "method": "control.capabilities"}));
|
||||
assert_eq!(r["result"]["param_check"], json!(["off", "warn", "strict"]), "{r}");
|
||||
|
||||
gate_tx.send(()).unwrap();
|
||||
assert_eq!(queued.join().unwrap()["result"], "engine.batch");
|
||||
@@ -886,6 +939,58 @@ mod tests {
|
||||
assert!(!WATCH.subscribed());
|
||||
}
|
||||
|
||||
/// #8315: a closed connection leaves the watch at once. The seat of a subscribed connection
|
||||
/// goes when the connection's own thread sees the end of the stream, not only when the
|
||||
/// events thread notices: that thread waits for the design to move, so a connection that
|
||||
/// closed while the design stood still kept the watch counting it for a second or more.
|
||||
#[test]
|
||||
fn a_closed_connection_leaves_the_watch_at_once() {
|
||||
static RUNNING: Running = Running::new();
|
||||
static WATCH: DesignWatch = DesignWatch::new();
|
||||
let token = "0123456789abcdef0123456789abcdef";
|
||||
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||
let addr = listener.local_addr().unwrap();
|
||||
let (tx, rx) = channel::<ControlRequest>();
|
||||
// The window names the design once and then leaves it alone: nothing moves it, so the
|
||||
// events thread has nothing to write and only the end of the connection can retire it.
|
||||
std::thread::spawn(move || {
|
||||
for req in rx {
|
||||
if req.method == "control.subscribe" {
|
||||
WATCH.publish("d-1", 3);
|
||||
let _ = req.reply.send(json!({"ok": true, "result": {"document_id": "d-1", "revision": 3}}));
|
||||
} else {
|
||||
let _ = req.reply.send(json!({"ok": true, "result": req.method}));
|
||||
}
|
||||
}
|
||||
});
|
||||
std::thread::spawn(move || {
|
||||
for stream in listener.incoming().flatten() {
|
||||
let tx = tx.clone();
|
||||
std::thread::spawn(move || serve_with(stream, token, tx, egui::Context::default(), false, WAITS, &RUNNING, &WATCH));
|
||||
}
|
||||
});
|
||||
let s = TcpStream::connect(addr).unwrap();
|
||||
s.set_read_timeout(Some(Duration::from_secs(10))).unwrap();
|
||||
let mut out = s.try_clone().unwrap();
|
||||
let mut lines = BufReader::new(s);
|
||||
writeln!(out, "{}", json!({"id": 1, "method": "control.subscribe", "token": token})).unwrap();
|
||||
let mut l = String::new();
|
||||
lines.read_line(&mut l).unwrap();
|
||||
let reply: Value = serde_json::from_str(&l).unwrap();
|
||||
assert!(reply["ok"] == true, "{reply}");
|
||||
assert!(WATCH.subscribed(), "the connection is subscribed");
|
||||
// Let the events thread settle into its wait: the design stands still, so it has nothing
|
||||
// to write, and only the end of the connection can retire it.
|
||||
std::thread::sleep(Duration::from_millis(200));
|
||||
// The client goes away without asking anything else.
|
||||
drop((out, lines));
|
||||
let deadline = Instant::now() + Duration::from_millis(400);
|
||||
while WATCH.subscribed() && Instant::now() < deadline {
|
||||
std::thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
assert!(!WATCH.subscribed(), "a closed connection is not counted on the watch");
|
||||
}
|
||||
|
||||
/// A timed-out request that only reads is not said to "still apply", nor is one that only
|
||||
/// writes a file (an export, #1989); a change is, by name.
|
||||
#[test]
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
//! Linux: the desktop's dark or light preference, for Theme: System (#7944).
|
||||
//!
|
||||
//! The window toolkit does not pass it on everywhere: on GNOME (Wayland, or X11 through XWayland)
|
||||
//! the window never learns it, nor when it changes, so the app kept its colours. This reads the
|
||||
//! freedesktop setting through the XDG Desktop Portal's Settings interface on the session bus
|
||||
//! (`org.freedesktop.appearance color-scheme`, then GNOME's `color-scheme` and `gtk-theme`), the
|
||||
//! same setting other applications follow, and inside a Flatpak too. It runs on a thread of its
|
||||
//! own: the first reading, then the portal's `SettingChanged` signal; where the signal can't be
|
||||
//! had, a reading every few seconds. Without a portal it asks `gsettings` instead, on the same
|
||||
//! thread. The window reads the last answer with an atomic load
|
||||
//! (`solidcraft_ui_egui::Services::desktop_dark`), and the decision from the raw values is
|
||||
//! `solidcraft_ui_egui::prefs::desktop_prefers_dark`.
|
||||
|
||||
use std::io::Read;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicU8, Ordering};
|
||||
use std::time::Duration;
|
||||
|
||||
use zbus::zvariant::{OwnedValue, Value};
|
||||
|
||||
const UNKNOWN: u8 = 0;
|
||||
const DARK: u8 = 1;
|
||||
const LIGHT: u8 = 2;
|
||||
|
||||
/// How often to read again where the portal's change signal is not available.
|
||||
const POLL: Duration = Duration::from_secs(5);
|
||||
/// How long one `gsettings` reading may take before it is given up.
|
||||
const GSETTINGS_TIMEOUT: Duration = Duration::from_secs(2);
|
||||
|
||||
const PORTAL: &str = "org.freedesktop.portal.Desktop";
|
||||
const PORTAL_PATH: &str = "/org/freedesktop/portal/desktop";
|
||||
const SETTINGS: &str = "org.freedesktop.portal.Settings";
|
||||
const APPEARANCE: &str = "org.freedesktop.appearance";
|
||||
const GNOME: &str = "org.gnome.desktop.interface";
|
||||
|
||||
/// Start following the desktop's preference; the closure answers the latest reading (`None`
|
||||
/// until there is one, or when the desktop says nothing).
|
||||
pub fn start(ctx: &egui::Context) -> Box<dyn Fn() -> Option<bool>> {
|
||||
let state = Arc::new(AtomicU8::new(UNKNOWN));
|
||||
let (thread_state, ctx) = (state.clone(), ctx.clone());
|
||||
if let Err(e) = std::thread::Builder::new().name("desktop-theme".into()).spawn(move || follow(&thread_state, &ctx)) {
|
||||
log::warn!("desktop theme: no thread to follow it: {e}");
|
||||
}
|
||||
Box::new(move || match state.load(Ordering::Relaxed) {
|
||||
DARK => Some(true),
|
||||
LIGHT => Some(false),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Keep `state` on the desktop's preference, asking for a frame whenever it changes.
|
||||
fn follow(state: &AtomicU8, ctx: &egui::Context) {
|
||||
let publish = |dark: Option<bool>| {
|
||||
let code = match dark {
|
||||
Some(true) => DARK,
|
||||
Some(false) => LIGHT,
|
||||
None => UNKNOWN,
|
||||
};
|
||||
if state.swap(code, Ordering::Relaxed) != code {
|
||||
ctx.request_repaint();
|
||||
}
|
||||
};
|
||||
let portal = match portal() {
|
||||
Ok(p) => Some(p),
|
||||
Err(e) => {
|
||||
log::info!("desktop theme: no settings portal ({e}); asking gsettings");
|
||||
None
|
||||
}
|
||||
};
|
||||
// The portal first; gsettings where there is none or it says nothing (its backend missing).
|
||||
let read = |portal: Option<&zbus::blocking::Proxy<'static>>| portal.and_then(read_portal).or_else(read_gsettings);
|
||||
let from_portal = portal.as_ref().and_then(read_portal);
|
||||
let first = from_portal.or_else(read_gsettings);
|
||||
publish(first);
|
||||
// Wait for the portal's signal only when the portal answers: else the reading is gsettings',
|
||||
// which only a new reading notices.
|
||||
if let Some(p) = portal.as_ref().filter(|_| from_portal.is_some()) {
|
||||
match p.receive_signal("SettingChanged") {
|
||||
Ok(changes) => {
|
||||
for message in changes {
|
||||
if relevant(&message) {
|
||||
publish(read(Some(p)));
|
||||
}
|
||||
}
|
||||
log::info!("desktop theme: the portal's change signal ended; reading every few seconds");
|
||||
}
|
||||
Err(e) => log::info!("desktop theme: no change signal ({e}); reading every few seconds"),
|
||||
}
|
||||
} else if portal.is_none() && first.is_none() && !gsettings_available() {
|
||||
// Neither the portal nor gsettings: nothing to follow; the window toolkit's theme stays.
|
||||
return;
|
||||
}
|
||||
loop {
|
||||
std::thread::sleep(POLL);
|
||||
publish(read(portal.as_ref()));
|
||||
}
|
||||
}
|
||||
|
||||
/// The portal's Settings interface on the session bus.
|
||||
fn portal() -> zbus::Result<zbus::blocking::Proxy<'static>> {
|
||||
let connection = zbus::blocking::Connection::session()?;
|
||||
zbus::blocking::Proxy::new(&connection, PORTAL, PORTAL_PATH, SETTINGS)
|
||||
}
|
||||
|
||||
/// One reading through the portal.
|
||||
fn read_portal(portal: &zbus::blocking::Proxy<'static>) -> Option<bool> {
|
||||
let appearance = read_setting(portal, APPEARANCE, "color-scheme").and_then(|v| as_u32(&v));
|
||||
// The GNOME settings only matter when the freedesktop one has no preference.
|
||||
if matches!(appearance, Some(1 | 2)) {
|
||||
return solidcraft_ui_egui::prefs::desktop_prefers_dark(appearance, None, None);
|
||||
}
|
||||
let gnome = read_setting(portal, GNOME, "color-scheme").and_then(|v| as_string(&v));
|
||||
let gtk_theme = read_setting(portal, GNOME, "gtk-theme").and_then(|v| as_string(&v));
|
||||
solidcraft_ui_egui::prefs::desktop_prefers_dark(appearance, gnome.as_deref(), gtk_theme.as_deref())
|
||||
}
|
||||
|
||||
/// A setting through `ReadOne` (portal version 2), else the older `Read`.
|
||||
fn read_setting(portal: &zbus::blocking::Proxy<'static>, namespace: &str, key: &str) -> Option<OwnedValue> {
|
||||
let one: zbus::Result<OwnedValue> = portal.call("ReadOne", &(namespace, key));
|
||||
one.or_else(|_| -> zbus::Result<OwnedValue> { portal.call("Read", &(namespace, key)) }).ok()
|
||||
}
|
||||
|
||||
/// Whether a `SettingChanged` signal is about a setting the preference is read from.
|
||||
fn relevant(message: &zbus::Message) -> bool {
|
||||
let body = message.body();
|
||||
let Ok((namespace, key, _)) = body.deserialize::<(String, String, OwnedValue)>() else { return false };
|
||||
matches!((namespace.as_str(), key.as_str()), (APPEARANCE, "color-scheme") | (GNOME, "color-scheme" | "gtk-theme"))
|
||||
}
|
||||
|
||||
/// The value inside the variants `Read` wraps it in (a portal answers one or two deep).
|
||||
fn unwrap_variants<'v, 'a>(value: &'v Value<'a>) -> &'v Value<'a> {
|
||||
let mut value = value;
|
||||
for _ in 0..4 {
|
||||
match value {
|
||||
Value::Value(inner) => value = &**inner,
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
value
|
||||
}
|
||||
|
||||
fn as_u32(value: &Value<'_>) -> Option<u32> {
|
||||
match unwrap_variants(value) {
|
||||
Value::U32(n) => Some(*n),
|
||||
Value::I32(n) => u32::try_from(*n).ok(),
|
||||
Value::U8(n) => Some(u32::from(*n)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_string(value: &Value<'_>) -> Option<String> {
|
||||
match unwrap_variants(value) {
|
||||
Value::Str(s) => Some(s.as_str().to_string()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Without a portal: GNOME's settings through `gsettings` (`None` when it is not there).
|
||||
fn read_gsettings() -> Option<bool> {
|
||||
let gnome = gsettings("color-scheme");
|
||||
let gtk_theme = gsettings("gtk-theme");
|
||||
solidcraft_ui_egui::prefs::desktop_prefers_dark(None, gnome.as_deref(), gtk_theme.as_deref())
|
||||
}
|
||||
|
||||
fn gsettings_available() -> bool {
|
||||
gsettings("gtk-theme").is_some()
|
||||
}
|
||||
|
||||
/// `gsettings get org.gnome.desktop.interface <key>`, its quoted answer, or `None` when the
|
||||
/// program is missing, fails or does not answer in time.
|
||||
fn gsettings(key: &str) -> Option<String> {
|
||||
let mut child = std::process::Command::new("gsettings")
|
||||
.args(["get", GNOME, key])
|
||||
.stdin(std::process::Stdio::null())
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::null())
|
||||
.spawn()
|
||||
.ok()?;
|
||||
let started = std::time::Instant::now();
|
||||
loop {
|
||||
match child.try_wait() {
|
||||
Ok(Some(status)) if status.success() => break,
|
||||
Ok(None) if started.elapsed() < GSETTINGS_TIMEOUT => std::thread::sleep(Duration::from_millis(20)),
|
||||
_ => {
|
||||
let _ = child.kill();
|
||||
let _ = child.wait();
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut out = String::new();
|
||||
child.stdout.take()?.take(4096).read_to_string(&mut out).ok()?;
|
||||
let value = out.trim().trim_matches('\'').to_string();
|
||||
(!value.is_empty()).then_some(value)
|
||||
}
|
||||
@@ -5,12 +5,25 @@
|
||||
//! for recovery as a forced quit does, and starts again ([`restart`]), and the new start says what
|
||||
//! happened ([`note`]). A device lost again soon after such a restart starts the next one on the
|
||||
//! software renderer, which doesn't depend on the graphics driver.
|
||||
//!
|
||||
//! The old process ends without tearing down its GPU objects ([`end`]): on a lost NVIDIA device
|
||||
//! destroying the window's swap chain busy-waits in the driver and never returns (#9044).
|
||||
|
||||
use crate::startup;
|
||||
|
||||
/// Set on the start after a lost device: when the device was lost (seconds since 1970).
|
||||
pub const LOST_ENV: &str = "SOLIDCRAFT_DEVICE_LOST";
|
||||
|
||||
/// Set on the start after a lost device: why it was lost, as wgpu said it. The old process's
|
||||
/// session journal, which recorded it, is gone by then.
|
||||
pub const REASON_ENV: &str = "SOLIDCRAFT_DEVICE_LOST_REASON";
|
||||
|
||||
/// The longest reason passed on ([`REASON_ENV`]) and shown, in characters.
|
||||
const REASON_CHARS: usize = 300;
|
||||
|
||||
/// How long the process may take to end ([`end`]) before it is aborted.
|
||||
const END_GRACE: std::time::Duration = std::time::Duration::from_secs(3);
|
||||
|
||||
/// A loss this recent (seconds) counts for the start after it: its note, and a second loss
|
||||
/// within it starts on the software renderer. Inherited by later starts, an older stamp is
|
||||
/// ignored.
|
||||
@@ -32,27 +45,54 @@ pub fn restart_on_software(after_recent_loss: bool, cpu_renderer: bool) -> bool
|
||||
after_recent_loss || cpu_renderer
|
||||
}
|
||||
|
||||
/// `reason` on one line, at most [`REASON_CHARS`] long; None when there is nothing to say.
|
||||
pub fn reason(reason: Option<&str>) -> Option<String> {
|
||||
let line = reason?.split_whitespace().collect::<Vec<_>>().join(" ");
|
||||
(!line.is_empty()).then(|| line.chars().take(REASON_CHARS).collect())
|
||||
}
|
||||
|
||||
/// What the start after a lost device tells the user, `software` when it runs on the software
|
||||
/// renderer.
|
||||
pub fn note(software: bool) -> String {
|
||||
/// renderer, with what the driver reported ([`REASON_ENV`]).
|
||||
pub fn note(software: bool, reason: Option<&str>) -> String {
|
||||
let mut note = String::from(
|
||||
"The graphics device was reset (the graphics driver stopped responding), so SolidCraft closed and started again. Unsaved changes were kept for recovery.",
|
||||
);
|
||||
if software {
|
||||
note.push_str(" This start uses software rendering, which doesn't depend on the graphics driver.");
|
||||
}
|
||||
if let Some(reason) = self::reason(reason) {
|
||||
note.push_str(&format!(" The graphics driver reported: {reason}"));
|
||||
}
|
||||
note
|
||||
}
|
||||
|
||||
/// Start SolidCraft again with the same arguments, the loss stamped at `lost_at`, on the software
|
||||
/// renderer when `software`.
|
||||
pub fn restart(software: bool, lost_at: u64) {
|
||||
let Ok(exe) = std::env::current_exe() else { return };
|
||||
/// The command that starts `exe` again with `args`, the loss stamped at `lost_at` and its
|
||||
/// `reason`, on the software renderer when `software`.
|
||||
fn command(
|
||||
exe: std::path::PathBuf,
|
||||
args: impl IntoIterator<Item = std::ffi::OsString>,
|
||||
software: bool,
|
||||
lost_at: u64,
|
||||
reason: &str,
|
||||
) -> std::process::Command {
|
||||
let mut command = std::process::Command::new(exe);
|
||||
command.args(std::env::args_os().skip(1)).env(LOST_ENV, lost_at.to_string());
|
||||
command.args(args).env(LOST_ENV, lost_at.to_string());
|
||||
match self::reason(Some(reason)) {
|
||||
Some(reason) => command.env(REASON_ENV, reason),
|
||||
// Not one inherited from an earlier loss.
|
||||
None => command.env_remove(REASON_ENV),
|
||||
};
|
||||
if software {
|
||||
command.env(startup::RENDERER_ENV, "cpu");
|
||||
}
|
||||
command
|
||||
}
|
||||
|
||||
/// Start SolidCraft again with the same arguments, the loss stamped at `lost_at` and its `reason`,
|
||||
/// on the software renderer when `software`.
|
||||
pub fn restart(software: bool, lost_at: u64, reason: &str) {
|
||||
let Ok(exe) = std::env::current_exe() else { return };
|
||||
let mut command = command(exe, std::env::args_os().skip(1), software, lost_at, reason);
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::process::CommandExt;
|
||||
@@ -64,6 +104,23 @@ pub fn restart(software: bool, lost_at: u64) {
|
||||
}
|
||||
}
|
||||
|
||||
/// End the process now, without tearing down what is left on the lost device: the window's swap
|
||||
/// chain, the viewport's buffers and pipelines. On NVIDIA's Vulkan driver destroying the swap
|
||||
/// chain of a lost device busy-waits forever, which left the old window frozen at 100% of a core
|
||||
/// beside the new one (#9044). Should the exit itself get stuck in a library's exit handler, the
|
||||
/// process is aborted after [`END_GRACE`].
|
||||
pub fn end() -> ! {
|
||||
let watchdog = std::thread::Builder::new().name("solidcraft-exit".into()).spawn(|| {
|
||||
std::thread::sleep(END_GRACE);
|
||||
eprintln!("SolidCraft graphics: still closing after the device was lost; aborting");
|
||||
std::process::abort();
|
||||
});
|
||||
if let Err(e) = watchdog {
|
||||
log::warn!("graphics: no exit watchdog: {e}");
|
||||
}
|
||||
std::process::exit(0)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
/// Only a recent, well-formed stamp counts: a later start that inherited it says nothing.
|
||||
@@ -84,8 +141,34 @@ mod tests {
|
||||
assert!(!super::restart_on_software(false, false));
|
||||
assert!(super::restart_on_software(true, false));
|
||||
assert!(super::restart_on_software(false, true));
|
||||
assert!(super::note(true).contains("software rendering"));
|
||||
assert!(!super::note(false).contains("software rendering"));
|
||||
assert!(super::note(false).contains("kept for recovery"));
|
||||
assert!(super::note(true, None).contains("software rendering"));
|
||||
assert!(!super::note(false, None).contains("software rendering"));
|
||||
assert!(super::note(false, None).contains("kept for recovery"));
|
||||
}
|
||||
|
||||
/// The start after a loss is told why (#9044): the old process's journal held the reason, and
|
||||
/// it is deleted as that process closes.
|
||||
#[test]
|
||||
fn the_next_start_is_told_why_the_device_was_lost() {
|
||||
let exe = std::path::PathBuf::from("solidcraft");
|
||||
let args = ["--control".into(), "7979".into()];
|
||||
let command = super::command(exe.clone(), args, true, 1_000, "Lost: Device lost\n (vkQueueSubmit)");
|
||||
let env = |command: &std::process::Command, name: &str| {
|
||||
command.get_envs().find(|(k, _)| *k == name).map(|(_, v)| v.map(|v| v.to_string_lossy().into_owned()))
|
||||
};
|
||||
assert_eq!(env(&command, super::LOST_ENV), Some(Some("1000".into())));
|
||||
assert_eq!(env(&command, super::REASON_ENV), Some(Some("Lost: Device lost (vkQueueSubmit)".into())), "on one line");
|
||||
assert_eq!(env(&command, crate::startup::RENDERER_ENV), Some(Some("cpu".into())));
|
||||
assert_eq!(command.get_args().collect::<Vec<_>>(), ["--control", "7979"]);
|
||||
// No reason: none inherited from an earlier loss either.
|
||||
let command = super::command(exe, Vec::new(), false, 1_000, " ");
|
||||
assert_eq!(env(&command, super::REASON_ENV), Some(None));
|
||||
assert_eq!(env(&command, crate::startup::RENDERER_ENV), None);
|
||||
|
||||
let note = super::note(false, Some("Lost: Device lost"));
|
||||
assert!(note.ends_with("The graphics driver reported: Lost: Device lost"), "{note}");
|
||||
assert!(!super::note(false, Some(" \n ")).contains("reported"));
|
||||
let long = "x".repeat(1_000);
|
||||
assert_eq!(super::reason(Some(&long)).map(|r| r.chars().count()), Some(300));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
mod app_icon;
|
||||
mod come_forward;
|
||||
mod control_server;
|
||||
#[cfg(target_os = "linux")]
|
||||
mod desktop_theme;
|
||||
mod device_lost;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod native_crash;
|
||||
@@ -202,13 +204,29 @@ impl eframe::App for App {
|
||||
}
|
||||
solidcraft_ui_egui::recovery_ui::close(&mut self.solve);
|
||||
solidcraft_ui_egui::open_elsewhere::close(&mut self.solve);
|
||||
// Before a restart below starts the next instance, which would otherwise get nothing from
|
||||
// the 3D mouse driver (`solidcraft_3dx_client::stop`).
|
||||
// Before a restart (`Drop for App`) starts the next instance, which would otherwise get
|
||||
// nothing from the 3D mouse driver (`solidcraft_3dx_client::stop`).
|
||||
#[cfg(target_os = "macos")]
|
||||
solidcraft_3dx_client::stop();
|
||||
solidcraft_engine::crash::end_session();
|
||||
if let Some(lost_at) = self.gpu.as_ref().and_then(|g| g.lost_at) {
|
||||
device_lost::restart(self.gpu.as_ref().is_some_and(|g| g.restart_on_software), lost_at);
|
||||
// The next start (after a lost device, or into an update) begins once the app is dropped
|
||||
// (`Drop for App`), when the settings are on disk.
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for App {
|
||||
/// eframe drops its settings storage first, which waits for `app.ron` to be written, then the
|
||||
/// app, then the window and its GPU surface. So the next start begins here: started from
|
||||
/// `on_exit`, it read `app.ron` while the old process was still writing it, found it
|
||||
/// unreadable and moved it aside, resetting every setting (#9044).
|
||||
fn drop(&mut self) {
|
||||
if let Some(gpu) = &self.gpu
|
||||
&& let Some(lost_at) = gpu.lost_at
|
||||
{
|
||||
device_lost::restart(gpu.restart_on_software, lost_at, &gpu.reason);
|
||||
// The window's GPU surface is still to be dropped, and on a lost device that may
|
||||
// never return (#9044).
|
||||
device_lost::end();
|
||||
}
|
||||
// An update asked to restart into the new version or to run its installer.
|
||||
if let Some((program, args)) = solidcraft_ui_egui::update::take_launch(&mut self.solve) {
|
||||
@@ -236,6 +254,8 @@ struct LostWatch {
|
||||
restart_on_software: bool,
|
||||
/// When the loss was seen: the app is closing to start again.
|
||||
lost_at: Option<u64>,
|
||||
/// Why, as wgpu said it: passed to the next start ([`device_lost::REASON_ENV`]).
|
||||
reason: String,
|
||||
}
|
||||
|
||||
impl App {
|
||||
@@ -249,6 +269,7 @@ impl App {
|
||||
eprintln!("SolidCraft graphics: the device was lost ({reason}); starting again");
|
||||
solidcraft_engine::crash::phase(&format!("graphics device lost: {reason}"));
|
||||
gpu.lost_at = Some(device_lost::now_secs());
|
||||
gpu.reason = reason;
|
||||
self.solve.quit_requested = true;
|
||||
}
|
||||
}
|
||||
@@ -422,6 +443,12 @@ fn services(cc: &eframe::CreationContext<'_>, folder: &DialogFolder) -> Services
|
||||
graphics_adapters: Vec::new(),
|
||||
graphics_path: graphics_path().map(|p| p.to_string_lossy().to_string()),
|
||||
window_system: window_system_now(),
|
||||
// Linux: the desktop's dark or light setting, which the window is not told on every
|
||||
// desktop (#7944). Windows and macOS: the window's own theme.
|
||||
#[cfg(target_os = "linux")]
|
||||
desktop_dark: Some(desktop_theme::start(&cc.egui_ctx)),
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
desktop_dark: None,
|
||||
http_get: Some(std::sync::Arc::new(http_get)),
|
||||
http_download: Some(std::sync::Arc::new(http_download)),
|
||||
// Already an installed app: only the browser build offers to install.
|
||||
@@ -750,6 +777,16 @@ fn main() -> eframe::Result {
|
||||
#[cfg(target_os = "macos")]
|
||||
objc2_foundation::NSProcessInfo::processInfo().setProcessName(objc2_foundation::ns_string!("SolidCraft"));
|
||||
mark("main");
|
||||
// On OCLP-patched Macs, wgpu picks a Metal Shading Language standard that the system's
|
||||
// compiler refuses (`invalid value 'metal3.2'`), because the patched OS reports a newer
|
||||
// macOS version than the GPU's Metal compiler supports. Re-launching with
|
||||
// SYSTEM_VERSION_COMPAT=1 makes the OS report the real (older) version so wgpu selects
|
||||
// a standard the compiler accepts. The variable must be set before the process starts, so
|
||||
// we detect OCLP here and re-exec immediately, before any macOS framework call (#3080, #4605).
|
||||
#[cfg(target_os = "macos")]
|
||||
if let Err(e) = startup::relaunch_with_version_compat() {
|
||||
log::warn!("startup: OCLP check failed (continuing normally): {e}");
|
||||
}
|
||||
// Validate before legacy adoption, crash markers or any user preference read.
|
||||
let settings = settings_dir().map_err(|error| eframe::Error::AppCreation(error.into()))?;
|
||||
if std::env::var_os("SOLIDCRAFT_SETTINGS_DIR").is_none() {
|
||||
@@ -821,7 +858,11 @@ fn main() -> eframe::Result {
|
||||
// The launch before this one closed because its graphics device was lost (`App::watch_device`).
|
||||
let after_loss = device_lost::recent(std::env::var(device_lost::LOST_ENV).ok().as_deref(), device_lost::now_secs());
|
||||
if after_loss {
|
||||
notes.push(device_lost::note(cpu_renderer));
|
||||
let reason = std::env::var(device_lost::REASON_ENV).ok();
|
||||
if let Some(reason) = device_lost::reason(reason.as_deref()) {
|
||||
log::warn!("graphics: the last launch lost its device ({reason})");
|
||||
}
|
||||
notes.push(device_lost::note(cpu_renderer, reason.as_deref()));
|
||||
}
|
||||
if safe_mode {
|
||||
notes.push("Safe mode: default settings (yours are kept for the next normal start), no recovery offer, software rendering.".into());
|
||||
@@ -961,6 +1002,7 @@ fn main() -> eframe::Result {
|
||||
lost: rs.device_lost.clone(),
|
||||
restart_on_software: device_lost::restart_on_software(after_loss, cpu_renderer),
|
||||
lost_at: None,
|
||||
reason: String::new(),
|
||||
});
|
||||
if !safe_mode {
|
||||
app.preferences.graphics_adapter =
|
||||
@@ -1016,6 +1058,10 @@ fn main() -> eframe::Result {
|
||||
app.session.doc_mut().name = "Sample Plate".into();
|
||||
app.session.mark_saved();
|
||||
app.session.undo.clear();
|
||||
// Built straight into the session, so nothing asked for a fit: frame it at the
|
||||
// home view as any opened design is (#9195). The fit waits for the first layout.
|
||||
app.cam.set_view(solidcraft_engine::render::StandardView::Iso);
|
||||
app.fit_view();
|
||||
}
|
||||
// Lock files next to open designs: a design open on another computer says so (#5467),
|
||||
// the files given at launch included.
|
||||
@@ -1099,12 +1145,13 @@ fn main() -> eframe::Result {
|
||||
}
|
||||
|
||||
/// egui-wgpu is about to start a step of getting the graphics device and the window's surface up:
|
||||
/// the crash journal keeps it, and the adapter once it is picked.
|
||||
/// the crash journal keeps it until the app runs (a window opened later goes through the same
|
||||
/// steps), and the adapter once it is picked.
|
||||
fn startup_step(step: &'static str, detail: &str) {
|
||||
if step == eframe::egui_wgpu::STEP_ADAPTER_CHOSEN {
|
||||
solidcraft_engine::crash::set_environment(&format!("Renderer: {detail}"));
|
||||
}
|
||||
solidcraft_engine::crash::phase(step);
|
||||
solidcraft_engine::crash::startup_step(step);
|
||||
}
|
||||
|
||||
/// The renderer settings: with `cpu`, the software adapter (WARP, lavapipe, llvmpipe). The
|
||||
|
||||
@@ -101,6 +101,7 @@ impl NativeMenu {
|
||||
])?;
|
||||
for (action, label) in [
|
||||
("docs", "Documentation"),
|
||||
("updates", app.tr("update.check_for_updates")),
|
||||
("shortcuts", "Keyboard Shortcuts"),
|
||||
("messages", "Messages\u{2026}"),
|
||||
("report", "Report an Issue\u{2026}"),
|
||||
|
||||
@@ -292,6 +292,71 @@ pub fn window_instance_flags(flags: eframe::wgpu::InstanceFlags, intel_mac: bool
|
||||
if intel_mac && env.map(str::trim).is_none_or(str::is_empty) { flags - eframe::wgpu::InstanceFlags::VALIDATION_INDIRECT_CALL } else { flags }
|
||||
}
|
||||
|
||||
/// The environment variable that makes the macOS compatibility shim report an older OS
|
||||
/// version to wgpu's Metal backend. On OCLP-patched machines the Metal compiler accepts
|
||||
/// only up to the MSL standard of the original GPU, not the one the patched OS reports,
|
||||
/// so wgpu picks a standard that the compiler refuses (`invalid value 'metal3.2'`).
|
||||
/// Setting this variable before the process starts causes the OS to report the older
|
||||
/// version and wgpu selects a supported MSL standard (#3080, #4605).
|
||||
#[cfg(target_os = "macos")]
|
||||
pub const SYSTEM_VERSION_COMPAT_ENV: &str = "SYSTEM_VERSION_COMPAT";
|
||||
|
||||
/// The NVRAM variable that OpenCore Legacy Patcher writes on every OCLP-booted system.
|
||||
/// Its presence is the primary signal that `SYSTEM_VERSION_COMPAT=1` is needed.
|
||||
#[cfg(target_os = "macos")]
|
||||
pub const OCLP_NVRAM_KEY: &str = "4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102:OCLP-Version";
|
||||
|
||||
/// `true` if the OCLP NVRAM key is present on this system.
|
||||
///
|
||||
/// Runs `nvram <key>` and treats exit 0 as present. Any error (missing binary,
|
||||
/// permission denied, key absent) returns `false` — this is a best-effort probe.
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn oclp_nvram_present() -> bool {
|
||||
std::process::Command::new("nvram").arg(OCLP_NVRAM_KEY).output().map(|o| o.status.success()).unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Whether to re-launch with `SYSTEM_VERSION_COMPAT=1`: it is not set yet and OCLP shows through
|
||||
/// the NVRAM key. The probe runs only when it is needed, so a re-launched process (variable
|
||||
/// set) never starts `nvram`.
|
||||
#[cfg(target_os = "macos")]
|
||||
fn compat_wanted(compat_set: bool, nvram: impl FnOnce() -> bool) -> bool {
|
||||
!compat_set && nvram()
|
||||
}
|
||||
|
||||
/// If this process is running under OCLP and `SYSTEM_VERSION_COMPAT` has not been set,
|
||||
/// re-launch the current binary with `SYSTEM_VERSION_COMPAT=1` added to the environment,
|
||||
/// then exit with the child's status code.
|
||||
///
|
||||
/// `SYSTEM_VERSION_COMPAT=1` must be present **before** the process starts: setting it
|
||||
/// with `std::env::set_var` inside `main` is too late because the macOS frameworks have
|
||||
/// already queried the OS version by then. Re-launching is the only safe approach that
|
||||
/// works without `unsafe` (a true `execve` replacement would require it).
|
||||
///
|
||||
/// Returns `Ok(false)` immediately when:
|
||||
/// - `SYSTEM_VERSION_COMPAT` is already set (re-launched process, no loop), or
|
||||
/// - the OCLP NVRAM key is absent.
|
||||
///
|
||||
/// Returns `Err` only when `std::env::current_exe()` fails or the child process cannot
|
||||
/// be spawned; the caller should log the error and continue normally.
|
||||
///
|
||||
/// Never returns `Ok(true)`: a successful re-launch exits the current process.
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn relaunch_with_version_compat() -> Result<bool, String> {
|
||||
// A set variable means this is the re-launched process: no loop.
|
||||
if !compat_wanted(std::env::var_os(SYSTEM_VERSION_COMPAT_ENV).is_some(), oclp_nvram_present) {
|
||||
return Ok(false);
|
||||
}
|
||||
log::info!("startup: OCLP detected; re-launching with {}=1", SYSTEM_VERSION_COMPAT_ENV);
|
||||
eprintln!("SolidCraft: OCLP detected; re-launching with {}=1", SYSTEM_VERSION_COMPAT_ENV);
|
||||
let exe = std::env::current_exe().map_err(|e| format!("could not find own executable: {e}"))?;
|
||||
let status = std::process::Command::new(&exe)
|
||||
.args(std::env::args_os().skip(1))
|
||||
.env(SYSTEM_VERSION_COMPAT_ENV, "1")
|
||||
.status()
|
||||
.map_err(|e| format!("could not re-launch with {}: {e}", SYSTEM_VERSION_COMPAT_ENV))?;
|
||||
std::process::exit(status.code().unwrap_or(1));
|
||||
}
|
||||
|
||||
/// Windows with OpenGL: the window keeps the system title bar (#2063). The window without OS
|
||||
/// decorations keeps its caption style while having no frame, and some OpenGL drivers (older
|
||||
/// Intel HD/UHD) then show the picture a caption's height lower than the window, so it no longer
|
||||
@@ -414,6 +479,27 @@ mod tests {
|
||||
assert_eq!(window_instance_flags(on, false, None), on);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[test]
|
||||
fn oclp_relaunch_needs_the_signal_and_no_compat_variable() {
|
||||
use super::compat_wanted;
|
||||
let probed = std::cell::Cell::new(false);
|
||||
let probe = |found: bool| {
|
||||
let probed = &probed;
|
||||
move || {
|
||||
probed.set(true);
|
||||
found
|
||||
}
|
||||
};
|
||||
// The re-launched process has the variable: no probe, no second re-launch.
|
||||
assert!(!compat_wanted(true, probe(true)));
|
||||
assert!(!probed.get());
|
||||
// The OCLP NVRAM key is the signal.
|
||||
assert!(compat_wanted(false, probe(true)));
|
||||
// No signal: an ordinary Mac starts as it is.
|
||||
assert!(!compat_wanted(false, probe(false)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn switches_read_from_the_environment() {
|
||||
assert!(env_on(Some("1")) && env_on(Some("yes")) && !env_on(Some("0")) && !env_on(Some(" ")) && !env_on(None) && !env_on(Some("false")));
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'.
|
||||
Copyright 2014-2021 Google Inc. with Reserved Font Name 'Noto'.
|
||||
Subset for SolidCraft web UI (glyphs used by the zh locale); renamed Craft UI SC (no Reserved Font Name).
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License,
|
||||
Version 1.1.
|
||||
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font
|
||||
creation efforts of academic and linguistic communities, and to
|
||||
provide a free and open framework in which fonts may be shared and
|
||||
improved in partnership with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply to
|
||||
any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software
|
||||
components as distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to,
|
||||
deleting, or substituting -- in part or in whole -- any of the
|
||||
components of the Original Version, by changing formats or by porting
|
||||
the Font Software to a new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed,
|
||||
modify, redistribute, and sell modified and unmodified copies of the
|
||||
Font Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components, in
|
||||
Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the
|
||||
corresponding Copyright Holder. This restriction only applies to the
|
||||
primary font name as presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created using
|
||||
the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
@@ -0,0 +1,96 @@
|
||||
Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'.
|
||||
Copyright 2014-2021 Google Inc. with Reserved Font Name 'Noto'.
|
||||
Subset for SolidCraft web UI (glyphs used by the zh-TW locale); renamed Craft UI TC (no Reserved Font Name).
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License,
|
||||
Version 1.1.
|
||||
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font
|
||||
creation efforts of academic and linguistic communities, and to
|
||||
provide a free and open framework in which fonts may be shared and
|
||||
improved in partnership with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply to
|
||||
any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software
|
||||
components as distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to,
|
||||
deleting, or substituting -- in part or in whole -- any of the
|
||||
components of the Original Version, by changing formats or by porting
|
||||
the Font Software to a new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed,
|
||||
modify, redistribute, and sell modified and unmodified copies of the
|
||||
Font Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components, in
|
||||
Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the
|
||||
corresponding Copyright Holder. This restriction only applies to the
|
||||
primary font name as presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created using
|
||||
the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
@@ -204,7 +204,10 @@ constraint = [
|
||||
"c_polygon", "c_smooth", "c_symmetry", "c_tangent",
|
||||
]
|
||||
construction = [
|
||||
"centerline", "construction", "infinite",
|
||||
"centerline", "construction", "construction_line", "construction_line_angle",
|
||||
"construction_line_bisector", "construction_line_horizontal", "construction_line_parallel",
|
||||
"construction_line_perpendicular", "construction_line_tangent_at", "construction_line_tangent_external",
|
||||
"construction_line_tangent_two", "construction_line_vertical", "infinite",
|
||||
]
|
||||
mesh = [
|
||||
"mesh_body",
|
||||
|
||||
@@ -70,6 +70,16 @@ configuration_table false
|
||||
conic false
|
||||
constrainer false
|
||||
construction false
|
||||
construction_line false
|
||||
construction_line_angle false
|
||||
construction_line_bisector false
|
||||
construction_line_horizontal false
|
||||
construction_line_parallel false
|
||||
construction_line_perpendicular false
|
||||
construction_line_tangent_at false
|
||||
construction_line_tangent_external false
|
||||
construction_line_tangent_two false
|
||||
construction_line_vertical false
|
||||
contact_all false
|
||||
contact_new false
|
||||
contact_off false
|
||||
@@ -191,6 +201,7 @@ polygon false
|
||||
polygon_circumscribed false
|
||||
polygon_edge false
|
||||
presspull false
|
||||
print_face false
|
||||
project false
|
||||
project_surface false
|
||||
rack_pinion false
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<title>Construction line through two points</title>
|
||||
<path d="M8 52L56 12" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round" stroke-dasharray="10 5"/>
|
||||
<circle cx="20" cy="42" r="5.8" fill="#ffffff"/>
|
||||
<circle cx="20" cy="42" r="3.4" fill="#236ead"/>
|
||||
<circle cx="44" cy="22" r="5.8" fill="#ffffff"/>
|
||||
<circle cx="44" cy="22" r="3.4" fill="#236ead"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 472 B |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<title>Construction line at an angle</title>
|
||||
<path d="M10.26 48.66L58.26 48.66" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path d="M28.65 33.23A24 24 0 0 1 34.26 48.66" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path d="M10.26 48.66L54.26 11.66" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<circle cx="10.26" cy="48.66" r="5.8" fill="#ffffff"/>
|
||||
<circle cx="10.26" cy="48.66" r="3.4" fill="#236ead"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 646 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<title>Angle bisector</title>
|
||||
<path d="M13.92 12.18L9.92 52.17L54.92 44.17" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path d="M12.11 30.29A22 22 0 0 1 31.58 48.33" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path d="M9.92 52.17L47.3 11.83" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 526 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<title>Horizontal construction line</title>
|
||||
<path d="M8 32L56 32" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round" stroke-dasharray="10 5"/>
|
||||
<circle cx="32" cy="32" r="5.8" fill="#ffffff"/>
|
||||
<circle cx="32" cy="32" r="3.4" fill="#236ead"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 362 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<title>Parallel construction line</title>
|
||||
<path d="M9 37L41 9" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path d="M23 55L55 27" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round" stroke-dasharray="10 5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 380 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<title>Perpendicular construction line</title>
|
||||
<path d="M8 44L56 44" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path d="M32 8L32 56" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path d="M32 34L42 34L42 44" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 490 B |
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<title>Tangent at a curve point</title>
|
||||
<circle cx="32" cy="34.26" r="17" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path d="M8 17.26L56 17.26" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<circle cx="32" cy="17.26" r="5.8" fill="#ffffff"/>
|
||||
<circle cx="32" cy="17.26" r="3.4" fill="#236ead"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 480 B |
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<title>Tangent through an external point</title>
|
||||
<circle cx="21.74" cy="38.77" r="14" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path d="M53.6 8.23L28.55 55.9" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<circle cx="51.74" cy="11.77" r="5.8" fill="#ffffff"/>
|
||||
<circle cx="51.74" cy="11.77" r="3.4" fill="#236ead"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 502 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<title>Common tangent to two curves</title>
|
||||
<circle cx="20" cy="33.59" r="12" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<circle cx="46" cy="33.59" r="8" fill="none" stroke="#6f879c" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path d="M6 19.27L58 27.36" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 509 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<title>Vertical construction line</title>
|
||||
<path d="M32 8L32 56" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round" stroke-dasharray="10 5"/>
|
||||
<circle cx="32" cy="32" r="5.8" fill="#ffffff"/>
|
||||
<circle cx="32" cy="32" r="3.4" fill="#236ead"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 360 B |
@@ -0,0 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<title>Print Face</title>
|
||||
<path d="M9.72 26.17L4.5 20.95L4.5 40.85L9.72 46.07Z" fill="#b8c3cf"/>
|
||||
<path d="M9.72 26.17L4.5 20.95L4.5 40.85L9.72 46.07Z" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path d="M18.77 23.15L9.72 26.17L9.72 46.07L18.77 43.05Z" fill="#328bd3"/>
|
||||
<path d="M18.77 23.15L9.72 26.17L9.72 46.07L18.77 43.05Z" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path d="M13.55 17.93L18.77 23.15L9.72 26.17L4.5 20.95Z" fill="#e2e8ef"/>
|
||||
<path d="M13.55 17.93L18.77 23.15L9.72 26.17L4.5 20.95Z" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path d="M50.45 37.54L38.27 25.35L38.27 33.88L50.45 46.07Z" fill="#b8c3cf"/>
|
||||
<path d="M50.45 37.54L38.27 25.35L38.27 33.88L50.45 46.07Z" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path d="M59.5 34.53L50.45 37.54L50.45 46.07L59.5 43.05Z" fill="#8e9faf"/>
|
||||
<path d="M59.5 34.53L50.45 37.54L50.45 46.07L59.5 43.05Z" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path d="M47.31 22.34L59.5 34.53L50.45 37.54L38.27 25.35Z" fill="#e2e8ef"/>
|
||||
<path d="M47.31 22.34L59.5 34.53L50.45 37.54L38.27 25.35Z" fill="none" stroke="#34465b" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path d="M50.45 46.07L38.27 33.88" fill="none" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path d="M59.5 43.05L50.45 46.07" fill="none" stroke="#236ead" stroke-width="2.55" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path d="M24.17 34.2L28.31 34.2" fill="none" stroke="#236ead" stroke-width="4.25" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<path d="M35.31 34.2L27.31 38.7L27.31 29.7Z" fill="#236ead"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -60,6 +60,7 @@
|
||||
"BLEND SURFACE": "سطح مزج",
|
||||
"BOUNDARY SURFACE": "سطح حدودي",
|
||||
"BROWSER": "المستعرض",
|
||||
"Back to the original order": "العودة إلى الترتيب الأصلي",
|
||||
"Ball": "كروي",
|
||||
"Bar": "قضيب",
|
||||
"Base Center": "مركز القاعدة",
|
||||
@@ -595,6 +596,8 @@
|
||||
"Snap to face and edge centres": "الالتقاط إلى مراكز الأوجه والحواف",
|
||||
"SolidCraft cannot submit reports directly. Submit on GitHub; no token is needed in the app.": "لا يستطيع SolidCraft إرسال التقارير مباشرةً. أرسلها على GitHub؛ لا حاجة إلى رمز وصول في التطبيق.",
|
||||
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "أُغلق SolidCraft قبل حفظ هذه التصاميم. يمكن فتح آخر حفظ تلقائي لها.",
|
||||
"Sort A to Z": "فرز تصاعدي",
|
||||
"Sort Z to A": "فرز تنازلي",
|
||||
"Spacing 2 must be a non-zero number": "يجب أن يكون التباعد 2 عددًا غير صفري",
|
||||
"Spacing must be a non-zero number": "يجب أن يكون التباعد عددًا غير صفري",
|
||||
"Specialise": "تخصيص",
|
||||
@@ -775,6 +778,8 @@
|
||||
"at the face": "عند الوجه",
|
||||
"bom.group_children": "الأجزاء الفرعية في صف الجزء الأصلي",
|
||||
"browser.body_name": "جسم {number}",
|
||||
"browser.calibrate_click_a": "انقر على الصورة لتحديد النقطة A، أو اكتب إحداثياتها.",
|
||||
"browser.calibrate_click_b": "انقر على الصورة لتحديد النقطة B، أو اكتب إحداثياتها.",
|
||||
"browser.calibrate_title": "معايرة: {name}",
|
||||
"browser.capture_many": "تم تحريك {count} مكوّنات دون تثبيت مواضعها.",
|
||||
"browser.capture_note": "التثبيت يحتفظ بالموضع الجديد، والإرجاع يعيده كما كان.",
|
||||
@@ -817,6 +822,7 @@
|
||||
"cmd.appearance_pick_up": "التقاط المظهر",
|
||||
"cmd.appearance_replace": "استبدال المظهر",
|
||||
"cmd.assembly_bom": "قائمة المواد",
|
||||
"cmd.body.print_face": "وجه الطباعة",
|
||||
"cmd.body_rename": "إعادة تسمية الجسم",
|
||||
"cmd.browser_collapse_all": "طي الكل",
|
||||
"cmd.browser_expand_all": "توسيع الكل",
|
||||
@@ -1101,6 +1107,16 @@
|
||||
"cmd.sheet_refold": "إعادة الطي",
|
||||
"cmd.sheet_rules": "قائمة قواعد الصفائح المعدنية",
|
||||
"cmd.sheet_unfold": "فرد",
|
||||
"cmd.sketch.construction_line": "خط إنشائي",
|
||||
"cmd.sketch.construction_line.angle": "خط إنشائي بزاوية",
|
||||
"cmd.sketch.construction_line.bisector": "منصف الزاوية",
|
||||
"cmd.sketch.construction_line.horizontal": "خط إنشائي أفقي",
|
||||
"cmd.sketch.construction_line.parallel": "خط إنشائي موازٍ",
|
||||
"cmd.sketch.construction_line.perpendicular": "خط إنشائي عمودي",
|
||||
"cmd.sketch.construction_line.tangent_at": "مماس عند نقطة على المنحنى",
|
||||
"cmd.sketch.construction_line.tangent_external": "مماس يمر بنقطة خارجية",
|
||||
"cmd.sketch.construction_line.tangent_two": "مماس مشترك",
|
||||
"cmd.sketch.construction_line.vertical": "خط إنشائي رأسي",
|
||||
"cmd.sketch_arc3d": "قوس ثلاثي الأبعاد",
|
||||
"cmd.sketch_arc_center_point": "قوس بنقطة مركز",
|
||||
"cmd.sketch_arc_tangent": "قوس مماسي",
|
||||
@@ -1389,7 +1405,18 @@
|
||||
"help.occurrence.move": "يحرّك جزءًا في التجميع فقط ضمن درجات الحرية التي تسمح بها مفاصله. حدد وجهًا للجزء، ثم اسحب سهمًا أو قوسًا متاحًا.",
|
||||
"help.parameters.change": "يفتح جدول القيم المسماة والتعبيرات التي تتحكم في أبعاد التصميم. استخدمه لتغيير مقاس في مكان واحد فتتبعه كل الميزات التي تستخدمه.",
|
||||
"help.pointcloud.insert": "أدرج مسحًا بصيغة PLY أو XYZ أو PTS كسحابة نقاط بجانب النموذج. استخدمه كمرجع عند النمذجة انطلاقًا من بيانات القياس.",
|
||||
"help.sketch.construction_line": "أنشئ خطًا إنشائيًا لا نهائيًا يمر بنقطتين. استخدمه لتخطيط الرسم دون تقسيم مقاطعه.",
|
||||
"help.sketch.construction_line.angle": "أنشئ خطًا إنشائيًا لا نهائيًا بزاوية من محور X للرسم. اختر النقطة ثم اكتب الزاوية، أو اختر اتجاهه.",
|
||||
"help.sketch.construction_line.bisector": "اختر خطين مستقيمين متقاطعين في الرسم، ثم اختر أحد منصفي الزاوية. يتبع الناتج الخطين اللذين أُنشئ منهما.",
|
||||
"help.sketch.construction_line.horizontal": "أنشئ خطًا إنشائيًا أفقيًا لا نهائيًا يمر بنقطة. يحافظ القيد الأفقي على الاتجاه بعد التعديلات.",
|
||||
"help.sketch.construction_line.parallel": "اختر خطًا مستقيمًا في الرسم ثم ضع خطًا إنشائيًا لا نهائيًا موازيًا له. انقر بزر الفأرة الأيمن لتغيير المرجع.",
|
||||
"help.sketch.construction_line.perpendicular": "اختر خطًا مستقيمًا في الرسم ثم ضع خطًا إنشائيًا لا نهائيًا عموديًا عليه. انقر بزر الفأرة الأيمن لتغيير المرجع.",
|
||||
"help.sketch.construction_line.tangent_at": "أنشئ مماسًا عند نقطة على دائرة أو قوس دائري، أو عند نهاية منحنى شريحي مفتوح أو قطع مخروطي مفتوح. اختر المنحنى ثم نقطة التماس.",
|
||||
"help.sketch.construction_line.tangent_external": "اختر دائرة أو قوسًا دائريًا، ثم نقطة خارجية وحل المماس. تعرض المعاينة نقاط التماس الواقعة على القوس المرسوم فقط.",
|
||||
"help.sketch.construction_line.tangent_two": "اختر دائرتين أو قوسين دائريين ثم اختر مماسًا مشتركًا من المعاينة. يجب أن تقع نقطتا التماس على المنحنيين المرسومين.",
|
||||
"help.sketch.construction_line.vertical": "أنشئ خطًا إنشائيًا رأسيًا لا نهائيًا يمر بنقطة. يحافظ القيد الرأسي على الاتجاه بعد التعديلات.",
|
||||
"help.sketch.equation_curve": "يرسم منحنى من صيغ على مدى: x(t) و y(t)، أو y = f(x). يمكن أن تستخدم الصيغ المعلمات فيتبعها المنحنى: استخدمه لموجة جيبية أو لولب أو منحنى انفلات لسن ترس.",
|
||||
"help.sketch.inspect": "يفتح تقريرًا عن الرسم المفتوح: عدد درجات الحرية المتبقية وهل هو مقيد بالكامل. استخدمه لمعرفة ما لا يزال يتحرك قبل إنهاء الرسم.",
|
||||
"help.solid.align": "يحرّك الأجسام المحددة بحيث تلتقي نقطة أو وجه أو مستوى منها بنقطة أو وجه أو مستوى في مكان آخر. استخدمه لوضع قطعة ملاصقة لأخرى دون قياس المسافة.",
|
||||
"help.solid.chamfer": "يقطع الحواف المحددة بشطفة مستوية بالمسافة التي تحددها. استخدمه لكسر الحواف الحادة أو لتسهيل إدخال مسمار أو برغي.",
|
||||
"help.solid.combine": "يضم الأجسام، أو يقتطعها من جسم هدف، أو يُبقي الحجم المشترك بينها فقط، ويمكنه الاحتفاظ بأجسام الأداة. استخدمه لدمج القطع، أو لقطع شكل بشكل آخر، أو لمعرفة موضع تداخل جسمين.",
|
||||
@@ -1592,6 +1619,10 @@
|
||||
"material.selection.mixed": "مواد مختلفة",
|
||||
"measure.at": "عند",
|
||||
"measure.item": "العنصر {number}",
|
||||
"measure.quick_copied": "تم نسخ القياس",
|
||||
"measure.quick_copy": "انقر لنسخ القياس",
|
||||
"measure.quick_edges": "{count} حواف",
|
||||
"measure.quick_faces": "{count} أوجه",
|
||||
"messages.ago_h": "قبل {n} ساعة",
|
||||
"messages.ago_min": "قبل {n} دقيقة",
|
||||
"messages.ago_s": "قبل {n} ث",
|
||||
@@ -1744,6 +1775,11 @@
|
||||
"not a move": "ليس تحريكًا",
|
||||
"not a number": "ليس عددًا",
|
||||
"open a sketch first": "افتح رسمًا أولًا",
|
||||
"parameters.apply_live": "طبّق كل تغيير عند مغادرة حقله",
|
||||
"parameters.apply_live_tip": "عند الإيقاف: تنتظر التغييرات حتى «تطبيق» أو «موافق»، ثم يُعاد بناء التصميم مرة واحدة لها جميعًا. «إلغاء» أو Escape يتجاهلها.",
|
||||
"parameters.held": "{count} تغييرات بانتظار «تطبيق»",
|
||||
"parameters.held_hint": "عدّل التعابير والوحدات. تنتظر التغييرات «تطبيق» أو «موافق».",
|
||||
"parameters.held_one": "تغيير واحد بانتظار «تطبيق»",
|
||||
"parts.automatic_name": "تلقائي",
|
||||
"parts.dimension.across_flats": "مقاس المفتاح",
|
||||
"parts.dimension.bore": "قطر الثقب",
|
||||
@@ -1766,6 +1802,8 @@
|
||||
"parts.family.set_screw": "برغي تثبيت بتجويف سداسي، طرف مسطح",
|
||||
"parts.family.socket_head_cap_screw": "برغي برأس أسطواني بتجويف سداسي",
|
||||
"parts.family.washer": "حلقة مسطحة",
|
||||
"parts.holes": "الثقوب (اختياري)",
|
||||
"parts.placement_hint": "اختر ثقبًا أو أكثر لتثبيت قطعة في كل منها، أو اضغط موافق لوضعها عند نقطة الأصل",
|
||||
"pattern.alignment_2": "المحاذاة 2",
|
||||
"pattern.alignment_hint": "العدد يشمل الأصل. «توسيط» يوازن النسخ؛ و«قلب» يختار الجانب الإضافي عندما يكون العدد زوجيًا.",
|
||||
"pattern.direction_1": "الاتجاه 1",
|
||||
@@ -1818,6 +1856,8 @@
|
||||
"prefs.bottom_centre": "أسفل الوسط",
|
||||
"prefs.bottom_right": "أسفل اليمين",
|
||||
"prefs.browser": "المستعرض",
|
||||
"prefs.browser_eye_left": "أيقونة الإظهار بجانب سهم الطي",
|
||||
"prefs.browser_eye_left_note": "تعرض صفوف المستعرض أيقونة الإظهار بجانب سهم الطي كما كانت من قبل، بدلاً من عمود في الطرف الأيمن للصفوف.",
|
||||
"prefs.browser_reveal_on_hover": "إظهار عند مرور المؤشر",
|
||||
"prefs.browser_show_note": "«ظاهر» يُبقي المستعرض على الشاشة. «إظهار عند مرور المؤشر» يُخفيه حتى يمر المؤشر فوق مكانه أو فوق الشريط الرفيع عند حافته اليسرى، ويبقى مفتوحًا ما دام المؤشر داخله أو كانت قائمة منه مفتوحة.",
|
||||
"prefs.browser_shown": "ظاهر",
|
||||
@@ -1868,6 +1908,8 @@
|
||||
"prefs.graphics_card_restart": "يُطبَّق بعد إعادة تشغيل SolidCraft. يتجاوز WGPU_ADAPTER_NAME هذا الاختيار.",
|
||||
"prefs.graphics_note": "وازن بين السرعة والنعومة. كل خيار يعرض أثره على عيّنة.",
|
||||
"prefs.graphics_presets_note": "الإعدادات المسبقة تضبط الخيارات أدناه معًا.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "إخفاء رموز القيود عند التصغير",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out_note": "تختفي رموز القيود عندما تُصغَّر المسودة بما يكفي حتى لا تزحم العرض. معطّل: تبقى بحجم شاشة ثابت عند أي تكبير.",
|
||||
"prefs.history": "السجل",
|
||||
"prefs.history_origin_bodies": "إظهار الأصل والأجسام في الأعلى",
|
||||
"prefs.history_origin_bodies_note": "يبدأ السجل على اليسار أو تحت المستعرض بمجلد الأجسام للمكوّن النشط وبالأصل، مع أيقونات الإظهار والقوائم كما في المستعرض. لا يتغير السجل في الأسفل.",
|
||||
@@ -1906,6 +1948,9 @@
|
||||
"prefs.max_file_size_step": "ملفات STEP والاستيراد",
|
||||
"prefs.max_file_size_web": "يفتح تطبيق الويب ملفات STEP والاستيراد حتى 512 MB والتصاميم حتى 256 MB؛ لا يتسع تبويب المتصفح لملفات أكبر. يمكن رفع هذه الحدود في تطبيق سطح المكتب.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "أيقونات كبيرة",
|
||||
"prefs.menu_list": "قائمة",
|
||||
"prefs.menus": "القوائم",
|
||||
"prefs.minutes": "دقائق",
|
||||
"prefs.minutes_suffix": " دقيقة",
|
||||
"prefs.mouse": "الفأرة ولوحة اللمس",
|
||||
@@ -1925,10 +1970,16 @@
|
||||
"prefs.origin_plane_names": "أسماء مستويات الأصل",
|
||||
"prefs.origin_plane_names_note": "الأسماء المعروضة في المستعرض والحوارات والقوائم. {top} هو XY و{front} هو XZ و{right} هو YZ، والمحور Z للأعلى. تحتفظ الأوامر والسكربتات والتصاميم المحفوظة بـ XY وXZ وYZ.",
|
||||
"prefs.overall_speed": "السرعة العامة",
|
||||
"prefs.parameter_list_rows": "صفوف قائمة المعاملات",
|
||||
"prefs.parameter_list_rows_tip": "يعرض السهم لأسفل في حقل قيمة فارغ المعاملات، المفضلة أولًا، حتى هذا العدد. العدد الأقل يبقي القائمة خفيفة في التصميم الكبير.",
|
||||
"prefs.persistent_ribbon_pressed": "تمييز أوامر الشريط النشطة",
|
||||
"prefs.persistent_ribbon_pressed_help": "تمييز أوامر الشريط أثناء تفعيل تأثيراتها. أوقف هذا الخيار للاحتفاظ بالمظهر المحايد السابق ومساعدة الأوامر.",
|
||||
"prefs.perspective_fov": "مجال الرؤية",
|
||||
"prefs.placement": "الموضع",
|
||||
"prefs.prefer_free_sketch_points": "تفضيل نقاط الرسم القابلة للتحريك عند تداخل النقاط",
|
||||
"prefs.preview_choices": "معاينة اختياراتك",
|
||||
"prefs.print_face_highlight": "إبراز وجه الطباعة",
|
||||
"prefs.print_face_highlight_tip": "مدى قوة تلوين وجه الطباعة للجسم وتحديده في العرض. 0 % لا يُظهر أي إبراز.",
|
||||
"prefs.profile_active": "ملف التعريف النشط",
|
||||
"prefs.profile_applied": "تم تطبيق ملف التعريف.",
|
||||
"prefs.profile_changed": "تغيّرت الإعدادات منذ حفظ ملف التعريف. «حفظ» يحفظها فيه.",
|
||||
@@ -1950,6 +2001,8 @@
|
||||
"prefs.quality_fastest": "الأسرع",
|
||||
"prefs.quality_slowest": "الأبطأ",
|
||||
"prefs.quality_smoother": "أنعم",
|
||||
"prefs.quick_measure": "قياس سريع في زاوية العرض",
|
||||
"prefs.quick_measure_note": "مقاس العناصر المحددة في الزاوية اليمنى السفلية من العرض: طول الحافة (أو Ø وR للحافة الدائرية)، ومساحة الوجه، وموضع النقطة، والمسافة والزاوية بين عنصرين، ومجموع عدة حواف أو أوجه. النقر ينسخه.",
|
||||
"prefs.render_previous_geometry_budget": "استخدام ميزانية الهندسة السابقة لمعاينة التصيير",
|
||||
"prefs.render_previous_geometry_budget_help": "استعادة حد الهندسة الثابت السابق (512 ميبيبايت على سطح المكتب 64 بت، و128 ميبيبايت على المنصات الأخرى) بدلًا من الميزانية المعتمدة على الجهاز.",
|
||||
"prefs.reports_saved": "{n} محفوظة",
|
||||
@@ -1963,6 +2016,8 @@
|
||||
"prefs.search_empty": "لا توجد إعدادات مطابقة",
|
||||
"prefs.select": "تحديد",
|
||||
"prefs.select_one_item": "تحديد عنصر واحد",
|
||||
"prefs.select_other_hidden": "تحديد العناصر الهندسية المخفية باستخدام «تحديد عنصر آخر»",
|
||||
"prefs.select_other_hidden_help": "عند التفعيل: تضمين الأجسام والمخططات المخفية والحواف المحجوبة قرب المؤشر. عند التعطيل: استخدام القائمة الأصلية للعناصر الهندسية المرئية أسفل المؤشر.",
|
||||
"prefs.selecting": "التحديد",
|
||||
"prefs.shift": "Shift",
|
||||
"prefs.shift_add_ctrl_toggle": "Shift يضيف فقط، وCtrl / Cmd يبدّل",
|
||||
@@ -1974,13 +2029,16 @@
|
||||
"prefs.sketch_align_points_note": "أثناء الرسم، تتحاذى النقطة أفقيًا مع نقطة رسم وعموديًا مع نقطة أخرى في الوقت نفسه، مع خط إرشادي منقط، وتحتفظ النقرة بالمحاذاتين. عند الإيقاف: مع النقطة السابقة فقط، محور واحد في كل مرة.",
|
||||
"prefs.sketch_conflict_colour": "الهندسة المتعارضة بلون التعارض",
|
||||
"prefs.sketch_conflict_colour_note": "عندما تتعارض قيود الرسم، تُرسم المنحنيات والنقاط التي يمسكها قيد فاشل بلون التعارض أيضًا، لا رمزه أو بُعده فقط. عند الإيقاف: تحتفظ بألوانها المعتادة.",
|
||||
"prefs.sketch_construction_lines": "أدوات الخطوط الإنشائية الموسعة",
|
||||
"prefs.sketch_exit_view": "عند انتهاء الرسم",
|
||||
"prefs.sketch_exit_view.keep": "إبقاء العرض الحالي",
|
||||
"prefs.sketch_exit_view.previous": "العودة إلى العرض السابق",
|
||||
"prefs.sketch_exit_view.unless_rotated": "العودة ما لم يُدوَّر العرض",
|
||||
"prefs.sketch_exit_view_note": "أين ينتقل العرض بعد إنهاء الرسم أو الإلغاء أو الضغط على Esc مرتين.",
|
||||
"prefs.sketch_open_look_at": "توجيه العرض نحو الرسم عند فتحه",
|
||||
"prefs.sketch_open_look_at_note": "عند الإيقاف: إنشاء الرسم أو تحريره يُبقي العرض كما هو. يوجّه «النظر إلى» العرض نحو الرسم عند الحاجة.",
|
||||
"prefs.sketch_recent_snap": "الالتقاط التلقائي إلى المراكز ونقاط المنتصف الأخيرة",
|
||||
"prefs.sketch_recent_snap_note": "أثناء الرسم، يلتقط المؤشر الموضع الذي يتقاطع فيه دليلا آخر مركزي دائرتين أو نقطتي منتصف حافتين مستقيمتين. إيقاف: تبقى الأدلة للعرض فقط ولا يتحرك المؤشر.",
|
||||
"prefs.sketch_recent_snap_note": "أثناء الرسم، يلتقط المؤشر الموضع الذي يتقاطع فيه دليلا آخر مركزي دائرتين أو نقطتي منتصف حافتين مستقيمتين. عند الإيقاف: تبقى تلك الأدلة للعرض فقط ولا يتحرك المؤشر.",
|
||||
"prefs.sketch_selection": "الرسم والتحديد",
|
||||
"prefs.sketch_selection_heading": "الرسم والتحديد",
|
||||
"prefs.sketch_selection_note": "كيف تلتقط الرسومات وكيف تحدد النقرات.",
|
||||
@@ -2038,13 +2096,21 @@
|
||||
"prefs.under_cursor": "تحت المؤشر",
|
||||
"prefs.units_precision": "الوحدات والدقة",
|
||||
"prefs.updates": "التحديثات",
|
||||
"prefs.value_boxes_in_view": "إظهار القيم بجانب مقابضها في العرض",
|
||||
"prefs.value_boxes_in_view_tip": "قيمة الأمر (المسافة، نصف القطر…) في مربع بجانب مقبضه، فوق النموذج. عند الإيقاف: تكتبها في نافذة الأمر، التي تتلقى لوحة المفاتيح بعد التحديد، ولا شيء يغطي النموذج.",
|
||||
"prefs.view": "سلوك العرض",
|
||||
"prefs.view_confirm_corner": "موافق وإلغاء في زاوية العرض",
|
||||
"prefs.view_confirm_corner_note": "أثناء فتح مربع حوار أو رسم، تؤدي علامة الصح وعلامة الإكس أعلى يمين العرض ما يؤديه زرّا موافق وإلغاء في مربع الحوار، أو تنهي الرسم وتلغيه. وينتقل متصفح العرض إلى أسفلهما.",
|
||||
"prefs.viewport": "منطقة العرض",
|
||||
"prefs.viewport_analysis_status": "إظهار حالة التحليل في منفذ العرض",
|
||||
"prefs.viewport_analysis_status_help": "عرض التحليلات النشطة أعلى منفذ العرض وفتح مفتاح كل تحليل من عنصره. ينهي Esc أحدث تحليل. أوقف الخيار لاستعادة المفاتيح في الزاوية وسلوك Esc السابق.",
|
||||
"prefs.viewport_background": "الخلفية",
|
||||
"prefs.viewport_background_colour": "لون",
|
||||
"prefs.viewport_background_theme": "تدرج السمة",
|
||||
"prefs.viewport_section_status": "إظهار حالة المقطع في منفذ العرض",
|
||||
"prefs.viewport_section_status_help": "عرض إعداد المقطع في منفذ العرض. يُوقف Escape وزر الشريط النشط المقطع. عطّل الخيار للاحتفاظ بأدوات المقطع السابقة.",
|
||||
"prefs.viewport_status_summary": "تلخيص عناصر حالة منفذ العرض",
|
||||
"prefs.viewport_status_summary_help": "اجمع ثلاثة عناصر أو أكثر لحالة منفذ العرض في شارة ملخّصة واحدة. عطّل الخيار لإبقاء كل عنصر في الصف الكامل.",
|
||||
"prefs.wheel": "العجلة",
|
||||
"prefs.wheel_trackpad": "العجلة ولوحة اللمس",
|
||||
"prefs.workspace": "مساحة العمل",
|
||||
@@ -2054,6 +2120,10 @@
|
||||
"properties.title": "الخصائص: {name}",
|
||||
"recovery.days_ago": "قبل {count} يوم",
|
||||
"recovery.detail": "{file} · {features} ميزة · حُفظ تلقائيًا {when}",
|
||||
"recovery.discard_all": "تجاهل الكل",
|
||||
"recovery.discard_all_detail": "لا يمكن التراجع عن هذا الإجراء.",
|
||||
"recovery.discard_all_title": "تجاهل {count} من عمليات الحفظ التلقائي؟",
|
||||
"recovery.discard_all_title_one": "تجاهل حفظ تلقائي واحد؟",
|
||||
"recovery.hours_ago": "قبل {count} ساعة",
|
||||
"recovery.just_now": "الآن",
|
||||
"recovery.min_ago": "قبل {count} دقيقة",
|
||||
@@ -2919,7 +2989,9 @@
|
||||
"render.output_ready": "جاهز للتصيير",
|
||||
"render.output_run_check": "تشغيل الفحص",
|
||||
"render.output_size_limit": "{method}: حتى {dimension} بكسل لكل ضلع و{pixels} بكسل. أكبر حجم مناسب: {width} × {height}.",
|
||||
"render.output_size_method_profile": "لتصيير {width} × {height}، اختر {method} مع ملف موارد CPU «{profile}»، أو حجمًا أصغر.",
|
||||
"render.output_size_ok": "الحجم مناسب · {width} × {height} بكسل",
|
||||
"render.output_size_profile": "لتصيير {width} × {height}، اختر ملف موارد CPU «{profile}»، أو حجمًا أصغر.",
|
||||
"render.output_swap_orientation": "تبديل العرض والارتفاع",
|
||||
"render.output_tools_inspector": "عدّل الحجم والصيغة في مفتش المخرجات.",
|
||||
"render.output_view_image": "عرض الصورة",
|
||||
@@ -3132,6 +3204,7 @@
|
||||
"render.size_custom": "مخصص…",
|
||||
"render.size_full_hd": "Full HD",
|
||||
"render.size_hd": "HD",
|
||||
"render.size_needs_profile": "{size} · يتطلب «{profile}»",
|
||||
"render.size_presets": "إعدادات الحجم المسبقة",
|
||||
"render.size_presets_pending": "تصل مع محرر المخرجات",
|
||||
"render.size_preview": "معاينة",
|
||||
@@ -3419,6 +3492,24 @@
|
||||
"sheet.flange_type_contour": "محيط",
|
||||
"sheet.flange_type_edge": "حافة",
|
||||
"sheet.no_body": "لا يوجد جسم صفائح معدنية: ابدأ واحدًا من حافة مثنية > أساسية، أو حوّل لوحًا إلى صفائح معدنية بأمر تحويل إلى صفائح معدنية (اختر وجهه المسطح الكبير)",
|
||||
"sheet.personal_rules.confirm_delete": "تأكيد الحذف",
|
||||
"sheet.personal_rules.delete": "حذف",
|
||||
"sheet.personal_rules.delete_prompt": "هل تحذف هذه القاعدة الشخصية؟ القواعد الموجودة في التصاميم لا تتغير.",
|
||||
"sheet.personal_rules.full": "المكتبة الشخصية فيها 64 قاعدة بالفعل.",
|
||||
"sheet.personal_rules.invalid_name": "أدخل اسمًا قصيرًا بلا فواصل أسطر ولا محارف تحكم.",
|
||||
"sheet.personal_rules.invalid_values": "أكمل قيم القاعدة: يجب أن تُحسب كل قيمة في هذا التصميم، بسماكة موجبة ومعامل K من 0 إلى 1.",
|
||||
"sheet.personal_rules.missing": "القاعدة الشخصية المحددة لم تعد متاحة.",
|
||||
"sheet.personal_rules.name": "اسم القاعدة",
|
||||
"sheet.personal_rules.name_exists": "توجد قاعدة شخصية بهذا الاسم. استخدم اسمًا آخر.",
|
||||
"sheet.personal_rules.name_hint": "اسم فريد",
|
||||
"sheet.personal_rules.overwrite": "حفظ التغييرات في القاعدة الشخصية المحددة",
|
||||
"sheet.personal_rules.rename": "إعادة تسمية",
|
||||
"sheet.personal_rules.rule": "القاعدة الشخصية",
|
||||
"sheet.personal_rules.save": "حفظ كجديدة",
|
||||
"sheet.personal_rules.scope": "محفوظة في ملفك الشخصي لكل التصاميم.",
|
||||
"sheet.personal_rules.select": "اختر قاعدة",
|
||||
"sheet.personal_rules.title": "القواعد الشخصية",
|
||||
"sheet.personal_rules.unsupported": "تحتوي هذه القاعدة على قيم لا يستطيع هذا الإصدار استخدامها.",
|
||||
"shortcut.json_error": "يجب أن تكون المعاملات بصيغة JSON: {error}",
|
||||
"shortcut.no_command": "لا يوجد أمر `{id}`، ويجب أن يكون النص قبل JSON معرّف أمر",
|
||||
"shortcut.pin": "تثبيت في أعلى مربع S",
|
||||
@@ -3455,6 +3546,7 @@
|
||||
"sketch_text.align_right": "يمين",
|
||||
"sketch_text.bold": "غامق",
|
||||
"sketch_text.builtin_font": "{family} (مدمج)",
|
||||
"sketch_text.formula": "ابدأ بـ = لتكوين النص من المعاملات: ='W' + number(Width)",
|
||||
"sketch_text.italic": "مائل",
|
||||
"sketch_text.loading_fonts": "جارٍ البحث عن الخطوط المثبتة…",
|
||||
"sketch_text.on_path": "النص يتبع مساره: المنحنى يحدد موضعه",
|
||||
@@ -3595,6 +3687,8 @@
|
||||
"ui.3d_mouse_section": "الفأرة ثلاثية الأبعاد",
|
||||
"ui.3d_mouse_show_in_view": "إظهار الفأرة ثلاثية الأبعاد في العرض ثلاثي الأبعاد",
|
||||
"ui.3d_mouse_show_in_view_tip": "صورة للفأرة ثلاثية الأبعاد في الزاوية السفلية اليسرى، تتحرك كلما حرّكتها.",
|
||||
"ui.3d_mouse_sketch_orbit": "تدوير الرسومات بالفأرة ثلاثية الأبعاد",
|
||||
"ui.3d_mouse_sketch_orbit_tip": "تدير الفأرة ثلاثية الأبعاد العرض أيضًا أثناء فتح رسم ورؤيته من الأمام مباشرة، كما تفعل الفأرة. عند الإيقاف: تُزيح الرسم وتكبّره فقط حتى تُميل العرض.",
|
||||
"ui.3d_mouse_skip": "جهازي لا يستطيع ذلك",
|
||||
"ui.3d_mouse_skip_tip": "يتخطى هذه الحركة في الاتجاهين، وتستبعدها المعايرة.",
|
||||
"ui.3d_mouse_speed": "سرعة الفأرة ثلاثية الأبعاد",
|
||||
@@ -3623,6 +3717,8 @@
|
||||
"ui.3d_mouse_step_twist_counterclockwise": "لُفّ الغطاء عكس عقارب الساعة، وأمسكه، ثم اتركه.",
|
||||
"ui.3d_mouse_switch_cap": "المعايرة كجهاز العودة إلى المركز",
|
||||
"ui.3d_mouse_switch_trackball": "المعايرة ككرة تتبّع",
|
||||
"ui.3d_mouse_use_direct": "المشغل المباشر",
|
||||
"ui.3d_mouse_use_direct_tip": "قراءة الفأرة ثلاثية الأبعاد مباشرة (hidraw) بدلًا من المرور عبر spacenavd.",
|
||||
"ui.3d_mouse_view_axis": "محور العرض",
|
||||
"ui.3d_mouse_view_off": "متوقف",
|
||||
"ui.3d_mouse_view_off_tip": "ملف تعريف مشغّل الفأرة ثلاثية الأبعاد يوقف هذا المحور، فلا تحرك حركتُه العرض. اختيار حركة محور آخر هنا يبادل بينهما، فتتوقف تلك الحركة عن تحريك العرض.",
|
||||
@@ -3756,6 +3852,8 @@
|
||||
"ui.agent_chat_remove": "إزالة من قائمة الانتظار",
|
||||
"ui.agent_chat_remove_file": "إزالته من هذه الرسالة",
|
||||
"ui.agent_chat_remove_imported": "إزالته من هذه الرسالة؛ تبقى القطعة في التصميم (التراجع يزيلها)",
|
||||
"ui.agent_chat_reply_cut_off": "بلغ الرد حد طوله قبل أن يكتمل، لذا قد يكون ناقصًا. اطلب من الوكيل أن يتابع، أو أن يجيب بإيجاز أكبر.",
|
||||
"ui.agent_chat_reply_declined": "رفض النموذج الإجابة عن هذه الرسالة.",
|
||||
"ui.agent_chat_retry": "إعادة المحاولة",
|
||||
"ui.agent_chat_right": "يمين",
|
||||
"ui.agent_chat_save": "حفظ",
|
||||
@@ -3939,6 +4037,7 @@
|
||||
"ui.circle": "دائرة",
|
||||
"ui.circular_pattern": "نمط دائري",
|
||||
"ui.clear": "مسح",
|
||||
"ui.clear_print_face": "مسح وجه الطباعة",
|
||||
"ui.clear_recent": "مسح الأخيرة",
|
||||
"ui.clear_the_selection": "مسح التحديد",
|
||||
"ui.click_selects_whole_bodies": "النقر يحدد الأجسام كاملة",
|
||||
@@ -3973,6 +4072,15 @@
|
||||
"ui.constant": "ثابت",
|
||||
"ui.constraints_conflict": "تعارض في القيود",
|
||||
"ui.construction": "مساعد",
|
||||
"ui.construction.angle_hint": "خط إنشائي: انقر نقطة ثم أدخل زاوية أو اختر اتجاهًا",
|
||||
"ui.construction.bisector_hint": "منصف زاوية: اختر خطين مستقيمين متقاطعين ثم الحل المطلوب",
|
||||
"ui.construction.common_hint": "مماس مشترك: اختر دائرتين/قوسين ثم الحل المطلوب",
|
||||
"ui.construction.contact_hint": "مماس: اختر دائرة/قوسًا أو منحنى شريحيًا/قطعًا مخروطيًا مفتوحًا ثم نقطة التماس (للمنحنيات الحرة: النهايات فقط)",
|
||||
"ui.construction.external_hint": "مماس: اختر دائرة أو قوسًا ثم نقطة خارجية ثم الحل المطلوب",
|
||||
"ui.construction.free_hint": "خط إنشائي: انقر نقطتين؛ انقر بزر الفأرة الأيمن للبدء من جديد",
|
||||
"ui.construction.point_hint": "خط إنشائي: انقر نقطة المرور",
|
||||
"ui.construction.reference_hint": "خط إنشائي: اختر خطًا مستقيمًا ثم نقطة؛ انقر بزر الفأرة الأيمن لتغيير المرجع",
|
||||
"ui.construction.ribbon_label": "خطوط مساعدة",
|
||||
"ui.context_menu": "القائمة السياقية",
|
||||
"ui.continuity_g0": "G0 تجعّد",
|
||||
"ui.continuity_g1": "G1 مماس",
|
||||
@@ -4362,12 +4470,17 @@
|
||||
"ui.plugins_skipped": "تعذّرت قراءة {count} مجلد(ات).",
|
||||
"ui.plugins_version": "{version}",
|
||||
"ui.point": "نقطة",
|
||||
"ui.point_cloud_add_filter": "إضافة مرشح…",
|
||||
"ui.point_cloud_auto_range": "نطاق تلقائي",
|
||||
"ui.point_cloud_color_by": "التلوين حسب",
|
||||
"ui.point_cloud_file_colors": "ألوان الملف",
|
||||
"ui.point_cloud_filter_by": "التصفية حسب",
|
||||
"ui.point_cloud_height_high": "أعلى ارتفاع",
|
||||
"ui.point_cloud_height_low": "أدنى ارتفاع",
|
||||
"ui.point_cloud_legend_filter": "تُعرض وتُخفى في وسيلة الإيضاح أعلاه",
|
||||
"ui.point_cloud_no_file_colors": "لا يحتوي الملف على ألوان",
|
||||
"ui.point_cloud_pause": "إيقاف مؤقت",
|
||||
"ui.point_cloud_play": "تشغيل",
|
||||
"ui.point_cloud_single_color": "لون واحد",
|
||||
"ui.point_cloud_size": "حجم النقطة",
|
||||
"ui.point_cloud_units": "وحدات الملف",
|
||||
@@ -4376,6 +4489,11 @@
|
||||
"ui.polygon": "مضلع",
|
||||
"ui.preferences": "التفضيلات",
|
||||
"ui.press_a_key": "اضغط مفتاحًا…",
|
||||
"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": "المقطع",
|
||||
"ui.profile_plane": "مستوى المقطع",
|
||||
"ui.profiles": "المقاطع",
|
||||
@@ -4439,6 +4557,7 @@
|
||||
"ui.revolve": "دوران",
|
||||
"ui.ribbon_exit": "خروج",
|
||||
"ui.ribbon_labels": "إظهار النص تحت أيقونات الشريط",
|
||||
"ui.ribbon_menus_skip_pinned": "استبعاد الأوامر المثبتة من القوائم المنسدلة",
|
||||
"ui.ribbon_pin_footer": "الأوامر المثبتة تظهر في الشريط. مرّر المؤشر على صف لتثبيته أو إلغاء تثبيته.",
|
||||
"ui.ribbon_workspace": "مساحة العمل",
|
||||
"ui.right": "يمين",
|
||||
@@ -4457,6 +4576,7 @@
|
||||
"ui.selectOther": "تحديد آخر…",
|
||||
"ui.selectTangentChain": "تحديد سلسلة مماسية",
|
||||
"ui.select_all": "تحديد الكل",
|
||||
"ui.select_components_by_face": "تحديد المكوّنات (تحديد وجه يحدّد المكوّن الذي ينتمي إليه)",
|
||||
"ui.selection": "التحديد",
|
||||
"ui.selection_modifiers": "مُعدِّلات التحديد",
|
||||
"ui.selection_shift_add_only": "Shift مع النقر يضيف دون إزالة",
|
||||
@@ -4498,6 +4618,11 @@
|
||||
"ui.sketch_intersect_selection_filter": "مرشح التحديد",
|
||||
"ui.sketch_modifiers": "مُعدِّلات الرسم",
|
||||
"ui.sketch_project_geometry": "الهندسة",
|
||||
"ui.sketch_report.all_constrained": "كل شيء مقيد بالكامل.",
|
||||
"ui.sketch_report.dof": "درجات الحرية",
|
||||
"ui.sketch_report.loose": "غير مقيد بالكامل",
|
||||
"ui.sketch_report.status": "الحالة",
|
||||
"ui.sketch_report.title": "تقرير الرسم",
|
||||
"ui.sketch_scale": "تحجيم الرسم",
|
||||
"ui.sketches": "الرسومات",
|
||||
"ui.slice": "تقطيع",
|
||||
@@ -4602,10 +4727,19 @@
|
||||
"ui.view_centre": "مركز العرض",
|
||||
"ui.viewport_status_clear": "إيقاف {name}",
|
||||
"ui.viewport_status_clear_hint": "إيقاف {name} · Esc",
|
||||
"ui.viewport_status_edges": "{count} حواف",
|
||||
"ui.viewport_status_from": "من {direction}",
|
||||
"ui.viewport_status_key": "إظهار مفتاح الألوان لـ {name}",
|
||||
"ui.viewport_status_minimum_radius": "أقل نصف قطر",
|
||||
"ui.viewport_status_pairs": "{count} أزواج",
|
||||
"ui.viewport_status_section_hatch": "{setting} · تهشير",
|
||||
"ui.viewport_status_section_plane": "المستوى الحالي",
|
||||
"ui.viewport_status_section_tilted": "مائل · {offset}",
|
||||
"ui.viewport_status_stripes": "{count} شريطًا",
|
||||
"ui.viewport_status_summary_clear": "إيقاف كل شيء",
|
||||
"ui.viewport_status_summary_heading": "في هذا العرض",
|
||||
"ui.viewport_status_summary_label": "{name} و{count} عناصر أخرى",
|
||||
"ui.viewport_status_summary_more": "+{count}",
|
||||
"ui.viewport_theme_follow": "اتباع السمة",
|
||||
"ui.visual_shaded_hidden_edges": "مظلل مع الحواف المخفية",
|
||||
"ui.visual_shaded_with_edges": "مظلل مع الحواف",
|
||||
@@ -4642,7 +4776,9 @@
|
||||
"update.button": "تحديث",
|
||||
"update.button_version": "التحديث إلى {version}",
|
||||
"update.check_automatically": "التحقق من التحديثات تلقائيًا",
|
||||
"update.check_for_updates": "التحقق من وجود تحديثات",
|
||||
"update.check_now": "تحقق الآن",
|
||||
"update.check_unavailable": "التحقق من التحديثات غير متاح لهذا التثبيت.",
|
||||
"update.checking": "جارٍ التحقق…",
|
||||
"update.copy": "نسخ",
|
||||
"update.dmg_opened": "صورة القرص مفتوحة وموثّقة: اسحب SolidCraft إلى مجلد التطبيقات مستبدلًا النسخة القديمة، ثم أعد تشغيل SolidCraft.",
|
||||
|
||||
@@ -206,6 +206,7 @@
|
||||
"Edges": "Ivice",
|
||||
"Edit": "Uredi",
|
||||
"Edit Profile Sketch": "Uredi skicu profila",
|
||||
"Edit shortcuts": "Uredi prečice",
|
||||
"Enable rule": "Omogući pravilo",
|
||||
"Enabled": "Omogućeno",
|
||||
"End Radius": "Krajnji poluprečnik",
|
||||
@@ -468,6 +469,7 @@
|
||||
"Radius": "Poluprečnik",
|
||||
"Radius Fit": "Uklapanje poluprečnika",
|
||||
"Ratio": "Omjer",
|
||||
"Read-only list: shortcuts can't be changed here.": "Popis samo za čitanje: prečice se ovdje ne mogu mijenjati.",
|
||||
"Reattach selected face": "Ponovo pričvrsti izabrano lice",
|
||||
"Recover": "Oporavi",
|
||||
"Recover unsaved design?": "Oporaviti nesačuvani dizajn?",
|
||||
@@ -652,6 +654,7 @@
|
||||
"Third point": "Treća tačka",
|
||||
"This child is shared. Deleting it removes it from every copy of its parent.": "Ovaj podređeni element je dijeljen. Brisanjem se uklanja iz svake kopije njegovog roditelja.",
|
||||
"This child is shared. Position changes apply to every copy of its parent.": "Ovaj podređeni element je dijeljen. Promjene položaja primjenjuju se na svaku kopiju njegovog roditelja.",
|
||||
"This component is inside a subassembly placed more than once: aligning it moves it in every placement.": "Ova komponenta je unutar podsklopa koji je postavljen više puta: njeno poravnavanje pomjera je u svakom postavljanju.",
|
||||
"This face cannot provide a durable attachment. Choose another face or explicitly use a fixed plane.": "Ovo lice ne može dati trajno pričvršćenje. Izaberite drugo lice ili izričito koristite fiksnu ravan.",
|
||||
"This image is not attached to GitHub yet. Copy or save it, then attach it on GitHub.": "Ova slika još nije priložena na GitHubu. Kopirajte je ili sačuvajte, a zatim je priložite na GitHubu.",
|
||||
"Thread depth": "Dubina navoja",
|
||||
@@ -772,6 +775,8 @@
|
||||
"at the face": "na licu",
|
||||
"bom.group_children": "Poddijelovi u redu svog dijela",
|
||||
"browser.body_name": "Tijelo{number}",
|
||||
"browser.calibrate_click_a": "Kliknite sliku da zadate tačku A ili upišite njene koordinate.",
|
||||
"browser.calibrate_click_b": "Kliknite sliku da zadate tačku B ili upišite njene koordinate.",
|
||||
"browser.calibrate_title": "Kalibriši: {name}",
|
||||
"browser.capture_many": "Pomjerene komponente bez zabilježenog položaja: {count}.",
|
||||
"browser.capture_note": "Zabilježi zadržava novi položaj; Vrati ga vraća nazad.",
|
||||
@@ -793,6 +798,11 @@
|
||||
"cam.roller": "Valjak",
|
||||
"cam.roller_pick": "Valjak (neobavezno)",
|
||||
"cam.tip_pick": "Vrh (neobavezno)",
|
||||
"camera.shot_pitch": "Nagib",
|
||||
"camera.shot_roll": "Valjanje",
|
||||
"camera.shot_yaw": "Skretanje",
|
||||
"canvas.decal_place_hint": "Kliknite lice da postavite naljepnicu",
|
||||
"canvas.place_hint": "Kliknite ravan ili lice da postavite platno",
|
||||
"choose a profile": "izaberite profil",
|
||||
"choose the cam's and the follower's joints": "izaberite spojeve brijega i pratioca",
|
||||
"choose the joints to couple": "izaberite spojeve koje treba spregnuti",
|
||||
@@ -800,12 +810,16 @@
|
||||
"click sketch curves": "kliknite na krive skice",
|
||||
"cmd.appearance_assign": "Izgled",
|
||||
"cmd.appearance_chain": "Lanac izgleda",
|
||||
"cmd.appearance_copy": "Kopiraj izgled",
|
||||
"cmd.appearance_favorite": "Omiljeni izgled",
|
||||
"cmd.appearance_library": "Biblioteka izgleda",
|
||||
"cmd.appearance_list": "Popis izgleda",
|
||||
"cmd.appearance_paint": "Oboji izgledom",
|
||||
"cmd.appearance_paste": "Zalijepi izgled",
|
||||
"cmd.appearance_pick_up": "Preuzmi izgled",
|
||||
"cmd.appearance_replace": "Zamijeni izgled",
|
||||
"cmd.assembly_bom": "Sastavnica",
|
||||
"cmd.body.print_face": "Površina za štampu",
|
||||
"cmd.body_rename": "Preimenuj tijelo",
|
||||
"cmd.browser_collapse_all": "Skupi sve",
|
||||
"cmd.browser_expand_all": "Proširi sve",
|
||||
@@ -813,6 +827,7 @@
|
||||
"cmd.browser_move": "Premjesti stavke stabla modela",
|
||||
"cmd.browser_order": "Poredaj stavke stabla modela",
|
||||
"cmd.browser_rename_group": "Preimenuj grupu",
|
||||
"cmd.browser_reparent": "Premjesti u komponentu",
|
||||
"cmd.browser_ungroup": "Razgrupiši",
|
||||
"cmd.browser_visibility": "Prikaži/sakrij",
|
||||
"cmd.canvas_calibrate": "Kalibriši platno",
|
||||
@@ -839,7 +854,9 @@
|
||||
"cmd.component_metadata": "Svojstva dijela",
|
||||
"cmd.component_metadata_set": "Promijeni svojstva dijela",
|
||||
"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_paste_from_design": "Zalijepi iz drugog dizajna",
|
||||
"cmd.component_paste_new": "Zalijepi kao novo",
|
||||
"cmd.component_properties": "Fizička svojstva komponente",
|
||||
"cmd.component_relink": "Ponovo poveži izvor",
|
||||
@@ -976,6 +993,7 @@
|
||||
"cmd.motion_trace": "Trag kretanja",
|
||||
"cmd.motion_trace_edit": "Uredi trag kretanja",
|
||||
"cmd.motion_trace_keep": "Zadrži trag kretanja",
|
||||
"cmd.occurrence_align": "Poravnaj primjerak",
|
||||
"cmd.occurrence_copy": "Zalijepi (primjerak)",
|
||||
"cmd.occurrence_drag": "Povuci primjerak",
|
||||
"cmd.occurrence_ground": "Učvrsti",
|
||||
@@ -1033,11 +1051,17 @@
|
||||
"cmd.render_library_surface_unbind": "Ukloni vezu površine",
|
||||
"cmd.render_library_targets": "Pronađi ciljeve biblioteke renderovanja",
|
||||
"cmd.render_lights_add": "Dodaj svjetlo",
|
||||
"cmd.render_lights_duplicate": "Dupliciraj svjetlo renderovanja",
|
||||
"cmd.render_lights_list": "Popis svjetala renderovanja",
|
||||
"cmd.render_lights_mute": "Isključi svjetlo",
|
||||
"cmd.render_lights_remove": "Ukloni svjetlo renderovanja",
|
||||
"cmd.render_lights_set": "Postavi svjetlo renderovanja",
|
||||
"cmd.render_lights_solo": "Samo ovo svjetlo",
|
||||
"cmd.render_optics_bind": "Sačuvaj optičku vezu",
|
||||
"cmd.render_optics_unbind": "Ukloni optičku vezu",
|
||||
"cmd.render_output_size": "Postavi veličinu izlaza renderovanja",
|
||||
"cmd.render_preflight": "Provjeri zahtjev za renderovanje",
|
||||
"cmd.render_preflight_copy": "Kopiraj izvještaj provjere renderovanja",
|
||||
"cmd.render_preset_apply": "Primijeni izvorni studio",
|
||||
"cmd.render_preset_resolve": "Pregledaj izvorni studio",
|
||||
"cmd.render_quality_prepare": "Pregledaj fiksni budžet uzorkovanja",
|
||||
@@ -1060,6 +1084,13 @@
|
||||
"cmd.render_start": "Renderuj sliku",
|
||||
"cmd.render_status": "Napredak renderovanja",
|
||||
"cmd.select_clear": "Poništi izbor",
|
||||
"cmd.settings_profile_apply": "Primijeni profil postavki",
|
||||
"cmd.settings_profile_delete": "Izbriši profil postavki",
|
||||
"cmd.settings_profile_export": "Izvezi profil postavki",
|
||||
"cmd.settings_profile_import": "Uvezi profil postavki",
|
||||
"cmd.settings_profile_list": "Profili postavki",
|
||||
"cmd.settings_profile_rename": "Preimenuj profil postavki",
|
||||
"cmd.settings_profile_save": "Sačuvaj profil postavki",
|
||||
"cmd.sheet_convert": "Pretvori u lim",
|
||||
"cmd.sheet_export_bend_table": "Izvezi tabelu savijanja",
|
||||
"cmd.sheet_export_dxf": "Izvezi razvijeni oblik kao DXF",
|
||||
@@ -1073,6 +1104,16 @@
|
||||
"cmd.sheet_refold": "Ponovo presavij",
|
||||
"cmd.sheet_rules": "Popis pravila za lim",
|
||||
"cmd.sheet_unfold": "Razvij",
|
||||
"cmd.sketch.construction_line": "Pomoćna prava",
|
||||
"cmd.sketch.construction_line.angle": "Pomoćna prava pod uglom",
|
||||
"cmd.sketch.construction_line.bisector": "Simetrala ugla",
|
||||
"cmd.sketch.construction_line.horizontal": "Horizontalna pomoćna prava",
|
||||
"cmd.sketch.construction_line.parallel": "Paralelna pomoćna prava",
|
||||
"cmd.sketch.construction_line.perpendicular": "Okomita pomoćna prava",
|
||||
"cmd.sketch.construction_line.tangent_at": "Tangenta u tački krive",
|
||||
"cmd.sketch.construction_line.tangent_external": "Tangenta kroz vanjsku tačku",
|
||||
"cmd.sketch.construction_line.tangent_two": "Zajednička tangenta",
|
||||
"cmd.sketch.construction_line.vertical": "Vertikalna pomoćna prava",
|
||||
"cmd.sketch_arc3d": "3D luk",
|
||||
"cmd.sketch_arc_center_point": "Luk iz centra",
|
||||
"cmd.sketch_arc_tangent": "Tangentni luk",
|
||||
@@ -1105,6 +1146,8 @@
|
||||
"cmd.sketch_drawing_bounds": "Veličina crteža",
|
||||
"cmd.sketch_edit": "Uredi skicu",
|
||||
"cmd.sketch_edit_text": "Uredi tekst",
|
||||
"cmd.sketch_equation_curve": "Kriva iz jednačine",
|
||||
"cmd.sketch_equation_curve_edit": "Uredi krivu iz jednačine",
|
||||
"cmd.sketch_export_dxf": "Sačuvaj kao DXF",
|
||||
"cmd.sketch_finish_auto_constrain": "Završi s automatskim ograničenjima",
|
||||
"cmd.sketch_fit_curves_to_section": "Uklopi krive u presjek mreže",
|
||||
@@ -1201,6 +1244,7 @@
|
||||
"cmd.view.render_browser": "Stablo renderovanja",
|
||||
"cmd.view.render_dock": "Premjesti panel renderovanja",
|
||||
"cmd.view.render_focus": "Prikaz fokusa renderovanja",
|
||||
"cmd.view.status_items": "Stavke statusa prozora prikaza",
|
||||
"cmd.view_ambient_occlusion": "Ambijentalna okluzija",
|
||||
"cmd.view_angle_readout": "Uglovi prikaza",
|
||||
"cmd.view_apply": "Vrati imenovani prikaz",
|
||||
@@ -1222,6 +1266,7 @@
|
||||
"cmd.view_normal_to": "Okomito na izbor",
|
||||
"cmd.view_previous_tab": "Prethodna kartica",
|
||||
"cmd.view_rename": "Preimenuj imenovani prikaz",
|
||||
"cmd.view_render_frame": "Pomoćne linije kadra renderovanja",
|
||||
"cmd.view_right": "Pogled zdesna",
|
||||
"cmd.view_save": "Novi imenovani prikaz",
|
||||
"cmd.view_shots_strip": "Traka kadrova",
|
||||
@@ -1232,10 +1277,14 @@
|
||||
"color.copy": "Kliknite da kopirate vrijednosti boje",
|
||||
"color.edit": "Kliknite da uredite boju",
|
||||
"color.gamma": "Prikaz vrijednosti boje u gama prostoru 0-255",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "Unesite #rrggbb ili #rrggbbaa; šest cifara zadržava neprozirnost",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
"color.hue": "Nijansa",
|
||||
"color.linear": "Prikaz vrijednosti boje u linearnom prostoru 0-1",
|
||||
"color.saturation_value": "Zasićenost i vrijednost",
|
||||
"color.selected": "Izabrana boja",
|
||||
"config.column_filter_empty": "Nema odgovarajućih kolona",
|
||||
"contact.components_count": "Komponente: {count}",
|
||||
"contact.enabled_label": "omogućeno",
|
||||
"coupling.gear_1": "Zupčanik 1",
|
||||
@@ -1350,8 +1399,20 @@
|
||||
"help.material.assign": "Određuje od čega su izabrana tijela napravljena, npr. čelik, aluminij ili ABS. Koristite ga da masa i težina odgovaraju stvarnom dijelu.",
|
||||
"help.mit": "MIT licenca",
|
||||
"help.notice": "NOTICE",
|
||||
"help.occurrence.move": "Pomjera dio sklopa samo duž stepeni slobode koje dozvoljavaju njegovi spojevi. Izaberite lice dijela, a zatim prevucite dostupnu strelicu ili luk.",
|
||||
"help.parameters.change": "Otvara tabelu imenovanih vrijednosti i izraza koji upravljaju kotama dizajna. Koristite je da promijenite mjeru na jednom mjestu, a sve operacije koje je koriste je slijede.",
|
||||
"help.pointcloud.insert": "Umetnite PLY, XYZ ili PTS skeniranje kao oblak tačaka uz model. Koristite ga kao referencu pri izradi modela iz izmjerenih podataka.",
|
||||
"help.sketch.construction_line": "Napravite beskonačnu pomoćnu pravu kroz dvije tačke. Koristite je da rasporedite skicu bez razdvajanja profila.",
|
||||
"help.sketch.construction_line.angle": "Napravite beskonačnu pomoćnu pravu pod uglom od ose X skice. Izaberite tačku i upišite ugao ili izaberite smjer.",
|
||||
"help.sketch.construction_line.bisector": "Izaberite dvije prave linije skice koje se sijeku, pa jednu od dvije simetrale ugla. Rezultat prati izvorne linije.",
|
||||
"help.sketch.construction_line.horizontal": "Napravite beskonačnu horizontalnu pomoćnu pravu kroz tačku. Njeno ograničenje horizontalnosti ostaje na snazi pri kasnijim izmjenama.",
|
||||
"help.sketch.construction_line.parallel": "Izaberite pravu liniju skice, pa postavite paralelnu beskonačnu pomoćnu pravu. Desnim klikom izaberite drugu referencu.",
|
||||
"help.sketch.construction_line.perpendicular": "Izaberite pravu liniju skice, pa postavite okomitu beskonačnu pomoćnu pravu. Desnim klikom izaberite drugu referencu.",
|
||||
"help.sketch.construction_line.tangent_at": "Napravite tangentu u tački na krugu ili kružnom luku, ili u krajnjoj tački otvorenog splajna ili konike. Izaberite krivu, pa tačku dodira.",
|
||||
"help.sketch.construction_line.tangent_external": "Izaberite krug ili kružni luk, pa vanjsku tačku i rješenje tangente. Pregledi prikazuju samo dodirne tačke na nacrtanom luku.",
|
||||
"help.sketch.construction_line.tangent_two": "Izaberite dva kruga ili kružna luka, pa u pregledu izaberite zajedničku tangentu. Dodirne tačke moraju ležati na nacrtanim krivama.",
|
||||
"help.sketch.construction_line.vertical": "Napravite beskonačnu vertikalnu pomoćnu pravu kroz tačku. Njeno ograničenje vertikalnosti ostaje na snazi pri kasnijim izmjenama.",
|
||||
"help.sketch.equation_curve": "Crta krivu iz formula: x(t) i y(t) ili y = f(x), u zadanom rasponu. Formule mogu koristiti parametre i kriva ih prati: koristite je za sinusoidu, spiralu ili evolventu zupca zupčanika.",
|
||||
"help.solid.align": "Pomjera izabrana tijela tako da se tačka, lice ili ravan na njima poklopi s tačkom, licem ili ravni negdje drugdje. Koristite ga da postavite dio uz drugi bez mjerenja odmaka.",
|
||||
"help.solid.chamfer": "Odsijeca izabrane ivice ravnim skošenjem zadate udaljenosti. Koristite ga da ublažite oštre ivice ili olakšate uvođenje zatika ili vijka.",
|
||||
"help.solid.combine": "Spaja tijela, oduzima ih od ciljnog tijela ili zadržava samo zajedničku zapreminu, a može zadržati i alatna tijela. Koristite ga da spojite dijelove, da jednim oblikom odsiječete drugi ili da pronađete gdje se dva tijela preklapaju.",
|
||||
@@ -1480,6 +1541,21 @@
|
||||
"joint.one_component": "Ovi dijelovi su tijela jedne komponente: {bodies}. Spoj pomjera jednu komponentu u odnosu na drugu, pa svaki dio treba svoju komponentu.",
|
||||
"joint.rigid_group_one_component": "Sva tijela ({count}) su u jednoj komponenti (ili u samom dizajnu): prvo koristite {command}.",
|
||||
"joint.rigid_group_select_components": "Izaberite dvije ili više komponenti.",
|
||||
"joint_move.blocked_hint": "Provjerite vrstu spoja (npr. Kruti), učvršćenje, granice spoja i vrijednosti koje pokreću parametri.",
|
||||
"joint_move.blocked_title": "Ovaj dio se ne može pomjeriti",
|
||||
"joint_move.coarse": "Korak rotacije blizu ose (°)",
|
||||
"joint_move.distance": "Udaljenost od početka",
|
||||
"joint_move.done": "Gotovo",
|
||||
"joint_move.fine": "Korak rotacije dalje od ose (°)",
|
||||
"joint_move.fixed": "Ovaj dio nema pomičnu osu spoja.",
|
||||
"joint_move.help": "Pomjeri — prevucite strelicu ili prsten. Esc otkazuje trenutno povlačenje.",
|
||||
"joint_move.legacy_hint": "Isključite da biste koristili prethodni panel Pomjeri.",
|
||||
"joint_move.precision": "Povucite izvan isprekidane vodilje za finu rotaciju.",
|
||||
"joint_move.rotation": "Rotacija od početka",
|
||||
"joint_move.select": "Izaberite lice ili jedan dio sklopa, a zatim izaberite Pomjeri.",
|
||||
"joint_move.settings": "Pomjeranje u sklopu",
|
||||
"joint_move.step": "Korak",
|
||||
"joint_move.use_handles": "Koristi ručice kretanja spojeva za Pomjeri u sklopu",
|
||||
"keymap.command_id": "ID komande",
|
||||
"keymap.empty": "Nema odgovarajućih komandi",
|
||||
"keymap.use_anyway": "Ipak koristi {key} ovdje",
|
||||
@@ -1574,6 +1650,8 @@
|
||||
"nav.maya.help": "Alt+lijevo povlačenje orbitira, Alt+srednje pomjera, Alt+desno zumira; srednje povlačenje također pomjera",
|
||||
"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.",
|
||||
"nav.right_drag_orbits.label": "Povlačenje desnim dugmetom orbitira",
|
||||
"nav.solidworks.help": "Povlačenje srednjim dugmetom orbitira, Ctrl+srednje pomjera, Shift+srednje zumira, povlačenje desnim orbitira",
|
||||
"nav.tinkercad.help": "Povlačenje desnim dugmetom orbitira, povlačenje srednjim ili Shift+desnim pomjera",
|
||||
"nav.touchpad.help": "Držite Shift i pomjerajte pokazivač za pomjeranje prikaza; držite Alt/Option i pomjerajte ga za orbitiranje. Dugme miša nije potrebno. Vertikalno skrolovanje zumira.",
|
||||
@@ -1595,6 +1673,9 @@
|
||||
"navbar.custom.customise": "Prilagodi navigacijsku traku…",
|
||||
"navbar.custom.design": "Dizajn",
|
||||
"navbar.custom.duplicate": "Ovo mjesto je već na traci.",
|
||||
"navbar.custom.edge": "Rub",
|
||||
"navbar.custom.edge_bottom": "Dolje",
|
||||
"navbar.custom.edge_top": "Gore",
|
||||
"navbar.custom.fade": "Izblijedi",
|
||||
"navbar.custom.full": "Uklonite jedno mjesto da biste dodali drugo.",
|
||||
"navbar.custom.hidden_design": "Skriveno u Dizajnu",
|
||||
@@ -1701,6 +1782,7 @@
|
||||
"parts.dimension.thickness": "Debljina",
|
||||
"parts.dimension.width": "Širina",
|
||||
"parts.family.bearing": "Kuglični ležaj s dubokim žlijebom (omotač)",
|
||||
"parts.family.button_head_cap_screw": "Vijak s poluokruglom glavom i imbusom",
|
||||
"parts.family.dowel_pin": "Cilindrični zatik",
|
||||
"parts.family.hex_bolt": "Vijak sa šestougaonom glavom",
|
||||
"parts.family.hex_nut": "Šestougaona matica",
|
||||
@@ -1759,6 +1841,11 @@
|
||||
"prefs.bottom_centre": "Dolje u sredini",
|
||||
"prefs.bottom_right": "Dolje desno",
|
||||
"prefs.browser": "Stablo modela",
|
||||
"prefs.browser_eye_left": "Oko pored strelice za skupljanje",
|
||||
"prefs.browser_eye_left_note": "Redovi stabla modela prikazuju oko za prikazivanje/skrivanje pored strelice za skupljanje, kao ranije, umjesto u koloni na desnom kraju redova.",
|
||||
"prefs.browser_reveal_on_hover": "Prikaži pri prelasku mišem",
|
||||
"prefs.browser_show_note": "Prikazano drži stablo modela na ekranu. Prikaži pri prelasku mišem drži ga skrivenim dok pokazivač ne pređe preko njegovog mjesta ili tankog jezička na njegovom lijevom rubu, a ostaje otvoreno dok je pokazivač u njemu ili dok je otvoren neki meni iz njega.",
|
||||
"prefs.browser_shown": "Prikazano",
|
||||
"prefs.built_in": "Ugrađeno",
|
||||
"prefs.button_left": "Povlačenje lijevim dugmetom",
|
||||
"prefs.button_middle": "Povlačenje srednjim dugmetom",
|
||||
@@ -1806,6 +1893,8 @@
|
||||
"prefs.graphics_card_restart": "Stupa na snagu nakon ponovnog pokretanja SolidCrafta. WGPU_ADAPTER_NAME ima prednost nad ovim izborom.",
|
||||
"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",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out_note": "Simboli ograničenja nestaju iz prikaza kada je skica toliko umanjena da bi je zatrpali. Isključeno: ostaju fiksne veličine na ekranu pri svakom zumiranju.",
|
||||
"prefs.history": "Vremenska traka",
|
||||
"prefs.history_origin_bodies": "Prikaži ishodište i tijela na vrhu",
|
||||
"prefs.history_origin_bodies_note": "Vremenska traka lijevo ili ispod stabla modela počinje folderom Tijela aktivne komponente i Ishodištem, s njihovim okom i menijima kao u stablu modela. Vremenska traka na dnu ostaje nepromijenjena.",
|
||||
@@ -1829,6 +1918,8 @@
|
||||
"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.linked_designs": "Povezani dizajni",
|
||||
"prefs.live_input": "Ulaz uživo",
|
||||
"prefs.live_preview": "Pregled uživo",
|
||||
@@ -1842,6 +1933,9 @@
|
||||
"prefs.max_file_size_step": "STEP i uvezene datoteke",
|
||||
"prefs.max_file_size_web": "Web aplikacija otvara STEP i uvezene datoteke do 512 MB i dizajne do 256 MB: kartica preglednika ne može držati veće datoteke. Desktop aplikacija može povećati ove granice.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "Velike ikone",
|
||||
"prefs.menu_list": "Popis",
|
||||
"prefs.menus": "Meniji",
|
||||
"prefs.minutes": "minuta",
|
||||
"prefs.minutes_suffix": " min",
|
||||
"prefs.mouse": "Miš i trackpad",
|
||||
@@ -1861,14 +1955,40 @@
|
||||
"prefs.origin_plane_names": "Nazivi ravni ishodišta",
|
||||
"prefs.origin_plane_names_note": "Nazivi prikazani u stablu modela, dijalozima i listama. {top} je XY, {front} je XZ, a {right} je YZ, sa Z prema gore. Komande, skripte i sačuvani dizajni zadržavaju XY, XZ i YZ.",
|
||||
"prefs.overall_speed": "Ukupna brzina",
|
||||
"prefs.persistent_ribbon_pressed": "Istakni aktivne komande na traci",
|
||||
"prefs.persistent_ribbon_pressed_help": "Ističe komande na traci dok su njihovi efekti uključeni. Isključite da biste zadržali prethodni neutralni izgled i pomoć za komande.",
|
||||
"prefs.perspective_fov": "Vidno polje",
|
||||
"prefs.placement": "Postavljanje",
|
||||
"prefs.prefer_free_sketch_points": "Daj prednost pokretnim tačkama skice kada se tačke preklapaju",
|
||||
"prefs.preview_choices": "Pregled vaših izbora",
|
||||
"prefs.print_face_highlight": "Isticanje površine za štampu",
|
||||
"prefs.print_face_highlight_tip": "Koliko se jako površina za štampu tijela boji i obrubljuje u prikazu. 0 % ne prikazuje isticanje.",
|
||||
"prefs.profile_active": "Aktivni profil",
|
||||
"prefs.profile_applied": "Profil je primijenjen.",
|
||||
"prefs.profile_changed": "Postavke su promijenjene otkako je profil sačuvan. Sačuvaj ih upisuje u profil.",
|
||||
"prefs.profile_deleted": "Profil je izbrisan.",
|
||||
"prefs.profile_export": "Izvezi…",
|
||||
"prefs.profile_exported": "Profil je izvezen.",
|
||||
"prefs.profile_imported": "Profil je uvezen. Izaberite ga pod Aktivni profil da se prebacite na njega.",
|
||||
"prefs.profile_name_hint": "Naziv profila, npr. Laptop",
|
||||
"prefs.profile_rename": "Preimenuj",
|
||||
"prefs.profile_renamed": "Profil je preimenovan.",
|
||||
"prefs.profile_save": "Sačuvaj",
|
||||
"prefs.profile_saved": "Trenutne postavke su sačuvane u profil.",
|
||||
"prefs.profile_skipped": "Profil je primijenjen; postavke koje su zadržale trenutnu vrijednost: {n} (pogledajte zapisnik).",
|
||||
"prefs.profiles": "Profili postavki",
|
||||
"prefs.profiles_note": "Prečice, traka s alatima, navigacija, radijalni meni i postavke interfejsa čuvaju se zajedno pod jednim nazivom i mijenjaju odjednom.",
|
||||
"prefs.project_options_dialog": "Dijalog opcija za projektovanje",
|
||||
"prefs.project_options_dialog_note": "Uključeno: prije projektovanja izaberite Geometrija ili Tijela i Veza projekcije. Isključeno: zadržava se prethodni tok rada s direktnim izborom.",
|
||||
"prefs.quality_default": "podrazumijevano",
|
||||
"prefs.quality_fastest": "najbrže",
|
||||
"prefs.quality_slowest": "najsporije",
|
||||
"prefs.quality_smoother": "glađe",
|
||||
"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_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",
|
||||
"prefs.saving": "Automatsko čuvanje i oporavak",
|
||||
"prefs.scale_note": "Također Ctrl + / Ctrl − u prozoru. Dugmad su 28 px pri 100%.",
|
||||
@@ -1877,18 +1997,29 @@
|
||||
"prefs.search_empty": "Nema odgovarajućih postavki",
|
||||
"prefs.select": "Izbor",
|
||||
"prefs.select_one_item": "Izaberi jednu stavku",
|
||||
"prefs.select_other_hidden": "Izaberi skrivenu geometriju pomoću Izaberi drugo",
|
||||
"prefs.select_other_hidden_help": "Uključeno: obuhvata skrivena tijela, skice i zaklonjene ivice u blizini pokazivača. Isključeno: koristi se izvorni popis vidljive geometrije ispod pokazivača.",
|
||||
"prefs.selecting": "Biranje",
|
||||
"prefs.shift": "Shift",
|
||||
"prefs.shift_add_ctrl_toggle": "Shift samo dodaje; Ctrl / Cmd uključuje i isključuje",
|
||||
"prefs.shift_add_note": "Uključeno: Shift dodaje, Ctrl / Cmd uključuje i isključuje. Isključeno (podrazumijevano): oba uključuju i isključuju.",
|
||||
"prefs.shortcuts": "Prečice",
|
||||
"prefs.single_surface_analysis": "Koristi samo jednu analizu površi",
|
||||
"prefs.single_surface_analysis_help": "Kada je isključeno, sjenčenje površi i Kontinuitet mogu ostati uključeni zajedno. Uključivanje zadržava posljednju pokrenutu ili promijenjenu analizu.",
|
||||
"prefs.sketch_align_points": "Poravnaj s drugim tačkama skice tokom crtanja",
|
||||
"prefs.sketch_align_points_note": "Tokom crtanja tačka se istovremeno poravnava vodoravno s jednom tačkom skice i okomito s drugom, uz tačkastu pomoćnu liniju, a klik zadržava oba poravnanja. Isključeno: samo s prethodnom tačkom, po jedna osa.",
|
||||
"prefs.sketch_conflict_colour": "Geometrija u sukobu u boji sukoba",
|
||||
"prefs.sketch_conflict_colour_note": "Dok su ograničenja skice u sukobu, krive i tačke koje drži ograničenje koje ne uspijeva crtaju se također u boji sukoba, ne samo njegov simbol ili kota. Isključeno: zadržavaju uobičajene boje.",
|
||||
"prefs.sketch_construction_lines": "Prošireni alati za pomoćne prave",
|
||||
"prefs.sketch_exit_view": "Kada se skica završi",
|
||||
"prefs.sketch_exit_view.keep": "Zadrži trenutni prikaz",
|
||||
"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_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",
|
||||
"prefs.sketch_recent_snap_note": "Tokom crtanja pokazivač se privlači tamo gdje se sijeku pomoćne linije dvaju posljednjih centara krugova ili središnjih tačaka pravih ivica. Isključeno: te pomoćne linije služe samo za prikaz i pokazivač se ne pomjera.",
|
||||
"prefs.sketch_selection": "Skica i izbor",
|
||||
"prefs.sketch_selection_heading": "Skica i izbor",
|
||||
"prefs.sketch_selection_note": "Kako se skice privlače i kako klikovi biraju.",
|
||||
@@ -1933,7 +2064,14 @@
|
||||
"prefs.toast_place": "Obavještenja",
|
||||
"prefs.toggle_item": "Dodaj stavku ili je ponovo ukloni",
|
||||
"prefs.toolbar": "Traka s alatima",
|
||||
"prefs.toolbar_tabs": "Kartice trake s alatima",
|
||||
"prefs.top": "Gore",
|
||||
"prefs.trackpad_zoom_modifier": "Zumiranje skrolovanjem s dva prsta uz držanje tipke",
|
||||
"prefs.trackpad_zoom_modifier.alt": "⌥ / Alt",
|
||||
"prefs.trackpad_zoom_modifier.cmd": "⌘",
|
||||
"prefs.trackpad_zoom_modifier.ctrl": "Ctrl",
|
||||
"prefs.trackpad_zoom_modifier.help": "Držite samo ovu tipku da zumirate prema pokazivaču vertikalnim skrolovanjem. Isključeno zadržava postojeće ponašanje skrolovanja.",
|
||||
"prefs.trackpad_zoom_modifier.off": "Isključeno",
|
||||
"prefs.translucent": "Prozirno",
|
||||
"prefs.under_browser": "Ispod stabla modela",
|
||||
"prefs.under_cursor": "Ispod pokazivača",
|
||||
@@ -1943,13 +2081,24 @@
|
||||
"prefs.view_confirm_corner": "U redu i Otkaži u uglu prikaza",
|
||||
"prefs.view_confirm_corner_note": "Dok je otvoren dijalog ili skica, kvačica i krstić gore desno u prikazu rade isto što i dugmad U redu i Otkaži u dijalogu, odnosno završavaju i otkazuju skicu. Navigator prikaza pomjera se ispod njih.",
|
||||
"prefs.viewport": "Prozor prikaza",
|
||||
"prefs.viewport_analysis_status": "Prikaži status analize u prozoru prikaza",
|
||||
"prefs.viewport_analysis_status_help": "Prikazuje aktivne analize iznad prozora prikaza, otvara njihove legende iz svake stavke, a tipka Esc isključuje najnoviju analizu. Isključite da biste vratili legende u uglovima i prethodno ponašanje tipke Esc.",
|
||||
"prefs.viewport_background": "Pozadina",
|
||||
"prefs.viewport_background_colour": "Boja",
|
||||
"prefs.viewport_background_theme": "Gradijent teme",
|
||||
"prefs.wheel": "Točkić",
|
||||
"prefs.wheel_trackpad": "Točkić i trackpad",
|
||||
"prefs.workspace": "Radni prostor",
|
||||
"prefs.zoom_stop_at_model": "Zaustavi zumiranje na modelu",
|
||||
"prefs.zoom_stop_at_model_tip": "3D miš i aplikacija uređaja zumiraju prema površini u sredini prikaza; zumiranje povlačenjem drži tačku na kojoj je povlačenje počelo. Isključeno: zumiranje može proći kroz model. Točkić i štipanje zasebno koriste Zumiranje prema pokazivaču",
|
||||
"prefs.zoom_toward_cursor": "Zumiranje prema pokazivaču",
|
||||
"properties.title": "Svojstva: {name}",
|
||||
"recovery.days_ago": "prije {count} dana",
|
||||
"recovery.detail": "{file} · operacija: {features} · automatski sačuvano {when}",
|
||||
"recovery.discard_all": "Odbaci sve",
|
||||
"recovery.discard_all_detail": "Ovo se ne može poništiti.",
|
||||
"recovery.discard_all_title": "Odbaciti automatska čuvanja ({count})?",
|
||||
"recovery.discard_all_title_one": "Odbaciti 1 automatsko čuvanje?",
|
||||
"recovery.hours_ago": "prije {count} h",
|
||||
"recovery.just_now": "upravo sada",
|
||||
"recovery.min_ago": "prije {count} min",
|
||||
@@ -2034,6 +2183,7 @@
|
||||
"render.appearance_editor.weight": "Težina",
|
||||
"render.appearance_physics_separate": "Vizuelni izgled ne dodjeljuje gustinu, toplotne podatke niti druga inženjerska svojstva.",
|
||||
"render.appearances": "Izgledi",
|
||||
"render.assign_help": "Kliknite za primjenu na izbor; prevucite za pregled i ispustite ili kliknite kada ništa nije izabrano da bojite.",
|
||||
"render.availability_native": "Izvorni sastav; pregledajte prije primjene",
|
||||
"render.availability_projection": "Podržana projekcija; pogledajte opseg i izostavke",
|
||||
"render.availability_reference": "Referentni podaci; nije automatska dodjela",
|
||||
@@ -2072,6 +2222,7 @@
|
||||
"render.bloom_threshold": "Prag linearnih svijetlih dijelova",
|
||||
"render.browse_capabilities": "Pregledaj kompletan katalog mogućnosti",
|
||||
"render.browse_environments": "Pregledaj izvorna HDR okruženja",
|
||||
"render.browse_looks": "Pregledaj…",
|
||||
"render.browse_native_scenes": "Pregledaj izvorne studije",
|
||||
"render.browse_physical": "Pregledaj fizičke reference",
|
||||
"render.browse_quality": "Pregledaj specifikacije kvaliteta",
|
||||
@@ -2095,6 +2246,82 @@
|
||||
"render.check_opaque": "Prozirnost: samo neprozirne površi; alfa pozadine je podržana.",
|
||||
"render.check_sorted_alpha": "Prozirnost: aproksimacija sortirane alfe.",
|
||||
"render.check_transmission": "Prozirnost: fizička transmisija.",
|
||||
"render.children.absolute_floor": "Apsolutni donji prag",
|
||||
"render.children.adaptive": "Adaptivno",
|
||||
"render.children.add_look_reason": "Dodajte izgled da biste uredili njegovo uključivanje i jačinu.",
|
||||
"render.children.all_shots": "Svi kadrovi",
|
||||
"render.children.bloom": "Sjaj",
|
||||
"render.children.check": "Provjera",
|
||||
"render.children.check_now": "Provjeri sada",
|
||||
"render.children.chromatic": "Hromatski pomak",
|
||||
"render.children.compact": "Kompaktno",
|
||||
"render.children.contrast": "Kontrast",
|
||||
"render.children.copy_image": "Kopiraj",
|
||||
"render.children.copy_job": "Kopiraj JSON posla",
|
||||
"render.children.copy_request": "Kopiraj JSON zahtjeva",
|
||||
"render.children.coverage_error": "Greška pokrivenosti",
|
||||
"render.children.cpu": "CPU praćenje zraka",
|
||||
"render.children.denoise": "Smanji šum na završenoj slici",
|
||||
"render.children.depth": "Dubina putanje",
|
||||
"render.children.distortion": "Izobličenje",
|
||||
"render.children.edge_samples": "Uzorci ivica",
|
||||
"render.children.effects_banner": "Ovi efekti zahtijevaju GPU praćenje putanja. Otvaranje ovoga nikad ne mijenja metodu.",
|
||||
"render.children.enable_adaptive": "Omogućite nezavisne probne uzorke da biste uredili adaptivna ograničenja.",
|
||||
"render.children.enable_bloom": "Omogućite sjaj da biste uredili njegovu jačinu, prag i poluprečnik.",
|
||||
"render.children.finishing": "Završna obrada",
|
||||
"render.children.flare": "Odsjaj",
|
||||
"render.children.fresnel": "Fresnelovi slojevi",
|
||||
"render.children.ghosts": "Duhovi prstenova",
|
||||
"render.children.glass": "Transport kroz staklo",
|
||||
"render.children.go_to": "Idi na {editor}",
|
||||
"render.children.grain": "Zrno",
|
||||
"render.children.halo": "Oreol",
|
||||
"render.children.hdr": "HDR",
|
||||
"render.children.keep_master": "Zadrži sirovi linearni master",
|
||||
"render.children.last_result": "Posljednje: {number} · {width} × {height} · {phase}",
|
||||
"render.children.lens_effects": "Efekti objektiva",
|
||||
"render.children.look_summary": "{tone} · izgledi: {count}",
|
||||
"render.children.looks": "Kreativni izgledi",
|
||||
"render.children.memory": "Memorija",
|
||||
"render.children.method": "Metoda",
|
||||
"render.children.minimum": "Minimum",
|
||||
"render.children.needs_cpu": "Potrebno je CPU praćenje zraka. Otvaranje ovoga nikad ne mijenja metodu.",
|
||||
"render.children.nested": "Potvrđeni ugniježđeni dielektrici",
|
||||
"render.children.not_checked": "Nije provjereno",
|
||||
"render.children.path": "Praćenje putanja",
|
||||
"render.children.pilots": "Probni uzorci",
|
||||
"render.children.preview": "Podaci pregleda",
|
||||
"render.children.progressive_reason": "Izaberite Progresivno da biste uredili broj uzoraka i sjeme.",
|
||||
"render.children.radius": "Poluprečnik",
|
||||
"render.children.reflect_depth": "Dubina refleksije",
|
||||
"render.children.reflect_samples": "Uzorci refleksije",
|
||||
"render.children.regrade": "Ponovna gradacija",
|
||||
"render.children.relative_error": "Relativna greška",
|
||||
"render.children.render": "Renderovanje",
|
||||
"render.children.render_shot": "Renderuj kadar",
|
||||
"render.children.renderer": "Renderer",
|
||||
"render.children.renderers": "Rendereri",
|
||||
"render.children.request": "Ovaj zahtjev",
|
||||
"render.children.reset_finishing": "Resetuj završnu obradu",
|
||||
"render.children.review_quality": "Pregledaj specifikaciju kvaliteta…",
|
||||
"render.children.rgb_gain": "RGB pojačanje",
|
||||
"render.children.samples": "Uzorci",
|
||||
"render.children.sampling": "Uzorkovanje",
|
||||
"render.children.saturation": "Zasićenost",
|
||||
"render.children.scene_summary": "Tijela: {bodies} · trouglovi: {triangles}",
|
||||
"render.children.seed": "Sjeme",
|
||||
"render.children.sharpening": "Izoštravanje",
|
||||
"render.children.single_image": "Jedna slika",
|
||||
"render.children.soft_focus": "Meki fokus",
|
||||
"render.children.streak": "Pruga",
|
||||
"render.children.strength": "Jačina",
|
||||
"render.children.studio": "Studio",
|
||||
"render.children.threshold": "Prag",
|
||||
"render.children.tone": "Ton",
|
||||
"render.children.under_hood": "Ispod haube",
|
||||
"render.children.vignette": "Vinjeta",
|
||||
"render.children.workstation": "Radna stanica",
|
||||
"render.choose_look": "Prvo izaberite izgled u Izgledima",
|
||||
"render.chromatic_offset": "Hromatski pomak ivica",
|
||||
"render.clear_stage": "Ukloni sačuvanu scenu",
|
||||
"render.copy_request": "Kopiraj JSON",
|
||||
@@ -2346,8 +2573,33 @@
|
||||
"render.flare_strength": "Jačina odsjaja svijetlih dijelova",
|
||||
"render.flare_threshold": "Linearni prag odsjaja scene",
|
||||
"render.fold_browser": "Skupi stablo modela",
|
||||
"render.frame_aspect": "Omjer",
|
||||
"render.frame_centre_mark": "Oznaka centra",
|
||||
"render.frame_copy_limit": "Nema slobodnog naziva za duplikat kadra",
|
||||
"render.frame_copy_name": "{name} kopija {number}",
|
||||
"render.frame_custom": "Prilagođeno",
|
||||
"render.frame_distance_unit": " × veličina",
|
||||
"render.frame_duplicate": "Dupliciraj",
|
||||
"render.frame_exposure": "Ekspozicija",
|
||||
"render.frame_fov": "Vidno polje",
|
||||
"render.frame_guides": "Pomoćne linije",
|
||||
"render.frame_invalid_aspect": "Neispravan predložak omjera",
|
||||
"render.frame_lens": "FOV {fov}° · ≈ {mm} mm",
|
||||
"render.frame_name": "Naziv",
|
||||
"render.frame_output_size": "Veličina izlaza",
|
||||
"render.frame_photo": "3:2 · Fotografija",
|
||||
"render.frame_portrait": "4:5 · Portret",
|
||||
"render.frame_presets": "Predlošci kadra",
|
||||
"render.frame_presets_pending": "Stiže s uređivačem kadrova",
|
||||
"render.frame_projection": "Projekcija",
|
||||
"render.frame_render_shot": "Renderuj ovaj kadar",
|
||||
"render.frame_rule_thirds": "Pravilo trećina",
|
||||
"render.frame_safe_area": "Sigurna zona",
|
||||
"render.frame_shot_title": "Kadar · {name}",
|
||||
"render.frame_square": "1:1 · Kvadrat",
|
||||
"render.frame_tall": "9:16 · Vertikalno",
|
||||
"render.frame_thirds": "Trećine",
|
||||
"render.frame_wide": "16:9 · Široki ekran",
|
||||
"render.gpu_depth": "Maksimalna dubina putanje",
|
||||
"render.gpu_options_help": "Uzorci, dubina, sjeme i ekspozicija čuvaju se s ovom postavkom. Čuva se samo aktivna metoda slike.",
|
||||
"render.gpu_path_failed": "GPU praćenje putanja nije dostupno",
|
||||
@@ -2375,6 +2627,7 @@
|
||||
"render.heading_results": "REZULTATI",
|
||||
"render.heading_shots": "KADROVI",
|
||||
"render.hide_shots_label": "Sakrij traku kadrova",
|
||||
"render.hover_look": "{object} · {look} · kliknite za izbor",
|
||||
"render.image_advanced": "Napredne GPU kontrole",
|
||||
"render.image_cancelled": "Renderovanje je otkazano",
|
||||
"render.image_cancelling": "Otkazivanje",
|
||||
@@ -2521,17 +2774,95 @@
|
||||
"render.library_uniform_ack": "Koristi jednoličnu aproksimaciju bez prostornih ili naprednih efekata",
|
||||
"render.library_usable": "Izvršne projekcije",
|
||||
"render.library_users": "Korisnici",
|
||||
"render.light_aim": "Usmjeri na",
|
||||
"render.light_anchor_pending": "Promjena koordinatnog sistema svjetla još nije podržana.",
|
||||
"render.light_azimuth": "Azimut",
|
||||
"render.light_backend_limit": "Izaberite CPU praćenje zraka ili izričit GPU studio da biste uređivali svjetla. Svjetla starog GPU studija ostaju samo za čitanje.",
|
||||
"render.light_behaviour": "PONAŠANJE",
|
||||
"render.light_default_fill": "Popunjavajuće",
|
||||
"render.light_default_key": "Glavno",
|
||||
"render.light_default_number": "Svjetlo {number}",
|
||||
"render.light_default_rim": "Rubno",
|
||||
"render.light_delete": "Izbriši",
|
||||
"render.light_distance": "Udaljenost",
|
||||
"render.light_duplicate": "Dupliciraj",
|
||||
"render.light_elevation": "Elevacija",
|
||||
"render.light_gpu_punctual": "Tačkasta svjetla i reflektori dostupni su uz CPU praćenje zraka.",
|
||||
"render.light_hard_shadow": "Ovo svjetlo ima oštre sjene; mekoća emitera nije dostupna.",
|
||||
"render.light_intensity": "Intenzitet",
|
||||
"render.light_missing": "Ovo svjetlo više ne postoji. Izaberite svjetlo u stablu modela.",
|
||||
"render.light_place_highlight": "Postavi prema odsjaju",
|
||||
"render.light_place_pending": "Postavljanje prema odsjaju zahtijeva izbor svjetla u prozoru prikaza i još nije podržano.",
|
||||
"render.light_position": "POLOŽAJ",
|
||||
"render.light_preview_help": "Prikaz uživo je bez sjena; površinska svjetla koriste aproksimaciju uzorkom iz centra. Renderovanje koristi potpuni model svjetla.",
|
||||
"render.light_product_centre": "Centar proizvoda",
|
||||
"render.light_size": "Veličina",
|
||||
"render.light_softness": "Mekoća",
|
||||
"render.light_temperature": "Temperatura boje",
|
||||
"render.light_temperature_help": "Približna nijansa crnog tijela; čuva se kao linearni RGB.",
|
||||
"render.light_type": "Vrsta",
|
||||
"render.light_type_area": "Površinsko",
|
||||
"render.light_type_point": "Tačkasto",
|
||||
"render.light_type_spot": "Reflektor",
|
||||
"render.light_type_sun": "Sunce",
|
||||
"render.light_view_only": "Samo ovo svjetlo i isključivanje svjetla utiču na ovaj prikaz i njegova renderovanja; nikad se ne čuvaju u dizajnu.",
|
||||
"render.light_world_origin": "Ishodište svijeta",
|
||||
"render.lighting_aim_point": "Tačka usmjeravanja",
|
||||
"render.lighting_apply": "Primijeni postavku",
|
||||
"render.lighting_area_samples": "Uzorci površinskog svjetla",
|
||||
"render.lighting_budget": "Budžet",
|
||||
"render.lighting_budget_help": "Brojevi su samo za čitanje. Dodajte, duplicirajte ili izbrišite svjetla da ih promijenite; metoda određuje ograničenja.",
|
||||
"render.lighting_colour": "Boja",
|
||||
"render.lighting_consent": "Zamijeni usmjerena svjetla ovom postavkom",
|
||||
"render.lighting_custom": "Prilagođena svjetla",
|
||||
"render.lighting_custom_help": "Izbor opcije Prilagođeno pretvara postavku u svjetla koja se mogu uređivati, svako u svom redu u stablu.",
|
||||
"render.lighting_direction": "Smjer X Y Z",
|
||||
"render.lighting_direction_help": "Ili upišite smjer prema svjetlu (X Y Z).",
|
||||
"render.lighting_distance_unit": " × veličina",
|
||||
"render.lighting_edge_u": "Vektor stranice U",
|
||||
"render.lighting_edge_v": "Vektor stranice V",
|
||||
"render.lighting_falloff": "Slabljenje",
|
||||
"render.lighting_falloff_help": "Intenzitet se navodi na referentnoj udaljenosti.",
|
||||
"render.lighting_height": "Visina V",
|
||||
"render.lighting_minimum": "Minimum",
|
||||
"render.lighting_mute": "Isključi ovo svjetlo",
|
||||
"render.lighting_point": "Tačka",
|
||||
"render.lighting_preset": "Postavka studija",
|
||||
"render.lighting_preset_cpu": "Usmjerene postavke studija zahtijevaju CPU praćenje zraka. GPU osvjetljenje koristi izričita sunčeva i površinska svjetla.",
|
||||
"render.lighting_preset_row": "Postavka",
|
||||
"render.lighting_radiance": "Radijansa",
|
||||
"render.lighting_reference": "Referenca",
|
||||
"render.lighting_review_help": "Zamjenjuje popis usmjerenih svjetala. Zadržava tačkasta i površinska svjetla, okruženje, sjene, kameru i sve ostale postavke kadra.",
|
||||
"render.lighting_review_title": "Pregledaj postavku osvjetljenja",
|
||||
"render.lighting_samples_cpu": "Uzorci površinskog svjetla primjenjuju se na CPU praćenje zraka.",
|
||||
"render.lighting_samples_need_area": "Dodajte CPU površinsko svjetlo da biste postavili uzorke površinskog svjetla.",
|
||||
"render.lighting_samples_reason": "Uzorci površinskog svjetla moraju biti 1, 4, 16 ili 64.",
|
||||
"render.lighting_shadow_cpu": "Prekidač sjena zahtijeva CPU praćenje zraka. GPU praćenje putanja uvijek baca sjene.",
|
||||
"render.lighting_shadows": "Baci sjene",
|
||||
"render.lighting_shadows_section": "Sjene i uzorkovanje",
|
||||
"render.lighting_shape": "Oblik",
|
||||
"render.lighting_solo": "Samo ovo svjetlo",
|
||||
"render.lighting_source": "Izvor",
|
||||
"render.lighting_source_section": "Izvor svjetla",
|
||||
"render.lighting_spot_preserved": "Ovo tačkasto svjetlo zadržava sačuvani konus reflektora dok mu se ne promijeni vrsta.",
|
||||
"render.lighting_sun_distance": "Smjer sunca ne zavisi od udaljenosti i tačke usmjeravanja.",
|
||||
"render.lighting_unsupported": "nepodržano",
|
||||
"render.lighting_used": "{used} od {limit}",
|
||||
"render.lighting_width": "Širina U",
|
||||
"render.lighting_world_position": "Svjetski X Y Z",
|
||||
"render.lights_fixed_to": "Svjetla vezana za",
|
||||
"render.linear_help": "Zadržava 32-bitni float RGBA master prije uklanjanja šuma, ekspozicije, mapiranja tonova, filtera i pozadine. Linearni sRGB/D65; pomnožen pokrivenošću. Dodatna memorija se izričito odobrava. Bez kompresije i dodatnih prolaza.",
|
||||
"render.linear_retain": "Zadrži sirovi linearni EXR",
|
||||
"render.linear_unavailable": "Izabrani završeni kadar nema zadržani EXR. Omogućite zadržavanje prije renderovanja; stariji PNG ne može vratiti sirovu radijansu.",
|
||||
"render.look_add": "Dodaj izgled boje",
|
||||
"render.look_applied": "Primijenjeno: {name}",
|
||||
"render.look_bleach": "Po uzoru na bleach bypass",
|
||||
"render.look_cool": "Hladno",
|
||||
"render.look_cyanotype": "Po uzoru na cijanotipiju",
|
||||
"render.look_faded": "Izblijedjeli otisak",
|
||||
"render.look_material": "Materijal",
|
||||
"render.look_mono": "Jednobojno",
|
||||
"render.look_pasted": "Izgled je zalijepljen",
|
||||
"render.look_sepia": "Sepija",
|
||||
"render.look_strength": "Jačina izgleda",
|
||||
"render.look_teal_orange": "Tirkizno / narandžasto",
|
||||
@@ -2607,9 +2938,11 @@
|
||||
"render.output_advanced": "Napredno",
|
||||
"render.output_backend_ready": "Spremno",
|
||||
"render.output_check": "Provjeri",
|
||||
"render.output_check_failed": "Provjera · greška",
|
||||
"render.output_check_help": "Pokrenite provjeru da pregledate ovaj zahtjev bez pokretanja renderovanja.",
|
||||
"render.output_check_summary": "Provjera · Napomene: {count}",
|
||||
"render.output_copy": "Kopiraj",
|
||||
"render.output_copy_report": "Kopiraj izvještaj",
|
||||
"render.output_destination": "Odredište",
|
||||
"render.output_destination_help": "Odredišnu datoteku birate pri izvozu završene slike.",
|
||||
"render.output_dimensions": "Veličina",
|
||||
@@ -2619,6 +2952,7 @@
|
||||
"render.output_fit": "Uklopi",
|
||||
"render.output_format": "Format",
|
||||
"render.output_format_status": "PNG je tačna završena slika za prikaz. Neobavezni GPU EXR zadržava sirovu linearnu radijansu scene u formatu FLOAT32 prije filtera prikaza. Izvoz nikad ne zamjenjuje keš pregleda ni drugi posao.",
|
||||
"render.output_grading": "Gradacija",
|
||||
"render.output_image": "Slika",
|
||||
"render.output_largest": "Najviše {width} × {height}",
|
||||
"render.output_lock_ratio": "Zaključaj omjer stranica",
|
||||
@@ -2628,6 +2962,8 @@
|
||||
"render.output_quality_waiting": "čeka se specifikacija kvaliteta",
|
||||
"render.output_ratio_refused": "Povezana dimenzija premašuje ograničenje veličine ove metode. Prethodna veličina se zadržava; otključajte omjer ili izaberite manju vrijednost.",
|
||||
"render.output_ready": "Spremno za renderovanje",
|
||||
"render.output_run_check": "Pokreni provjeru",
|
||||
"render.output_size_limit": "{method}: do {dimension} px po strani i {pixels} piksela. Najveća veličina koja staje: {width} × {height}.",
|
||||
"render.output_size_ok": "Veličina u redu · {width} × {height} px",
|
||||
"render.output_swap_orientation": "Zamijeni širinu i visinu",
|
||||
"render.output_tools_inspector": "Veličinu i format uredite u inspektoru Izlaz.",
|
||||
@@ -2644,6 +2980,7 @@
|
||||
"render.page_output": "Izlaz",
|
||||
"render.page_scene": "Scena",
|
||||
"render.paint_label": "Oboji",
|
||||
"render.painting_with": "Bojenje izgledom: {name}",
|
||||
"render.passive_stage": "Podloga i pasivna scena",
|
||||
"render.passive_stage_help": "Konačni pasivni paneli pripadaju samo renderovanim slikama. Ne dodaju CAD tijela ni masu. Postavljanje je fiksno nakon primjene; presijecanje granica proizvoda se odbija. CPU ne daje difuzno odbijeno osvjetljenje.",
|
||||
"render.perspective": "Perspektivna kamera",
|
||||
@@ -2665,6 +3002,7 @@
|
||||
"render.pick_help": "Izaberite Tijelo, Lice ili Komponenta. Kliknite prazan prostor da očistite izbor; Shift ili Ctrl dodaje ili uključuje/isključuje. Isticanja se pojavljuju samo u živom prikazu proizvoda.",
|
||||
"render.pick_label": "Izaberi",
|
||||
"render.pick_up_label": "Preuzmi",
|
||||
"render.pick_up_prompt": "Kliknite objekt da preuzmete njegov izgled",
|
||||
"render.pick_waiting": "Izbor proizvoda još nije podržan.",
|
||||
"render.post_contrast": "Kontrast",
|
||||
"render.post_gain_b": "Pojačanje plave",
|
||||
@@ -2704,13 +3042,17 @@
|
||||
"render.radial_distortion": "Radijalno izobličenje",
|
||||
"render.radial_fit": "Uklopi proizvod",
|
||||
"render.radial_save": "Sačuvaj kadar",
|
||||
"render.recent_looks": "Nedavni izgledi",
|
||||
"render.regrade_action": "Ponovna gradacija sirovog mastera (bez uklanjanja šuma)",
|
||||
"render.regrade_help": "Pravi zaseban PNG iz izabranog završenog sirovog mastera, koristeći ove kontrole prikaza. Ne prati zrake, ne mijenja zabilježenu veličinu, kameru ni svjetla i ne prepisuje EXR. Uklanjanje šuma se ne primjenjuje ponovo: sirovi masteri ne čuvaju njegove vodilje. Izvedena slika se i dalje može izvesti istim kontrolama posla.",
|
||||
"render.regrade_label": "Ponovna gradacija",
|
||||
"render.reload_settings": "Ponovo učitaj sačuvane postavke",
|
||||
"render.remove_override": "Ukloni nadjačavanje",
|
||||
"render.replace_all": "Zamijeni sve",
|
||||
"render.replace_ground": "Potvrdi zamjenu postojeće scene",
|
||||
"render.replace_label": "Zamijeni",
|
||||
"render.replace_saved_surface": "Zamijeni postojeću sačuvanu vezu za ovaj cilj",
|
||||
"render.replace_with": "Zamijeni sa",
|
||||
"render.restore_camera": "Vrati sačuvanu kameru",
|
||||
"render.result_actual": "100%",
|
||||
"render.result_actual_help": "Jedan piksel slike po pikselu ekrana.",
|
||||
@@ -2762,14 +3104,28 @@
|
||||
"render.saved_surface_remove": "Ukloni sačuvanu vezu",
|
||||
"render.saved_surfaces": "Sačuvane veze površina",
|
||||
"render.saved_surfaces_help": "Fiksirane ugrađene mape prate izabrani primjerak u lokalnim milimetrima. Zamijenjen ili nedostajući cilj je greška, nikad automatska ponovna dodjela.",
|
||||
"render.scene_all_shots": "Svi kadrovi ({count})",
|
||||
"render.scene_apply": "Primijeni pregledani studio",
|
||||
"render.scene_batch_reason": "Za Svi kadrovi potreban je red čekanja za renderovanje sačuvanih kadrova, koji još nije dostupan.",
|
||||
"render.scene_check_now": "Provjeri sada",
|
||||
"render.scene_check_passed": "Provjera uspješna · napomene: {notes}",
|
||||
"render.scene_check_stale": "Provjera je zastarjela · provjerite ponovo",
|
||||
"render.scene_check_unchecked": "Još nije provjereno · 0 napomena",
|
||||
"render.scene_consent": "Potvrdi zamjenu osvjetljenja, okruženja, ekspozicije i scene",
|
||||
"render.scene_light_summary": "{environment} · svjetla: {lights}",
|
||||
"render.scene_look_summary": "Izgledi: {looks} · nadjačavanja: {overrides}",
|
||||
"render.scene_open_check": "Otvori provjeru",
|
||||
"render.scene_preserved_fields": "Kamera, uzorkovanje, dimenzije, optičke veze, CAD geometrija i inženjerski materijali se čuvaju.",
|
||||
"render.scene_preserves_design": "Sastavite osvjetljenje i pasivnu scenu za vidljivi predmet. Geometrija, inženjerski materijali i kamera ostaju odvojeni.",
|
||||
"render.scene_ready": "Spremno za renderovanje",
|
||||
"render.scene_reminder": "Scena čuva dizajn. Geometrija, inženjerska svojstva i CAD nikad se ne mijenjaju iz Renderovanja.",
|
||||
"render.scene_reminders": "Podsjetnici",
|
||||
"render.scene_renderer_ready": "{renderer}: spremno",
|
||||
"render.scene_replaces": "Zamjenjuje",
|
||||
"render.scene_review": "Razriješi i pregledaj",
|
||||
"render.scene_review_help": "Pregledajte kompletnu izvornu kompoziciju prije primjene. Nevezane napredne SCENE specifikacije se ne zamjenjuju.",
|
||||
"render.scene_scope_details": "Opseg kompozicije",
|
||||
"render.scene_stages": "Faze",
|
||||
"render.search_appearances": "Pretraži {count} izgleda",
|
||||
"render.setup_legacy_environment": "Ova starija postavka koristi naslijeđenu rotaciju ili intenzitet okruženja. Te vrijednosti se zadržavaju; izričito ih pretvorite prije čuvanja punog posla slike.",
|
||||
"render.setup_name": "Naziv postavke",
|
||||
@@ -2782,7 +3138,22 @@
|
||||
"render.shell_tab_camera": "KAMERA RENDERA",
|
||||
"render.shell_tab_lighting": "OSVJETLJENJE",
|
||||
"render.shell_tab_scene": "SCENA RENDERA",
|
||||
"render.shot_aperture": "Otvor blende",
|
||||
"render.shot_distance": "Udaljenost",
|
||||
"render.shot_dof": "Dubinska oštrina",
|
||||
"render.shot_duplicate": "Dupliciraj sačuvani kadar",
|
||||
"render.shot_edit_output": "Uredi u Izlazu",
|
||||
"render.shot_exposure_reason": "Ekspozicija zahtijeva CPU praćenje zraka ili GPU praćenje putanja.",
|
||||
"render.shot_exposure_section": "Ekspozicija",
|
||||
"render.shot_focus_distance": "Udaljenost fokusa",
|
||||
"render.shot_fov_reason": "Vidno polje zahtijeva perspektivnu projekciju.",
|
||||
"render.shot_frame_subject": "Uokviri predmet",
|
||||
"render.shot_invalid_camera": "Vrijednosti kamere moraju biti konačne i unutar raspona.",
|
||||
"render.shot_lens": "Objektiv",
|
||||
"render.shot_lens_enable_reason": "Omogućite dubinsku oštrinu da biste uredili otvor blende i udaljenost fokusa.",
|
||||
"render.shot_lens_gpu_reason": "Dubinska oštrina zahtijeva GPU praćenje putanja.",
|
||||
"render.shot_lens_perspective_reason": "Dubinska oštrina zahtijeva perspektivnu projekciju.",
|
||||
"render.shot_look_selection": "Usmjeri na izbor",
|
||||
"render.shot_manager": "Sačuvani kadrovi",
|
||||
"render.shot_new_name": "Naziv novog kadra",
|
||||
"render.shot_remove": "Ukloni sačuvani kadar",
|
||||
@@ -2790,6 +3161,9 @@
|
||||
"render.shot_remove_title": "Ukloniti sačuvani kadar?",
|
||||
"render.shot_rename": "Preimenuj sačuvani kadar",
|
||||
"render.shot_saved_only": "Prvo sačuvajte trenutni nacrt. Dupliciranje i preimenovanje čuvaju tačne sačuvane opcije i reference na resurse.",
|
||||
"render.shot_selection_reason": "Izaberite tijelo ili lice prema kojem treba usmjeriti kameru.",
|
||||
"render.shot_size": "Veličina",
|
||||
"render.shot_target": "Cilj",
|
||||
"render.shots_camera_changed": "Kamera je promijenjena od čuvanja ovog kadra",
|
||||
"render.shots_name_help": "Unesite jedinstven naziv za trenutni kadar.",
|
||||
"render.shots_no_result": "Još nema renderovanja",
|
||||
@@ -3052,6 +3426,7 @@
|
||||
"select an axis": "izaberite osu",
|
||||
"select an axis, a straight or circular edge, or a cylinder, cone or torus face first": "prvo izaberite osu, pravu ili kružnu ivicu, ili lice cilindra, konusa ili torusa",
|
||||
"select bodies first": "prvo izaberite tijela",
|
||||
"select components first": "prvo izaberite komponente",
|
||||
"select corner points, or the two lines of one corner": "izaberite tačke uglova ili dvije linije jednog ugla",
|
||||
"select one corner point, or the two lines of one corner": "izaberite jednu tačku ugla ili dvije linije jednog ugla",
|
||||
"select sheet edges first": "prvo izaberite ivice lima",
|
||||
@@ -3070,6 +3445,7 @@
|
||||
"settings.double_click_components": "Dvaput kliknite komponentu da je aktivirate ili da uredite povezanu komponentu na mjestu",
|
||||
"settings.frame_rate_details_note": "Dodaje graf vremena posljednjih kadrova s brojem iscrtanih trouglova, histogram vremena kadrova i šta je GPU nacrtao. Kliknite broj kadrova u sekundi u prikazu da ih prikažete ili sakrijete.",
|
||||
"settings.frame_rate_note": "Prikazuje broj kadrova u sekundi u donjem desnom uglu prikaza.",
|
||||
"settings.insert_lists_open_designs": "Umetni dizajn prvo navodi dizajne otvorene na drugim karticama",
|
||||
"settings.measure_axis_distances": "Prikaži udaljenosti X/Y/Z",
|
||||
"settings.measure_axis_distances_help": "Prikazuj komponente udaljenosti po svjetskim osama uz ukupnu udaljenost dok je Mjerenje otvoreno.",
|
||||
"settings.msaa_applies_on_restart": "Uglađivanje ivica primjenjuje se pri sljedećem pokretanju SolidCrafta",
|
||||
@@ -3094,6 +3470,10 @@
|
||||
"shortcut.unpin": "Otkači",
|
||||
"sketch is missing": "skica nedostaje",
|
||||
"sketch.degrees_of_freedom": "Stepeni slobode: {count}",
|
||||
"sketch.dimension_added_driven": "Skica već određuje tu kotu, pa je dodana kao zavisna (referentna) kota",
|
||||
"sketch.dimension_applied": "Kota je primijenjena",
|
||||
"sketch.dimension_applied_defined": "Kota je primijenjena; parametar `{name}` je definisan",
|
||||
"sketch.dimension_applied_renamed": "Kota je primijenjena; parametar je nazvan `{name}`",
|
||||
"sketch.dof_left": "Preostali stepeni slobode: {count}",
|
||||
"sketch.dof_left_one": "Preostali stepeni slobode: {count}",
|
||||
"sketch.draw_curves": "Crtaj kao {linetype}",
|
||||
@@ -3128,6 +3508,8 @@
|
||||
"sketch_text.use_font_file": "Koristi datoteku fonta {path}",
|
||||
"solid": "puno",
|
||||
"status.editing": "Uređivanje: {name}",
|
||||
"status.navigation": "Mapiranje miša: {mapping}",
|
||||
"status.navigation_tip": "Kako miš pomjera prikaz. Kliknite da izaberete drugo mapiranje",
|
||||
"status.under_constrained": "Nedovoljno ograničeno (stepeni slobode: {count})",
|
||||
"status.under_constrained_one": "Nedovoljno ograničeno ({count} stepen slobode)",
|
||||
"status.units_tip": "Jedinice dizajna. Kliknite da ih promijenite",
|
||||
@@ -3161,8 +3543,10 @@
|
||||
"timeline.delete_folders_tip": "Zadrži njihove operacije i ugniježđene foldere u vremenskoj traci",
|
||||
"timeline.delete_folders_with_features_tip": "Izbriši foldere sa svim operacijama u njima i njihove ugniježđene foldere",
|
||||
"timeline.delete_many": "Izbriši izabrane operacije: {count}",
|
||||
"timeline.edited_rolled_back": "Operacija {name} je promijenjena, ali je iza oznake vraćanja, pa se ne gradi: pomjerite oznaku iza nje da vidite rezultat",
|
||||
"timeline.feature_error": "Greška: {message}",
|
||||
"timeline.feature_hint": "Dvaput kliknite za uređivanje · povucite za promjenu redoslijeda · desni klik za više",
|
||||
"timeline.feature_rolled_back": "Vraćeno: iza oznake vraćanja, pa se ne gradi",
|
||||
"timeline.feature_warning": "Upozorenje: {message}",
|
||||
"timeline.group": "Novi folder operacija",
|
||||
"timeline.move_many": "Premjesti stavke vremenske trake",
|
||||
@@ -3244,6 +3628,10 @@
|
||||
"ui.3d_mouse_offer": "Kalibracija 3D miša ispravlja zamijenjene ili obrnute ose i podešava njegovu brzinu i osjećaj.",
|
||||
"ui.3d_mouse_offer_never": "Ne prikazuj više",
|
||||
"ui.3d_mouse_offer_swap": "Prebaci na Hidraw (direktni drajver)",
|
||||
"ui.3d_mouse_orbit": "Orbitiranje",
|
||||
"ui.3d_mouse_orbit_classic": "Klasično",
|
||||
"ui.3d_mouse_orbit_tip": "Prema modelu: 3D miš okreće prikaz oko dijela modela koji je u sredini prikaza kada okretanje počne, označenog blijedom kuglom. Klasično: oko centra prikaza",
|
||||
"ui.3d_mouse_orbit_traced": "Prema modelu",
|
||||
"ui.3d_mouse_reading": "Očitanje",
|
||||
"ui.3d_mouse_reset_axes": "Resetuj dodjelu osa",
|
||||
"ui.3d_mouse_reset_view": "Resetuj prikaz",
|
||||
@@ -3280,6 +3668,8 @@
|
||||
"ui.3d_mouse_step_twist_counterclockwise": "Zakrenite kapicu suprotno od kazaljke na satu, držite je, a zatim pustite.",
|
||||
"ui.3d_mouse_switch_cap": "Kalibriši kao Povratak u centar",
|
||||
"ui.3d_mouse_switch_trackball": "Kalibriši kao trackball",
|
||||
"ui.3d_mouse_use_direct": "Koristi direktni drajver",
|
||||
"ui.3d_mouse_use_direct_tip": "Čitaj 3D miš direktno (hidraw) umjesto preko spacenavd.",
|
||||
"ui.3d_mouse_view_axis": "Osa prikaza",
|
||||
"ui.3d_mouse_view_off": "Isključeno",
|
||||
"ui.3d_mouse_view_off_tip": "Profil drajvera 3D miša isključuje ovu osu, pa njeno kretanje ne pomjera prikaz. Izbor kretanja druge ose ovdje ih zamjenjuje, a to kretanje prestaje pomjerati prikaz.",
|
||||
@@ -3596,6 +3986,7 @@
|
||||
"ui.circle": "Krug",
|
||||
"ui.circular_pattern": "Kružni niz",
|
||||
"ui.clear": "Očisti",
|
||||
"ui.clear_print_face": "Ukloni površinu za štampu",
|
||||
"ui.clear_recent": "Očisti nedavno",
|
||||
"ui.clear_the_selection": "Poništi izbor",
|
||||
"ui.click_selects_whole_bodies": "Klik bira cijela tijela",
|
||||
@@ -3630,6 +4021,15 @@
|
||||
"ui.constant": "Konstantno",
|
||||
"ui.constraints_conflict": "Ograničenja su u sukobu",
|
||||
"ui.construction": "Pomoćna",
|
||||
"ui.construction.angle_hint": "Pomoćna prava: kliknite tačku, pa upišite ugao ili izaberite smjer",
|
||||
"ui.construction.bisector_hint": "Simetrala: izaberite dvije prave linije koje se sijeku, pa željeno rješenje",
|
||||
"ui.construction.common_hint": "Zajednička tangenta: izaberite dva kruga ili luka, pa željeno rješenje",
|
||||
"ui.construction.contact_hint": "Tangenta: izaberite krug/luk ili otvoreni splajn/koniku, pa tačku dodira (krive slobodnog oblika: krajnje tačke)",
|
||||
"ui.construction.external_hint": "Tangenta: izaberite krug ili luk, vanjsku tačku, pa željeno rješenje",
|
||||
"ui.construction.free_hint": "Pomoćna prava: kliknite dvije tačke; desnim klikom počnite ispočetka",
|
||||
"ui.construction.point_hint": "Pomoćna prava: kliknite tačku kroz koju prolazi",
|
||||
"ui.construction.reference_hint": "Pomoćna prava: izaberite pravu liniju, pa tačku; desnim klikom promijenite referencu",
|
||||
"ui.construction.ribbon_label": "Pomoćne",
|
||||
"ui.context_menu": "Kontekstni meni",
|
||||
"ui.continuity_g0": "G0 pregib",
|
||||
"ui.continuity_g1": "G1 tangentno",
|
||||
@@ -3670,6 +4070,7 @@
|
||||
"ui.design_properties.part_number": "Broj dijela",
|
||||
"ui.design_properties.project": "Projekat",
|
||||
"ui.design_properties.revision": "Revizija",
|
||||
"ui.design_saved": "Sačuvano: {path}",
|
||||
"ui.diameter": "Prečnik",
|
||||
"ui.dimension": "Kota",
|
||||
"ui.direct_interaction": "Direktna interakcija",
|
||||
@@ -3720,6 +4121,14 @@
|
||||
"ui.end_scale": "Krajnje skaliranje",
|
||||
"ui.entities": "Elementi",
|
||||
"ui.equal": "Jednakost",
|
||||
"ui.equation_end": "Kraj",
|
||||
"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_parametric": "Parametarski",
|
||||
"ui.equation_x_of_t": "x(t)",
|
||||
"ui.equation_y_of_t": "y(t)",
|
||||
"ui.equation_y_of_x": "y(x)",
|
||||
"ui.equivalent_radius": "Ekvivalentni poluprečnik",
|
||||
"ui.expand": "Proširi",
|
||||
"ui.exportDxf": "Izvezi DXF…",
|
||||
@@ -3785,6 +4194,7 @@
|
||||
"ui.heading_design": "DIZAJN",
|
||||
"ui.heading_draft": "NAGIB",
|
||||
"ui.heading_edit": "UREDI",
|
||||
"ui.heading_equation_curve": "KRIVA IZ JEDNAČINE",
|
||||
"ui.heading_extrude": "EKSTRUDIRAJ",
|
||||
"ui.heading_fillet": "ZAOBLJENJE",
|
||||
"ui.heading_finish_editing": "ZAVRŠI UREĐIVANJE",
|
||||
@@ -3876,6 +4286,10 @@
|
||||
"ui.import_ellipsis": "Uvezi…",
|
||||
"ui.infinite": "Beskonačno",
|
||||
"ui.insert": "Umetni",
|
||||
"ui.insert_design.browse": "Pregledaj…",
|
||||
"ui.insert_design.open_designs": "Otvoreni dizajni",
|
||||
"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.intersect": "Presjek",
|
||||
"ui.isolate": "Izoluj",
|
||||
@@ -4019,12 +4433,29 @@
|
||||
"ui.polygon": "Poligon",
|
||||
"ui.preferences": "Postavke",
|
||||
"ui.press_a_key": "pritisnite tipku…",
|
||||
"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",
|
||||
"ui.profile_plane": "Ravan profila",
|
||||
"ui.profiles": "Profili",
|
||||
"ui.project": "Projektuj",
|
||||
"ui.project_face_edges_sketch": "Projektuj ivice lica u nove skice",
|
||||
"ui.properties": "Svojstva",
|
||||
"ui.properties.copy_matrix": "Kopiraj matricu",
|
||||
"ui.properties.first_moments": "Statički momenti",
|
||||
"ui.properties.inertia": "Moment inercije",
|
||||
"ui.properties.inertia_note": "Oko centra mase, u svjetskim osama (g·{unit}²)",
|
||||
"ui.properties.inertia_unavailable": "Nije dostupno: mreža jednog tijela nije zatvorena.",
|
||||
"ui.properties.inertia_x": "Ixx, Ixy, Ixz",
|
||||
"ui.properties.inertia_y": "Iyx, Iyy, Iyz",
|
||||
"ui.properties.inertia_z": "Izx, Izy, Izz",
|
||||
"ui.properties.measuring": "Mjerenje…",
|
||||
"ui.properties.measuring_failed": "Mjerenje je zaustavljeno prije završetka.",
|
||||
"ui.properties.principal_axis": "Glavna osa {number}",
|
||||
"ui.properties.principal_moments": "Glavni momenti",
|
||||
"ui.pull_direction": "Smjer vađenja iz kalupa",
|
||||
"ui.quality": "Kvalitet",
|
||||
"ui.quantity": "Broj",
|
||||
@@ -4070,6 +4501,7 @@
|
||||
"ui.revolve": "Rotiraj",
|
||||
"ui.ribbon_exit": "Izađi",
|
||||
"ui.ribbon_labels": "Prikaži tekst ispod ikona na traci",
|
||||
"ui.ribbon_menus_skip_pinned": "Izostavi prikačene komande iz padajućih menija",
|
||||
"ui.ribbon_pin_footer": "Prikačene komande prikazuju se na traci. Zadržite pokazivač iznad reda da ga prikačite ili otkačite.",
|
||||
"ui.ribbon_workspace": "Radni prostor",
|
||||
"ui.right": "Desno",
|
||||
@@ -4087,6 +4519,7 @@
|
||||
"ui.select": "Izaberi",
|
||||
"ui.selectOther": "Izaberi drugo…",
|
||||
"ui.selectTangentChain": "Izaberi tangentni lanac",
|
||||
"ui.select_all": "Izaberi sve",
|
||||
"ui.selection": "Izbor",
|
||||
"ui.selection_modifiers": "Modifikatori izbora",
|
||||
"ui.selection_shift_add_only": "Shift+klik dodaje bez uklanjanja",
|
||||
@@ -4123,7 +4556,11 @@
|
||||
"ui.sketch": "Skica",
|
||||
"ui.sketch_dimension": "Kota skice",
|
||||
"ui.sketch_grid": "Mreža skice",
|
||||
"ui.sketch_intersect_geometry": "Geometrija",
|
||||
"ui.sketch_intersect_projection_link": "Veza projekcije",
|
||||
"ui.sketch_intersect_selection_filter": "Filter izbora",
|
||||
"ui.sketch_modifiers": "Modifikatori skice",
|
||||
"ui.sketch_project_geometry": "Geometrija",
|
||||
"ui.sketch_scale": "Skaliranje skice",
|
||||
"ui.sketches": "Skice",
|
||||
"ui.slice": "Presijeci",
|
||||
@@ -4170,6 +4607,11 @@
|
||||
"ui.symmetric": "Simetrično",
|
||||
"ui.symmetry": "Simetrija",
|
||||
"ui.system": "Sistem",
|
||||
"ui.tab_assemble": "Sklopi",
|
||||
"ui.tab_manage": "Upravljanje",
|
||||
"ui.tab_sheet_metal": "Lim",
|
||||
"ui.tab_view": "Prikaz",
|
||||
"ui.tab_weldments": "Zavarene konstrukcije",
|
||||
"ui.tangent": "Tangentno",
|
||||
"ui.taper_angle": "Ugao suženja",
|
||||
"ui.tapped": "S navojem",
|
||||
@@ -4185,6 +4627,7 @@
|
||||
"ui.tip_angle": "Ugao vrha",
|
||||
"ui.to_object": "Do objekta",
|
||||
"ui.tool_direction": "Smjer alata",
|
||||
"ui.toolbar_tabs_hidden": "Sve kartice trake s alatima su skrivene. Kliknite + ili kliknite desnim dugmetom ovdje da prikažete jednu.",
|
||||
"ui.top": "Odozgo",
|
||||
"ui.top_diameter": "Gornji prečnik",
|
||||
"ui.top_radius": "Gornji poluprečnik",
|
||||
@@ -4222,6 +4665,8 @@
|
||||
"ui.view_centre": "Centar prikaza",
|
||||
"ui.viewport_status_clear": "Isključi: {name}",
|
||||
"ui.viewport_status_clear_hint": "Isključi: {name} · Esc",
|
||||
"ui.viewport_status_edges": "Ivice: {count}",
|
||||
"ui.viewport_status_from": "iz smjera {direction}",
|
||||
"ui.viewport_status_key": "Prikaži legendu: {name}",
|
||||
"ui.viewport_status_minimum_radius": "Minimalni poluprečnik",
|
||||
"ui.viewport_status_pairs": "Parovi: {count}",
|
||||
@@ -4231,6 +4676,7 @@
|
||||
"ui.visual_shaded_with_edges": "Osjenčeno s ivicama",
|
||||
"ui.visual_style": "Vizuelni stil",
|
||||
"ui.visual_wireframe": "Žičani model",
|
||||
"ui.wheel_zoom_speed": "Brzina zumiranja točkićem miša",
|
||||
"ui.width": "Širina",
|
||||
"ui.window_system": "Sistem prozora",
|
||||
"ui.window_system_auto": "Automatski",
|
||||
@@ -4261,7 +4707,9 @@
|
||||
"update.button": "Ažuriraj",
|
||||
"update.button_version": "Ažuriraj na {version}",
|
||||
"update.check_automatically": "Automatski provjeravaj ažuriranja",
|
||||
"update.check_for_updates": "Provjeri ažuriranja",
|
||||
"update.check_now": "Provjeri sada",
|
||||
"update.check_unavailable": "Provjera ažuriranja nije dostupna za ovu instalaciju.",
|
||||
"update.checking": "Provjera…",
|
||||
"update.copy": "Kopiraj",
|
||||
"update.dmg_opened": "Slika diska je otvorena i provjerena: prevucite SolidCraft u Applications, zamijenite staru kopiju, a zatim ponovo pokrenite SolidCraft.",
|
||||
@@ -4284,6 +4732,9 @@
|
||||
"update.prefs_managed": "Ovo je SolidCraft {version}, kojim upravlja {manager}. Provjera pita GitHub za najnovije izdanje i ne šalje ništa drugo; ažurirajte SolidCraft u {manager}.",
|
||||
"update.prefs_note": "Ovo je SolidCraft {version} ({kind}). Provjera pita GitHub za najnovije izdanje i ne šalje ništa drugo.",
|
||||
"update.prefs_web": "Ponovo učitajte stranicu da dobijete najnoviju web aplikaciju. Ako mreža nije dostupna ili se ažuriranje ne može preuzeti na vrijeme, otvara se sačuvana verzija; preuzimanje u toku nastavlja se za sljedeće ponovno učitavanje.",
|
||||
"update.refresh_checking": "Provjera najnovijeg izdanja…",
|
||||
"update.refresh_fallback": "Provjera novijeg izdanja nije uspjela; ažurira se na verziju {version}, pronađenu ranije.",
|
||||
"update.refresh_selected": "Ažuriranje na {version}.",
|
||||
"update.release_page": "Otvori stranicu izdanja",
|
||||
"update.restart": "Ponovo pokreni sada",
|
||||
"update.restart_later": "Kasnije",
|
||||
|
||||
@@ -60,8 +60,10 @@
|
||||
"BLEND SURFACE": "ÜBERGANGSFLÄCHE",
|
||||
"BOUNDARY SURFACE": "BERANDUNGSFLÄCHE",
|
||||
"BROWSER": "BROWSER",
|
||||
"Back to the original order": "Zurück zur ursprünglichen Reihenfolge",
|
||||
"Ball": "Kugelgelenk",
|
||||
"Bar": "Flachstab",
|
||||
"Base Center": "Mittelpunkt der Basis",
|
||||
"Base Height": "Sockelhöhe",
|
||||
"Bend": "Biegung",
|
||||
"Bend Edges": "Biegekanten",
|
||||
@@ -205,6 +207,7 @@
|
||||
"Edges": "Kanten",
|
||||
"Edit": "Bearbeiten",
|
||||
"Edit Profile Sketch": "Profile-Skizze bearbeiten",
|
||||
"Edit shortcuts": "Tastenkürzel bearbeiten",
|
||||
"Enable rule": "Regel aktivieren",
|
||||
"Enabled": "Aktiviert",
|
||||
"End Radius": "Endradius",
|
||||
@@ -259,6 +262,8 @@
|
||||
"Flanges": "Flansche",
|
||||
"Flat Pattern": "Flachmuster",
|
||||
"Flat size": "Abwicklungsgröße",
|
||||
"Flat size X": "Abwicklungsmaß X",
|
||||
"Flat size Y": "Abwicklungsmaß Y",
|
||||
"Follow camera (Look At sketches)": "Kamera folgen (Skizzen ansehen)",
|
||||
"Follow the camera (Look At sketches)": "Der Kamera folgen (Skizzen ansehen)",
|
||||
"Font": "Schriftart",
|
||||
@@ -290,6 +295,7 @@
|
||||
"Horiz./Vert.": "Horiz./Vert.",
|
||||
"I-Beam": "I-Träger",
|
||||
"IMAGES": "BILDER",
|
||||
"If you are offline or GitHub does not open, use Copy report and keep the text to submit later.": "Wenn Sie offline sind oder GitHub nicht geöffnet werden kann, verwenden Sie „Bericht kopieren“ und bewahren Sie den Text auf, um ihn später einzureichen.",
|
||||
"Import material into design": "Material ins Design importieren",
|
||||
"Import…": "Importieren…",
|
||||
"In This Design": "In diesem Design",
|
||||
@@ -400,13 +406,16 @@
|
||||
"Of": "Freiheitsgrad",
|
||||
"On your computer": "Auf diesem Computer",
|
||||
"Opacity": "Deckkraft",
|
||||
"Open GitHub": "GitHub öffnen",
|
||||
"Open GitHub draft": "GitHub-Entwurf öffnen",
|
||||
"Open GitHub issue form": "GitHub-Issueformular öffnen",
|
||||
"Open bodies (no volume)": "Offene Körper (ohne Volumen)",
|
||||
"Open library...": "Bibliothek öffnen...",
|
||||
"Open local libraries in the desktop app. You can export this design's library here.": "Öffnen Sie lokale Bibliotheken in der Desktop-App. Die Bibliothek dieses Designs können Sie hier exportieren.",
|
||||
"Open surface — no volume": "Offene Oberfläche – kein Volumen",
|
||||
"Open-source parametric 3D CAD in Rust. Licensed MIT OR Apache-2.0.": "Quelloffenes parametrisches 3D-CAD in Rust. Lizenziert unter MIT OR Apache-2.0.",
|
||||
"Opens your browser and copies the report text for you to paste into GitHub.": "Öffnet den Browser und kopiert den Berichtstext, den Sie anschließend auf GitHub einfügen können.",
|
||||
"Opens your browser and copies the report text. Review and submit your report on GitHub.": "Öffnet den Browser und kopiert den Berichtstext. Prüfen Sie den Bericht und reichen Sie ihn anschließend auf GitHub ein.",
|
||||
"Opens your browser. Review and submit your report on GitHub.": "Öffnet den Browser. Prüfen Sie den Bericht und reichen Sie ihn auf GitHub ein.",
|
||||
"Operator": "Operator",
|
||||
"Orbit (right drag)": "Drehen (mit rechts ziehen)",
|
||||
@@ -461,6 +470,7 @@
|
||||
"Radius": "Radius",
|
||||
"Radius Fit": "Radiusanpassung",
|
||||
"Ratio": "Verhältnis",
|
||||
"Read-only list: shortcuts can't be changed here.": "Schreibgeschützte Liste: Tastenkürzel können hier nicht geändert werden.",
|
||||
"Reattach selected face": "Ausgewählte Fläche neu anbinden",
|
||||
"Recover": "Wiederherstellen",
|
||||
"Recover unsaved design?": "Nicht gespeichertes Design wiederherstellen?",
|
||||
@@ -487,6 +497,7 @@
|
||||
"Reopen the designs that were open when SolidCraft was last closed": "Die Designs erneut öffnen, die beim letzten Beenden von SolidCraft geöffnet waren",
|
||||
"Replace existing library file": "Vorhandene Bibliotheksdatei ersetzen",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "Fehler melden — Screenshot prüfen und einen GitHub-Entwurf öffnen",
|
||||
"Report copied. Keep this text to submit on GitHub when you are online.": "Bericht kopiert. Bewahren Sie diesen Text auf, um ihn auf GitHub einzureichen, sobald Sie online sind.",
|
||||
"Report text copied. Paste it into GitHub and submit your report there.": "Berichtstext kopiert. Fügen Sie ihn auf GitHub ein und reichen Sie den Bericht dort ein.",
|
||||
"Reports are public. A GitHub account is required.": "Berichte sind öffentlich. Ein GitHub-Konto ist erforderlich.",
|
||||
"Reset Toolbar": "Symbolleiste zurücksetzen",
|
||||
@@ -583,7 +594,10 @@
|
||||
"Slider": "Schubgelenk",
|
||||
"Slot width": "Langlochbreite",
|
||||
"Snap to face and edge centres": "An Flächen- und Kantenmittelpunkten einrasten",
|
||||
"SolidCraft cannot submit reports directly. Submit on GitHub; no token is needed in the app.": "SolidCraft kann Berichte nicht direkt einreichen. Reichen Sie Ihren Bericht auf GitHub ein. In der App wird dafür kein Token benötigt.",
|
||||
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft wurde beendet, bevor diese Designs gespeichert wurden. Ihr letzter Autosave kann geöffnet werden.",
|
||||
"Sort A to Z": "Von A bis Z sortieren",
|
||||
"Sort Z to A": "Von Z bis A sortieren",
|
||||
"Spacing 2 must be a non-zero number": "Abstand 2 muss eine Zahl ungleich null sein",
|
||||
"Spacing must be a non-zero number": "Der Abstand muss eine Zahl ungleich null sein",
|
||||
"Specialise": "Spezialisieren",
|
||||
@@ -643,6 +657,7 @@
|
||||
"Third point": "Dritter Punkt",
|
||||
"This child is shared. Deleting it removes it from every copy of its parent.": "Dieses untergeordnete Element ist geteilt. Löschen entfernt es aus jeder Kopie des übergeordneten Elements.",
|
||||
"This child is shared. Position changes apply to every copy of its parent.": "Dieses untergeordnete Element ist geteilt. Positionsänderungen gelten für jede Kopie des übergeordneten Elements.",
|
||||
"This component is inside a subassembly placed more than once: aligning it moves it in every placement.": "Diese Komponente gehört zu einer Unterbaugruppe, die mehrfach platziert wurde. Beim Ausrichten wird sie an allen Platzierungen verschoben.",
|
||||
"This face cannot provide a durable attachment. Choose another face or explicitly use a fixed plane.": "Diese Fläche kann keine dauerhafte Anbindung bieten. Wählen Sie eine andere Fläche oder verwenden Sie ausdrücklich eine feste Ebene.",
|
||||
"This image is not attached to GitHub yet. Copy or save it, then attach it on GitHub.": "Dieses Bild wurde noch nicht an GitHub angehängt. Kopieren oder speichern Sie es und fügen Sie es anschließend Ihrem GitHub-Bericht hinzu.",
|
||||
"Thread depth": "Gewindetiefe",
|
||||
@@ -763,6 +778,8 @@
|
||||
"at the face": "an der Fläche",
|
||||
"bom.group_children": "Unterteile in der Zeile ihres Teils",
|
||||
"browser.body_name": "Körper {number}",
|
||||
"browser.calibrate_click_a": "Klicken Sie im Bild auf die Position von Punkt A oder geben Sie dessen Koordinaten ein.",
|
||||
"browser.calibrate_click_b": "Klicken Sie im Bild auf die Position von Punkt B oder geben Sie dessen Koordinaten ein.",
|
||||
"browser.calibrate_title": "Kalibrieren: {name}",
|
||||
"browser.capture_many": "{count} Komponenten wurden verschoben, ihre Positionen sind aber nicht erfasst.",
|
||||
"browser.capture_note": "„Position erfassen“ behält die neue Position bei; „Position zurücksetzen“ stellt die vorherige wieder her.",
|
||||
@@ -787,6 +804,8 @@
|
||||
"camera.shot_pitch": "Nickwinkel",
|
||||
"camera.shot_roll": "Rollwinkel",
|
||||
"camera.shot_yaw": "Gierwinkel",
|
||||
"canvas.decal_place_hint": "Klicken Sie auf eine Fläche, um das Dekal zu platzieren.",
|
||||
"canvas.place_hint": "Klicken Sie auf eine Ebene oder Fläche, um die Zeichenfläche zu platzieren.",
|
||||
"choose a profile": "ein Profil wählen",
|
||||
"choose the cam's and the follower's joints": "die Gelenke von Nocken und Stößel wählen",
|
||||
"choose the joints to couple": "die zu koppelnden Gelenke wählen",
|
||||
@@ -803,6 +822,7 @@
|
||||
"cmd.appearance_pick_up": "Aussehen aufnehmen",
|
||||
"cmd.appearance_replace": "Erscheinungsbild ersetzen",
|
||||
"cmd.assembly_bom": "Stückliste",
|
||||
"cmd.body.print_face": "Auflagefläche",
|
||||
"cmd.body_rename": "Körper umbenennen",
|
||||
"cmd.browser_collapse_all": "Alles einklappen",
|
||||
"cmd.browser_expand_all": "Alles ausklappen",
|
||||
@@ -839,6 +859,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_paste_from_design": "Aus einem anderen Design einfügen",
|
||||
"cmd.component_paste_new": "Neu einfügen",
|
||||
"cmd.component_properties": "Physikalische Eigenschaften der Komponente",
|
||||
"cmd.component_relink": "Quelle neu verknüpfen",
|
||||
@@ -975,6 +996,7 @@
|
||||
"cmd.motion_trace": "Bewegungsbahn",
|
||||
"cmd.motion_trace_edit": "Bewegungsbahn bearbeiten",
|
||||
"cmd.motion_trace_keep": "Bewegungsbahn behalten",
|
||||
"cmd.occurrence_align": "Instanz ausrichten",
|
||||
"cmd.occurrence_copy": "Einfügen (Instanz)",
|
||||
"cmd.occurrence_drag": "Instanz ziehen",
|
||||
"cmd.occurrence_ground": "Festsetzen",
|
||||
@@ -1085,6 +1107,16 @@
|
||||
"cmd.sheet_refold": "Wieder falten",
|
||||
"cmd.sheet_rules": "Blechregeln auflisten",
|
||||
"cmd.sheet_unfold": "Entfalten",
|
||||
"cmd.sketch.construction_line": "Konstruktionslinie",
|
||||
"cmd.sketch.construction_line.angle": "Konstruktionslinie unter Winkel",
|
||||
"cmd.sketch.construction_line.bisector": "Winkelhalbierende",
|
||||
"cmd.sketch.construction_line.horizontal": "Horizontale Konstruktionslinie",
|
||||
"cmd.sketch.construction_line.parallel": "Parallele Konstruktionslinie",
|
||||
"cmd.sketch.construction_line.perpendicular": "Senkrechte Konstruktionslinie",
|
||||
"cmd.sketch.construction_line.tangent_at": "Tangente an Kurvenpunkt",
|
||||
"cmd.sketch.construction_line.tangent_external": "Tangente durch äußeren Punkt",
|
||||
"cmd.sketch.construction_line.tangent_two": "Gemeinsame Tangente",
|
||||
"cmd.sketch.construction_line.vertical": "Vertikale Konstruktionslinie",
|
||||
"cmd.sketch_arc3d": "3D-Bogen",
|
||||
"cmd.sketch_arc_center_point": "Mittelpunkt-Kreisbogen",
|
||||
"cmd.sketch_arc_tangent": "Tangentialbogen",
|
||||
@@ -1300,6 +1332,7 @@
|
||||
"dialog.dimension_insert_hint": "Doppelklicken Sie auf eine andere steuernde Bemaßung, um ihren Namen einzufügen",
|
||||
"dialog.dimension_name": "Bemaßung: {name}",
|
||||
"dialog.edit_title": "{title} bearbeiten",
|
||||
"dialog.no_shown_body": "Kein sichtbarer Körper zum Ändern vorhanden. Blenden Sie einen Körper ein oder wählen Sie „Neuer Körper“.",
|
||||
"dialog.select": "↖ Auswählen",
|
||||
"dialog.selected_count": "↖ {count} ausgewählt",
|
||||
"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.",
|
||||
@@ -1356,31 +1389,59 @@
|
||||
"from the profile": "aus dem Profil",
|
||||
"from the rule": "aus der Regel",
|
||||
"help.apache": "Apache-Lizenz 2.0",
|
||||
"help.appearance.assign": "Weist den ausgewählten Körpern, Flächen oder Komponenten eine Farbe oder ein Erscheinungsbild zu, ohne ihre Masse zu verändern. So können Sie Bauteile optisch unterscheiden oder das Aussehen einer Oberfläche vorab prüfen.",
|
||||
"help.attributions": "Quellenangaben (ATTRIBUTION.md)",
|
||||
"help.build": "Version {version} · Commit {commit} · erstellt am {date}",
|
||||
"help.component.finish_edit": "Speichert das an Ort und Stelle bearbeitete Teil, kehrt zu seiner Baugruppe zurück und aktualisiert die Baugruppe daraus. Verwenden Sie es, wenn Ihre Änderungen am verknüpften Teil fertig sind.",
|
||||
"help.connect_note": "Starten Sie jeden Assistenten neu, damit er den SolidCraft-Server lädt. Jede Datei wurde vor der Änderung als .bak gesichert.",
|
||||
"help.connect_title": "KI-Assistenten verbinden",
|
||||
"help.face.delete": "Entfernt die ausgewählten Flächen. Die angrenzenden Flächen schließen dabei die entstandene Lücke, oder sie bleibt als offene Fläche bestehen. So können Sie beispielsweise eine Rundung, eine Bohrung oder einen störenden Ansatz entfernen.",
|
||||
"help.font_details": "Die Oberfläche verwendet die integrierten Schriftarten von egui (Ubuntu Light und Hack unter der Ubuntu Font Licence und der MIT/Bitstream-Vera-Lizenz; Noto Emoji und die Emoji-Symbolschrift unter OFL und MIT). Skizzentext verwendet Liberation Sans (SIL OFL 1.1). Das optionale Schriftpaket craft-fonts von The Crafting Apps (BIZ UDPGothic, Shippori Mincho, BIZ UDMincho, Noto Sans CJK SC, Noto Sans Arabic; SIL OFL 1.1) ist nicht in SolidCraft-Builds enthalten.",
|
||||
"help.fonts": "Schriftarten",
|
||||
"help.licences": "Lizenzen",
|
||||
"help.material.assign": "Legt fest, aus welchem Werkstoff die ausgewählten Körper bestehen, beispielsweise Stahl, Aluminium oder ABS. Dadurch entspricht die berechnete Masse derjenigen des realen Bauteils.",
|
||||
"help.mit": "MIT-Lizenz",
|
||||
"help.notice": "NOTICE",
|
||||
"help.occurrence.move": "Bewegt eine Baugruppeninstanz ausschließlich entlang der Freiheitsgrade, die ihre Gelenke zulassen. Wählen Sie eine Fläche des Bauteils aus und ziehen Sie anschließend einen der verfügbaren Pfeile oder Bögen.",
|
||||
"help.parameters.change": "Öffnet die Tabelle mit benannten Parametern und Ausdrücken, die die Abmessungen des Designs steuern. Ändern Sie einen Wert an einer zentralen Stelle, werden alle Features, die diesen Wert verwenden, entsprechend aktualisiert.",
|
||||
"help.pointcloud.insert": "Fügt einen PLY-, XYZ- oder PTS-Scan als Punktwolke neben dem Modell ein. Verwenden Sie ihn als Referenz beim Modellieren anhand von Messdaten.",
|
||||
"help.sketch.construction_line": "Erstellt eine unendliche Konstruktionslinie durch zwei Punkte, ohne Skizzenprofile zu teilen.",
|
||||
"help.sketch.construction_line.angle": "Erstellt eine unendliche Konstruktionslinie in einem Winkel zur Skizzen-X-Achse. Punkt wählen und Winkel eingeben oder Richtung wählen.",
|
||||
"help.sketch.construction_line.bisector": "Zwei sich schneidende gerade Skizzenlinien wählen, dann eine der beiden Winkelhalbierenden. Das Ergebnis folgt den Ausgangslinien.",
|
||||
"help.sketch.construction_line.horizontal": "Erstellt eine unendliche horizontale Konstruktionslinie durch einen Punkt. Die Horizontalbedingung bleibt bei späteren Änderungen erhalten.",
|
||||
"help.sketch.construction_line.parallel": "Gerade Skizzenlinie wählen, dann eine parallele unendliche Konstruktionslinie platzieren. Rechtsklick wählt eine andere Referenz.",
|
||||
"help.sketch.construction_line.perpendicular": "Gerade Skizzenlinie wählen, dann eine senkrechte unendliche Konstruktionslinie platzieren. Rechtsklick wählt eine andere Referenz.",
|
||||
"help.sketch.construction_line.tangent_at": "Erstellt eine Tangente an einem Punkt eines Kreises oder Kreisbogens oder an einem Endpunkt eines offenen Splines oder Kegelschnitts. Kurve wählen, dann Berührpunkt.",
|
||||
"help.sketch.construction_line.tangent_external": "Kreis oder Kreisbogen wählen, dann einen äußeren Punkt und eine Tangentenlösung. Die Vorschau zeigt nur Berührpunkte auf dem gezeichneten Bogen.",
|
||||
"help.sketch.construction_line.tangent_two": "Zwei Kreise oder Kreisbögen wählen, dann eine gemeinsame Tangente in der Vorschau. Die Berührpunkte müssen auf den gezeichneten Kurven liegen.",
|
||||
"help.sketch.construction_line.vertical": "Erstellt eine unendliche vertikale Konstruktionslinie durch einen Punkt. Die Vertikalbedingung bleibt bei späteren Änderungen erhalten.",
|
||||
"help.sketch.equation_curve": "Zeichnet eine Kurve aus Formeln über einen Bereich: x(t) und y(t) oder y = f(x). Die Formeln können Parameter verwenden, und die Kurve folgt ihnen: für eine Sinuswelle, eine Spirale oder die Evolvente eines Zahnrads.",
|
||||
"help.sketch.inspect": "Öffnet einen Bericht zur offenen Skizze: wie viele Freiheitsgrade übrig sind und ob sie vollständig bestimmt ist. Damit sehen Sie, was sich noch bewegt, bevor Sie die Skizze abschließen.",
|
||||
"help.solid.align": "Verschiebt die ausgewählten Körper so, dass ein Punkt, eine Fläche oder eine Ebene eines Körpers mit einem entsprechenden Punkt, einer Fläche oder Ebene an anderer Stelle zusammenfällt. So können Sie Bauteile aneinander ausrichten, ohne Abstände messen zu müssen.",
|
||||
"help.solid.chamfer": "Schneidet die gewählten Kanten mit einer ebenen Fase im eingestellten Abstand ab. Damit brechen Sie scharfe Kanten oder erleichtern das Einführen eines Stifts oder einer Schraube.",
|
||||
"help.solid.combine": "Kombiniert Körper: vereinigt sie, zieht sie von einem Zielkörper ab oder behält nur das gemeinsame Volumen; die Werkzeugkörper können erhalten bleiben. Damit fügen Sie Teile zusammen, schneiden eine Form mit einer anderen aus oder finden, wo sich zwei Körper überschneiden.",
|
||||
"help.solid.draft": "Schrägt die ausgewählten Flächen um den eingestellten Winkel gegenüber einer neutralen Ebene ab. Diese Funktion wird bei Guss- und Spritzgussteilen verwendet, damit sich die Bauteile leichter aus der Form lösen lassen.",
|
||||
"help.solid.extrude": "Zieht ein Skizzenprofil über eine Strecke in die dritte Dimension und fügt dabei Material hinzu oder entfernt es. Damit machen Sie aus einer flachen Kontur einen Block, eine Erhebung oder eine Tasche.",
|
||||
"help.solid.fillet": "Rundet die gewählten Kanten mit einem glatten Kreisübergang im eingestellten Radius ab. Damit entschärfen Sie scharfe Kanten für Festigkeit, Griffigkeit oder Optik.",
|
||||
"help.solid.hole": "Bohrt an einem gewählten Punkt auf einer Fläche ein rundes Loch im eingestellten Durchmesser. Für Durchgangsbohrungen für Schrauben, Stifte und andere einfache runde Öffnungen.",
|
||||
"help.solid.loft": "Erzeugt einen glatten Übergangskörper zwischen zwei oder mehr Skizzenprofilen oder von einem Profil zu einem Punkt. Für Übergänge, Trichter, Griffe und alle Formen, deren Querschnitt sich über die Länge ändert.",
|
||||
"help.solid.mirror": "Erzeugt eine gespiegelte Kopie der gewählten Körper oder Features an einer Ebene und kann sie mit dem Original vereinigen. Für symmetrische Teile: Sie modellieren eine Hälfte und erhalten beide.",
|
||||
"help.solid.move": "Verschiebt oder dreht die ausgewählten Körper oder deren Kopien um den eingestellten Abstand und Winkel. So können Sie einen Körper neu positionieren oder an anderer Stelle eine Kopie erstellen.",
|
||||
"help.solid.offset_face": "Versetzt die ausgewählten Flächen um den eingestellten Abstand nach innen oder außen. Die angrenzenden Flächen werden entsprechend angepasst. So können Sie beispielsweise eine Wand verdicken, eine Tasche vertiefen oder einen Ansatz erhöhen, ohne die Skizze bearbeiten zu müssen.",
|
||||
"help.solid.pattern.circular": "Wiederholt die gewählten Objekte um eine Achse, mit der eingestellten Anzahl und dem Gesamtwinkel. Für Lochkreise, Speichen, Kühlrippen und Zähne.",
|
||||
"help.solid.pattern.rectangular": "Wiederholt die gewählten Objekte in einer oder zwei Richtungen mit der eingestellten Anzahl und dem Abstand. Für Reihen und Raster von Bohrungen, Rippen, Stiften oder Füßen.",
|
||||
"help.solid.press_pull": "Fügt Material hinzu oder trägt es ab, indem Sie eine Fläche ziehen. Durch Ziehen an einer Kante können Sie diese abrunden. So lassen sich schnelle Änderungen direkt am Modell vornehmen.",
|
||||
"help.solid.remove": "Entfernt die ausgewählten Körper ab dieser Stelle der Timeline aus dem Design. In den vorherigen Schritten bleiben sie erhalten. So können Sie beispielsweise einen nicht mehr benötigten Hilfs- oder Schneidkörper entfernen.",
|
||||
"help.solid.replace_face": "Verschiebt die ausgewählten ebenen Flächen auf eine Ebene oder eine parallele Fläche. Der Körper wird dabei entsprechend vergrößert oder verkleinert. So können Sie eine Fläche bündig zu einem anderen Bauteil oder einer Konstruktionsebene ausrichten.",
|
||||
"help.solid.revolve": "Dreht ein Skizzenprofil um eine Achse und erzeugt so einen runden Körper. Für Wellen, Knöpfe, Schalen und alle Teile, die um eine Mittellinie rund sind.",
|
||||
"help.solid.scale": "Skaliert die ausgewählten Körper mit einem festgelegten Faktor, gleichmäßig in alle Richtungen oder getrennt entlang der einzelnen Achsen. So können Sie beispielsweise importierte Geometrien anpassen oder Materialschwindung berücksichtigen.",
|
||||
"help.solid.shell": "Höhlt einen Körper aus, lässt Wände in der eingestellten Dicke stehen und kann gewählte Flächen öffnen. Für Kästen, Deckel und andere dünnwandige Teile.",
|
||||
"help.solid.split_body": "Teilt einen Körper mithilfe einer Ebene, der Fläche eines anderen Körpers oder einer Skizzenlinie in mehrere getrennte Körper. So können Sie Bauteile erstellen, die einzeln gedruckt, bearbeitet oder eingefärbt werden können.",
|
||||
"help.solid.split_face": "Teilt die ausgewählten Flächen entlang einer Ebene, der Fläche eines anderen Körpers oder von Skizzenkurven, ohne den Körper selbst zu teilen. So können Sie Teilbereiche einer Fläche mit einer eigenen Formschräge, Farbe oder einem Versatz versehen.",
|
||||
"help.solid.sweep": "Führt ein Skizzenprofil entlang eines Pfads und erzeugt so einen Körper. Für Rohre, Schienen, Griffe und alle Teile mit gleichbleibendem Querschnitt entlang einer Kurve.",
|
||||
"help.third_party": "Drittanbieterlizenzen",
|
||||
"help.timeline.compute_all": "Berechnet alle Features der Timeline von Anfang an neu. Dadurch wird das Modell aktualisiert und Sie sehen, bei welchen Features Berechnungsfehler auftreten.",
|
||||
"help.timeline.delete": "Löscht die ausgewählten Features aus der Timeline. Beim Löschen einer Skizze werden auch alle daraus erstellten Features gelöscht. So können Sie nicht mehr benötigte Konstruktionsschritte entfernen.",
|
||||
"help.timeline.play": "Die verbleibenden Features nacheinander abspielen, mit einer halben Sekunde Pause nach jedem Neuaufbau. Diesen Befehl erneut ausführen oder Escape drücken, um anzuhalten; Zum Ende springt sofort ans Ende.",
|
||||
"history.sketch_under_constrained": "Unterbestimmt ({count} Freiheitsgrade)",
|
||||
"history.sketch_under_constrained_one": "Unterbestimmt ({count} Freiheitsgrad)",
|
||||
@@ -1484,6 +1545,21 @@
|
||||
"joint.one_component": "Diese Teile sind Körper einer Komponente: {bodies}. Ein Gelenk bewegt eine Komponente gegenüber einer anderen, daher braucht jedes Teil eine eigene Komponente.",
|
||||
"joint.rigid_group_one_component": "Diese {count} Körper liegen alle in einer Komponente (oder direkt im Design): Verwenden Sie zuerst {command}.",
|
||||
"joint.rigid_group_select_components": "Wählen Sie zwei oder mehr Komponenten aus.",
|
||||
"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.distance": "Abstand vom Ausgangspunkt",
|
||||
"joint_move.done": "Fertig",
|
||||
"joint_move.fine": "Drehschritt weit von der Achse entfernt (°)",
|
||||
"joint_move.fixed": "Dieses Bauteil verfügt über keine bewegliche Gelenkachse.",
|
||||
"joint_move.help": "Verschieben: Ziehen Sie einen Pfeil oder Ring. Mit Esc brechen Sie den aktuellen Ziehvorgang ab.",
|
||||
"joint_move.legacy_hint": "Deaktivieren, um wieder den bisherigen Verschiebebereich zu verwenden.",
|
||||
"joint_move.precision": "Ziehen Sie außerhalb der gestrichelten Führung, um die Drehung fein einzustellen.",
|
||||
"joint_move.rotation": "Drehwinkel vom Ausgangspunkt",
|
||||
"joint_move.select": "Wählen Sie eine Fläche oder eine Baugruppenkomponente aus und anschließend „Verschieben“.",
|
||||
"joint_move.settings": "Baugruppe verschieben",
|
||||
"joint_move.step": "Schrittweite",
|
||||
"joint_move.use_handles": "Gelenkgriffe zum Bewegen von Baugruppen verwenden",
|
||||
"keymap.command_id": "Befehls-ID",
|
||||
"keymap.empty": "Keine passenden Befehle",
|
||||
"keymap.use_anyway": "{key} hier trotzdem verwenden",
|
||||
@@ -1543,6 +1619,10 @@
|
||||
"material.selection.mixed": "Unterschiedliche Materialien",
|
||||
"measure.at": "Bei",
|
||||
"measure.item": "Element {number}",
|
||||
"measure.quick_copied": "Messwert kopiert",
|
||||
"measure.quick_copy": "Klicken, um den Messwert zu kopieren",
|
||||
"measure.quick_edges": "{count} Kanten",
|
||||
"measure.quick_faces": "{count} Flächen",
|
||||
"messages.ago_h": "vor {n} Std.",
|
||||
"messages.ago_min": "vor {n} Min.",
|
||||
"messages.ago_s": "vor {n} Sek.",
|
||||
@@ -1695,6 +1775,11 @@
|
||||
"not a move": "keine Verschiebung",
|
||||
"not a number": "keine Zahl",
|
||||
"open a sketch first": "zuerst eine Skizze öffnen",
|
||||
"parameters.apply_live": "Jede Änderung beim Verlassen des Feldes übernehmen",
|
||||
"parameters.apply_live_tip": "Aus: Änderungen warten auf Übernehmen oder OK, dann wird das Modell einmal für alle neu aufgebaut. Abbrechen oder Esc verwirft sie.",
|
||||
"parameters.held": "{count} Änderungen warten auf Übernehmen",
|
||||
"parameters.held_hint": "Bearbeiten Sie Ausdrücke und Einheiten. Änderungen warten auf Übernehmen oder OK.",
|
||||
"parameters.held_one": "1 Änderung wartet auf Übernehmen",
|
||||
"parts.automatic_name": "automatisch",
|
||||
"parts.dimension.across_flats": "Schlüsselweite",
|
||||
"parts.dimension.bore": "Bohrung",
|
||||
@@ -1717,6 +1802,8 @@
|
||||
"parts.family.set_screw": "Gewindestift mit Innensechskant und Kegelstumpf",
|
||||
"parts.family.socket_head_cap_screw": "Zylinderschraube mit Innensechskant",
|
||||
"parts.family.washer": "Unterlegscheibe",
|
||||
"parts.holes": "Bohrungen (optional)",
|
||||
"parts.placement_hint": "Wählen Sie eine oder mehrere Bohrungen, um in jede ein Teil einzusetzen, oder OK platziert es im Ursprung",
|
||||
"pattern.alignment_2": "Ausrichtung 2",
|
||||
"pattern.alignment_hint": "Die Anzahl enthält das Original. Die zentrierte Anordnung verteilt Kopien auf beide Seiten; bei gerader Anzahl legt die Richtungsumkehr die Seite mit der zusätzlichen Kopie fest.",
|
||||
"pattern.direction_1": "Richtung 1",
|
||||
@@ -1757,6 +1844,8 @@
|
||||
"prefs.anti_aliasing_note": "Glättet gezackte Kanten.",
|
||||
"prefs.applies_on_restart": "Wird nach einem Neustart übernommen",
|
||||
"prefs.assistant": "Assistent",
|
||||
"prefs.auto_operation": "Operation automatisch auswählen",
|
||||
"prefs.auto_operation_note": "Die Feature-Dialoge wählen automatisch zwischen „Verbinden“, „Neuer Körper“ und „Ausschneiden“, abhängig davon, was das neue Material berührt. Sobald Sie selbst eine Operation auswählen, wird diese Einstellung nicht mehr angewendet. Deaktiviert: Die beim Öffnen des Dialogs voreingestellte Operation wird beibehalten.",
|
||||
"prefs.axes": "Achsen",
|
||||
"prefs.axis": "Achse",
|
||||
"prefs.bodies_folders": "Körperordner",
|
||||
@@ -1767,6 +1856,8 @@
|
||||
"prefs.bottom_centre": "Unten mittig",
|
||||
"prefs.bottom_right": "Unten rechts",
|
||||
"prefs.browser": "Browser",
|
||||
"prefs.browser_eye_left": "Auge neben dem Aufklapppfeil",
|
||||
"prefs.browser_eye_left_note": "Browserzeilen zeigen ihr Auge zum Ein- und Ausblenden wie bisher neben dem Aufklapppfeil statt in einer Spalte am rechten Zeilenende.",
|
||||
"prefs.browser_reveal_on_hover": "Beim Überfahren einblenden",
|
||||
"prefs.browser_show_note": "Angezeigt lässt den Browser auf dem Bildschirm. Beim Überfahren einblenden blendet ihn aus, bis der Zeiger über seinen Platz oder die schmale Lasche an seinem linken Rand kommt; er bleibt offen, solange der Zeiger darin ist oder ein Menü daraus offen ist.",
|
||||
"prefs.browser_shown": "Angezeigt",
|
||||
@@ -1817,6 +1908,8 @@
|
||||
"prefs.graphics_card_restart": "Diese Einstellung wird nach einem Neustart von SolidCraft wirksam. Die Umgebungsvariable WGPU_ADAPTER_NAME hat Vorrang vor dieser Auswahl.",
|
||||
"prefs.graphics_note": "Wägen Sie Leistung und Darstellungsqualität gegeneinander ab. Jede Option veranschaulicht ihre Wirkung anhand eines Beispiels.",
|
||||
"prefs.graphics_presets_note": "Mit den Profilen werden die unten aufgeführten Optionen gemeinsam eingestellt.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Bindungssymbole bei starkem Herauszoomen ausblenden",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out_note": "Bindungssymbole verschwinden, wenn die Skizze so weit herausgezoomt ist, dass sie sie überladen würden. Aus: sie behalten bei jedem Zoom eine feste Bildschirmgröße.",
|
||||
"prefs.history": "Verlauf",
|
||||
"prefs.history_origin_bodies": "Ursprung und Körper oben zeigen",
|
||||
"prefs.history_origin_bodies_note": "Der Verlauf links oder unter dem Browser beginnt mit dem Körper-Ordner der aktiven Komponente und dem Ursprung, mit Augen und Menüs wie im Browser. Der Verlauf unten bleibt unverändert.",
|
||||
@@ -1843,6 +1936,11 @@
|
||||
"prefs.line_drag_draws_one": "Drücken und Ziehen zeichnet eine Linie",
|
||||
"prefs.line_drag_draws_one_note": "Im Linienwerkzeug zeichnet Drücken, Ziehen und Loslassen eine Linie vom Drückpunkt bis zum Loslassen, und dort endet sie. Klicks zeichnen weiterhin eine Kette. Aus: Ein Ziehen setzt wie ein Klick einen Punkt dort, wo losgelassen wird.",
|
||||
"prefs.linked_designs": "Verknüpfte Designs",
|
||||
"prefs.live_input": "Live-Eingabe",
|
||||
"prefs.live_preview": "Live-Vorschau",
|
||||
"prefs.logo": "Logo",
|
||||
"prefs.logo_accent": "Akzentfarbe",
|
||||
"prefs.logo_theme": "Designfarben",
|
||||
"prefs.max_file_size": "Maximale Dateigröße zum Öffnen",
|
||||
"prefs.max_file_size_design": "Designdateien",
|
||||
"prefs.max_file_size_flag": "Dieser Lauf wurde mit --max-file-size {size} gestartet. Der Wert gilt, bis SolidCraft geschlossen wird. Diese Einstellungen gelten beim Start ohne diese Option.",
|
||||
@@ -1850,10 +1948,36 @@
|
||||
"prefs.max_file_size_step": "STEP- und Importdateien",
|
||||
"prefs.max_file_size_web": "Die Web-App öffnet STEP- und Importdateien bis 512 MB und Designs bis 256 MB: Ein Browser-Tab kann keine größeren Dateien aufnehmen. In der Desktop-App lassen sich diese Grenzen erhöhen.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "Große Symbole",
|
||||
"prefs.menu_list": "Liste",
|
||||
"prefs.menus": "Menüs",
|
||||
"prefs.minutes": "Minuten",
|
||||
"prefs.minutes_suffix": " min",
|
||||
"prefs.mouse": "Maus und Trackpad",
|
||||
"prefs.mouse_heading": "Maus",
|
||||
"prefs.mouse_mapping": "Mausbelegung",
|
||||
"prefs.navigation": "Navigation",
|
||||
"prefs.navigation_note": "Legt fest, wie Maus, Trackpad und 3D-Maus die Ansicht steuern.",
|
||||
"prefs.network_host_remove": "Entfernen",
|
||||
"prefs.network_locations": "Netzwerkspeicherorte in Designs zulassen",
|
||||
"prefs.network_locations_note": "Deaktiviert: Verknüpfte Dateien eines Designs auf einem anderen Computer (\\\\server\\share) werden nur von den hier aufgeführten Servern geöffnet oder nachdem Sie einen Server auf Nachfrage ausdrücklich zugelassen haben.",
|
||||
"prefs.network_locations_section": "Netzwerkspeicherorte",
|
||||
"prefs.not_on_gpu": "Auf dieser GPU nicht verfügbar",
|
||||
"prefs.on": "An",
|
||||
"prefs.opaque": "Undurchsichtig",
|
||||
"prefs.orbit_snap": "Halten Sie beim Drehen die Umschalttaste gedrückt, um die Ansicht an einer Achse einrasten zu lassen.",
|
||||
"prefs.orientation": "Ausrichtung",
|
||||
"prefs.origin_plane_names": "Namen der Ursprungsebenen",
|
||||
"prefs.origin_plane_names_note": "Namen in Browser, Dialogen und Listen. {top} ist XY, {front} ist XZ und {right} ist YZ, mit Z nach oben. Befehle, Skripte und gespeicherte Konstruktionen behalten XY, XZ und YZ.",
|
||||
"prefs.overall_speed": "Gesamtgeschwindigkeit",
|
||||
"prefs.persistent_ribbon_pressed": "Aktive Menübandbefehle hervorheben",
|
||||
"prefs.persistent_ribbon_pressed_help": "Hebt Befehle im Menüband hervor, solange ihre Effekte eingeschaltet sind. Deaktivieren Sie diese Option, um die bisherige neutrale Darstellung und Befehlshilfe beizubehalten.",
|
||||
"prefs.perspective_fov": "Sichtfeld",
|
||||
"prefs.placement": "Platzierung",
|
||||
"prefs.prefer_free_sketch_points": "Bei überlappenden Punkten bewegliche Skizzenpunkte bevorzugen",
|
||||
"prefs.preview_choices": "Vorschau Ihrer Einstellungen",
|
||||
"prefs.print_face_highlight": "Hervorhebung der Auflagefläche",
|
||||
"prefs.print_face_highlight_tip": "Wie stark die Auflagefläche eines Körpers in der Ansicht eingefärbt und umrandet wird. 0 % zeigt keine Hervorhebung.",
|
||||
"prefs.profile_active": "Aktives Profil",
|
||||
"prefs.profile_applied": "Profil angewendet.",
|
||||
"prefs.profile_changed": "Die Einstellungen wurden seit dem Speichern des Profils geändert. „Speichern“ übernimmt sie ins Profil.",
|
||||
@@ -1871,24 +1995,62 @@
|
||||
"prefs.profiles_note": "Tastenkürzel, Werkzeugleiste, Navigation, Radialmenü und Oberflächeneinstellungen, gemeinsam unter einem Namen gespeichert und auf einmal umgeschaltet.",
|
||||
"prefs.project_options_dialog": "Project-Optionendialog",
|
||||
"prefs.project_options_dialog_note": "Ein: Vor dem Projizieren Geometrie oder Körper und die Projektionsverknüpfung auswählen. Aus: Den bisherigen Direktwahl-Ablauf beibehalten.",
|
||||
"prefs.quality_default": "Standard",
|
||||
"prefs.quality_fastest": "Am schnellsten",
|
||||
"prefs.quality_slowest": "Am langsamsten",
|
||||
"prefs.quality_smoother": "Stärker geglättet",
|
||||
"prefs.quick_measure": "Schnellmessung in der Ecke der Ansicht",
|
||||
"prefs.quick_measure_note": "Zeigt unten rechts in der Ansicht die Größe der Auswahl an: bei einer Kante die Länge, bei einer kreisförmigen Kante zusätzlich Durchmesser (Ø) und Radius (R), bei einer Fläche den Flächeninhalt, bei einem Punkt die Position, bei zwei Elementen Abstand und Winkel sowie bei mehreren Kanten oder Flächen die Gesamtlänge bzw. Gesamtfläche. Ein Klick kopiert den Messwert.",
|
||||
"prefs.render_previous_geometry_budget": "Bisheriges Geometriebudget für die Render-Vorschau verwenden",
|
||||
"prefs.render_previous_geometry_budget_help": "Stellt die bisherige feste Obergrenze für die Geometrie wieder her (512 MiB auf 64-Bit-Desktop-Systemen, andernfalls 128 MiB) anstelle des geräteabhängigen Budgets.",
|
||||
"prefs.reports_saved": "{n} gespeichert",
|
||||
"prefs.ribbon_reveal": "Suchergebnisse im Menüband zeigen",
|
||||
"prefs.ribbon_reveal_note": "Steht der Zeiger über einem Ergebnis in Befehle suchen, zeigt das Menüband, wo der Befehl liegt: seine Registerkarte, seine Schaltfläche oder seine Zeile im Aufklappmenü eines Panels. Ein aus der Suche gestarteter Befehl blinkt dort, und ein laufender Befehl aus einem Aufklappmenü markiert sein Panel.",
|
||||
"prefs.right_click": "Rechtsklick",
|
||||
"prefs.saving": "Automatisches Speichern und Wiederherstellung",
|
||||
"prefs.scale_note": "Die Skalierung lässt sich auch mit Strg + / Strg − im Fenster ändern. Bei 100 % sind die Schaltflächen 28 px groß.",
|
||||
"prefs.scale_text": "Skalierung und Text",
|
||||
"prefs.search": "Einstellungen suchen",
|
||||
"prefs.search_empty": "Keine passenden Einstellungen",
|
||||
"prefs.select": "Auswählen",
|
||||
"prefs.select_one_item": "Ein Element auswählen",
|
||||
"prefs.select_other_hidden": "Verborgene Geometrie mit „Andere auswählen“ auswählen",
|
||||
"prefs.select_other_hidden_help": "Ein: Verborgene Körper, Skizzen und verdeckte Kanten nahe dem Zeiger einbeziehen. Aus: Die ursprüngliche Liste sichtbarer Geometrie direkt unter dem Zeiger verwenden.",
|
||||
"prefs.selecting": "Auswahl",
|
||||
"prefs.shift": "Umschalt",
|
||||
"prefs.shift_add_ctrl_toggle": "Umschalt fügt nur hinzu; Strg / Cmd schaltet die Auswahl um",
|
||||
"prefs.shift_add_note": "Aktiviert: Umschalt fügt Elemente zur Auswahl hinzu, Strg / Cmd schaltet ihre Auswahl um. Deaktiviert (Standard): Beide Tasten schalten die Auswahl um.",
|
||||
"prefs.shortcuts": "Tastenkürzel",
|
||||
"prefs.single_surface_analysis": "Nur eine Flächenanalyse verwenden",
|
||||
"prefs.single_surface_analysis_help": "Wenn deaktiviert, können Flächenschattierung und Stetigkeitsanalyse gleichzeitig aktiv bleiben. Beim Aktivieren bleibt die zuletzt gestartete oder geänderte Analyse erhalten.",
|
||||
"prefs.sketch_align_points": "Beim Zeichnen an anderen Skizzenpunkten ausrichten",
|
||||
"prefs.sketch_align_points_note": "Beim Zeichnen richtet sich ein Punkt zugleich waagerecht an einem Skizzenpunkt und senkrecht an einem anderen aus, mit einer gepunkteten Hilfslinie; der Klick behält beide Ausrichtungen. Aus: nur am vorherigen Punkt, jeweils eine Achse.",
|
||||
"prefs.sketch_conflict_colour": "Widersprüchliche Geometrie in der Konfliktfarbe",
|
||||
"prefs.sketch_conflict_colour_note": "Solange die Bedingungen einer Skizze im Konflikt stehen, werden auch die Kurven und Punkte, die eine nicht erfüllte Bedingung hält, in der Konfliktfarbe gezeichnet, nicht nur ihr Symbol oder ihre Bemaßung. Aus: Sie behalten ihre üblichen Farben.",
|
||||
"prefs.sketch_construction_lines": "Erweiterte Konstruktionslinien-Werkzeuge",
|
||||
"prefs.sketch_exit_view": "Ansicht nach dem Beenden einer Skizze",
|
||||
"prefs.sketch_exit_view.keep": "Aktuelle Ansicht beibehalten",
|
||||
"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_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",
|
||||
"prefs.sketch_recent_snap_note": "Fängt den Zeiger beim Zeichnen dort ein, wo sich die Hilfslinien der beiden zuletzt verwendeten Kreismittelpunkte oder Kantenmitten kreuzen. Aus: die Hilfslinien bleiben sichtbar und der Zeiger bewegt sich nicht.",
|
||||
"prefs.sketch_selection": "Skizzen und Auswahl",
|
||||
"prefs.sketch_selection_heading": "Skizzen und Auswahl",
|
||||
"prefs.sketch_selection_note": "Legt fest, wie Skizzenelemente einrasten und wie Klicks Elemente auswählen.",
|
||||
"prefs.sketch_state_icons": "Skizzensymbole in der Farbe ihres Zustands",
|
||||
"prefs.sketch_state_icons_note": "Im Verlauf und im Browser nimmt das Symbol einer Skizze die Farbe ihres Zustands an (unterbestimmt, vollständig bestimmt oder im Konflikt), und ihr Tooltip nennt den Zustand. Aus: Alle Skizzen haben dasselbe Symbol.",
|
||||
"prefs.sketch_tab_always": "Registerkarte Skizze immer zeigen",
|
||||
"prefs.sketch_tab_always_note": "Die Registerkarte Skizze bleibt im Menüband direkt nach Modell, auch wenn keine Skizze offen ist. Ihre Zeichenwerkzeuge beginnen dann eine Skizze auf der ausgewählten Ebene oder Fläche oder auf der Ebene, die Sie als Nächstes wählen, und das Werkzeug ist darin bereit.",
|
||||
"prefs.sketch_tracking_guides": "Ausrichtungshilfen für Mittelpunkte",
|
||||
"prefs.sketch_tracking_guides_note": "Den Mauszeiger über einen Kreismittelpunkt oder eine Kantenmitte bewegen und dann waagerecht oder senkrecht bewegen, um eine temporäre gepunktete Hilfslinie anzuzeigen.",
|
||||
"prefs.sketching": "Skizzieren",
|
||||
"prefs.snap_drawing_note": "Gilt beim Zeichnen oder Verschieben eines Skizzenpunkts.",
|
||||
"prefs.source": "Quelle",
|
||||
"prefs.speed": "Geschwindigkeit",
|
||||
"prefs.stand_up_note": "Wenn eine STEP- oder IGES-Datei mit der Z-Achse nach oben geöffnet wird, wird eine Vierteldrehung um die X-Achse angeboten.",
|
||||
"prefs.stand_up_unknown": "Für Dateien aus unbekannten Programmen",
|
||||
"prefs.stand_up_unknown.ask": "In einem Dialog fragen",
|
||||
"prefs.stand_up_unknown.note": "Einen Hinweis zeigen",
|
||||
@@ -1896,6 +2058,13 @@
|
||||
"prefs.stand_up_unknown_note": "Wie das Aufrichten angeboten wird, wenn der Dateikopf kein Programm nennt, das bekanntlich mit Y nach oben speichert. Ein Hinweis erscheint nur, wenn die Datei auf dem Rücken zu liegen scheint; „In einem Dialog fragen“ fragt wie bisher bei jeder solchen Datei.",
|
||||
"prefs.status_bar": "Statusleiste",
|
||||
"prefs.status_bar_note": "Eine schmale Leiste am unteren Fensterrand zeigt, was gerade bearbeitet wird, den Zustand der offenen Skizze in seiner Farbe und die Einheiten der Konstruktion; ein Klick auf die Einheiten ändert sie. Der Zustand der Skizze steht dann nicht mehr in der Ecke der Ansicht.",
|
||||
"prefs.status_done": "Fertig",
|
||||
"prefs.status_error": "Fehler",
|
||||
"prefs.status_selected": "Ausgewählt",
|
||||
"prefs.status_warning": "Warnung",
|
||||
"prefs.supersampling_brief": "Rendert die Ansicht größer und verkleinert sie dann: sehr scharf, aber rechenintensiver.",
|
||||
"prefs.tab_accent_note": "Die Akzentfarbe richtet sich nach dem aktiven Tab.",
|
||||
"prefs.theme_file": "Designdatei",
|
||||
"prefs.theme_file_or_paste": "Datei oder Einfügen",
|
||||
"prefs.theme_follow": "Einer Designdatei folgen",
|
||||
"prefs.theme_follow_error": "Nicht angewendet: {error}. Das letzte gültige Design bleibt aktiv.",
|
||||
@@ -1906,27 +2075,95 @@
|
||||
"prefs.theme_import_hint": "Klicken, um eine Designdatei zu wählen, eine hierher ziehen oder mit kopiertem Design Strg+V (Cmd+V auf dem Mac) drücken",
|
||||
"prefs.theme_paste": "Design einfügen",
|
||||
"prefs.theme_paste_none": "Die Zwischenablage enthält kein SolidCraft-Design",
|
||||
"prefs.timeline_half_hour": "−30 min",
|
||||
"prefs.timeline_now": "Jetzt",
|
||||
"prefs.timeline_quarter_hour": "−15",
|
||||
"prefs.toast_place": "Hinweise",
|
||||
"prefs.toggle_item": "Element hinzufügen oder wieder entfernen",
|
||||
"prefs.toolbar": "Werkzeugleiste",
|
||||
"prefs.toolbar_tabs": "Werkzeugleisten-Registerkarten",
|
||||
"prefs.top": "Oben",
|
||||
"prefs.trackpad_zoom_modifier": "Mit zwei Fingern zoomen bei gedrückter Taste",
|
||||
"prefs.trackpad_zoom_modifier.alt": "⌥ / Alt",
|
||||
"prefs.trackpad_zoom_modifier.cmd": "⌘",
|
||||
"prefs.trackpad_zoom_modifier.ctrl": "Strg",
|
||||
"prefs.trackpad_zoom_modifier.help": "Nur diese Taste gedrückt halten, um durch vertikales Scrollen am Zeiger zu zoomen. Aus behält das bisherige Scrollverhalten bei.",
|
||||
"prefs.trackpad_zoom_modifier.off": "Aus",
|
||||
"prefs.translucent": "Durchscheinend",
|
||||
"prefs.under_browser": "Unter dem Browser",
|
||||
"prefs.under_cursor": "Unter dem Cursor",
|
||||
"prefs.units_precision": "Einheiten und Genauigkeit",
|
||||
"prefs.updates": "Updates",
|
||||
"prefs.value_boxes_in_view": "Werte in der Ansicht neben ihren Griffen zeigen",
|
||||
"prefs.value_boxes_in_view_tip": "Der Wert eines Befehls (Abstand, Radius …) in einem Feld neben seinem Griff, über dem Modell. Aus: Sie geben ihn im Befehlsfenster ein, das nach einer Auswahl die Tastatur erhält, und nichts verdeckt das Modell.",
|
||||
"prefs.view": "Ansichtsverhalten",
|
||||
"prefs.view_confirm_corner": "OK und Abbrechen in der Ansichtsecke",
|
||||
"prefs.view_confirm_corner_note": "Solange ein Dialog oder eine Skizze geöffnet ist, übernehmen ein Häkchen und ein Kreuz oben rechts in der Ansicht dieselben Funktionen wie „OK“ und „Abbrechen“ des Dialogs. Eine Skizze wird damit beendet oder verworfen. Der Ansichtsnavigator rückt darunter.",
|
||||
"prefs.viewport": "Ansichtsfenster",
|
||||
"prefs.viewport_analysis_status": "Analysestatus im Ansichtsfenster anzeigen",
|
||||
"prefs.viewport_analysis_status_help": "Zeigt aktive Analysen über dem Ansichtsfenster und ihre Legenden am jeweiligen Eintrag. Esc beendet die neueste Analyse. Ausschalten stellt Ecklegenden und das bisherige Esc-Verhalten wieder her.",
|
||||
"prefs.viewport_background": "Hintergrund",
|
||||
"prefs.viewport_background_colour": "Farbe",
|
||||
"prefs.viewport_background_theme": "Verlauf des Designs",
|
||||
"prefs.viewport_section_status": "Schnittstatus im Ansichtsfenster anzeigen",
|
||||
"prefs.viewport_section_status_help": "Die Schnitteinstellung im Ansichtsfenster anzeigen. Escape und die aktive Schaltfläche schalten den Schnitt aus. Deaktivieren, um die bisherigen Schnittbedienelemente zu behalten.",
|
||||
"prefs.viewport_status_summary": "Status im Ansichtsfenster zusammenfassen",
|
||||
"prefs.viewport_status_summary_help": "Drei oder mehr Statuselemente in einer Zusammenfassung anzeigen. Deaktivieren, um jedes Element in der vollständigen Zeile zu behalten.",
|
||||
"prefs.wheel": "Mausrad",
|
||||
"prefs.wheel_trackpad": "Mausrad und Trackpad",
|
||||
"prefs.workspace": "Arbeitsbereich",
|
||||
"prefs.zoom_stop_at_model": "Zoom am Modell stoppen",
|
||||
"prefs.zoom_stop_at_model_tip": "3D-Maus und Geräte-App zoomen zur Oberfläche in der Ansichtsmitte; Zieh-Zoom hält den Punkt am Beginn des Ziehens fest. Aus: Zoom kann durch das Modell gehen. Mausrad und Pinch verwenden separat Zoom zum Mauszeiger",
|
||||
"prefs.zoom_toward_cursor": "Zoomen zum Mauszeiger hin",
|
||||
"properties.title": "Eigenschaften: {name}",
|
||||
"recovery.days_ago": "vor {count} Tagen",
|
||||
"recovery.detail": "{file} · {features} Merkmale · automatisch gespeichert {when}",
|
||||
"recovery.discard_all": "Alle verwerfen",
|
||||
"recovery.discard_all_detail": "Dies kann nicht rückgängig gemacht werden.",
|
||||
"recovery.discard_all_title": "{count} automatische Sicherungen verwerfen?",
|
||||
"recovery.discard_all_title_one": "1 automatische Sicherung verwerfen?",
|
||||
"recovery.hours_ago": "vor {count} Std.",
|
||||
"recovery.just_now": "gerade eben",
|
||||
"recovery.min_ago": "vor {count} Min.",
|
||||
"recovery.never_saved": "nie gespeichert",
|
||||
"render.adaptive_absolute": "Absolute RGB-Fehleruntergrenze",
|
||||
"render.adaptive_coverage": "Zielwert für den Abdeckungsfehler",
|
||||
"render.adaptive_enable": "Samples mithilfe unabhängiger Pilot-Samples zuteilen",
|
||||
"render.adaptive_help": "Nur GPU. Unabhängige Pilot-Samples werden verworfen. Anschließend werden pro Pixel 16 oder mehr Produktions-Samples zugeteilt, bis zu den Samples pro Pixel. Die Zuteilung richtet sich nach der RGB- und Abdeckungsvarianz; seltene Pfade können übersehen werden, und Pixel, die ihr Limit erreicht haben, können verrauscht bleiben. Der Abschlussbericht zählt den tatsächlichen Aufwand. Keine zertifizierte Garantie für ein bestimmtes Rauschniveau oder eine bestimmte Renderzeit.",
|
||||
"render.adaptive_minimum": "Minimale Produktions-Samples",
|
||||
"render.adaptive_pending": "Die adaptive Zuteilung mit unabhängigen GPU-Pilot-Samples und bedingte Fresnel-Schichten sind verfügbar. Zertifizierte Konvergenz, Garantien für seltene Ereignisse und adaptiver Lichttransport auf der CPU bleiben nicht verfügbar.",
|
||||
"render.adaptive_pilots": "Pilot-Samples pro Pixel",
|
||||
"render.adaptive_relative": "Zielwert für den relativen RGB-Fehler",
|
||||
"render.add_ground": "Add fitted ground to setup",
|
||||
"render.advanced_material_scope": "Ein endliches, homogenes GPU-Medium ist innerhalb ausdrücklich festgelegter Grenzen verfügbar. Dichtegitter, verschachtelte Glas-/Volumen-Kombinationen, Subsurface-Oberflächen, Displacement, spektraler Lichttransport und nicht zugeordnete erweiterte Materialkonzepte bleiben nicht verfügbar.",
|
||||
"render.aging_add": "Alterungsschicht hinzufügen",
|
||||
"render.aging_airborne": "Luftgetragener Staub / teilnehmendes Medium",
|
||||
"render.aging_airborne_help": "Luftgetragener Staub bildet ein separates, endliches und homogenes Streuvolumen. Er beeinflusst den Lichttransport und das unbearbeitete EXR-Bild, nicht jedoch die Oberflächenalterungsschichten. Die bestehenden Einschränkungen für Volumen, Glas und Entrauschen gelten weiterhin. Eine Atmosphäre wird nicht automatisch hinzugefügt.",
|
||||
"render.aging_all_bodies": "Alle geeigneten undurchsichtigen Körper",
|
||||
"render.aging_chips": "Abgeplatzter Lack",
|
||||
"render.aging_class_any": "Alle undurchsichtigen Materialklassen",
|
||||
"render.aging_class_dielectric": "Nur nichtmetallische Oberflächen",
|
||||
"render.aging_class_metal": "Nur metallische Oberflächen",
|
||||
"render.aging_color": "Schichtfarbe · sRGB",
|
||||
"render.aging_coverage": "Abdeckung",
|
||||
"render.aging_dust": "Abgelagerter Staub",
|
||||
"render.aging_empty": "Keine Alterungsschichten: Die ursprünglichen Oberflächen bleiben unverändert.",
|
||||
"render.aging_fading": "Ausgeblichene Beschichtung",
|
||||
"render.aging_grime": "Schmutz und Verfärbungen",
|
||||
"render.aging_help": "Bis zu acht optische Schichten der Reihe nach kombinieren. Rost und Patina sind gestaltete Erscheinungsbilder, keine Simulationen von Alterung oder Korrosion. Die Mustergröße wird in Weltmillimetern angegeben. Geometrie, Masse und technische Eigenschaften bleiben unverändert; Glas und passive Bühnenflächen sind ausgenommen.",
|
||||
"render.aging_metallic": "Metallanteil der Schicht",
|
||||
"render.aging_origin": "Feldursprung · Weltkoordinaten (mm)",
|
||||
"render.aging_patina": "Kupfergrüne Patina",
|
||||
"render.aging_refresh_targets": "Aktuelle Körper prüfen",
|
||||
"render.aging_roughness": "Rauheit der Schicht",
|
||||
"render.aging_rust": "Oxidiertes Eisen / Rost",
|
||||
"render.aging_scale": "Mustergröße",
|
||||
"render.aging_scratches": "Feine Kratzer",
|
||||
"render.aging_target": "Ziel und Koordinaten",
|
||||
"render.aging_target_help": "Ein benanntes Ziel muss weiterhin zu seinem erfassten Index und Namen passen. Globale Masken sind im Weltkoordinatensystem verankert und folgen bewegten Teilen nicht. Die Funktion erkennt weder Hohlräume noch echten Kantenverschleiß. Filtern Sie die Liste, um weitere Körper zu erreichen.",
|
||||
"render.aging_target_search": "Körpernamen filtern",
|
||||
"render.aging_upward": "Ablagerung auf nach oben zeigenden Flächen",
|
||||
"render.all_controls_help": "Kompakte Übersicht aller aktuellen Einstellungen. Die Abschnittsseiten erlauben eine gezielte Bearbeitung, ohne die zugrunde liegende Anfrage zu ändern.",
|
||||
"render.all_shots_label": "Alle Aufnahmen",
|
||||
"render.appearance_editor.absorption": "Absorption",
|
||||
"render.appearance_editor.base_colour": "Grundfarbe",
|
||||
@@ -1967,18 +2204,64 @@
|
||||
"render.appearance_editor.v_repeat": "V-Wiederholung",
|
||||
"render.appearance_editor.v_up": "Textur-V zeigt nach oben",
|
||||
"render.appearance_editor.weight": "Gewicht",
|
||||
"render.appearance_physics_separate": "Das visuelle Aussehen weist weder Dichte noch thermische Daten oder andere technische Eigenschaften zu.",
|
||||
"render.appearances": "Erscheinungsbilder",
|
||||
"render.assign_help": "Klicken wendet den Look auf die Auswahl an. Ziehen zeigt eine Vorschau; ohne Auswahl startet Malen.",
|
||||
"render.availability_native": "Native Zusammenstellung; vor dem Anwenden prüfen",
|
||||
"render.availability_projection": "Unterstützte Projektion; Umfang und Auslassungen beachten",
|
||||
"render.availability_reference": "Referenzdaten; keine automatische Zuweisung",
|
||||
"render.backdrop_alpha_conflict": "Wählen Sie entweder diesen Hintergrund oder die Ausgabe als transparentes PNG; der Renderer ignoriert keine der beiden Einstellungen stillschweigend.",
|
||||
"render.backdrop_enable": "Verlaufshintergrund nur für die Kamera verwenden",
|
||||
"render.backdrop_first": "Start-/Mittelfarbe",
|
||||
"render.backdrop_help": "Dieser Hintergrund im Bildschirmraum beleuchtet nicht, wirft keine Schatten, spiegelt nicht und stellt kein Gelände dar. Er ist im unbearbeiteten EXR-Bild nicht enthalten. Geometrie, Lichter und die HDR-Umgebung bleiben unabhängig.",
|
||||
"render.backdrop_horizontal": "Horizontaler Verlauf",
|
||||
"render.backdrop_radial": "Radialer Verlauf",
|
||||
"render.backdrop_radius": "Radius",
|
||||
"render.backdrop_second": "End-/Randfarbe",
|
||||
"render.backdrop_vertical": "Vertikaler Verlauf",
|
||||
"render.backdrop_x": "Mittelpunkt X",
|
||||
"render.backdrop_y": "Mittelpunkt Y",
|
||||
"render.background": "Szenenhintergrund",
|
||||
"render.background_camera_section": "Kamerahintergrund · PNG-Darstellung",
|
||||
"render.background_completed_frozen": "Das angezeigte Ergebnis ist ein eingefrorenes, fertiges Bild. Eine Änderung des Hintergrunds zeichnet dieses Ergebnis nicht automatisch neu. Verwenden Sie „Neu graden“ für reine Kameraänderungen oder „Rendern“ für eine neue physische Bühne.",
|
||||
"render.background_hdr_separate": "Die aktuellen HDR-Ressourcen beleuchten Oberflächen und spiegeln sich darin; sie werden nicht automatisch als Panorama hinter dem Motiv angezeigt. Das Ändern eines Verlaufs dreht oder verfärbt die Beleuchtung nicht.",
|
||||
"render.background_lighting_section": "HDR-Beleuchtung · getrennt vom Kamerahintergrund",
|
||||
"render.background_no_alpha_override": "Ein Kamerahintergrund wird einer transparenten Ausgabe nicht stillschweigend hinzugefügt. Beim Neu-Graden des linearen Rohbilds wird die erfasste Abdeckung verwendet und das Entrauschen nicht erneut angewendet; das Quell-EXR bleibt unverändert.",
|
||||
"render.background_not_light": "Die Hintergrundfarbe dient der Darstellung und ist keine zusätzliche Lichtquelle.",
|
||||
"render.background_regrade": "Auf das behaltene lineare Rohbild anwenden",
|
||||
"render.background_stage_occlusion": "Diese Aufnahme enthält physische Bühnengeometrie. Ein großer Boden oder eine große Wand kann das Bild füllen und einen reinen Kamerahintergrund verdecken. Entfernen oder verkleinern Sie die Bühne gezielt oder ändern Sie ihren physikalischen Reflexionsgrad. Neu graden kann bereits erfasste Geometrie nicht entfernen.",
|
||||
"render.background_workflow": "Wählen Sie den gewünschten Hintergrund: eine reine Kamerafarbe bzw. einen Verlauf, eine physische Bühne, die Licht empfängt, oder HDR-Beleuchtung. Sie sind voneinander unabhängig. Die Live-Vorschau ist eine Modellieransicht; rendern Sie ein neues Bild oder graden Sie ein behaltenes lineares Rohbild neu, um die Nachbearbeitung der Kamera zu sehen.",
|
||||
"render.base_appearance": "Bearbeitbares Aussehen des Körpers",
|
||||
"render.base_apply": "Aussehen des Körpers anwenden",
|
||||
"render.base_apply_help": "Eine rückgängig machbare visuelle Zuweisung an genau den geprüften Körper. Bestehende flächenspezifische Looks und technische Materialien bleiben davon getrennt.",
|
||||
"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.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.",
|
||||
"render.bloom_radius": "Bloom-Radius",
|
||||
"render.bloom_strength": "Bloom-Stärke",
|
||||
"render.bloom_threshold": "Linearer Glanzlichtschwellwert",
|
||||
"render.browse_capabilities": "Vollständigen Funktionskatalog ansehen",
|
||||
"render.browse_environments": "Originale HDR-Umgebungen durchsuchen",
|
||||
"render.browse_looks": "Durchsuchen…",
|
||||
"render.browse_native_scenes": "Native Studios durchsuchen",
|
||||
"render.browse_physical": "Physikalische Referenzen ansehen",
|
||||
"render.browse_quality": "Qualitätsspezifikationen ansehen",
|
||||
"render.browse_rigs": "Erstellte Lichtaufbauten durchsuchen",
|
||||
"render.browse_scene_specs": "Erweiterte Szenenspezifikationen durchsuchen",
|
||||
"render.browse_surfaces": "Erstellte Oberflächen durchsuchen",
|
||||
"render.browser_library": "Bibliothek",
|
||||
"render.browser_model": "Modell",
|
||||
"render.camera_distance": "Abstand zwischen Kamera und Ziel",
|
||||
"render.camera_exact_help": "Weltmillimeter; Winkel werden in Grad angezeigt und als Bogenmaß gespeichert. Die Navigation verwendet dieselbe Kamera wie die Ansicht.",
|
||||
"render.camera_fov": "Vertikaler Bildwinkel",
|
||||
"render.camera_pitch": "Kameraneigung",
|
||||
"render.camera_roll": "Rollwinkel",
|
||||
"render.camera_set": "Render-Kamera einstellen",
|
||||
"render.camera_target": "Zielposition · mm",
|
||||
"render.camera_yaw": "Gierwinkel",
|
||||
"render.category_metal": "Metall",
|
||||
"render.category_paint": "Lack",
|
||||
"render.category_plastic": "Kunststoff",
|
||||
@@ -2062,7 +2345,22 @@
|
||||
"render.children.vignette": "Vignette",
|
||||
"render.children.workstation": "Workstation",
|
||||
"render.choose_look": "Zuerst einen Look in der Bibliothek wählen",
|
||||
"render.chromatic_offset": "Chromatischer Kantenversatz",
|
||||
"render.clear_stage": "Remove saved stage",
|
||||
"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",
|
||||
"render.cpu_area_editor": "Rechteckige CPU-Flächenlichter",
|
||||
"render.cpu_area_editor_help": "Vollständige Seitenvektoren in Millimetern; das Kreuzprodukt U × V zeigt zur abstrahlenden Seite. Das unterscheidet sich von den halben Kantenvektoren der GPU.",
|
||||
"render.cpu_area_full_u": "Volle Seite U · mm",
|
||||
"render.cpu_area_full_v": "Volle Seite V · mm",
|
||||
"render.cpu_area_samples": "Flächenlicht-Samples",
|
||||
"render.cpu_binding_confirm": "Diesen Körper bestätigen",
|
||||
"render.cpu_binding_stale": "Die Szene hat sich geändert. Bestätigen Sie dieses Reflexionsziel vor dem Rendern erneut.",
|
||||
"render.cpu_body_choose": "Körper wählen",
|
||||
"render.cpu_body_filter": "Körper nach Namen filtern",
|
||||
"render.cpu_body_filter_more": "Verfeinern Sie den Filter, um weitere Körper zu sehen.",
|
||||
"render.cpu_body_missing": "Ein Reflexionsziel fehlt, ist ausgeblendet, mehrdeutig oder hat einen Namen von mehr als 128 Byte. Wählen Sie es erneut.",
|
||||
"render.cpu_budget": "CPU-Ressourcenprofil",
|
||||
"render.cpu_budget_bodies": "Körperobergrenze",
|
||||
"render.cpu_budget_compact": "Kompakt (bisherige Grenzen)",
|
||||
@@ -2074,7 +2372,77 @@
|
||||
"render.cpu_budget_studio": "Studio (große Bilder)",
|
||||
"render.cpu_budget_triangles": "Dreiecksobergrenze",
|
||||
"render.cpu_budget_workstation": "Workstation (komplexe Szenen)",
|
||||
"render.cpu_captured_hdr": "Für dieses Bild erfasstes HDR",
|
||||
"render.cpu_check_capture_help": "Prüfen und Starten verifizieren dieselbe Datei-Prüfsumme oder verwenden das eingebettete HDR. Jedes Bild behält seine erfasste Quelle.",
|
||||
"render.cpu_check_changed": "Die Einstellungen oder das Modell haben sich nach dieser Prüfung geändert. Prüfen Sie erneut, bevor Sie rendern.",
|
||||
"render.cpu_constant_help": "Konstante Strahldichte ist für reflektierte oder durchgelassene Strahlen sichtbar. Sie liefert keine direkte diffuse Beleuchtung.",
|
||||
"render.cpu_custom_directions": "Vorgabe durch eigene Richtungslichter ersetzen",
|
||||
"render.cpu_direction": "Richtung zum Licht (X, Y, Z)",
|
||||
"render.cpu_directional": "Richtungslicht",
|
||||
"render.cpu_directional_add": "Richtungslicht hinzufügen",
|
||||
"render.cpu_environment": "CPU-Umgebung",
|
||||
"render.cpu_environment_background": "Der Bildhintergrund bleibt von der Umgebungsbeleuchtung getrennt.",
|
||||
"render.cpu_environment_bounds": "Die CPU-Intensität muss 0–64 betragen, die HDR-Drehung endlich innerhalb von ±1.000.000 Radiant liegen, und die HDR-Samples müssen 1, 4, 16 oder 64 sein.",
|
||||
"render.cpu_environment_constant": "Konstante Umgebung für Sekundärstrahlen",
|
||||
"render.cpu_environment_none": "Keine CPU-Umgebung",
|
||||
"render.cpu_environment_samples": "HDR-Beleuchtungs-Samples",
|
||||
"render.cpu_exposure_help": "Die CPU-Belichtung muss zwischen −16 und 16 EV liegen. Eine GPU-Belichtung außerhalb dieses Bereichs bleibt erhalten, bis Sie sie ändern.",
|
||||
"render.cpu_fixed": "Einzelbild",
|
||||
"render.cpu_hdr_help": "Radiance-HDR-Datei (.hdr), Panorama im Format 2:1, bis zu 2048 × 1024 Pixel und 16 MiB. Für die CPU müssen die sRGB/D65-Primärfarben ausdrücklich in der Datei angegeben sein. Dateien ohne Angabe der Primärfarben sowie EXR-Dateien werden abgelehnt.",
|
||||
"render.cpu_hdr_reflection_help": "Raue HDR-Reflexionen erfordern vollständig metallische Körper- und Flächenoberflächen. Die Prüfung meldet nicht unterstützte Kombinationen.",
|
||||
"render.cpu_key_rim": "Haupt- und Konturlicht",
|
||||
"render.cpu_last_check": "Letzte CPU-Prüfung",
|
||||
"render.cpu_light_help": "Bis zu acht Richtungslichter. Eine leere eigene Liste schaltet sie aus; die Wahl einer Vorgabe stellt deren Lichter wieder her. Farben sind lineares RGB, und die Intensität ist ein relativer Wert, keine Leistungsangabe in Watt.",
|
||||
"render.cpu_light_intensity": "Relative Intensität (0–64)",
|
||||
"render.cpu_light_minimum": "Mindestabstand für die Abschwächung",
|
||||
"render.cpu_light_position": "Weltposition (X, Y, Z)",
|
||||
"render.cpu_light_reference": "Referenzabstand für die Intensität",
|
||||
"render.cpu_light_remove": "Licht entfernen",
|
||||
"render.cpu_lighting": "Beleuchtungsvorgabe",
|
||||
"render.cpu_lights": "CPU-Lichter",
|
||||
"render.cpu_linear_color": "Lineare Farbe (R, G, B)",
|
||||
"render.cpu_neutral": "Neutral",
|
||||
"render.cpu_opaque_required": "CPU-Raytracing benötigt einen undurchsichtigen Hintergrund. Entfernen Sie das Häkchen bei „Transparenter Hintergrund“, um fortzufahren.",
|
||||
"render.cpu_options_pending": "Dieses CPU-Bedienfeld kann Emission, Oberflächeneffekte, Glas oder Anisotropie noch nicht bearbeiten. Entfernen Sie diese API-Optionen, bevor Sie es verwenden.",
|
||||
"render.cpu_point": "Punktlicht",
|
||||
"render.cpu_point_add": "Punktlicht hinzufügen",
|
||||
"render.cpu_point_help": "Bis zu acht Punktlichter, zusätzlich zur gewählten Richtungsbeleuchtung. Abstände in mm; der Mindestabstand darf den Referenzabstand nicht überschreiten.",
|
||||
"render.cpu_primary_samples": "Kanten-Samples pro Pixel",
|
||||
"render.cpu_progressive": "CPU progressiv",
|
||||
"render.cpu_progressive_help": "Den vorhandenen CPU-Integrator in Durchgängen mit je einem Primär-Sample akkumulieren. Ausgewählte Reflexionen erfordern ein Sample; Flächenlichter, Dielektrika und Anisotropie erfordern „Einzelbild“.",
|
||||
"render.cpu_progressive_limits": "Höchstens 1.048.576 Pixel und 16.777.216 Auswertungen von Pixel × Sample. Das Ändern der Samples passt die Bildgröße nie automatisch an.",
|
||||
"render.cpu_progressive_seed": "Startwert für das CPU-Sampling",
|
||||
"render.cpu_progressive_spp": "Samples pro Pixel",
|
||||
"render.cpu_reflection_add": "Raue Reflexion hinzufügen",
|
||||
"render.cpu_reflection_depth": "Reflexionstiefe (1–8)",
|
||||
"render.cpu_reflection_empty": "Keine Körper ausgewählt: Szenenreflexionen sind aus.",
|
||||
"render.cpu_reflection_help": "Wählen Sie Körper und stellen Sie „Aus“, „Spiegelnd“ oder „Rau“ ein. „Rau“ verwendet die zugewiesene Oberflächenrauheit. Mehr Samples und Reflexionsstufen verlängern die Rechenzeit; dies ist begrenzter spiegelnder Transport, keine globale Beleuchtung.",
|
||||
"render.cpu_reflection_limit": "Bis zu 64 reflektierte Körper werden unterstützt.",
|
||||
"render.cpu_reflection_mirror": "Spiegelnd",
|
||||
"render.cpu_reflection_off": "Aus",
|
||||
"render.cpu_reflection_rough": "Rau",
|
||||
"render.cpu_reflection_samples": "Reflexions-Samples (1–64)",
|
||||
"render.cpu_reflections": "CPU-Reflexionen",
|
||||
"render.cpu_runtime_help": "Numerische Licht- und Umgebungseinstellungen werden mit dem Setup gespeichert. Körperreflexionen und HDR-Dateien bleiben vorübergehend. Vorhandene Werte für Flächenlicht und Spotkegel bleiben erhalten.",
|
||||
"render.cpu_sampling_mode": "CPU-Sampling-Modus",
|
||||
"render.cpu_shadows": "Schatten werfen",
|
||||
"render.cpu_summary": "CPU-Raytracing · Reflexionen · HDR · Richtungs- und Punktlichter",
|
||||
"render.cpu_warm_cool": "Warm und kühl",
|
||||
"render.diag_bodies": "Sichtbare Körper",
|
||||
"render.diag_job_details": "Erfasste Auftragsdetails",
|
||||
"render.diag_last_check": "Letzte Vorabprüfung",
|
||||
"render.diag_memory": "Geschätzter logischer Speicherbedarf",
|
||||
"render.diag_not_implemented": "Ausdrückliche Funktionsgrenzen",
|
||||
"render.diag_preflight_details": "Details der Vorabprüfung",
|
||||
"render.diag_request": "Aktuelle Anfrage im Wortlaut",
|
||||
"render.diag_triangles": "Dreiecke",
|
||||
"render.discard_continue": "Entwurf verwerfen und fortfahren",
|
||||
"render.draft_confirm_help": "Der aktuelle Render-Entwurf enthält nicht angewendete Änderungen. Speichern Sie sie vor dem Wechsel, verwerfen Sie sie ausdrücklich oder bearbeiten Sie weiter.",
|
||||
"render.draft_confirm_title": "Bearbeitete Rendereinstellungen beibehalten",
|
||||
"render.effects_gpu_only": "Diese Effekte erfordern GPU-Pfadverfolgung. Ein Seitenwechsel schaltet den Renderer niemals automatisch um.",
|
||||
"render.effects_help": "Geordnete Farb-Looks und Objektiveffekte im Bildraum kombinieren. Das Rendern und das Neu-Graden des linearen Rohbilds verwenden dieselben gespeicherten Einstellungen. Das unbearbeitete EXR bleibt unverändert. Dies sind eigene künstlerische Regler, keine kalibrierten Objektiv- oder Filmemulationen bestimmter Marken.",
|
||||
"render.effects_reset_lens": "Objektiveffekte zurücksetzen",
|
||||
"render.effects_seed": "Startwert des Musters",
|
||||
"render.emitted_axis": "Abstrahlachse",
|
||||
"render.env_advanced": "Erweitert",
|
||||
"render.env_backdrop": "Hintergrund",
|
||||
@@ -2214,11 +2582,19 @@
|
||||
"render.error_vertex_packing": "Produktvorschau stimmt nicht mit den zugelassenen Scheitelpunktdaten überein",
|
||||
"render.error_visible_solid": "In Design einen sichtbaren Volumenkörper erstellen, um ihn hier anzuzeigen.",
|
||||
"render.exit_focus": "Fokus beenden",
|
||||
"render.export_display": "Export: Anzeige-PNG",
|
||||
"render.export_image_label": "Bild exportieren",
|
||||
"render.export_linear": "Export: lineares Roh-EXR",
|
||||
"render.exr_pending": "Die GPU kann rohe lineare EXR-Dateien behalten. Kompression, mehrschichtige Bilder und zusätzliche Renderpässe bleiben nicht verfügbar.",
|
||||
"render.film_grain": "Fotografisches Korn",
|
||||
"render.fit": "Produkt einpassen",
|
||||
"render.fixed_camera": "Kamera",
|
||||
"render.fixed_world": "Welt",
|
||||
"render.flare_ghosts": "Geisterringe pro Quelle",
|
||||
"render.flare_halo": "Reflex-Lichthof",
|
||||
"render.flare_streak": "Streifen im anamorphen Stil",
|
||||
"render.flare_strength": "Lichtreflex-Stärke an Glanzlichtern",
|
||||
"render.flare_threshold": "Szenenlinearer Lichtreflex-Schwellwert",
|
||||
"render.fold_browser": "Browser einklappen",
|
||||
"render.frame_aspect": "Seitenverhältnis",
|
||||
"render.frame_centre_mark": "Mittelpunkt",
|
||||
@@ -2247,6 +2623,27 @@
|
||||
"render.frame_tall": "9:16 · Vertikal",
|
||||
"render.frame_thirds": "Drittellinien",
|
||||
"render.frame_wide": "16:9 · Breitbild",
|
||||
"render.gpu_depth": "Maximale Pfadtiefe",
|
||||
"render.gpu_options_help": "Samples, Tiefe, Startwert und Belichtung werden mit diesem Setup gespeichert. Es wird nur die aktive Bildmethode gespeichert.",
|
||||
"render.gpu_path_failed": "GPU-Pathtracer nicht verfügbar",
|
||||
"render.gpu_path_first_setup": "Das erste Setup auf dieser GPU kann mehrere Minuten dauern; SolidCraft bleibt währenddessen bedienbar. Spätere Starts verwenden die kompilierten Shader erneut, sofern der Treiber sie behält.",
|
||||
"render.gpu_path_preparing": "GPU-Pathtracer: Shader werden kompiliert…",
|
||||
"render.gpu_seed": "Zufallsstartwert",
|
||||
"render.gpu_unavailable": "GPU-Pathtracing ist auf diesem Gerät nicht bereit.",
|
||||
"render.ground_apply": "Boden anpassen und speichern",
|
||||
"render.ground_gap": "Definierter Abstand",
|
||||
"render.ground_linear_rgb": "Diffuser Reflexionsgrad · lineares RGB",
|
||||
"render.ground_margin": "Randzugabe der Bodenfläche",
|
||||
"render.hdr_details": "Details zur HDR-Quelle",
|
||||
"render.hdr_embedded": "In diesem Design gespeichertes HDR",
|
||||
"render.hdr_embedded_help": "Das Rendern verwendet die im Design enthaltenen Bytes; die Originaldatei wird nicht mehr benötigt.",
|
||||
"render.hdr_file_captured": "HDR-Datei geprüft. Beim Speichern werden diese Bytes eingebettet; ändert sich die Datei, prüfen Sie sie erneut ausdrücklich.",
|
||||
"render.hdr_inspect": "HDR-Datei prüfen",
|
||||
"render.hdr_inspect_required": "Wählen und prüfen Sie eine HDR-Datei, bevor Sie speichern oder rendern.",
|
||||
"render.hdr_primaries_declared": "Die Quelle gibt ihre Primärfarben an.",
|
||||
"render.hdr_primaries_srgb": "Primärfarben: sRGB/D65",
|
||||
"render.hdr_primaries_undeclared": "Die Quelle gibt keine Primärfarben an; prüfen Sie die Farbanforderungen des Renderers.",
|
||||
"render.hdr_resource_missing": "In diesem Design fehlt die gespeicherte HDR-Ressource.",
|
||||
"render.heading_batch": "Stapel",
|
||||
"render.heading_frame": "Bildrahmen",
|
||||
"render.heading_lens": "Objektiv",
|
||||
@@ -2254,19 +2651,26 @@
|
||||
"render.heading_shots": "Aufnahmen",
|
||||
"render.hide_shots_label": "Aufnahmenleiste ausblenden",
|
||||
"render.hover_look": "{object} · {look} · Zum Auswählen klicken",
|
||||
"render.image_advanced": "Erweiterte GPU-Einstellungen",
|
||||
"render.image_cancelled": "Rendering abgebrochen",
|
||||
"render.image_cancelling": "Wird abgebrochen",
|
||||
"render.image_completed": "Bild bereit zum Export",
|
||||
"render.image_encoding": "PNG wird kodiert",
|
||||
"render.image_export": "Fertiges Bild exportieren",
|
||||
"render.image_exposure": "Belichtung",
|
||||
"render.image_failed": "Rendering fehlgeschlagen",
|
||||
"render.image_height": "Höhe",
|
||||
"render.image_help": "CPU-Vorschaubild · PNG · angenäherte Transparenz",
|
||||
"render.image_host_missing": "Es ist kein Bild-Host installiert.",
|
||||
"render.image_method": "Bildmethode",
|
||||
"render.image_overwrite": "Vorhandene Datei ersetzen",
|
||||
"render.image_preparing": "Bild wird vorbereitet",
|
||||
"render.image_progressive": "Progressives Rendering",
|
||||
"render.image_queued": "Warten auf Rendering",
|
||||
"render.image_reduce": "Kleineres Bild oder einfachere Szene versuchen.",
|
||||
"render.image_running": "Bild wird gerendert",
|
||||
"render.image_samples": "Samples pro Pixel",
|
||||
"render.image_scene_stale": "Schließen Sie die Modellierungsgeste ab oder aktualisieren Sie die Szene, bevor Sie ein Bild prüfen oder starten.",
|
||||
"render.image_stale": "Dieses Bild verwendet die Szene vor den nachfolgenden Änderungen.",
|
||||
"render.image_start": "Rendering starten",
|
||||
"render.image_supported": "Renderanfrage unterstützt; Warteschlange und Speicher werden beim Start erneut geprüft.",
|
||||
@@ -2278,6 +2682,7 @@
|
||||
"render.image_width": "Breite",
|
||||
"render.include_rig_cards": "Include passive cards and flags",
|
||||
"render.inner_half_angle": "Innerer Halbwinkel",
|
||||
"render.inspect_scene": "Erfasste Szenenübersicht aktualisieren",
|
||||
"render.inspector": "Inspektor",
|
||||
"render.inspector_advanced": "Erweitert",
|
||||
"render.inspector_all_users": "Alle {count} Nutzer",
|
||||
@@ -2309,7 +2714,17 @@
|
||||
"render.inspector_this_face": "Diese Fläche",
|
||||
"render.inspector_this_target": "Dieses Ziel",
|
||||
"render.inspector_used_by": "Verwendet von {count}",
|
||||
"render.keep_editing": "Weiter bearbeiten",
|
||||
"render.layer_down": "Nach unten",
|
||||
"render.layer_enabled": "Schicht aktiviert",
|
||||
"render.layer_off_suffix": " (aus)",
|
||||
"render.layer_remove": "Schicht entfernen",
|
||||
"render.layer_strength": "Schichtstärke",
|
||||
"render.layer_up": "Nach oben",
|
||||
"render.lens_effects_help": "Lichtreflexe nutzen bis zu vier getrennte helle Quellen im tatsächlichen linearen Bild. Geisterringe, Lichthof, Streifen, chromatische Versätze und radiale Verzeichnung sind künstlerische Näherungen im Bildraum. Räumliche Effekte benötigen eine undurchsichtige Ausgabe; Samples außerhalb setzen den Bildrand fort. Das Korn ist deterministisch. Physik, EXR und Geometrie bleiben unverändert.",
|
||||
"render.lens_effects_title": "Objektivinspiriert und fotografisch",
|
||||
"render.library_all": "Katalog",
|
||||
"render.library_all_states": "Alle Verfügbarkeitszustände",
|
||||
"render.library_appearance_available": "Verfügbares Erscheinungsbild",
|
||||
"render.library_applicable": "Passende Ressourcen",
|
||||
"render.library_applied": "Ressourcenvariante angewendet. Vollständigen Renderauftrag vor dem Start prüfen.",
|
||||
@@ -2321,10 +2736,16 @@
|
||||
"render.library_apply_uniform": "Einheitliches opakes Aussehen anwenden",
|
||||
"render.library_authored_help": "Original erstellte Ressourcen dienen der Darstellung, nicht als gemessene technische Daten. Jede Aktion wendet nur ihre angegebene Variante an.",
|
||||
"render.library_blocked": "Diese Ressource benötigt noch Kalibrierung oder eine externe Sonne und kann noch nicht angewendet werden.",
|
||||
"render.library_capabilities": "Funktionsspezifikationen",
|
||||
"render.library_center": "Mittelpunkt des Lichtaufbaus",
|
||||
"render.library_clear_search": "Leeren",
|
||||
"render.library_details": "Spezifikation und Herkunft",
|
||||
"render.library_emitters_only_ack": "Nur Strahler verwenden; dokumentierte passive Karten auslassen",
|
||||
"render.library_empty": "Keine passenden Ressourcen. Versuchen Sie eine andere Suche oder einen anderen Verfügbarkeitsfilter.",
|
||||
"render.library_environment_concepts": "Umgebungskonzepte",
|
||||
"render.library_extent": "Objektausdehnung",
|
||||
"render.library_favorite": "Zu Favoriten hinzufügen",
|
||||
"render.library_favorites": "Favoriten",
|
||||
"render.library_filters": "Katalog und Verfügbarkeit",
|
||||
"render.library_grid": "Raster",
|
||||
"render.library_hdr": "Integrierte HDR-Umgebungen",
|
||||
@@ -2337,12 +2758,15 @@
|
||||
"render.library_maps_help": "Built-in maps can be saved with pinned sources and local occurrence mapping. Temporary optical overrides remain a separate mode.",
|
||||
"render.library_material_concepts": "Materialkonzepte",
|
||||
"render.library_metallic": "Metallisch",
|
||||
"render.library_native_scenes": "Native Studios",
|
||||
"render.library_next": "Nächste Seite",
|
||||
"render.library_omitted": "Nicht enthalten",
|
||||
"render.library_opacity": "Deckkraft",
|
||||
"render.library_other": "Weitere Erscheinungsbilder",
|
||||
"render.library_physical_profiles": "Physical reference profiles",
|
||||
"render.library_previous": "Vorherige Seite",
|
||||
"render.library_quality_concepts": "Qualitätsspezifikationen",
|
||||
"render.library_recent": "Zuletzt",
|
||||
"render.library_replace_environment": "Vorhandene Umgebung ausdrücklich ersetzen",
|
||||
"render.library_replace_studio": "Replace saved lighting, environment and stage",
|
||||
"render.library_results": "Passende Ressourcen",
|
||||
@@ -2353,12 +2777,25 @@
|
||||
"render.library_scene_bounds": "Erfasste Szenengrenzen verwenden",
|
||||
"render.library_scenes": "Szenenspezifikationen",
|
||||
"render.library_search": "Bibliothek durchsuchen",
|
||||
"render.library_select_hint": "Wählen Sie eine Ressource in der Bibliothek, um ihre unterstützten Aktionen und Einschränkungen zu sehen.",
|
||||
"render.library_selene": "SELENE: opake Oberflächen",
|
||||
"render.library_selene_shots": "SELENE-Aufnahmespezifikationen",
|
||||
"render.library_short_blocked": "Nicht verfügbar",
|
||||
"render.library_short_native": "Native Zusammenstellung",
|
||||
"render.library_short_projection": "Unterstützte Projektion",
|
||||
"render.library_short_reference": "Nur Referenz",
|
||||
"render.library_short_specification": "Nur Spezifikation",
|
||||
"render.library_source_preview": "Vorschau der Quellmap laden",
|
||||
"render.library_source_preview_help": "Erstellte Quellmap der Grundfarbe. Dies ist keine gerenderte oder gemessene Materialvorschau.",
|
||||
"render.library_specification_only": "Nur Spezifikation: keine vollständig ausführbare Voreinstellung. Erforderliche Effekte, Ressourcen und Objektrollen müssen noch implementiert und gebunden werden.",
|
||||
"render.library_specs": "Nur Spezifikationen",
|
||||
"render.library_status": "Verfügbarkeit",
|
||||
"render.library_surfaces": "Erstellte PBR-Oberflächen",
|
||||
"render.library_target": "Zielkörper",
|
||||
"render.library_title": "Render-Ressourcenbibliothek",
|
||||
"render.library_unfavorite": "Aus Favoriten entfernen",
|
||||
"render.library_uniform_ack": "Einheitliche Näherung ohne räumliche oder erweiterte Effekte verwenden",
|
||||
"render.library_usable": "Ausführbare Projektionen",
|
||||
"render.library_users": "Verwendungen",
|
||||
"render.light_aim": "Ausrichten auf",
|
||||
"render.light_anchor_pending": "Das Koordinatensystem der Lichter kann noch nicht geändert werden.",
|
||||
@@ -2437,9 +2874,22 @@
|
||||
"render.lighting_width": "Breite U",
|
||||
"render.lighting_world_position": "Welt X Y Z",
|
||||
"render.lights_fixed_to": "Lichter gebunden an",
|
||||
"render.linear_help": "Behält ein lineares 32-Bit-Gleitkomma-RGBA-Rohbild vor Entrauschen, Belichtung, Tone-Mapping, Filtern und Hintergrund. Lineares sRGB/D65; Abdeckung vormultipliziert. Zusätzlicher Speicher wird ausdrücklich zugelassen. Keine Komprimierung und keine zusätzlichen Durchgänge.",
|
||||
"render.linear_retain": "Lineares Roh-EXR behalten",
|
||||
"render.linear_unavailable": "Für das gewählte fertige Bild wurde kein EXR behalten. Aktivieren Sie das Behalten vor dem Rendern; ein älteres PNG kann die rohe Strahldichte nicht wiederherstellen.",
|
||||
"render.look_add": "Farblook hinzufügen",
|
||||
"render.look_applied": "{name} angewendet",
|
||||
"render.look_bleach": "Von Bleach-Bypass inspiriert",
|
||||
"render.look_cool": "Kühl",
|
||||
"render.look_cyanotype": "Von Cyanotypie inspiriert",
|
||||
"render.look_faded": "Verblasster Abzug",
|
||||
"render.look_material": "Material",
|
||||
"render.look_mono": "Monochrom",
|
||||
"render.look_pasted": "Look eingefügt",
|
||||
"render.look_sepia": "Sepia",
|
||||
"render.look_strength": "Look-Stärke",
|
||||
"render.look_teal_orange": "Türkis / Orange",
|
||||
"render.look_title": "Stapel kreativer Looks",
|
||||
"render.material_color_replace": "Basismap ersetzt Körperfarbe",
|
||||
"render.material_limits": "Höchstens 128 Körperüberschreibungen, 32 getrennte Glaskörper, 64 Beschichtungen und 128 Texturbindungen. Identische Maps teilen Speicher; Geräte- und Gesamtspeichergrenzen bleiben aktiv.",
|
||||
"render.material_metallic_map": "Metallanteil-Map (linear)",
|
||||
@@ -2475,7 +2925,9 @@
|
||||
"render.output_ready": "Bereit zum Rendern",
|
||||
"render.output_run_check": "Prüfung starten",
|
||||
"render.output_size_limit": "{method}: bis zu {dimension} px pro Seite und {pixels} Pixel. Größte passende Größe: {width} × {height}.",
|
||||
"render.output_size_method_profile": "Für {width} × {height} {method} mit dem CPU-Ressourcenprofil {profile} wählen oder eine kleinere Größe.",
|
||||
"render.output_size_ok": "Größe OK · {width} × {height} px",
|
||||
"render.output_size_profile": "Für {width} × {height} das CPU-Ressourcenprofil {profile} wählen oder eine kleinere Größe.",
|
||||
"render.output_tools_inspector": "Größe und Format im Ausgabe-Inspektor bearbeiten.",
|
||||
"render.output_view_image": "Bild anzeigen",
|
||||
"render.paint_label": "Aufmalen",
|
||||
@@ -2607,6 +3059,7 @@
|
||||
"render.size_custom": "Benutzerdefiniert…",
|
||||
"render.size_full_hd": "Full HD",
|
||||
"render.size_hd": "HD",
|
||||
"render.size_needs_profile": "{size} · erfordert {profile}",
|
||||
"render.size_presets": "Größenvorlagen",
|
||||
"render.size_presets_pending": "Kommt mit dem Ausgabeeditor",
|
||||
"render.size_preview": "Vorschau",
|
||||
@@ -2829,6 +3282,24 @@
|
||||
"sheet.flange_type_base": "Basislasche",
|
||||
"sheet.flange_type_contour": "Konturlasche",
|
||||
"sheet.flange_type_edge": "Kantenlasche",
|
||||
"sheet.personal_rules.confirm_delete": "Löschen bestätigen",
|
||||
"sheet.personal_rules.delete": "Löschen",
|
||||
"sheet.personal_rules.delete_prompt": "Möchten Sie diese persönliche Regel wirklich löschen? Regeln, die bereits in Entwürfen sind, bleiben unverändert.",
|
||||
"sheet.personal_rules.full": "Die persönliche Bibliothek enthält bereits 64 Regeln.",
|
||||
"sheet.personal_rules.invalid_name": "Geben Sie einen kurzen Namen ohne Zeilenumbrüche oder Steuerzeichen ein.",
|
||||
"sheet.personal_rules.invalid_values": "Vervollständigen Sie die Werte der Regel: Jeder muss sich in diesem Entwurf berechnen lassen, mit positiver Dicke und einem K-Faktor von 0 bis 1.",
|
||||
"sheet.personal_rules.missing": "Die ausgewählte persönliche Regel ist nicht mehr verfügbar.",
|
||||
"sheet.personal_rules.name": "Regelname",
|
||||
"sheet.personal_rules.name_exists": "Eine persönliche Regel mit diesem Namen existiert bereits. Wählen Sie einen anderen Namen.",
|
||||
"sheet.personal_rules.name_hint": "Eindeutigen Namen eingeben",
|
||||
"sheet.personal_rules.overwrite": "Änderungen in der ausgewählten persönlichen Regel speichern",
|
||||
"sheet.personal_rules.rename": "Umbenennen",
|
||||
"sheet.personal_rules.rule": "Persönliche Regel",
|
||||
"sheet.personal_rules.save": "Als neue Regel speichern",
|
||||
"sheet.personal_rules.scope": "In Ihrem Profil gespeichert und für alle Entwürfe verfügbar.",
|
||||
"sheet.personal_rules.select": "Regel auswählen",
|
||||
"sheet.personal_rules.title": "Persönliche Regeln",
|
||||
"sheet.personal_rules.unsupported": "Diese Regel enthält Werte, die diese Version nicht verwenden kann.",
|
||||
"shortcut.json_error": "Parameter müssen JSON sein: {error}",
|
||||
"shortcut.no_command": "kein Befehl `{id}`; der Text vor dem JSON muss eine Befehls-ID sein",
|
||||
"shortcut.pin": "Oben im S-Feld anheften",
|
||||
@@ -2991,6 +3462,8 @@
|
||||
"ui.3d_mouse_section": "3D-Maus",
|
||||
"ui.3d_mouse_show_in_view": "3D-Maus in der 3D-Ansicht anzeigen",
|
||||
"ui.3d_mouse_show_in_view_tip": "Ein Bild Ihrer 3D-Maus unten links, das sich mit ihr bewegt.",
|
||||
"ui.3d_mouse_sketch_orbit": "Skizzen mit der 3D-Maus drehen",
|
||||
"ui.3d_mouse_sketch_orbit_tip": "Die 3D-Maus dreht die Ansicht auch, solange eine Skizze offen ist und frontal gesehen wird, wie es die Maus kann. Aus: Sie verschiebt und zoomt die Skizze, bis Sie die Ansicht neigen.",
|
||||
"ui.3d_mouse_skip": "Mein Gerät kann das nicht",
|
||||
"ui.3d_mouse_skip_tip": "Überspringt diese Bewegung in beide Richtungen; die Kalibrierung lässt sie aus.",
|
||||
"ui.3d_mouse_speed": "Geschwindigkeit der 3D-Maus",
|
||||
@@ -3019,6 +3492,8 @@
|
||||
"ui.3d_mouse_step_twist_counterclockwise": "Kappe gegen den Uhrzeigersinn drehen, halten, dann loslassen.",
|
||||
"ui.3d_mouse_switch_cap": "Als Zurück zur Mitte kalibrieren",
|
||||
"ui.3d_mouse_switch_trackball": "Als Trackball kalibrieren",
|
||||
"ui.3d_mouse_use_direct": "Direkten Treiber verwenden",
|
||||
"ui.3d_mouse_use_direct_tip": "Die 3D-Maus direkt lesen (hidraw) statt über spacenavd.",
|
||||
"ui.3d_mouse_view_axis": "Ansichtsachse",
|
||||
"ui.3d_mouse_view_off": "Aus",
|
||||
"ui.3d_mouse_view_off_tip": "Das Profil des 3D-Maus-Treibers schaltet diese Achse ab, daher bewegt ihre Bewegung die Ansicht nicht. Wählen Sie hier die Bewegung einer anderen Achse, werden die beiden getauscht und sie bewegt die Ansicht nicht mehr.",
|
||||
@@ -3134,6 +3609,8 @@
|
||||
"ui.agent_chat_remove": "Aus der Warteschlange entfernen",
|
||||
"ui.agent_chat_remove_file": "Von dieser Nachricht entfernen",
|
||||
"ui.agent_chat_remove_imported": "Von dieser Nachricht entfernen; das Teil bleibt im Design (Rückgängig entfernt es)",
|
||||
"ui.agent_chat_reply_cut_off": "Die Antwort hat ihre Längengrenze erreicht, bevor sie fertig war, und ist daher vielleicht unvollständig. Bitten Sie den Agenten weiterzumachen oder kürzer zu antworten.",
|
||||
"ui.agent_chat_reply_declined": "Das Modell hat abgelehnt, auf diese Nachricht zu antworten.",
|
||||
"ui.agent_chat_right": "Rechts",
|
||||
"ui.agent_chat_save": "Speichern",
|
||||
"ui.agent_chat_send": "Senden",
|
||||
@@ -3285,6 +3762,7 @@
|
||||
"ui.circle": "Kreis",
|
||||
"ui.circular_pattern": "Radiales Muster",
|
||||
"ui.clear": "Leeren",
|
||||
"ui.clear_print_face": "Auflagefläche entfernen",
|
||||
"ui.clear_recent": "Zuletzt verwendet leeren",
|
||||
"ui.clear_the_selection": "Auswahl aufheben",
|
||||
"ui.click_selects_whole_bodies": "Klick wählt ganze Körper aus",
|
||||
@@ -3319,6 +3797,15 @@
|
||||
"ui.constant": "Konstant",
|
||||
"ui.constraints_conflict": "Bedingungen konfliktreich",
|
||||
"ui.construction": "Konstruktionslinie",
|
||||
"ui.construction.angle_hint": "Konstruktionslinie: Punkt anklicken, dann Winkel eingeben oder Richtung wählen",
|
||||
"ui.construction.bisector_hint": "Winkelhalbierende: zwei sich schneidende Geraden wählen, dann gewünschte Lösung",
|
||||
"ui.construction.common_hint": "Gemeinsame Tangente: zwei Kreise/Bögen wählen, dann gewünschte Lösung",
|
||||
"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.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.context_menu": "Kontextmenü",
|
||||
"ui.continuity_g0": "G0 Knick",
|
||||
"ui.continuity_g1": "G1 Tangente",
|
||||
@@ -3678,12 +4165,17 @@
|
||||
"ui.plugins_skipped": "{count} Ordner konnte(n) nicht gelesen werden.",
|
||||
"ui.plugins_version": "{version}",
|
||||
"ui.point": "Punkt",
|
||||
"ui.point_cloud_add_filter": "Filter hinzufügen…",
|
||||
"ui.point_cloud_auto_range": "Bereich automatisch",
|
||||
"ui.point_cloud_color_by": "Färben nach",
|
||||
"ui.point_cloud_file_colors": "Farben der Datei",
|
||||
"ui.point_cloud_filter_by": "Filtern nach",
|
||||
"ui.point_cloud_height_high": "Oberste Höhe",
|
||||
"ui.point_cloud_height_low": "Unterste Höhe",
|
||||
"ui.point_cloud_legend_filter": "In der Legende oben ein- und ausgeblendet",
|
||||
"ui.point_cloud_no_file_colors": "Die Datei hat keine Farben",
|
||||
"ui.point_cloud_pause": "Pause",
|
||||
"ui.point_cloud_play": "Abspielen",
|
||||
"ui.point_cloud_single_color": "Einheitliche Farbe",
|
||||
"ui.point_cloud_size": "Punktgröße",
|
||||
"ui.point_cloud_units": "Einheit der Datei",
|
||||
@@ -3692,6 +4184,11 @@
|
||||
"ui.polygon": "Polygon",
|
||||
"ui.preferences": "Einstellungen",
|
||||
"ui.press_a_key": "Taste drücken…",
|
||||
"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",
|
||||
"ui.profile_plane": "Profilebene",
|
||||
"ui.profiles": "Profile",
|
||||
@@ -3752,6 +4249,7 @@
|
||||
"ui.revolve": "Drehen",
|
||||
"ui.ribbon_exit": "Beenden",
|
||||
"ui.ribbon_labels": "Text unter den Symbolen im Menüband anzeigen",
|
||||
"ui.ribbon_menus_skip_pinned": "Angeheftete Befehle nicht in den Aufklappmenüs wiederholen",
|
||||
"ui.ribbon_pin_footer": "Angeheftete Befehle erscheinen im Menüband. Fahren Sie über eine Zeile, um sie anzuheften oder zu lösen.",
|
||||
"ui.ribbon_workspace": "Arbeitsbereich",
|
||||
"ui.right": "Rechts",
|
||||
@@ -3769,6 +4267,7 @@
|
||||
"ui.selectOther": "Andere auswählen…",
|
||||
"ui.selectTangentChain": "Tangentialkette auswählen",
|
||||
"ui.select_all": "Alles auswählen",
|
||||
"ui.select_components_by_face": "Komponenten auswählen (eine Fläche wählt ihre Komponente)",
|
||||
"ui.selection": "Auswahl",
|
||||
"ui.selection_modifiers": "Auswahlmodifikatoren",
|
||||
"ui.selection_shift_add_only": "Umschalt+Klick fügt hinzu, ohne zu entfernen",
|
||||
@@ -3806,6 +4305,11 @@
|
||||
"ui.sketch_intersect_selection_filter": "Auswahlfilter",
|
||||
"ui.sketch_modifiers": "Skizzenmodifikatoren",
|
||||
"ui.sketch_project_geometry": "Geometrie",
|
||||
"ui.sketch_report.all_constrained": "Alles ist vollständig bestimmt.",
|
||||
"ui.sketch_report.dof": "Freiheitsgrade",
|
||||
"ui.sketch_report.loose": "Nicht vollständig bestimmt",
|
||||
"ui.sketch_report.status": "Status",
|
||||
"ui.sketch_report.title": "Skizzenbericht",
|
||||
"ui.sketch_scale": "Skizzenskalierung",
|
||||
"ui.sketches": "Skizzen",
|
||||
"ui.slice": "Scheibe",
|
||||
@@ -3897,10 +4401,19 @@
|
||||
"ui.view_centre": "Ansichtszentrum",
|
||||
"ui.viewport_status_clear": "{name} ausschalten",
|
||||
"ui.viewport_status_clear_hint": "{name} ausschalten · Esc",
|
||||
"ui.viewport_status_edges": "{count} Kanten",
|
||||
"ui.viewport_status_from": "aus {direction}",
|
||||
"ui.viewport_status_key": "Legende für {name} anzeigen",
|
||||
"ui.viewport_status_minimum_radius": "Mindestradius",
|
||||
"ui.viewport_status_pairs": "{count} Paare",
|
||||
"ui.viewport_status_section_hatch": "{setting} · Schraffur",
|
||||
"ui.viewport_status_section_plane": "Aktuelle Ebene",
|
||||
"ui.viewport_status_section_tilted": "geneigt · {offset}",
|
||||
"ui.viewport_status_stripes": "{count} Streifen",
|
||||
"ui.viewport_status_summary_clear": "Alles ausschalten",
|
||||
"ui.viewport_status_summary_heading": "In dieser Ansicht",
|
||||
"ui.viewport_status_summary_label": "{name} und {count} weitere Elemente",
|
||||
"ui.viewport_status_summary_more": "+{count}",
|
||||
"ui.viewport_theme_follow": "Dem Design folgen",
|
||||
"ui.visual_shaded_hidden_edges": "Schattiert mit verdeckten Kanten",
|
||||
"ui.visual_shaded_with_edges": "Schattiert mit Kanten",
|
||||
@@ -3933,7 +4446,9 @@
|
||||
"update.button": "Update",
|
||||
"update.button_version": "Auf {version} aktualisieren",
|
||||
"update.check_automatically": "Automatisch nach Updates suchen",
|
||||
"update.check_for_updates": "Nach Updates suchen",
|
||||
"update.check_now": "Jetzt suchen",
|
||||
"update.check_unavailable": "Für diese Installation ist keine Update-Prüfung verfügbar.",
|
||||
"update.checking": "Suche läuft…",
|
||||
"update.copy": "Kopieren",
|
||||
"update.dmg_opened": "Das Disk-Image ist geöffnet und geprüft: SolidCraft in „Programme“ ziehen, die alte Version ersetzen und SolidCraft neu starten.",
|
||||
|
||||
@@ -1,22 +1,42 @@
|
||||
{
|
||||
"A Single hole snaps to the middle of its face and to the centres of its round edges": "A Single hole snaps to the middle of its face and to the centres of its round edges",
|
||||
"Base Center": "Base Centre",
|
||||
"Center X": "Centre X",
|
||||
"Center Y": "Centre Y",
|
||||
"Center of mass": "Centre of mass",
|
||||
"Centre to centre": "Centre to centre",
|
||||
"Circle center": "Circle centre",
|
||||
"Colour": "Colour",
|
||||
"Copy all licences": "Copy all licences",
|
||||
"Customize Toolbar": "Customise Toolbar",
|
||||
"Customize…": "Customise…",
|
||||
"Face center": "Face centre",
|
||||
"Favorites": "Favourites",
|
||||
"Favourite": "Favourite",
|
||||
"Mass Center": "Mass Centre",
|
||||
"Modeled": "Modelled",
|
||||
"Select the centre the pattern turns around first": "Select the centre the pattern turns around first",
|
||||
"Snap to face and edge centres": "Snap to face and edge centres",
|
||||
"Specialise": "Specialise",
|
||||
"cmd.appearance_favorite": "Favourite Look",
|
||||
"cmd.component_cycle_color": "Cycle Component Colour",
|
||||
"cmd.construct_point_center": "Point At Centre Of Circle/Sphere/Torus",
|
||||
"cmd.inspect_center_of_mass": "Centre of Mass",
|
||||
"cmd.inspect_colored_components": "Enable Component Colours",
|
||||
"cmd.inspect_colored_components_disable": "Disable Component Colours",
|
||||
"cmd.parameters_favorite": "Favourite Parameter",
|
||||
"cmd.render_aging_describe": "Layered Surface Aging",
|
||||
"cmd.render_effects_describe": "Lens and Creative Effects",
|
||||
"cmd.sketch.construction_line": "Construction Line",
|
||||
"cmd.sketch.construction_line.angle": "Construction Line at Angle",
|
||||
"cmd.sketch.construction_line.bisector": "Angle Bisector",
|
||||
"cmd.sketch.construction_line.horizontal": "Horizontal Construction Line",
|
||||
"cmd.sketch.construction_line.parallel": "Parallel Construction Line",
|
||||
"cmd.sketch.construction_line.perpendicular": "Perpendicular Construction Line",
|
||||
"cmd.sketch.construction_line.tangent_at": "Tangent at Curve Point",
|
||||
"cmd.sketch.construction_line.tangent_external": "Tangent through External Point",
|
||||
"cmd.sketch.construction_line.tangent_two": "Common Tangent",
|
||||
"cmd.sketch.construction_line.vertical": "Vertical Construction Line",
|
||||
"cmd.sketch_arc_center_point": "Centre Point Arc",
|
||||
"cmd.sketch_circle_center": "Centre Diameter Circle",
|
||||
"cmd.sketch_rectangle_center": "Centre Rectangle",
|
||||
@@ -28,7 +48,26 @@
|
||||
"color.gamma": "Showing colour values in 0-255 gamma space",
|
||||
"color.linear": "Showing colour values in 0-1 linear space",
|
||||
"color.selected": "Selected colour",
|
||||
"dxf.cancelled": "DXF export cancelled",
|
||||
"dxf.cancelling": "Cancelling DXF export",
|
||||
"help.apache": "Apache licence 2.0",
|
||||
"help.appearance.assign": "Gives the selected bodies, faces or components a colour or a material look, without changing their weight. Use it to tell parts apart or to preview a finish.",
|
||||
"help.face.delete": "Removes the selected faces and lets the neighbouring faces close the gap, or leaves the gap open as a surface. Use it to take off a rounded edge, a hole or a boss that is in the way.",
|
||||
"help.font_details": "The interface uses egui's built-in fonts (Ubuntu Light and Hack, under the Ubuntu Font Licence and the MIT/Bitstream Vera licence; Noto Emoji and the emoji icon font, under the OFL and MIT). Sketch text uses Liberation Sans (SIL OFL 1.1). The Crafting Apps' optional font pack, craft-fonts (BIZ UDPGothic, Shippori Mincho, BIZ UDMincho, Noto Sans CJK SC, Noto Sans Arabic; SIL OFL 1.1), is not bundled in SolidCraft builds.",
|
||||
"help.licences": "Licences",
|
||||
"help.material.assign": "Sets what the selected bodies are made of, such as steel, aluminium or ABS. Use it so that the mass and weight match the real part.",
|
||||
"help.mit": "MIT licence",
|
||||
"help.solid.draft": "Tilts the selected faces by the angle you set, measured from a neutral plane. Use it on moulded or cast parts so that they slide out of the mould.",
|
||||
"help.solid.revolve": "Spins a sketch profile around an axis to make a round solid. Use it for shafts, knobs, bowls and any part that is round about a centre line.",
|
||||
"help.solid.split_body": "Cuts a body into separate bodies with a plane, the face of another body or a sketch line. Use it to make parts that are printed, machined or coloured on their own.",
|
||||
"help.solid.split_face": "Divides the selected faces along a plane, the face of another body or sketch curves, without cutting the body. Use it to give part of a face its own draft, colour or offset.",
|
||||
"help.third_party": "Third-party licences",
|
||||
"home.new_mm_hint": "Millimetres · opens in Design",
|
||||
"navbar.custom.customise": "Customise navigation bar…",
|
||||
"navbar.custom.position_centre": "Centre",
|
||||
"navbar.description.separator": "A thin line that groups neighbouring slots.",
|
||||
"pattern.alignment_hint": "Counts include the original. Centre balances copies; Flip chooses the extra side for even totals.",
|
||||
"prefs.bottom_centre": "Bottom centre",
|
||||
"prefs.logo_theme": "Theme's colours",
|
||||
"prefs.max_file_size": "Largest file to open",
|
||||
"prefs.max_file_size_design": "Design files",
|
||||
@@ -39,6 +78,7 @@
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.sketch_conflict_colour": "Conflicting geometry in the conflict colour",
|
||||
"prefs.sketch_conflict_colour_note": "While a sketch's constraints conflict, the curves and points a failing constraint holds are drawn in the conflict colour too, not only its glyph or dimension. Off: they keep their usual colours.",
|
||||
"prefs.sketch_construction_lines": "Extended construction line tools",
|
||||
"prefs.sketch_recent_snap": "Auto snap to recent centres and midpoints",
|
||||
"prefs.sketch_recent_snap_note": "While drawing, snap the cursor where the guides of the two most recent circle centres or straight-edge midpoints cross. Off: those guides stay visual and the cursor does not move.",
|
||||
"prefs.sketch_state_icons": "Sketch icons in the colour of their state",
|
||||
@@ -52,6 +92,7 @@
|
||||
"prefs.theme_follow_note": "Reloaded whenever another tool rewrites it. Choosing a theme stops following it.",
|
||||
"prefs.theme_follow_off": "Off",
|
||||
"prefs.theme_follow_stop": "Stop following",
|
||||
"prefs.trackpad_zoom_modifier.help": "Hold only this key to zoom at the pointer with vertical scroll. Off keeps the existing scroll behaviour.",
|
||||
"prefs.view": "View behaviour",
|
||||
"prefs.viewport_background_colour": "Colour",
|
||||
"render.aging_add": "Add aging layer",
|
||||
@@ -81,6 +122,7 @@
|
||||
"render.aging_target_help": "A named target must still match its captured index and name. Global masks are world-anchored; they do not follow moving parts. This is neither cavity detection nor true edge wear. Filter the list to reach additional bodies.",
|
||||
"render.aging_target_search": "Filter body names",
|
||||
"render.aging_upward": "Upward-facing deposition",
|
||||
"render.appearance_editor.base_colour": "Base colour",
|
||||
"render.backdrop_first": "Start / centre colour",
|
||||
"render.backdrop_second": "End / edge colour",
|
||||
"render.backdrop_x": "Centre X",
|
||||
@@ -97,21 +139,31 @@
|
||||
"render.base_color": "Base colour · sRGB",
|
||||
"render.bloom_help": "Creative output-only glow from positive linear highlights, spread with three normalised 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.chromatic_offset": "Chromatic edge offset",
|
||||
"render.cpu_area_editor_help": "Full side vectors in millimeters; U cross V points towards the emitting side. This differs from GPU half-edge vectors.",
|
||||
"render.cpu_light_help": "Up to eight directional lights. An empty custom list turns them off; selecting a preset restores its lights. Colours are linear RGB and intensity is relative, not watts.",
|
||||
"render.cpu_linear_color": "Linear colour (R, G, B)",
|
||||
"render.effects_gpu_only": "These effects require GPU path tracing. Changing the page never switches the renderer automatically.",
|
||||
"render.effects_help": "Combine ordered colour looks and image-space lens effects. Rendering and raw-master regrading use the same saved settings. Raw EXR stays unchanged. These are original artistic controls, not calibrated lens or branded-film emulations.",
|
||||
"render.effects_reset_lens": "Reset lens effects",
|
||||
"render.effects_seed": "Pattern seed",
|
||||
"render.env_colour": "Colour",
|
||||
"render.environment.radial_only": "Centre and radius apply to Radial mode.",
|
||||
"render.error_finish_gesture": "finish the modelling gesture before previewing",
|
||||
"render.error_finish_operation": "finish the current modelling operation before entering Render",
|
||||
"render.film_grain": "Photographic grain",
|
||||
"render.flare_ghosts": "Ghost rings per source",
|
||||
"render.flare_halo": "Flare halo",
|
||||
"render.flare_streak": "Anamorphic-style streak",
|
||||
"render.flare_strength": "Highlight flare strength",
|
||||
"render.flare_threshold": "Scene-linear flare threshold",
|
||||
"render.frame_centre_mark": "Centre mark",
|
||||
"render.hdr_primaries_declared": "The source declares its colour primaries.",
|
||||
"render.hdr_primaries_srgb": "Colour primaries: sRGB/D65",
|
||||
"render.hdr_primaries_undeclared": "The source does not declare primaries; review the renderer colour requirements.",
|
||||
"render.image_cancelled": "Render cancelled",
|
||||
"render.image_cancelling": "Cancelling",
|
||||
"render.image_scene_stale": "Finish the modelling gesture or refresh the scene before checking or starting an image.",
|
||||
"render.inspector_colour": "Colour",
|
||||
"render.layer_down": "Move down",
|
||||
"render.layer_enabled": "Layer enabled",
|
||||
"render.layer_remove": "Remove layer",
|
||||
@@ -126,6 +178,11 @@
|
||||
"render.library_rig_help": "Fit once at an explicit centre and physical extent. Complete rigs include passive cards; calibration blockers still refuse. Conservative emitter/card clearance is checked before saving.",
|
||||
"render.library_source_preview_help": "Authored base-colour source map. This is not a rendered or measured material preview.",
|
||||
"render.library_unfavorite": "Remove favourite",
|
||||
"render.light_behaviour": "BEHAVIOUR",
|
||||
"render.light_preview_help": "Live view is unshadowed; Area lights use a centre-sample approximation. Render uses the complete light model.",
|
||||
"render.light_product_centre": "Product centre",
|
||||
"render.light_temperature": "Colour temperature",
|
||||
"render.lighting_colour": "Colour",
|
||||
"render.look_add": "Add colour look",
|
||||
"render.look_bleach": "Bleach-bypass-inspired",
|
||||
"render.look_cool": "Cool",
|
||||
@@ -149,30 +206,45 @@
|
||||
"render.radial_distortion": "Radial distortion",
|
||||
"render.ribbon_aging": "Aging",
|
||||
"render.ribbon_effects": "Effects",
|
||||
"render.ribbon_studios_help": "Browse implemented studio compositions and review their replacements before applying. Concept-only scenes remain labelled.",
|
||||
"render.sharpening": "Detail sharpening",
|
||||
"render.soft_focus": "Soft focus",
|
||||
"render.stage_panels.centre": "Centre",
|
||||
"render.stage_panels.source_note": "Authored base-colour source map. Not a measured material.",
|
||||
"render.studio_area_help": "Up to four one-sided rectangles. Orthogonal half-edges U and V define size; U × V faces the lit side. Coordinates are world-space millimetres.",
|
||||
"render.studio_center": "Centre X, Y, Z (mm)",
|
||||
"render.studio_hdr_help": "Radiance .hdr, 2:1 panorama, up to 2048 × 1024. EXR is unavailable. The colour assumption applies only when primaries are unspecified.",
|
||||
"sketch_text.align_center": "Centre",
|
||||
"ui.3d_mouse_kind_cap": "Return to Centre",
|
||||
"ui.3d_mouse_looks_like_cap": "The reading stays put while you hold it, the way a Return to Centre device does.",
|
||||
"ui.3d_mouse_orbit_tip": "Traced: the 3D mouse turns the view around the part of the model in the middle of the view when the turn begins, marked by a faint ball. Classic: around the view's centre",
|
||||
"ui.3d_mouse_step_pull": "Pull the cap towards you, hold it, then let go.",
|
||||
"ui.3d_mouse_switch_cap": "Calibrate as Return to Centre",
|
||||
"ui.agent_chat_minimize": "Minimise to a strip",
|
||||
"ui.ai_mode_edits_note": "Modelling commands run without asking; saving, opening, exporting and other file or destructive actions still ask.",
|
||||
"ui.center": "Centre",
|
||||
"ui.center_arc_follows_cursor": "Centre point arc follows the cursor's direction",
|
||||
"ui.center_arc_follows_cursor_tip": "A centre point arc runs clockwise or counter-clockwise the way the cursor goes round its centre. Off: always counter-clockwise from the start.",
|
||||
"ui.centerline": "Centreline",
|
||||
"ui.colour_vision": "Colour vision",
|
||||
"ui.colour_vision_note": "Changes the colours that carry a meaning: sketch states, axes, bends, analyses and icons. If you use Windows colour filters, keep this Off.",
|
||||
"ui.colour_vision_off_tip": "The theme's own colours",
|
||||
"ui.construction.ribbon_label": "X-Lines",
|
||||
"ui.customizeToolbar": "Customise Toolbar…",
|
||||
"ui.default_modelling_orientation": "Default modelling orientation",
|
||||
"ui.design_properties.license": "Licence",
|
||||
"ui.design_properties.organization": "Organisation",
|
||||
"ui.export_keymap_tip": "Keys, S box favourites and the toolbar, as a file",
|
||||
"ui.layout_compact": "Compact",
|
||||
"ui.layout_floating": "Floating",
|
||||
"ui.material_review.claims": "Sources and licences are unverified claims. Missing values remain unknown.",
|
||||
"ui.point_cloud_color_by": "Colour By",
|
||||
"ui.point_cloud_file_colors": "File Colours",
|
||||
"ui.point_cloud_no_file_colors": "The file has no colours",
|
||||
"ui.point_cloud_single_color": "Single Colour",
|
||||
"ui.properties.inertia_note": "About the centre of mass, in world axes (g·{unit}²)",
|
||||
"ui.ribbon_labels": "Show text under ribbon icons",
|
||||
"ui.view_centre": "View centre",
|
||||
"ui.workspace_layout": "Workspace layout",
|
||||
"ui.zoom_to_cursor_tip": "The wheel zooms towards the point under the cursor (off: towards the view's centre)",
|
||||
"update.prefs_flatpak": "Flatpak updates this copy ({version}): run {command} or use your software centre. SolidCraft doesn't check."
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"2. Attach your screenshot: copy and paste it, or save and upload the PNG.": "2. Attach your screenshot: copy and paste it, or save and upload the PNG.",
|
||||
"2D": "2D",
|
||||
"3. Click Submit new issue on GitHub to send your report.": "3. Click Submit new issue on GitHub to send your report.",
|
||||
"A Single hole snaps to the middle of its face and to the centres of its round edges": "A Single hole snaps to the middle of its face and to the centres of its round edges",
|
||||
"A Single hole snaps to the middle of its face and to the centres of its round edges": "A Single hole snaps to the middle of its face and to the centers of its round edges",
|
||||
"A new configuration, a copy of the active one": "A new configuration, a copy of the active one",
|
||||
"A placed Single hole has a handle to drag it, and picking its face again moves the hole there instead of taking it out": "A placed Single hole has a handle to drag it, and picking its face again moves the hole there instead of taking it out",
|
||||
"A short title for the problem (optional)": "A short title for the problem (optional)",
|
||||
@@ -60,6 +60,7 @@
|
||||
"BLEND SURFACE": "BLEND SURFACE",
|
||||
"BOUNDARY SURFACE": "BOUNDARY SURFACE",
|
||||
"BROWSER": "BROWSER",
|
||||
"Back to the original order": "Back to the original order",
|
||||
"Ball": "Ball",
|
||||
"Bar": "Bar",
|
||||
"Base Center": "Base Center",
|
||||
@@ -100,7 +101,7 @@
|
||||
"Center X": "Center X",
|
||||
"Center Y": "Center Y",
|
||||
"Center of mass": "Center of mass",
|
||||
"Centre to centre": "Centre to centre",
|
||||
"Centre to centre": "Center to center",
|
||||
"Centroid": "Centroid",
|
||||
"Chamfer Type": "Chamfer Type",
|
||||
"Channel": "Channel",
|
||||
@@ -117,7 +118,7 @@
|
||||
"Close draft": "Close draft",
|
||||
"Collapse": "Collapse",
|
||||
"Collisions": "Collisions",
|
||||
"Colour": "Colour",
|
||||
"Colour": "Color",
|
||||
"Command": "Command",
|
||||
"Comment": "Comment",
|
||||
"Component 1": "Component 1",
|
||||
@@ -137,7 +138,7 @@
|
||||
"Copies the definition only. Assign it from Physical Material when ready.": "Copies the definition only. Assign it from Physical Material when ready.",
|
||||
"Copy Error Message": "Copy Error Message",
|
||||
"Copy Warning Message": "Copy Warning Message",
|
||||
"Copy all licences": "Copy all licences",
|
||||
"Copy all licences": "Copy all licenses",
|
||||
"Copy diagnostic information": "Copy diagnostic information",
|
||||
"Copy report": "Copy report",
|
||||
"Copy screenshot": "Copy screenshot",
|
||||
@@ -244,7 +245,7 @@
|
||||
"Factor must be a positive number": "Factor must be a positive number",
|
||||
"Fastener": "Fastener",
|
||||
"Favorites": "Favorites",
|
||||
"Favourite": "Favourite",
|
||||
"Favourite": "Favorite",
|
||||
"Feature": "Feature",
|
||||
"Files from many CAD programs have Y up, so they open lying on their back here (Z up).": "Files from many CAD programs have Y up, so they open lying on their back here (Z up).",
|
||||
"Filter": "Filter",
|
||||
@@ -563,7 +564,7 @@
|
||||
"Select bodies or faces instead": "Select bodies or faces instead",
|
||||
"Select edges": "Select edges",
|
||||
"Select points of the sketch being edited": "Select points of the sketch being edited",
|
||||
"Select the centre the pattern turns around first": "Select the centre the pattern turns around first",
|
||||
"Select the centre the pattern turns around first": "Select the center the pattern turns around first",
|
||||
"Select the curves to offset first": "Select the curves to offset first",
|
||||
"Select the mirror line": "Select the mirror line",
|
||||
"Select the sketch curves or points to mirror first": "Select the sketch curves or points to mirror first",
|
||||
@@ -592,12 +593,14 @@
|
||||
"Sketch points": "Sketch points",
|
||||
"Slider": "Slider",
|
||||
"Slot width": "Slot width",
|
||||
"Snap to face and edge centres": "Snap to face and edge centres",
|
||||
"Snap to face and edge centres": "Snap to face and edge centers",
|
||||
"SolidCraft cannot submit reports directly. Submit on GitHub; no token is needed in the app.": "SolidCraft cannot submit reports directly. Submit on GitHub; no token is needed in the app.",
|
||||
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft closed before these designs were saved. Their last autosave can be opened.",
|
||||
"Sort A to Z": "Sort A to Z",
|
||||
"Sort Z to A": "Sort Z to A",
|
||||
"Spacing 2 must be a non-zero number": "Spacing 2 must be a non-zero number",
|
||||
"Spacing must be a non-zero number": "Spacing must be a non-zero number",
|
||||
"Specialise": "Specialise",
|
||||
"Specialise": "Specialize",
|
||||
"Speed": "Speed",
|
||||
"Splitting tool": "Splitting tool",
|
||||
"Square": "Square",
|
||||
@@ -775,6 +778,8 @@
|
||||
"at the face": "at the face",
|
||||
"bom.group_children": "Sub-parts in their part's row",
|
||||
"browser.body_name": "Body{number}",
|
||||
"browser.calibrate_click_a": "Click the image for Point A, or type its coordinates.",
|
||||
"browser.calibrate_click_b": "Click the image for Point B, or type its coordinates.",
|
||||
"browser.calibrate_title": "Calibrate: {name}",
|
||||
"browser.capture_many": "{count} components were moved but their positions aren't captured.",
|
||||
"browser.capture_note": "Capture keeps the new position; Revert puts it back.",
|
||||
@@ -809,7 +814,7 @@
|
||||
"cmd.appearance_assign": "Appearance",
|
||||
"cmd.appearance_chain": "Appearance Chain",
|
||||
"cmd.appearance_copy": "Copy Look",
|
||||
"cmd.appearance_favorite": "Favourite Look",
|
||||
"cmd.appearance_favorite": "Favorite Look",
|
||||
"cmd.appearance_library": "Appearance Library",
|
||||
"cmd.appearance_list": "List Appearances",
|
||||
"cmd.appearance_paint": "Paint Appearance",
|
||||
@@ -817,6 +822,7 @@
|
||||
"cmd.appearance_pick_up": "Pick Up Appearance",
|
||||
"cmd.appearance_replace": "Replace Appearance",
|
||||
"cmd.assembly_bom": "Bill of Materials",
|
||||
"cmd.body.print_face": "Print Face",
|
||||
"cmd.body_rename": "Rename Body",
|
||||
"cmd.browser_collapse_all": "Collapse All",
|
||||
"cmd.browser_expand_all": "Expand All",
|
||||
@@ -1003,7 +1009,7 @@
|
||||
"cmd.parameters_complete": "Complete Expression",
|
||||
"cmd.parameters_delete": "Delete Parameter",
|
||||
"cmd.parameters_export": "Export Parameters",
|
||||
"cmd.parameters_favorite": "Favourite Parameter",
|
||||
"cmd.parameters_favorite": "Favorite Parameter",
|
||||
"cmd.parameters_graph": "Parameter Graph",
|
||||
"cmd.parameters_import": "Import Parameters",
|
||||
"cmd.parameters_list": "List Parameters",
|
||||
@@ -1101,6 +1107,16 @@
|
||||
"cmd.sheet_refold": "Refold",
|
||||
"cmd.sheet_rules": "List Sheet Metal Rules",
|
||||
"cmd.sheet_unfold": "Unfold",
|
||||
"cmd.sketch.construction_line": "Construction Line",
|
||||
"cmd.sketch.construction_line.angle": "Construction Line at Angle",
|
||||
"cmd.sketch.construction_line.bisector": "Angle Bisector",
|
||||
"cmd.sketch.construction_line.horizontal": "Horizontal Construction Line",
|
||||
"cmd.sketch.construction_line.parallel": "Parallel Construction Line",
|
||||
"cmd.sketch.construction_line.perpendicular": "Perpendicular Construction Line",
|
||||
"cmd.sketch.construction_line.tangent_at": "Tangent at Curve Point",
|
||||
"cmd.sketch.construction_line.tangent_external": "Tangent through External Point",
|
||||
"cmd.sketch.construction_line.tangent_two": "Common Tangent",
|
||||
"cmd.sketch.construction_line.vertical": "Vertical Construction Line",
|
||||
"cmd.sketch_arc3d": "3D Arc",
|
||||
"cmd.sketch_arc_center_point": "Center Point Arc",
|
||||
"cmd.sketch_arc_tangent": "Tangent Arc",
|
||||
@@ -1340,8 +1356,8 @@
|
||||
"drive.stopped_by_loop": "Stopped at {value}: the loop through {joint} can't close any further",
|
||||
"drive.unconnected": "Moves nothing: {part} is not connected to anything grounded",
|
||||
"dxf.cancel_wait": "Waiting for the export to stop…",
|
||||
"dxf.cancelled": "DXF export cancelled",
|
||||
"dxf.cancelling": "Cancelling DXF export",
|
||||
"dxf.cancelled": "DXF export canceled",
|
||||
"dxf.cancelling": "Canceling DXF export",
|
||||
"dxf.exporting": "Exporting DXF",
|
||||
"dxf.invalid_tolerance": "Curve tolerance must be between 0.0001 and 1 mm.",
|
||||
"dxf.progress.index": "Preparing surfaces (2/5)",
|
||||
@@ -1372,28 +1388,39 @@
|
||||
"fits the face, e.g. M8": "fits the face, e.g. M8",
|
||||
"from the profile": "from the profile",
|
||||
"from the rule": "from the rule",
|
||||
"help.apache": "Apache licence 2.0",
|
||||
"help.appearance.assign": "Gives the selected bodies, faces or components a colour or a material look, without changing their weight. Use it to tell parts apart or to preview a finish.",
|
||||
"help.apache": "Apache license 2.0",
|
||||
"help.appearance.assign": "Gives the selected bodies, faces or components a color or a material look, without changing their weight. Use it to tell parts apart or to preview a finish.",
|
||||
"help.attributions": "Attributions (ATTRIBUTION.md)",
|
||||
"help.build": "Version {version} · commit {commit} · built {date}",
|
||||
"help.component.finish_edit": "Saves the part you are editing in place, returns to its assembly and updates the assembly from it. Use it when your changes to the linked part are done.",
|
||||
"help.connect_note": "Restart each assistant to load the SolidCraft server. Each file was backed up as .bak before it changed.",
|
||||
"help.connect_title": "Connect AI assistants",
|
||||
"help.face.delete": "Removes the selected faces and lets the neighbouring faces close the gap, or leaves the gap open as a surface. Use it to take off a rounded edge, a hole or a boss that is in the way.",
|
||||
"help.font_details": "The interface uses egui's built-in fonts (Ubuntu Light and Hack, under the Ubuntu Font Licence and the MIT/Bitstream Vera licence; Noto Emoji and the emoji icon font, under the OFL and MIT). Sketch text uses Liberation Sans (SIL OFL 1.1). The Crafting Apps' optional font pack, craft-fonts (BIZ UDPGothic, Shippori Mincho, BIZ UDMincho, Noto Sans CJK SC, Noto Sans Arabic; SIL OFL 1.1), is not bundled in SolidCraft builds.",
|
||||
"help.face.delete": "Removes the selected faces and lets the neighboring faces close the gap, or leaves the gap open as a surface. Use it to take off a rounded edge, a hole or a boss that is in the way.",
|
||||
"help.font_details": "The interface uses egui's built-in fonts (Ubuntu Light and Hack, under the Ubuntu Font Licence and the MIT/Bitstream Vera license; Noto Emoji and the emoji icon font, under the OFL and MIT). Sketch text uses Liberation Sans (SIL OFL 1.1). The Crafting Apps' optional font pack, craft-fonts (BIZ UDPGothic, Shippori Mincho, BIZ UDMincho, Noto Sans CJK SC, Noto Sans Arabic; SIL OFL 1.1), is not bundled in SolidCraft builds.",
|
||||
"help.fonts": "Fonts",
|
||||
"help.licences": "Licences",
|
||||
"help.material.assign": "Sets what the selected bodies are made of, such as steel, aluminium or ABS. Use it so that the mass and weight match the real part.",
|
||||
"help.mit": "MIT licence",
|
||||
"help.licences": "Licenses",
|
||||
"help.material.assign": "Sets what the selected bodies are made of, such as steel, aluminum or ABS. Use it so that the mass and weight match the real part.",
|
||||
"help.mit": "MIT license",
|
||||
"help.notice": "NOTICE",
|
||||
"help.occurrence.move": "Moves an assembly part only along freedoms allowed by its joints. Select a part face, then drag an available arrow or arc.",
|
||||
"help.parameters.change": "Opens the table of named values and expressions that drive the dimensions of the design. Use it to change a size in one place and let every feature that uses it follow.",
|
||||
"help.pointcloud.insert": "Insert a PLY, XYZ or PTS scan as a point cloud alongside the model. Use it as a reference when building a model from measured data.",
|
||||
"help.sketch.construction_line": "Create an infinite construction line through two points. Use it to lay out a sketch without splitting profiles.",
|
||||
"help.sketch.construction_line.angle": "Create an infinite construction line at an angle from sketch X. Pick the point and type the angle, or pick its direction.",
|
||||
"help.sketch.construction_line.bisector": "Pick two intersecting straight sketch lines, then choose either angle bisector. The result follows its source lines.",
|
||||
"help.sketch.construction_line.horizontal": "Create an infinite horizontal construction line through a point. Its horizontal constraint follows later edits.",
|
||||
"help.sketch.construction_line.parallel": "Pick a straight sketch line, then place a parallel infinite construction line. Right-click to choose another reference.",
|
||||
"help.sketch.construction_line.perpendicular": "Pick a straight sketch line, then place a perpendicular infinite construction line. Right-click to choose another reference.",
|
||||
"help.sketch.construction_line.tangent_at": "Create a tangent at a point on a circle or circular arc, or an endpoint of an open spline or conic. Pick the curve, then its contact point.",
|
||||
"help.sketch.construction_line.tangent_external": "Pick a circle or circular arc, then an external point and a tangent solution. The previews show only contacts on the drawn arc.",
|
||||
"help.sketch.construction_line.tangent_two": "Pick two circles or circular arcs, then choose a common tangent in the preview. Contacts must lie on the drawn curves.",
|
||||
"help.sketch.construction_line.vertical": "Create an infinite vertical construction line through a point. Its vertical constraint follows later edits.",
|
||||
"help.sketch.equation_curve": "Draws a curve from formulas: x(t) and y(t), or y = f(x), over a range. The formulas can use parameters and the curve follows them: use it for a sine wave, a spiral or a gear tooth's involute.",
|
||||
"help.sketch.inspect": "Opens a report on the open sketch: how many degrees of freedom are left and whether it is fully constrained. Use it to find out what still moves before you finish the sketch.",
|
||||
"help.solid.align": "Moves the selected bodies so that a point, face or plane on them meets a point, face or plane elsewhere. Use it to place a part against another one without measuring the offset.",
|
||||
"help.solid.chamfer": "Cuts the selected edges with a flat bevel of the distance you set. Use it to break sharp edges or to ease the entry of a pin or screw.",
|
||||
"help.solid.combine": "Joins bodies, cuts them out of a target body, or keeps only the volume they share, and can keep the tool bodies. Use it to merge parts, to cut one shape with another or to find where two bodies overlap.",
|
||||
"help.solid.draft": "Tilts the selected faces by the angle you set, measured from a neutral plane. Use it on moulded or cast parts so that they slide out of the mould.",
|
||||
"help.solid.draft": "Tilts the selected faces by the angle you set, measured from a neutral plane. Use it on molded or cast parts so that they slide out of the mold.",
|
||||
"help.solid.extrude": "Pushes a sketch profile into 3D along a distance, adding or removing material. Use it to turn a flat outline into a block, a boss or a pocket.",
|
||||
"help.solid.fillet": "Rounds the selected edges with a smooth circular blend of the radius you set. Use it to soften sharp edges for strength, comfort or looks.",
|
||||
"help.solid.hole": "Drills a round hole of the diameter you set at a chosen point on a face. Use it for screw clearances, pins and other simple round openings.",
|
||||
@@ -1406,13 +1433,13 @@
|
||||
"help.solid.press_pull": "Adds or removes material as you drag a face, or rounds edges as you drag them. Use it for quick changes straight on the model.",
|
||||
"help.solid.remove": "Takes the selected bodies out of the design from this point in the timeline on; earlier steps keep them. Use it to drop a helper body, such as a cutting tool you no longer need.",
|
||||
"help.solid.replace_face": "Moves the selected flat faces onto a plane or onto a parallel face, and the body grows or shrinks to meet it. Use it to bring a face flush with another part or a construction plane.",
|
||||
"help.solid.revolve": "Spins a sketch profile around an axis to make a round solid. Use it for shafts, knobs, bowls and any part that is round about a centre line.",
|
||||
"help.solid.revolve": "Spins a sketch profile around an axis to make a round solid. Use it for shafts, knobs, bowls and any part that is round about a center line.",
|
||||
"help.solid.scale": "Makes the selected bodies larger or smaller by a factor, the same in every direction or one per axis. Use it to resize an imported shape or to allow for shrinkage.",
|
||||
"help.solid.shell": "Hollows out a solid, leaving walls of the thickness you set, and can open the faces you pick. Use it for boxes, covers and other thin-walled parts.",
|
||||
"help.solid.split_body": "Cuts a body into separate bodies with a plane, the face of another body or a sketch line. Use it to make parts that are printed, machined or coloured on their own.",
|
||||
"help.solid.split_face": "Divides the selected faces along a plane, the face of another body or sketch curves, without cutting the body. Use it to give part of a face its own draft, colour or offset.",
|
||||
"help.solid.split_body": "Cuts a body into separate bodies with a plane, the face of another body or a sketch line. Use it to make parts that are printed, machined or colored on their own.",
|
||||
"help.solid.split_face": "Divides the selected faces along a plane, the face of another body or sketch curves, without cutting the body. Use it to give part of a face its own draft, color or offset.",
|
||||
"help.solid.sweep": "Drags a sketch profile along a path to make a solid. Use it for pipes, rails, handles and any part of constant section that follows a curve.",
|
||||
"help.third_party": "Third-party licences",
|
||||
"help.third_party": "Third-party licenses",
|
||||
"help.timeline.compute_all": "Rebuilds every feature in the timeline from the start. Use it to refresh the model and to see which features fail.",
|
||||
"help.timeline.delete": "Deletes the selected features from the timeline; deleting a sketch also deletes the features built from it. Use it to remove a step that is no longer wanted.",
|
||||
"help.timeline.play": "Replay the remaining features one at a time, with a half-second pause after each rebuild. Run this command again or press Escape to stop; Go to end jumps immediately.",
|
||||
@@ -1466,7 +1493,7 @@
|
||||
"home.new_inches": "New design, inches",
|
||||
"home.new_inches_hint": "Inches · opens in Design",
|
||||
"home.new_mm": "New design",
|
||||
"home.new_mm_hint": "Millimetres · opens in Design",
|
||||
"home.new_mm_hint": "Millimeters · opens in Design",
|
||||
"home.open": "Open…",
|
||||
"home.open_hint": "A .sc design ({shortcut})",
|
||||
"home.open_now": "Open now",
|
||||
@@ -1592,6 +1619,10 @@
|
||||
"material.selection.mixed": "Different materials",
|
||||
"measure.at": "At",
|
||||
"measure.item": "Item {number}",
|
||||
"measure.quick_copied": "Measurement copied",
|
||||
"measure.quick_copy": "Click to copy the measurement",
|
||||
"measure.quick_edges": "{count} edges",
|
||||
"measure.quick_faces": "{count} faces",
|
||||
"messages.ago_h": "{n} h ago",
|
||||
"messages.ago_min": "{n} min ago",
|
||||
"messages.ago_s": "{n} s ago",
|
||||
@@ -1647,7 +1678,7 @@
|
||||
"navbar.custom.buttons": "Buttons",
|
||||
"navbar.custom.compact": "Compact",
|
||||
"navbar.custom.count": "{count} of 16 slots",
|
||||
"navbar.custom.customise": "Customise navigation bar…",
|
||||
"navbar.custom.customise": "Customize navigation bar…",
|
||||
"navbar.custom.design": "Design",
|
||||
"navbar.custom.duplicate": "This slot is already on the bar.",
|
||||
"navbar.custom.edge": "Edge",
|
||||
@@ -1667,7 +1698,7 @@
|
||||
"navbar.custom.labels": "Icons and labels",
|
||||
"navbar.custom.left": "Move left",
|
||||
"navbar.custom.position": "Position",
|
||||
"navbar.custom.position_centre": "Centre",
|
||||
"navbar.custom.position_centre": "Center",
|
||||
"navbar.custom.position_left": "Left",
|
||||
"navbar.custom.position_right": "Right",
|
||||
"navbar.custom.preview_as": "Preview as",
|
||||
@@ -1696,7 +1727,7 @@
|
||||
"navbar.description.orbit": "Turns the view around the model.",
|
||||
"navbar.description.pan": "Slides the view sideways, up or down.",
|
||||
"navbar.description.projection": "Switches between orthographic and perspective views.",
|
||||
"navbar.description.separator": "A thin line that groups neighbouring slots.",
|
||||
"navbar.description.separator": "A thin line that groups neighboring slots.",
|
||||
"navbar.description.spin": "Keeps the view turning until you click or press a key.",
|
||||
"navbar.description.visual_style": "Switches how the model is drawn: shaded, wireframe or with hidden edges.",
|
||||
"navbar.description.zoom": "Moves the view closer to or further from the model.",
|
||||
@@ -1744,6 +1775,11 @@
|
||||
"not a move": "not a move",
|
||||
"not a number": "not a number",
|
||||
"open a sketch first": "open a sketch first",
|
||||
"parameters.apply_live": "Apply each change as you leave its field",
|
||||
"parameters.apply_live_tip": "Off: changes wait until Apply or OK, then the design rebuilds once for all of them. Cancel or Escape drops them.",
|
||||
"parameters.held": "{count} changes waiting for Apply",
|
||||
"parameters.held_hint": "Edit expressions and units. Changes wait for Apply or OK.",
|
||||
"parameters.held_one": "1 change waiting for Apply",
|
||||
"parts.automatic_name": "automatic",
|
||||
"parts.dimension.across_flats": "Across flats",
|
||||
"parts.dimension.bore": "Bore",
|
||||
@@ -1766,6 +1802,8 @@
|
||||
"parts.family.set_screw": "Hex socket set screw, flat point",
|
||||
"parts.family.socket_head_cap_screw": "Socket head cap screw",
|
||||
"parts.family.washer": "Plain washer",
|
||||
"parts.holes": "Holes (optional)",
|
||||
"parts.placement_hint": "pick one or more holes to seat a part in each, or OK places it at the origin",
|
||||
"pattern.alignment_2": "Alignment 2",
|
||||
"pattern.alignment_hint": "Counts include the original. Center balances copies; Flip chooses the extra side for even totals.",
|
||||
"pattern.direction_1": "Direction 1",
|
||||
@@ -1815,9 +1853,11 @@
|
||||
"prefs.bodies_folders_never": "Never",
|
||||
"prefs.bodies_folders_several": "Several bodies only",
|
||||
"prefs.bottom": "Bottom",
|
||||
"prefs.bottom_centre": "Bottom centre",
|
||||
"prefs.bottom_centre": "Bottom center",
|
||||
"prefs.bottom_right": "Bottom right",
|
||||
"prefs.browser": "Browser",
|
||||
"prefs.browser_eye_left": "Eye next to the fold arrow",
|
||||
"prefs.browser_eye_left_note": "Browser rows show their show/hide eye next to the fold arrow, as before, instead of in a column at the right end of the rows.",
|
||||
"prefs.browser_reveal_on_hover": "Reveal on hover",
|
||||
"prefs.browser_show_note": "Shown keeps the browser on screen. Reveal on hover keeps it out of sight until the pointer comes over its place or the thin tab at its left edge, and it stays open while the pointer is inside it or a menu from it is open.",
|
||||
"prefs.browser_shown": "Shown",
|
||||
@@ -1868,6 +1908,8 @@
|
||||
"prefs.graphics_card_restart": "Takes effect after restarting SolidCraft. WGPU_ADAPTER_NAME overrides this choice.",
|
||||
"prefs.graphics_note": "Trade speed for smoothness. Each choice shows its effect on a sample.",
|
||||
"prefs.graphics_presets_note": "Presets set the choices below together.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Hide constraint glyphs when zoomed out",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out_note": "Constraint glyphs leave the view when the sketch is zoomed out far enough that they would clutter it. Off: they stay at a fixed screen size at every zoom.",
|
||||
"prefs.history": "History",
|
||||
"prefs.history_origin_bodies": "Show origin and bodies at the top",
|
||||
"prefs.history_origin_bodies_note": "The History on the left or under the browser starts with the active component's Bodies folder and the Origin, with their eyes and menus as in the browser. The History at the bottom is unchanged.",
|
||||
@@ -1906,6 +1948,9 @@
|
||||
"prefs.max_file_size_step": "STEP and import files",
|
||||
"prefs.max_file_size_web": "The web app opens STEP and import files up to 512 MB and designs up to 256 MB: a browser tab can't hold larger files. The desktop app can raise these limits.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "Large icons",
|
||||
"prefs.menu_list": "List",
|
||||
"prefs.menus": "Menus",
|
||||
"prefs.minutes": "minutes",
|
||||
"prefs.minutes_suffix": " min",
|
||||
"prefs.mouse": "Mouse and trackpad",
|
||||
@@ -1925,10 +1970,16 @@
|
||||
"prefs.origin_plane_names": "Origin plane names",
|
||||
"prefs.origin_plane_names_note": "Names shown in the browser, dialogs and lists. {top} is XY, {front} is XZ and {right} is YZ, with Z up. Commands, scripts and saved designs keep XY, XZ and YZ.",
|
||||
"prefs.overall_speed": "Overall speed",
|
||||
"prefs.parameter_list_rows": "Parameter list rows",
|
||||
"prefs.parameter_list_rows_tip": "Down arrow in an empty value field lists the parameters, favourites first, up to this many. Fewer keeps the list light in a large design.",
|
||||
"prefs.persistent_ribbon_pressed": "Highlight active ribbon commands",
|
||||
"prefs.persistent_ribbon_pressed_help": "Highlight ribbon commands while their effects are on. Turn off to keep the previous neutral appearance and command help.",
|
||||
"prefs.perspective_fov": "Field of view",
|
||||
"prefs.placement": "Placement",
|
||||
"prefs.prefer_free_sketch_points": "Prefer movable sketch points when points overlap",
|
||||
"prefs.preview_choices": "Preview of your choices",
|
||||
"prefs.print_face_highlight": "Print face highlight",
|
||||
"prefs.print_face_highlight_tip": "How strongly the print face of a body is tinted and outlined in the view. 0 % shows no highlight.",
|
||||
"prefs.profile_active": "Active profile",
|
||||
"prefs.profile_applied": "Profile applied.",
|
||||
"prefs.profile_changed": "Settings changed since the profile was saved. Save keeps them in it.",
|
||||
@@ -1950,6 +2001,8 @@
|
||||
"prefs.quality_fastest": "fastest",
|
||||
"prefs.quality_slowest": "slowest",
|
||||
"prefs.quality_smoother": "smoother",
|
||||
"prefs.quick_measure": "Quick measurement in the view's corner",
|
||||
"prefs.quick_measure_note": "The selection's size in the view's bottom right corner: an edge's length, or Ø and R for a round one, a face's area, a point's position, two items' distance and angle, several edges' or faces' total. A click copies it.",
|
||||
"prefs.render_previous_geometry_budget": "Use previous Render preview geometry budget",
|
||||
"prefs.render_previous_geometry_budget_help": "Restore the previous fixed geometry limit (512 MiB on 64-bit desktop, 128 MiB elsewhere) instead of the device-based budget.",
|
||||
"prefs.reports_saved": "{n} saved",
|
||||
@@ -1963,6 +2016,8 @@
|
||||
"prefs.search_empty": "No matching preferences",
|
||||
"prefs.select": "Select",
|
||||
"prefs.select_one_item": "Select one item",
|
||||
"prefs.select_other_hidden": "Select hidden geometry with Select Other",
|
||||
"prefs.select_other_hidden_help": "On: include hidden bodies, sketches and occluded edges near the pointer. Off: use the original list of visible geometry under the pointer.",
|
||||
"prefs.selecting": "Selecting",
|
||||
"prefs.shift": "Shift",
|
||||
"prefs.shift_add_ctrl_toggle": "Shift adds only; Ctrl / Cmd toggles",
|
||||
@@ -1974,11 +2029,14 @@
|
||||
"prefs.sketch_align_points_note": "While drawing, a point lines up horizontally with one sketch point and vertically with another at the same time, with a dotted guide, and the click keeps both alignments. Off: only with the previous point, one axis at a time.",
|
||||
"prefs.sketch_conflict_colour": "Conflicting geometry in the conflict color",
|
||||
"prefs.sketch_conflict_colour_note": "While a sketch's constraints conflict, the curves and points a failing constraint holds are drawn in the conflict color too, not only its glyph or dimension. Off: they keep their usual colors.",
|
||||
"prefs.sketch_construction_lines": "Extended construction line tools",
|
||||
"prefs.sketch_exit_view": "When a sketch ends",
|
||||
"prefs.sketch_exit_view.keep": "Keep the current view",
|
||||
"prefs.sketch_exit_view.previous": "Return to the previous view",
|
||||
"prefs.sketch_exit_view.unless_rotated": "Return unless rotated",
|
||||
"prefs.sketch_exit_view_note": "Where the view goes after Finish Sketch, Cancel or a double Esc.",
|
||||
"prefs.sketch_open_look_at": "Turn to face a sketch when it opens",
|
||||
"prefs.sketch_open_look_at_note": "Off: creating or editing a sketch leaves the view as it is. Look At faces the sketch when you want it.",
|
||||
"prefs.sketch_recent_snap": "Auto snap to recent centers and midpoints",
|
||||
"prefs.sketch_recent_snap_note": "While drawing, snap the cursor where the guides of the two most recent circle centers or straight-edge midpoints cross. Off: those guides stay visual and the cursor does not move.",
|
||||
"prefs.sketch_selection": "Sketch & selection",
|
||||
@@ -2031,20 +2089,28 @@
|
||||
"prefs.trackpad_zoom_modifier.alt": "⌥ / Alt",
|
||||
"prefs.trackpad_zoom_modifier.cmd": "⌘",
|
||||
"prefs.trackpad_zoom_modifier.ctrl": "Ctrl",
|
||||
"prefs.trackpad_zoom_modifier.help": "Hold only this key to zoom at the pointer with vertical scroll. Off keeps the existing scroll behaviour.",
|
||||
"prefs.trackpad_zoom_modifier.help": "Hold only this key to zoom at the pointer with vertical scroll. Off keeps the existing scroll behavior.",
|
||||
"prefs.trackpad_zoom_modifier.off": "Off",
|
||||
"prefs.translucent": "Translucent",
|
||||
"prefs.under_browser": "Under browser",
|
||||
"prefs.under_cursor": "Under cursor",
|
||||
"prefs.units_precision": "Units and precision",
|
||||
"prefs.updates": "Updates",
|
||||
"prefs.value_boxes_in_view": "Show values beside their handles in the view",
|
||||
"prefs.value_boxes_in_view_tip": "A command's value (Distance, Radius…) in a box next to its handle, over the model. Off: you type it in the command window, which takes the keyboard after a pick, and nothing covers the model.",
|
||||
"prefs.view": "View behavior",
|
||||
"prefs.view_confirm_corner": "OK and Cancel in the view's corner",
|
||||
"prefs.view_confirm_corner_note": "While a dialog or a sketch is open, a check and a cross at the top right of the view do what the dialog's OK and Cancel do, or finish and cancel the sketch. The view navigator moves down below them.",
|
||||
"prefs.viewport": "Viewport",
|
||||
"prefs.viewport_analysis_status": "Show viewport analysis status",
|
||||
"prefs.viewport_analysis_status_help": "Show active analyses above the viewport, open their legends from each item, and use Esc to turn off the newest analysis. Turn off to restore corner legends and the previous Esc behaviour.",
|
||||
"prefs.viewport_background": "Background",
|
||||
"prefs.viewport_background_colour": "Color",
|
||||
"prefs.viewport_background_theme": "Theme's gradient",
|
||||
"prefs.viewport_section_status": "Show viewport section status",
|
||||
"prefs.viewport_section_status_help": "Show the section setting in the viewport. Escape and the active ribbon button turn it off. Disable to keep the previous section controls.",
|
||||
"prefs.viewport_status_summary": "Fold viewport status items",
|
||||
"prefs.viewport_status_summary_help": "Fold three or more viewport status items into one summary badge. Disable to keep every item in the full row.",
|
||||
"prefs.wheel": "Wheel",
|
||||
"prefs.wheel_trackpad": "Wheel and trackpad",
|
||||
"prefs.workspace": "Workspace",
|
||||
@@ -2054,6 +2120,10 @@
|
||||
"properties.title": "Properties: {name}",
|
||||
"recovery.days_ago": "{count} days ago",
|
||||
"recovery.detail": "{file} · {features} features · autosaved {when}",
|
||||
"recovery.discard_all": "Discard all",
|
||||
"recovery.discard_all_detail": "This cannot be undone.",
|
||||
"recovery.discard_all_title": "Discard {count} autosaves?",
|
||||
"recovery.discard_all_title_one": "Discard 1 autosave?",
|
||||
"recovery.hours_ago": "{count} h ago",
|
||||
"recovery.just_now": "just now",
|
||||
"recovery.min_ago": "{count} min ago",
|
||||
@@ -2076,13 +2146,13 @@
|
||||
"render.aging_class_any": "All opaque material classes",
|
||||
"render.aging_class_dielectric": "Nonmetallic surfaces only",
|
||||
"render.aging_class_metal": "Metallic surfaces only",
|
||||
"render.aging_color": "Layer colour · sRGB",
|
||||
"render.aging_color": "Layer color · sRGB",
|
||||
"render.aging_coverage": "Coverage",
|
||||
"render.aging_dust": "Deposited dust",
|
||||
"render.aging_empty": "No aging layers: the original surfaces remain unchanged.",
|
||||
"render.aging_fading": "Faded coating",
|
||||
"render.aging_grime": "Grime & staining",
|
||||
"render.aging_help": "Combine up to eight optical layers, in order. Rust and patina are authored appearances, not age/corrosion simulations. Scale is in world millimetres. No geometry, mass or engineering property is changed; glass and passive stages are excluded.",
|
||||
"render.aging_help": "Combine up to eight optical layers, in order. Rust and patina are authored appearances, not age/corrosion simulations. Scale is in world millimeters. No geometry, mass or engineering property is changed; glass and passive stages are excluded.",
|
||||
"render.aging_metallic": "Layer metallic fraction",
|
||||
"render.aging_origin": "Field origin · world mm",
|
||||
"render.aging_patina": "Copper-green patina",
|
||||
@@ -2098,7 +2168,7 @@
|
||||
"render.all_controls_help": "Dense inspection of all current controls. Section pages provide focused editing without changing the underlying request.",
|
||||
"render.all_shots_label": "All Shots",
|
||||
"render.appearance_editor.absorption": "Absorption",
|
||||
"render.appearance_editor.base_colour": "Base colour",
|
||||
"render.appearance_editor.base_colour": "Base color",
|
||||
"render.appearance_editor.body_reason": "Optical bindings target one whole body. Pick Body to edit these controls.",
|
||||
"render.appearance_editor.boundary": "Boundary",
|
||||
"render.appearance_editor.box": "Box",
|
||||
@@ -2162,7 +2232,7 @@
|
||||
"render.background_not_light": "Background color is presentation, not an additional lighting source.",
|
||||
"render.background_regrade": "Apply to retained raw master",
|
||||
"render.background_stage_occlusion": "This shot includes physical stage geometry. A large floor or wall can fill the frame and hide a camera-only background. Clear or resize the stage deliberately, or change its physical reflectance. Regrading cannot remove captured geometry.",
|
||||
"render.background_workflow": "Choose the background you intend: a camera-only colour/gradient, a physical stage that receives light, or HDR illumination. They are independent. Live Preview is a modelling view; render a new image or regrade a retained master to see camera finishing.",
|
||||
"render.background_workflow": "Choose the background you intend: a camera-only color/gradient, a physical stage that receives light, or HDR illumination. They are independent. Live Preview is a modeling view; render a new image or regrade a retained master to see camera finishing.",
|
||||
"render.base_appearance": "Editable body appearance",
|
||||
"render.base_apply": "Apply body appearance",
|
||||
"render.base_apply_help": "One undoable visual assignment to the reviewed exact body. Existing face-specific looks and engineering materials remain separate.",
|
||||
@@ -2283,7 +2353,7 @@
|
||||
"render.cpu_aa_help": "Spatial samples: more samples improve edges and increase render time.",
|
||||
"render.cpu_area_add": "Add rectangular CPU light",
|
||||
"render.cpu_area_editor": "CPU rectangular lights",
|
||||
"render.cpu_area_editor_help": "Full side vectors in millimeters; U cross V points towards the emitting side. This differs from GPU half-edge vectors.",
|
||||
"render.cpu_area_editor_help": "Full side vectors in millimeters; U cross V points toward the emitting side. This differs from GPU half-edge vectors.",
|
||||
"render.cpu_area_full_u": "Full U side · mm",
|
||||
"render.cpu_area_full_v": "Full V side · mm",
|
||||
"render.cpu_area_samples": "Area-light samples",
|
||||
@@ -2372,7 +2442,7 @@
|
||||
"render.draft_confirm_help": "The current render draft has unapplied edits. Save them before switching, discard them explicitly, or keep editing.",
|
||||
"render.draft_confirm_title": "Preserve edited render settings",
|
||||
"render.effects_gpu_only": "These effects require GPU path tracing. Changing the page never switches the renderer automatically.",
|
||||
"render.effects_help": "Combine ordered colour looks and image-space lens effects. Rendering and raw-master regrading use the same saved settings. Raw EXR stays unchanged. These are original artistic controls, not calibrated lens or branded-film emulations.",
|
||||
"render.effects_help": "Combine ordered color looks and image-space lens effects. Rendering and raw-master regrading use the same saved settings. Raw EXR stays unchanged. These are original artistic controls, not calibrated lens or branded-film emulations.",
|
||||
"render.effects_reset_lens": "Reset lens effects",
|
||||
"render.effects_seed": "Pattern seed",
|
||||
"render.emitted_axis": "Emitted axis",
|
||||
@@ -2383,7 +2453,7 @@
|
||||
"render.env_change": "Change",
|
||||
"render.env_choose_source": "Choose an environment in Studios and Environments before editing the lighting.",
|
||||
"render.env_clear": "Clear",
|
||||
"render.env_colour": "Colour",
|
||||
"render.env_colour": "Color",
|
||||
"render.env_environments": "Environments",
|
||||
"render.env_ground": "Ground",
|
||||
"render.env_ground_pending": "Physical ground receives shadows as a finite diffuse surface. Shadow-only catching and ground reflections are not supported yet.",
|
||||
@@ -2438,7 +2508,7 @@
|
||||
"render.environment.primaries": "Treat unspecified primaries as linear sRGB",
|
||||
"render.environment.primaries_gpu": "Interpreting unspecified primaries needs GPU path tracing.",
|
||||
"render.environment.radial": "Radial",
|
||||
"render.environment.radial_only": "Centre and radius apply to Radial mode.",
|
||||
"render.environment.radial_only": "Center and radius apply to Radial mode.",
|
||||
"render.environment.reflectance": "Reflectance",
|
||||
"render.environment.remove": "Remove stage",
|
||||
"render.environment.remove_confirm": "Remove the saved stage?",
|
||||
@@ -2470,8 +2540,8 @@
|
||||
"render.error_duplicate_face": "duplicate product face appearance",
|
||||
"render.error_export_document": "The render export document changed while choosing its destination",
|
||||
"render.error_face_cap": "product preview exceeds the face-appearance admission cap",
|
||||
"render.error_finish_gesture": "finish the modelling gesture before previewing",
|
||||
"render.error_finish_operation": "finish the current modelling operation before entering Render",
|
||||
"render.error_finish_gesture": "finish the modeling gesture before previewing",
|
||||
"render.error_finish_operation": "finish the current modeling operation before entering Render",
|
||||
"render.error_fixed_preview": "fixed studio preview requires exposure 0, environment rotation 0 and intensity 1",
|
||||
"render.error_geometry_size": "product geometry size overflow",
|
||||
"render.error_glass_recipe": "unknown glass recipe",
|
||||
@@ -2529,7 +2599,7 @@
|
||||
"render.flare_threshold": "Scene-linear flare threshold",
|
||||
"render.fold_browser": "Fold Browser",
|
||||
"render.frame_aspect": "Aspect",
|
||||
"render.frame_centre_mark": "Centre mark",
|
||||
"render.frame_centre_mark": "Center mark",
|
||||
"render.frame_copy_limit": "No available duplicate shot name",
|
||||
"render.frame_copy_name": "{name} copy {number}",
|
||||
"render.frame_custom": "Custom",
|
||||
@@ -2584,8 +2654,8 @@
|
||||
"render.hide_shots_label": "Hide Shots strip",
|
||||
"render.hover_look": "{object} · {look} · click to select",
|
||||
"render.image_advanced": "Advanced GPU controls",
|
||||
"render.image_cancelled": "Render cancelled",
|
||||
"render.image_cancelling": "Cancelling",
|
||||
"render.image_cancelled": "Render canceled",
|
||||
"render.image_cancelling": "Canceling",
|
||||
"render.image_completed": "Image ready to export",
|
||||
"render.image_encoding": "Encoding PNG",
|
||||
"render.image_export": "Export completed image",
|
||||
@@ -2602,7 +2672,7 @@
|
||||
"render.image_reduce": "Try a smaller image or simpler scene.",
|
||||
"render.image_running": "Rendering image",
|
||||
"render.image_samples": "Samples per pixel",
|
||||
"render.image_scene_stale": "Finish the modelling gesture or refresh the scene before checking or starting an image.",
|
||||
"render.image_scene_stale": "Finish the modeling gesture or refresh the scene before checking or starting an image.",
|
||||
"render.image_stale": "This image uses the scene captured before subsequent changes.",
|
||||
"render.image_start": "Start Render",
|
||||
"render.image_supported": "Render request is supported; the queue and memory pool are checked again at start.",
|
||||
@@ -2621,7 +2691,7 @@
|
||||
"render.inspector_applies_to": "Applies to",
|
||||
"render.inspector_both_users": "Both users",
|
||||
"render.inspector_chain": "Where this look comes from",
|
||||
"render.inspector_colour": "Colour",
|
||||
"render.inspector_colour": "Color",
|
||||
"render.inspector_edit": "Edit",
|
||||
"render.inspector_finish": "Finish",
|
||||
"render.inspector_from_material": "From Material",
|
||||
@@ -2733,7 +2803,7 @@
|
||||
"render.light_anchor_pending": "Changing the light coordinate frame is not supported yet.",
|
||||
"render.light_azimuth": "Azimuth",
|
||||
"render.light_backend_limit": "Choose CPU ray tracing or an explicit GPU studio to edit lights. Legacy GPU studio lights remain read-only.",
|
||||
"render.light_behaviour": "BEHAVIOUR",
|
||||
"render.light_behaviour": "BEHAVIOR",
|
||||
"render.light_default_fill": "Fill",
|
||||
"render.light_default_key": "Key",
|
||||
"render.light_default_number": "Light {number}",
|
||||
@@ -2749,11 +2819,11 @@
|
||||
"render.light_place_highlight": "Place by highlight",
|
||||
"render.light_place_pending": "Highlight placement needs viewport light picking and is not supported yet.",
|
||||
"render.light_position": "POSITION",
|
||||
"render.light_preview_help": "Live view is unshadowed; Area lights use a centre-sample approximation. Render uses the complete light model.",
|
||||
"render.light_product_centre": "Product centre",
|
||||
"render.light_preview_help": "Live view is unshadowed; Area lights use a center-sample approximation. Render uses the complete light model.",
|
||||
"render.light_product_centre": "Product center",
|
||||
"render.light_size": "Size",
|
||||
"render.light_softness": "Softness",
|
||||
"render.light_temperature": "Colour temperature",
|
||||
"render.light_temperature": "Color temperature",
|
||||
"render.light_temperature_help": "Approximate blackbody tint; saved as linear RGB.",
|
||||
"render.light_type": "Type",
|
||||
"render.light_type_area": "Area",
|
||||
@@ -2767,7 +2837,7 @@
|
||||
"render.lighting_area_samples": "Area samples",
|
||||
"render.lighting_budget": "Budget",
|
||||
"render.lighting_budget_help": "Counts are read-only. Add, duplicate or delete lights to change them; the method sets the limits.",
|
||||
"render.lighting_colour": "Colour",
|
||||
"render.lighting_colour": "Color",
|
||||
"render.lighting_consent": "Replace the directional lights with this preset",
|
||||
"render.lighting_custom": "Custom lights",
|
||||
"render.lighting_custom_help": "Choosing Custom turns the preset into editable lights, one row each in the tree.",
|
||||
@@ -2809,7 +2879,7 @@
|
||||
"render.linear_help": "Keeps a 32-bit float RGBA master before denoising, exposure, tone mapping, filters and backdrop. Linear sRGB/D65; coverage-premultiplied. Additional memory is admitted explicitly. No compression or extra passes.",
|
||||
"render.linear_retain": "Retain raw linear EXR",
|
||||
"render.linear_unavailable": "The selected completed frame has no retained EXR. Enable retention before rendering; an older PNG cannot recover raw radiance.",
|
||||
"render.look_add": "Add colour look",
|
||||
"render.look_add": "Add color look",
|
||||
"render.look_applied": "Applied {name}",
|
||||
"render.look_bleach": "Bleach-bypass-inspired",
|
||||
"render.look_cool": "Cool",
|
||||
@@ -2919,7 +2989,9 @@
|
||||
"render.output_ready": "Ready to render",
|
||||
"render.output_run_check": "Run check",
|
||||
"render.output_size_limit": "{method}: up to {dimension} px per side and {pixels} pixels. Largest fitting size: {width} × {height}.",
|
||||
"render.output_size_method_profile": "To render {width} × {height}, choose {method} with the {profile} CPU resource profile, or a smaller size.",
|
||||
"render.output_size_ok": "Size OK · {width} × {height} px",
|
||||
"render.output_size_profile": "To render {width} × {height}, choose the {profile} CPU resource profile, or a smaller size.",
|
||||
"render.output_swap_orientation": "Swap width and height",
|
||||
"render.output_tools_inspector": "Edit size and format in the Output inspector.",
|
||||
"render.output_view_image": "View image",
|
||||
@@ -3049,7 +3121,7 @@
|
||||
"render.ribbon_saved_camera": "Saved Camera",
|
||||
"render.ribbon_shots": "Shots",
|
||||
"render.ribbon_studios": "Studios",
|
||||
"render.ribbon_studios_help": "Browse implemented studio compositions and review their replacements before applying. Concept-only scenes remain labelled.",
|
||||
"render.ribbon_studios_help": "Browse implemented studio compositions and review their replacements before applying. Concept-only scenes remain labeled.",
|
||||
"render.ribbon_surface_maps": "Surface Maps",
|
||||
"render.sampling_title": "Adaptive and transmission sampling",
|
||||
"render.save_continue": "Save and continue",
|
||||
@@ -3132,6 +3204,7 @@
|
||||
"render.size_custom": "Custom…",
|
||||
"render.size_full_hd": "Full HD",
|
||||
"render.size_hd": "HD",
|
||||
"render.size_needs_profile": "{size} · needs {profile}",
|
||||
"render.size_presets": "Size presets",
|
||||
"render.size_presets_pending": "Arrives with the Output editor",
|
||||
"render.size_preview": "Preview",
|
||||
@@ -3160,7 +3233,7 @@
|
||||
"render.stage_panels.badge_specification": "spec only",
|
||||
"render.stage_panels.candidate_summary": "Candidates: {count} sourced values. Grade and finish need review.",
|
||||
"render.stage_panels.captured_bounds": "Use captured scene bounds",
|
||||
"render.stage_panels.centre": "Centre",
|
||||
"render.stage_panels.centre": "Center",
|
||||
"render.stage_panels.depth": "depth",
|
||||
"render.stage_panels.environments": "Environments",
|
||||
"render.stage_panels.extent": "Extent",
|
||||
@@ -3215,7 +3288,7 @@
|
||||
"render.stage_panels.scope": "Scope",
|
||||
"render.stage_panels.search_studios": "Search studios",
|
||||
"render.stage_panels.search_surfaces": "Search surfaces and references",
|
||||
"render.stage_panels.source_note": "Authored base-colour source map. Not a measured material.",
|
||||
"render.stage_panels.source_note": "Authored base-color source map. Not a measured material.",
|
||||
"render.stage_panels.specifications": "Specifications · read-only",
|
||||
"render.stage_panels.studio_ack": "Confirm replacing lighting, environment, exposure and stage",
|
||||
"render.stage_panels.studio_note": "Click to review. Applying replaces lighting, environment and stage only after you confirm. Lights stay editable.",
|
||||
@@ -3245,7 +3318,7 @@
|
||||
"render.studio_ambient_rgb": "Constant environment radiance (linear RGB)",
|
||||
"render.studio_aperture": "Aperture radius",
|
||||
"render.studio_area": "Area light",
|
||||
"render.studio_area_help": "Up to four one-sided rectangles. Orthogonal half-edges U and V define size; U × V faces the lit side. Coordinates are world-space millimetres.",
|
||||
"render.studio_area_help": "Up to four one-sided rectangles. Orthogonal half-edges U and V define size; U × V faces the lit side. Coordinates are world-space millimeters.",
|
||||
"render.studio_center": "Center X, Y, Z (mm)",
|
||||
"render.studio_denoise": "Reduce noise in the finished image",
|
||||
"render.studio_denoise_help": "Final edge-aware filter; fine detail can soften. Glass and sharp specular surfaces bypass the filter.",
|
||||
@@ -3419,6 +3492,24 @@
|
||||
"sheet.flange_type_contour": "Contour",
|
||||
"sheet.flange_type_edge": "Edge",
|
||||
"sheet.no_body": "there is no sheet metal body: start one with Flange > Base, or turn a plate into sheet metal with Convert to Sheet Metal (pick its large flat face)",
|
||||
"sheet.personal_rules.confirm_delete": "Confirm delete",
|
||||
"sheet.personal_rules.delete": "Delete",
|
||||
"sheet.personal_rules.delete_prompt": "Delete this personal rule? Rules already in designs are unchanged.",
|
||||
"sheet.personal_rules.full": "The personal library already contains 64 rules.",
|
||||
"sheet.personal_rules.invalid_name": "Enter a short name without line breaks or control characters.",
|
||||
"sheet.personal_rules.invalid_values": "Complete the rule's values: each must work out in this design, with a positive thickness and a K factor from 0 to 1.",
|
||||
"sheet.personal_rules.missing": "The selected personal rule is no longer available.",
|
||||
"sheet.personal_rules.name": "Rule name",
|
||||
"sheet.personal_rules.name_exists": "A personal rule with this name already exists. Use a different name.",
|
||||
"sheet.personal_rules.name_hint": "A unique name",
|
||||
"sheet.personal_rules.overwrite": "Save changes to selected personal rule",
|
||||
"sheet.personal_rules.rename": "Rename",
|
||||
"sheet.personal_rules.rule": "Personal rule",
|
||||
"sheet.personal_rules.save": "Save new",
|
||||
"sheet.personal_rules.scope": "Saved in your profile for all designs.",
|
||||
"sheet.personal_rules.select": "Select a rule",
|
||||
"sheet.personal_rules.title": "Personal rules",
|
||||
"sheet.personal_rules.unsupported": "This rule has values that this version can't use.",
|
||||
"shortcut.json_error": "parameters must be JSON: {error}",
|
||||
"shortcut.no_command": "no command `{id}`; the text before the JSON must be a command id",
|
||||
"shortcut.pin": "Pin to the top of the S box",
|
||||
@@ -3455,6 +3546,7 @@
|
||||
"sketch_text.align_right": "Right",
|
||||
"sketch_text.bold": "Bold",
|
||||
"sketch_text.builtin_font": "{family} (built-in)",
|
||||
"sketch_text.formula": "Start with = to build the words from parameters: ='W' + number(Width)",
|
||||
"sketch_text.italic": "Italic",
|
||||
"sketch_text.loading_fonts": "Looking for installed fonts…",
|
||||
"sketch_text.on_path": "The text follows its path: the curve places it",
|
||||
@@ -3585,7 +3677,7 @@
|
||||
"ui.3d_mouse_offer_swap": "Swap to Hidraw (Direct Driver)",
|
||||
"ui.3d_mouse_orbit": "Orbit",
|
||||
"ui.3d_mouse_orbit_classic": "Classic",
|
||||
"ui.3d_mouse_orbit_tip": "Traced: the 3D mouse turns the view around the part of the model in the middle of the view when the turn begins, marked by a faint ball. Classic: around the view's centre",
|
||||
"ui.3d_mouse_orbit_tip": "Traced: the 3D mouse turns the view around the part of the model in the middle of the view when the turn begins, marked by a faint ball. Classic: around the view's center",
|
||||
"ui.3d_mouse_orbit_traced": "Traced",
|
||||
"ui.3d_mouse_reading": "Reading",
|
||||
"ui.3d_mouse_reset_axes": "Reset axis assignment",
|
||||
@@ -3595,6 +3687,8 @@
|
||||
"ui.3d_mouse_section": "3D mouse",
|
||||
"ui.3d_mouse_show_in_view": "Show the 3D mouse in the 3D view",
|
||||
"ui.3d_mouse_show_in_view_tip": "A picture of your 3D mouse in the lower left corner, moving as you move it.",
|
||||
"ui.3d_mouse_sketch_orbit": "Turn sketches with the 3D mouse",
|
||||
"ui.3d_mouse_sketch_orbit_tip": "The 3D mouse also turns the view while a sketch is open and seen square on, as the mouse can. Off: it pans and zooms the sketch until you tilt the view.",
|
||||
"ui.3d_mouse_skip": "My device can't do this",
|
||||
"ui.3d_mouse_skip_tip": "Skips this motion both ways, and the calibration leaves it out.",
|
||||
"ui.3d_mouse_speed": "3D mouse speed",
|
||||
@@ -3623,6 +3717,8 @@
|
||||
"ui.3d_mouse_step_twist_counterclockwise": "Twist the cap anticlockwise, hold it, then let go.",
|
||||
"ui.3d_mouse_switch_cap": "Calibrate as Return to Center",
|
||||
"ui.3d_mouse_switch_trackball": "Calibrate as a trackball",
|
||||
"ui.3d_mouse_use_direct": "Use Direct Driver",
|
||||
"ui.3d_mouse_use_direct_tip": "Read the 3D mouse directly (hidraw) instead of through spacenavd.",
|
||||
"ui.3d_mouse_view_axis": "View axis",
|
||||
"ui.3d_mouse_view_off": "Off",
|
||||
"ui.3d_mouse_view_off_tip": "The 3D mouse driver's profile turns this axis off, so its motion doesn't move the view. Picking another axis's motion here swaps the two, and that motion stops moving the view.",
|
||||
@@ -3713,7 +3809,7 @@
|
||||
"ui.agent_chat_left": "Left",
|
||||
"ui.agent_chat_limit": "The agent reached the limit of {limit} changes for one message. Send another message to let it go on.",
|
||||
"ui.agent_chat_login_found": "login found",
|
||||
"ui.agent_chat_minimize": "Minimise to a strip",
|
||||
"ui.agent_chat_minimize": "Minimize to a strip",
|
||||
"ui.agent_chat_mode_all": "All",
|
||||
"ui.agent_chat_mode_ask": "Ask",
|
||||
"ui.agent_chat_mode_edits": "Edits",
|
||||
@@ -3756,6 +3852,8 @@
|
||||
"ui.agent_chat_remove": "Remove from the queue",
|
||||
"ui.agent_chat_remove_file": "Take it off this message",
|
||||
"ui.agent_chat_remove_imported": "Take it off this message; the part stays in the design (Undo removes it)",
|
||||
"ui.agent_chat_reply_cut_off": "The reply reached its length limit before it finished, so it may be incomplete. Ask the agent to go on, or for a shorter answer.",
|
||||
"ui.agent_chat_reply_declined": "The model declined to answer this message.",
|
||||
"ui.agent_chat_retry": "Retry",
|
||||
"ui.agent_chat_right": "Right",
|
||||
"ui.agent_chat_save": "Save",
|
||||
@@ -3830,7 +3928,7 @@
|
||||
"ui.ai_mode_ask": "Ask every time",
|
||||
"ui.ai_mode_ask_note": "Every change to the design, and every file action, waits for your Allow.",
|
||||
"ui.ai_mode_edits": "Auto-allow model edits",
|
||||
"ui.ai_mode_edits_note": "Modelling commands run without asking; saving, opening, exporting and other file or destructive actions still ask.",
|
||||
"ui.ai_mode_edits_note": "Modeling commands run without asking; saving, opening, exporting and other file or destructive actions still ask.",
|
||||
"ui.ai_mode_global": "As set above",
|
||||
"ui.ai_model": "Model",
|
||||
"ui.ai_model_hint": "qwen3:14b",
|
||||
@@ -3939,6 +4037,7 @@
|
||||
"ui.circle": "Circle",
|
||||
"ui.circular_pattern": "Circular Pattern",
|
||||
"ui.clear": "Clear",
|
||||
"ui.clear_print_face": "Clear Print Face",
|
||||
"ui.clear_recent": "Clear Recent",
|
||||
"ui.clear_the_selection": "Clear the selection",
|
||||
"ui.click_selects_whole_bodies": "Click selects whole bodies",
|
||||
@@ -3948,15 +4047,15 @@
|
||||
"ui.coincident": "Coincident",
|
||||
"ui.collapse": "Collapse",
|
||||
"ui.collinear": "Collinear",
|
||||
"ui.colour_vision": "Colour vision",
|
||||
"ui.colour_vision": "Color vision",
|
||||
"ui.colour_vision_deutan": "Deutan",
|
||||
"ui.colour_vision_deutan_tip": "For a weak or missing green sense",
|
||||
"ui.colour_vision_failed": "Failed",
|
||||
"ui.colour_vision_fixed": "Fixed",
|
||||
"ui.colour_vision_mode": "Mode",
|
||||
"ui.colour_vision_note": "Changes the colours that carry a meaning: sketch states, axes, bends, analyses and icons. If you use Windows colour filters, keep this Off.",
|
||||
"ui.colour_vision_note": "Changes the colors that carry a meaning: sketch states, axes, bends, analyses and icons. If you use Windows color filters, keep this Off.",
|
||||
"ui.colour_vision_off": "Off",
|
||||
"ui.colour_vision_off_tip": "The theme's own colours",
|
||||
"ui.colour_vision_off_tip": "The theme's own colors",
|
||||
"ui.colour_vision_projected": "Projected",
|
||||
"ui.colour_vision_protan": "Protan",
|
||||
"ui.colour_vision_protan_tip": "For a weak or missing red sense",
|
||||
@@ -3973,6 +4072,15 @@
|
||||
"ui.constant": "Constant",
|
||||
"ui.constraints_conflict": "Constraints conflict",
|
||||
"ui.construction": "Construction",
|
||||
"ui.construction.angle_hint": "Construction line: click a point, then type an angle or pick a direction",
|
||||
"ui.construction.bisector_hint": "Bisector: pick two intersecting straight lines, then the desired solution",
|
||||
"ui.construction.common_hint": "Common tangent: pick two circles/arcs, then the desired solution",
|
||||
"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.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.context_menu": "Context menu",
|
||||
"ui.continuity_g0": "G0 crease",
|
||||
"ui.continuity_g1": "G1 tangent",
|
||||
@@ -3998,7 +4106,7 @@
|
||||
"ui.cut": "Cut",
|
||||
"ui.cylinder": "Cylinder",
|
||||
"ui.dark": "Dark",
|
||||
"ui.default_modelling_orientation": "Default modelling orientation",
|
||||
"ui.default_modelling_orientation": "Default modeling orientation",
|
||||
"ui.delete": "Delete",
|
||||
"ui.depth": "Depth",
|
||||
"ui.design": "Design",
|
||||
@@ -4076,7 +4184,7 @@
|
||||
"ui.expand": "Expand",
|
||||
"ui.exportDxf": "Export DXF…",
|
||||
"ui.export_ellipsis_keymap": "Export…",
|
||||
"ui.export_keymap_tip": "Keys, S box favourites and the toolbar, as a file",
|
||||
"ui.export_keymap_tip": "Keys, S box favorites and the toolbar, as a file",
|
||||
"ui.extend": "Extend",
|
||||
"ui.extent_type": "Extent Type",
|
||||
"ui.extract_component": "Extract to File…",
|
||||
@@ -4261,7 +4369,7 @@
|
||||
"ui.manipulator_free_alt_tip": "On: Alt/Option or Ctrl/Cmd on extrude / gizmo arrows disables value snapping. Off: Ctrl/Cmd only.",
|
||||
"ui.manufacture": "Manufacture",
|
||||
"ui.material_review.axes": "Material-local axes; no body orientation is assigned.",
|
||||
"ui.material_review.claims": "Sources and licences are unverified claims. Missing values remain unknown.",
|
||||
"ui.material_review.claims": "Sources and licenses are unverified claims. Missing values remain unknown.",
|
||||
"ui.material_review.compressive_strength": "Compressive strength",
|
||||
"ui.material_review.conditions": "Stated conditions",
|
||||
"ui.material_review.density": "Density",
|
||||
@@ -4362,12 +4470,17 @@
|
||||
"ui.plugins_skipped": "{count} folder(s) could not be read.",
|
||||
"ui.plugins_version": "{version}",
|
||||
"ui.point": "Point",
|
||||
"ui.point_cloud_add_filter": "Add filter…",
|
||||
"ui.point_cloud_auto_range": "Automatic Range",
|
||||
"ui.point_cloud_color_by": "Color By",
|
||||
"ui.point_cloud_file_colors": "File Colors",
|
||||
"ui.point_cloud_filter_by": "Filter by",
|
||||
"ui.point_cloud_height_high": "Highest",
|
||||
"ui.point_cloud_height_low": "Lowest",
|
||||
"ui.point_cloud_legend_filter": "Shown and hidden in the legend above",
|
||||
"ui.point_cloud_no_file_colors": "The file has no colors",
|
||||
"ui.point_cloud_pause": "Pause",
|
||||
"ui.point_cloud_play": "Play",
|
||||
"ui.point_cloud_single_color": "Single Color",
|
||||
"ui.point_cloud_size": "Point Size",
|
||||
"ui.point_cloud_units": "File Units",
|
||||
@@ -4376,6 +4489,11 @@
|
||||
"ui.polygon": "Polygon",
|
||||
"ui.preferences": "Preferences",
|
||||
"ui.press_a_key": "press a key…",
|
||||
"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",
|
||||
"ui.profile_plane": "Profile Plane",
|
||||
"ui.profiles": "Profiles",
|
||||
@@ -4439,6 +4557,7 @@
|
||||
"ui.revolve": "Revolve",
|
||||
"ui.ribbon_exit": "Exit",
|
||||
"ui.ribbon_labels": "Show text under ribbon icons",
|
||||
"ui.ribbon_menus_skip_pinned": "Leave pinned commands out of the drop-down menus",
|
||||
"ui.ribbon_pin_footer": "Pinned commands show on the ribbon. Hover a row to pin or unpin it.",
|
||||
"ui.ribbon_workspace": "Workspace",
|
||||
"ui.right": "Right",
|
||||
@@ -4457,6 +4576,7 @@
|
||||
"ui.selectOther": "Select Other…",
|
||||
"ui.selectTangentChain": "Select Tangent Chain",
|
||||
"ui.select_all": "Select All",
|
||||
"ui.select_components_by_face": "Select components (a face picks its component)",
|
||||
"ui.selection": "Selection",
|
||||
"ui.selection_modifiers": "Selection modifiers",
|
||||
"ui.selection_shift_add_only": "Shift+click adds without removing",
|
||||
@@ -4498,6 +4618,11 @@
|
||||
"ui.sketch_intersect_selection_filter": "Selection Filter",
|
||||
"ui.sketch_modifiers": "Sketch modifiers",
|
||||
"ui.sketch_project_geometry": "Geometry",
|
||||
"ui.sketch_report.all_constrained": "Everything is fully constrained.",
|
||||
"ui.sketch_report.dof": "Degrees of freedom",
|
||||
"ui.sketch_report.loose": "Not fully constrained",
|
||||
"ui.sketch_report.status": "Status",
|
||||
"ui.sketch_report.title": "Sketch Report",
|
||||
"ui.sketch_scale": "Sketch Scale",
|
||||
"ui.sketches": "Sketches",
|
||||
"ui.slice": "Slice",
|
||||
@@ -4599,13 +4724,22 @@
|
||||
"ui.view_angles_readout": "Pitch {pitch} · Yaw {yaw} · Roll {roll}",
|
||||
"ui.view_angles_roll": "Roll",
|
||||
"ui.view_angles_yaw": "Yaw",
|
||||
"ui.view_centre": "View centre",
|
||||
"ui.view_centre": "View center",
|
||||
"ui.viewport_status_clear": "Turn off {name}",
|
||||
"ui.viewport_status_clear_hint": "Turn off {name} · Esc",
|
||||
"ui.viewport_status_edges": "{count} edges",
|
||||
"ui.viewport_status_from": "from {direction}",
|
||||
"ui.viewport_status_key": "Show key for {name}",
|
||||
"ui.viewport_status_minimum_radius": "Minimum radius",
|
||||
"ui.viewport_status_pairs": "{count} pairs",
|
||||
"ui.viewport_status_section_hatch": "{setting} · hatch",
|
||||
"ui.viewport_status_section_plane": "Current plane",
|
||||
"ui.viewport_status_section_tilted": "tilted · {offset}",
|
||||
"ui.viewport_status_stripes": "{count} stripes",
|
||||
"ui.viewport_status_summary_clear": "Turn everything off",
|
||||
"ui.viewport_status_summary_heading": "In this view",
|
||||
"ui.viewport_status_summary_label": "{name} and {count} more items",
|
||||
"ui.viewport_status_summary_more": "+{count}",
|
||||
"ui.viewport_theme_follow": "Follow theme",
|
||||
"ui.visual_shaded_hidden_edges": "Shaded with hidden edges",
|
||||
"ui.visual_shaded_with_edges": "Shaded with edges",
|
||||
@@ -4636,13 +4770,15 @@
|
||||
"ui.y_up": "Y up",
|
||||
"ui.z_up": "Z up",
|
||||
"ui.zoom": "Zoom",
|
||||
"ui.zoom_to_cursor_tip": "The wheel zooms toward the point under the cursor (off: toward the view's centre)",
|
||||
"ui.zoom_to_cursor_tip": "The wheel zooms toward the point under the cursor (off: toward the view's center)",
|
||||
"ui.zoom_to_the_cursor": "Zoom to the cursor",
|
||||
"update.available": "SolidCraft {version} is available",
|
||||
"update.button": "Update",
|
||||
"update.button_version": "Update to {version}",
|
||||
"update.check_automatically": "Check for updates automatically",
|
||||
"update.check_for_updates": "Check for updates",
|
||||
"update.check_now": "Check now",
|
||||
"update.check_unavailable": "Update checks are not available for this installation.",
|
||||
"update.checking": "Checking…",
|
||||
"update.copy": "Copy",
|
||||
"update.dmg_opened": "The disk image is open and verified: drag SolidCraft to Applications, replacing the old copy, then restart SolidCraft.",
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
"BLEND SURFACE": "משטח מעבר",
|
||||
"BOUNDARY SURFACE": "משטח גבול",
|
||||
"BROWSER": "עץ התכן",
|
||||
"Back to the original order": "חזרה לסדר המקורי",
|
||||
"Ball": "כדורי",
|
||||
"Bar": "מוט",
|
||||
"Base Center": "מרכז הבסיס",
|
||||
@@ -595,6 +596,8 @@
|
||||
"Snap to face and edge centres": "הצמדה למרכזי פאות ומקצועות",
|
||||
"SolidCraft cannot submit reports directly. Submit on GitHub; no token is needed in the app.": "אין אפשרות לשלוח דיווחים ישירות מתוך SolidCraft. יש לשלוח אותם ב-GitHub; אין צורך באסימון גישה באפליקציה.",
|
||||
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft נסגרה לפני שתכנים אלה נשמרו. אפשר לפתוח את השמירה האוטומטית האחרונה שלהם.",
|
||||
"Sort A to Z": "מיון בסדר עולה",
|
||||
"Sort Z to A": "מיון בסדר יורד",
|
||||
"Spacing 2 must be a non-zero number": "מרווח 2 חייב להיות מספר שונה מאפס",
|
||||
"Spacing must be a non-zero number": "המרווח חייב להיות מספר שונה מאפס",
|
||||
"Specialise": "התמחות",
|
||||
@@ -775,6 +778,8 @@
|
||||
"at the face": "על הפאה",
|
||||
"bom.group_children": "תת-חלקים בשורת החלק שלהם",
|
||||
"browser.body_name": "גוף{number}",
|
||||
"browser.calibrate_click_a": "לחצו על התמונה כדי לבחור את נקודה A, או הקלידו את הקואורדינטות שלה.",
|
||||
"browser.calibrate_click_b": "לחצו על התמונה כדי לבחור את נקודה B, או הקלידו את הקואורדינטות שלה.",
|
||||
"browser.calibrate_title": "כיול: {name}",
|
||||
"browser.capture_many": "{count} רכיבים הוזזו, אך המיקומים שלהם לא נלכדו.",
|
||||
"browser.capture_note": "לכידה שומרת את המיקום החדש; החזרה מחזירה את המיקום הקודם.",
|
||||
@@ -817,6 +822,7 @@
|
||||
"cmd.appearance_pick_up": "בחירת מראה",
|
||||
"cmd.appearance_replace": "החלפת מראה",
|
||||
"cmd.assembly_bom": "רשימת חלקים",
|
||||
"cmd.body.print_face": "פאת הדפסה",
|
||||
"cmd.body_rename": "שינוי שם גוף",
|
||||
"cmd.browser_collapse_all": "כיווץ הכול",
|
||||
"cmd.browser_expand_all": "הרחבת הכול",
|
||||
@@ -1101,6 +1107,16 @@
|
||||
"cmd.sheet_refold": "קיפול מחדש",
|
||||
"cmd.sheet_rules": "רשימת כללי פח",
|
||||
"cmd.sheet_unfold": "פתיחת קיפול",
|
||||
"cmd.sketch.construction_line": "ישר עזר",
|
||||
"cmd.sketch.construction_line.angle": "ישר עזר בזווית",
|
||||
"cmd.sketch.construction_line.bisector": "חוצה זווית",
|
||||
"cmd.sketch.construction_line.horizontal": "ישר עזר אופקי",
|
||||
"cmd.sketch.construction_line.parallel": "ישר עזר מקביל",
|
||||
"cmd.sketch.construction_line.perpendicular": "ישר עזר ניצב",
|
||||
"cmd.sketch.construction_line.tangent_at": "משיק בנקודה על העקומה",
|
||||
"cmd.sketch.construction_line.tangent_external": "משיק דרך נקודה חיצונית",
|
||||
"cmd.sketch.construction_line.tangent_two": "משיק משותף",
|
||||
"cmd.sketch.construction_line.vertical": "ישר עזר אנכי",
|
||||
"cmd.sketch_arc3d": "קשת תלת־ממדית",
|
||||
"cmd.sketch_arc_center_point": "קשת מרכז ונקודה",
|
||||
"cmd.sketch_arc_tangent": "קשת משיקה",
|
||||
@@ -1389,7 +1405,18 @@
|
||||
"help.occurrence.move": "מזיז חלק בהרכבה רק בכיוונים שהמפרקים שלו מאפשרים. בחרו פאה של החלק, ואז גררו חץ או קשת זמינים.",
|
||||
"help.parameters.change": "פתיחת הטבלה של ערכים בעלי שם וביטויים שקובעים את מידות התכנון. כך משנים מידה במקום אחד וכל הפיצ'רים שמשתמשים בה מתעדכנים.",
|
||||
"help.pointcloud.insert": "הוספת סריקה בפורמט PLY, XYZ או PTS כענן נקודות לצד המודל. משמש כעזר למידול על סמך נתוני מדידה.",
|
||||
"help.sketch.construction_line": "יצירת ישר עזר אינסופי דרך שתי נקודות, לתכנון הסקיצה בלי לפצל את הפרופילים.",
|
||||
"help.sketch.construction_line.angle": "יצירת ישר עזר אינסופי בזווית מציר X של הסקיצה. יש לבחור נקודה ולהקליד זווית או לבחור כיוון.",
|
||||
"help.sketch.construction_line.bisector": "יש לבחור שני ישרים נחתכים בסקיצה ואז אחד משני חוצי הזווית. התוצאה עוקבת אחר עריכת ישרי המקור.",
|
||||
"help.sketch.construction_line.horizontal": "יצירת ישר עזר אופקי אינסופי דרך נקודה. האילוץ האופקי נשמר בעריכות מאוחרות יותר.",
|
||||
"help.sketch.construction_line.parallel": "יש לבחור ישר בסקיצה ואז למקם ישר עזר אינסופי מקביל. לחיצה ימנית מאפשרת לבחור ייחוס אחר.",
|
||||
"help.sketch.construction_line.perpendicular": "יש לבחור ישר בסקיצה ואז למקם ישר עזר אינסופי ניצב. לחיצה ימנית מאפשרת לבחור ייחוס אחר.",
|
||||
"help.sketch.construction_line.tangent_at": "יצירת משיק בנקודה על מעגל או קשת מעגלית, או בנקודת קצה של ספליין פתוח או חתך חרוט פתוח. יש לבחור את העקומה ואז את נקודת ההשקה.",
|
||||
"help.sketch.construction_line.tangent_external": "יש לבחור מעגל או קשת מעגלית, נקודה חיצונית ואז פתרון למשיק. התצוגה המקדימה מציגה רק נקודות השקה על הקשת המצוירת.",
|
||||
"help.sketch.construction_line.tangent_two": "יש לבחור שני מעגלים או קשתות מעגליות ואז משיק משותף בתצוגה המקדימה. נקודות ההשקה חייבות להיות על העקומות המצוירות.",
|
||||
"help.sketch.construction_line.vertical": "יצירת ישר עזר אנכי אינסופי דרך נקודה. האילוץ האנכי נשמר בעריכות מאוחרות יותר.",
|
||||
"help.sketch.equation_curve": "משרטט עקומה מנוסחאות על פני טווח: x(t) ו־y(t), או y = f(x). הנוסחאות יכולות להשתמש בפרמטרים והעקומה עוקבת אחריהם: לגל סינוס, לספירלה או לאבולבנטה של שן גלגל שיניים.",
|
||||
"help.sketch.inspect": "פותח דוח על הסקיצה הפתוחה: כמה דרגות חופש נותרו והאם היא מאולצת במלואה. השתמשו בו כדי לגלות מה עדיין זז לפני שמסיימים את הסקיצה.",
|
||||
"help.solid.align": "הזזת הגופים שנבחרו כך שנקודה, פאה או מישור שלהם יפגשו נקודה, פאה או מישור במקום אחר. מתאים להצמדת חלק לחלק אחר בלי למדוד את המרחק.",
|
||||
"help.solid.chamfer": "חיתוך המקצועות שנבחרו בשיפוע ישר במרחק שנקבע. כך שוברים מקצועות חדים או מקלים על הכנסה של פין או בורג.",
|
||||
"help.solid.combine": "איחוד גופים, חיסורם מגוף יעד או השארת הנפח המשותף להם בלבד, עם אפשרות לשמור את גופי הכלי. מתאים למיזוג חלקים, לחיתוך צורה אחת בעזרת אחרת או למציאת החפיפה בין שני גופים.",
|
||||
@@ -1592,6 +1619,10 @@
|
||||
"material.selection.mixed": "חומרים שונים",
|
||||
"measure.at": "מיקום",
|
||||
"measure.item": "פריט {number}",
|
||||
"measure.quick_copied": "המדידה הועתקה",
|
||||
"measure.quick_copy": "לחיצה מעתיקה את המדידה",
|
||||
"measure.quick_edges": "{count} מקצועות",
|
||||
"measure.quick_faces": "{count} פאות",
|
||||
"messages.ago_h": "לפני {n} שע'",
|
||||
"messages.ago_min": "לפני {n} דק'",
|
||||
"messages.ago_s": "לפני {n} שנ'",
|
||||
@@ -1744,6 +1775,11 @@
|
||||
"not a move": "זו אינה הזזה",
|
||||
"not a number": "לא מספר",
|
||||
"open a sketch first": "יש לפתוח סקיצה תחילה",
|
||||
"parameters.apply_live": "להחיל כל שינוי ביציאה מהשדה שלו",
|
||||
"parameters.apply_live_tip": "כבוי: השינויים ממתינים עד ל„החל” או „אישור”, ואז התכן נבנה מחדש פעם אחת עבור כולם. „ביטול” או Escape משליכים אותם.",
|
||||
"parameters.held": "{count} שינויים ממתינים ל„החל”",
|
||||
"parameters.held_hint": "עריכת ביטויים ויחידות. השינויים ממתינים ל„החל” או ל„אישור”.",
|
||||
"parameters.held_one": "שינוי אחד ממתין ל„החל”",
|
||||
"parts.automatic_name": "אוטומטי",
|
||||
"parts.dimension.across_flats": "מידת מפתח",
|
||||
"parts.dimension.bore": "קוטר קדח",
|
||||
@@ -1766,6 +1802,8 @@
|
||||
"parts.family.set_screw": "בורג אלן ללא ראש, קצה שטוח",
|
||||
"parts.family.socket_head_cap_screw": "בורג ראש גלילי עם משושה פנימי",
|
||||
"parts.family.washer": "דסקית שטוחה",
|
||||
"parts.holes": "חורים (אופציונלי)",
|
||||
"parts.placement_hint": "בחרו חור אחד או יותר כדי להושיב חלק בכל אחד, או לחצו אישור כדי למקם אותו בראשית",
|
||||
"pattern.alignment_2": "יישור 2",
|
||||
"pattern.alignment_hint": "הספירה כוללת את המקור. מרכוז מאזן את העותקים; היפוך בוחר את הצד שמקבל את העותק הנוסף כשהסך זוגי.",
|
||||
"pattern.direction_1": "כיוון 1",
|
||||
@@ -1818,6 +1856,8 @@
|
||||
"prefs.bottom_centre": "למטה במרכז",
|
||||
"prefs.bottom_right": "למטה מימין",
|
||||
"prefs.browser": "עץ התכן",
|
||||
"prefs.browser_eye_left": "סמל העין ליד חץ הקיפול",
|
||||
"prefs.browser_eye_left_note": "שורות עץ התכן מציגות את סמל העין ליד חץ הקיפול, כמו קודם, במקום בעמודה בקצה הימני של השורות.",
|
||||
"prefs.browser_reveal_on_hover": "הצגה בריחוף",
|
||||
"prefs.browser_show_note": "״מוצג״ משאיר את הדפדפן על המסך. ״הצגה בריחוף״ מסתיר אותו עד שהמצביע מגיע למקומו או ללשונית הדקה בקצהו השמאלי, והוא נשאר פתוח כל עוד המצביע בתוכו או שתפריט ממנו פתוח.",
|
||||
"prefs.browser_shown": "מוצג",
|
||||
@@ -1868,6 +1908,8 @@
|
||||
"prefs.graphics_card_restart": "ייכנס לתוקף אחרי הפעלה מחדש של SolidCraft. WGPU_ADAPTER_NAME גובר על בחירה זו.",
|
||||
"prefs.graphics_note": "איזון בין מהירות לחלקות. כל בחירה מציגה את השפעתה על דוגמה.",
|
||||
"prefs.graphics_presets_note": "הגדרות קבועות מראש קובעות יחד את הבחירות שלמטה.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "הסתרת סמלי אילוצים בהקטנה",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out_note": "סמלי האילוצים נעלמים כשהשרטוט מוקטן מספיק כדי שלא יעמיסו על התצוגה. כבוי: הם נשארים בגודל מסך קבוע בכל זום.",
|
||||
"prefs.history": "ציר הזמן",
|
||||
"prefs.history_origin_bodies": "הצגת הראשית והגופים למעלה",
|
||||
"prefs.history_origin_bodies_note": "ההיסטוריה משמאל או מתחת לדפדפן מתחילה בתיקיית הגופים של הרכיב הפעיל ובראשית, עם סמלי העין והתפריטים כמו בדפדפן. ההיסטוריה למטה לא משתנה.",
|
||||
@@ -1906,6 +1948,9 @@
|
||||
"prefs.max_file_size_step": "קובצי STEP וייבוא",
|
||||
"prefs.max_file_size_web": "יישום הדפדפן פותח קובצי STEP וייבוא עד 512 MB וקובצי תכן עד 256 MB: לשונית דפדפן אינה יכולה להכיל קבצים גדולים יותר. ביישום למחשב ניתן להעלות מגבלות אלה.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "סמלים גדולים",
|
||||
"prefs.menu_list": "רשימה",
|
||||
"prefs.menus": "תפריטים",
|
||||
"prefs.minutes": "דקות",
|
||||
"prefs.minutes_suffix": " דק'",
|
||||
"prefs.mouse": "עכבר ומשטח מגע",
|
||||
@@ -1925,10 +1970,16 @@
|
||||
"prefs.origin_plane_names": "שמות מישורי הראשית",
|
||||
"prefs.origin_plane_names_note": "השמות המוצגים בדפדפן, בתיבות הדו־שיח וברשימות. {top} הוא XY, {front} הוא XZ ו־{right} הוא YZ, כש־Z למעלה. פקודות, סקריפטים ותכנונים שמורים שומרים על XY, XZ ו־YZ.",
|
||||
"prefs.overall_speed": "מהירות כללית",
|
||||
"prefs.parameter_list_rows": "שורות ברשימת הפרמטרים",
|
||||
"prefs.parameter_list_rows_tip": "חץ למטה בשדה ערך ריק מציג את הפרמטרים, המועדפים תחילה, עד מספר זה. מספר קטן יותר שומר על רשימה קלה בתכנון גדול.",
|
||||
"prefs.persistent_ribbon_pressed": "הדגשת פקודות פעילות ברצועת הכלים",
|
||||
"prefs.persistent_ribbon_pressed_help": "הדגשת פקודות ברצועת הכלים כל עוד ההשפעות שלהן פעילות. כבו אפשרות זו כדי לשמור על המראה הניטרלי הקודם ועל עזרת הפקודות.",
|
||||
"prefs.perspective_fov": "שדה ראייה",
|
||||
"prefs.placement": "מיקום",
|
||||
"prefs.prefer_free_sketch_points": "העדפת נקודות סקיצה ניתנות להזזה כשנקודות חופפות",
|
||||
"prefs.preview_choices": "תצוגה מקדימה של הבחירות שלך",
|
||||
"prefs.print_face_highlight": "הדגשת פאת ההדפסה",
|
||||
"prefs.print_face_highlight_tip": "עד כמה חזק צובעים ומקיפים את פאת ההדפסה של גוף בתצוגה. 0 % אינו מציג הדגשה.",
|
||||
"prefs.profile_active": "פרופיל פעיל",
|
||||
"prefs.profile_applied": "הפרופיל הוחל.",
|
||||
"prefs.profile_changed": "ההגדרות השתנו מאז שהפרופיל נשמר. „שמירה“ מכניסה אותן לפרופיל.",
|
||||
@@ -1950,6 +2001,8 @@
|
||||
"prefs.quality_fastest": "המהיר ביותר",
|
||||
"prefs.quality_slowest": "האיטי ביותר",
|
||||
"prefs.quality_smoother": "חלק יותר",
|
||||
"prefs.quick_measure": "מדידה מהירה בפינת התצוגה",
|
||||
"prefs.quick_measure_note": "מציג את מידות הבחירה בפינה הימנית התחתונה של התצוגה, מעל ההיסטוריה: אורך מקצוע (או Ø ו-R למקצוע עגול), שטח פאה, מיקום נקודה, המרחק והזווית בין שני פריטים, והסכום של כמה מקצועות או פאות. לחיצה מעתיקה אותו.",
|
||||
"prefs.render_previous_geometry_budget": "שימוש בתקציב הגאומטריה הקודם לתצוגה מקדימה של רינדור",
|
||||
"prefs.render_previous_geometry_budget_help": "שחזור מגבלת הגאומטריה הקבועה הקודמת (512 MiB במחשב שולחני של 64 סיביות, 128 MiB בפלטפורמות אחרות) במקום התקציב המבוסס על ההתקן.",
|
||||
"prefs.reports_saved": "{n} שמורים",
|
||||
@@ -1963,6 +2016,8 @@
|
||||
"prefs.search_empty": "אין הגדרות תואמות",
|
||||
"prefs.select": "בחירה",
|
||||
"prefs.select_one_item": "בחירת פריט אחד",
|
||||
"prefs.select_other_hidden": "בחירת גאומטריה מוסתרת באמצעות בחירה אחרת",
|
||||
"prefs.select_other_hidden_help": "מופעל: כולל גופים, סקיצות וקצוות מוסתרים בקרבת הסמן. כבוי: שימוש ברשימה המקורית של גאומטריה גלויה מתחת לסמן.",
|
||||
"prefs.selecting": "בחירה",
|
||||
"prefs.shift": "Shift",
|
||||
"prefs.shift_add_ctrl_toggle": "Shift מוסיף בלבד; Ctrl / Cmd מחליף מצב",
|
||||
@@ -1974,11 +2029,14 @@
|
||||
"prefs.sketch_align_points_note": "בזמן השרטוט, נקודה מתיישרת אופקית עם נקודת סקיצה אחת ואנכית עם אחרת בו-זמנית, עם קו עזר מנוקד, והלחיצה שומרת את שני היישורים. כבוי: רק עם הנקודה הקודמת, ציר אחד בכל פעם.",
|
||||
"prefs.sketch_conflict_colour": "גאומטריה סותרת בצבע הסתירה",
|
||||
"prefs.sketch_conflict_colour_note": "כשאילוצי סקיצה סותרים, העקומות והנקודות שאילוץ כושל מחזיק מצוירות גם הן בצבע הסתירה, לא רק הסמל או המידה שלו. כבוי: הן שומרות על צבעיהן הרגילים.",
|
||||
"prefs.sketch_construction_lines": "כלי ישרי עזר מורחבים",
|
||||
"prefs.sketch_exit_view": "בסיום סקיצה",
|
||||
"prefs.sketch_exit_view.keep": "שמירה על התצוגה הנוכחית",
|
||||
"prefs.sketch_exit_view.previous": "חזרה לתצוגה הקודמת",
|
||||
"prefs.sketch_exit_view.unless_rotated": "חזרה אלא אם התצוגה סובבה",
|
||||
"prefs.sketch_exit_view_note": "לאן התצוגה עוברת אחרי סיום סקיצה, ביטול או Esc כפול.",
|
||||
"prefs.sketch_open_look_at": "הפניית התצוגה אל הסקיצה בפתיחתה",
|
||||
"prefs.sketch_open_look_at_note": "כבוי: יצירה או עריכה של סקיצה משאירות את התצוגה כפי שהיא. «מבט אל» מפנה את התצוגה אל הסקיצה כשרוצים.",
|
||||
"prefs.sketch_recent_snap": "הצמדה אוטומטית למרכזים ולנקודות אמצע אחרונים",
|
||||
"prefs.sketch_recent_snap_note": "בזמן שרטוט, הסמן נצמד למקום שבו נחתכים קווי העזר של שני מרכזי המעגלים או נקודות האמצע של קצוות ישרים האחרונים. כבוי: קווי העזר נשארים לתצוגה בלבד והסמן אינו זז.",
|
||||
"prefs.sketch_selection": "סקיצה ובחירה",
|
||||
@@ -2038,13 +2096,21 @@
|
||||
"prefs.under_cursor": "מתחת לסמן",
|
||||
"prefs.units_precision": "יחידות ודיוק",
|
||||
"prefs.updates": "עדכונים",
|
||||
"prefs.value_boxes_in_view": "הצגת ערכים ליד הידיות שלהם בתצוגה",
|
||||
"prefs.value_boxes_in_view_tip": "ערך הפקודה (מרחק, רדיוס…) בתיבה ליד הידית שלו, מעל המודל. כבוי: מקלידים אותו בחלון הפקודה, שמקבל את המקלדת אחרי בחירה, ושום דבר לא מסתיר את המודל.",
|
||||
"prefs.view": "התנהגות המבט",
|
||||
"prefs.view_confirm_corner": "אישור וביטול בפינת התצוגה",
|
||||
"prefs.view_confirm_corner_note": "כשתיבת דו־שיח או סקיצה פתוחות, סימן וי וסימן איקס בפינה הימנית העליונה של התצוגה עושים את מה שעושים אישור וביטול של תיבת הדו־שיח, או מסיימים ומבטלים את הסקיצה. ניווט התצוגה זז אל מתחתיהם.",
|
||||
"prefs.viewport": "אזור התצוגה",
|
||||
"prefs.viewport_analysis_status": "הצגת מצב ניתוח בחלון התצוגה",
|
||||
"prefs.viewport_analysis_status_help": "הצגת ניתוחים פעילים מעל חלון התצוגה ופתיחת המקרא מכל פריט. Esc מכבה את הניתוח האחרון. כיבוי האפשרות משחזר את המקרא בפינה ואת פעולת Esc הקודמת.",
|
||||
"prefs.viewport_background": "רקע",
|
||||
"prefs.viewport_background_colour": "צבע",
|
||||
"prefs.viewport_background_theme": "מעבר הצבעים של ערכת הנושא",
|
||||
"prefs.viewport_section_status": "הצגת מצב חתך בחלון התצוגה",
|
||||
"prefs.viewport_section_status_help": "הצגת הגדרת החתך בחלון התצוגה. Escape ולחצן הסרט הפעיל מכבים את החתך. כיבוי האפשרות שומר על בקרי החתך הקודמים.",
|
||||
"prefs.viewport_status_summary": "סיכום פריטי מצב בחלון התצוגה",
|
||||
"prefs.viewport_status_summary_help": "קיבוץ שלושה פריטי מצב או יותר לתג סיכום אחד. כיבוי האפשרות משאיר כל פריט בשורה המלאה.",
|
||||
"prefs.wheel": "גלגלת",
|
||||
"prefs.wheel_trackpad": "גלגלת ומשטח מגע",
|
||||
"prefs.workspace": "סביבת עבודה",
|
||||
@@ -2054,6 +2120,10 @@
|
||||
"properties.title": "מאפיינים: {name}",
|
||||
"recovery.days_ago": "לפני {count} ימים",
|
||||
"recovery.detail": "{file} · {features} פיצ'רים · נשמר אוטומטית {when}",
|
||||
"recovery.discard_all": "השלכת הכול",
|
||||
"recovery.discard_all_detail": "לא ניתן לבטל פעולה זו.",
|
||||
"recovery.discard_all_title": "להשליך {count} שמירות אוטומטיות?",
|
||||
"recovery.discard_all_title_one": "להשליך שמירה אוטומטית אחת?",
|
||||
"recovery.hours_ago": "לפני {count} שע'",
|
||||
"recovery.just_now": "זה עתה",
|
||||
"recovery.min_ago": "לפני {count} דק'",
|
||||
@@ -2919,7 +2989,9 @@
|
||||
"render.output_ready": "מוכן לרינדור",
|
||||
"render.output_run_check": "הפעל בדיקה",
|
||||
"render.output_size_limit": "{method}: עד {dimension} פיקסלים לצלע ו-{pixels} פיקסלים. הגודל הגדול ביותר שמתאים: {width} × {height}.",
|
||||
"render.output_size_method_profile": "כדי לרנדר {width} × {height}, יש לבחור ב-{method} עם פרופיל משאבי CPU {profile} או בגודל קטן יותר.",
|
||||
"render.output_size_ok": "הגודל תקין · {width} × {height} פיקסלים",
|
||||
"render.output_size_profile": "כדי לרנדר {width} × {height}, יש לבחור בפרופיל משאבי CPU {profile} או בגודל קטן יותר.",
|
||||
"render.output_swap_orientation": "החלפת רוחב וגובה",
|
||||
"render.output_tools_inspector": "ערוך גודל ופורמט במפקח הפלט.",
|
||||
"render.output_view_image": "הצג תמונה",
|
||||
@@ -3132,6 +3204,7 @@
|
||||
"render.size_custom": "מותאם…",
|
||||
"render.size_full_hd": "Full HD",
|
||||
"render.size_hd": "HD",
|
||||
"render.size_needs_profile": "{size} · נדרש {profile}",
|
||||
"render.size_presets": "הגדרות גודל מוכנות",
|
||||
"render.size_presets_pending": "יגיע עם עורך הפלט",
|
||||
"render.size_preview": "תצוגה מקדימה",
|
||||
@@ -3419,6 +3492,24 @@
|
||||
"sheet.flange_type_contour": "מתאר",
|
||||
"sheet.flange_type_edge": "מקצוע",
|
||||
"sheet.no_body": "אין גוף פח: יש להתחיל אחד עם אוגן > בסיס, או להמיר לוח לפח עם המרה לפח (יש לבחור את הפאה השטוחה הגדולה שלו)",
|
||||
"sheet.personal_rules.confirm_delete": "אישור מחיקה",
|
||||
"sheet.personal_rules.delete": "מחיקה",
|
||||
"sheet.personal_rules.delete_prompt": "למחוק את הכלל האישי הזה? כללים שכבר נמצאים בתכנים לא ישתנו.",
|
||||
"sheet.personal_rules.full": "הספרייה האישית כבר מכילה 64 כללים.",
|
||||
"sheet.personal_rules.invalid_name": "יש להזין שם קצר ללא מעברי שורה או תווי בקרה.",
|
||||
"sheet.personal_rules.invalid_values": "יש להשלים את ערכי הכלל: כל ערך צריך להיות ניתן לחישוב בתכן זה, עם עובי חיובי ומקדם K בין 0 ל־1.",
|
||||
"sheet.personal_rules.missing": "הכלל האישי שנבחר כבר אינו זמין.",
|
||||
"sheet.personal_rules.name": "שם הכלל",
|
||||
"sheet.personal_rules.name_exists": "כבר קיים כלל אישי בשם הזה. יש להשתמש בשם אחר.",
|
||||
"sheet.personal_rules.name_hint": "שם ייחודי",
|
||||
"sheet.personal_rules.overwrite": "שמירת השינויים בכלל האישי שנבחר",
|
||||
"sheet.personal_rules.rename": "שינוי שם",
|
||||
"sheet.personal_rules.rule": "כלל אישי",
|
||||
"sheet.personal_rules.save": "שמירה כחדש",
|
||||
"sheet.personal_rules.scope": "נשמרים בפרופיל שלך לכל התכנים.",
|
||||
"sheet.personal_rules.select": "בחירת כלל",
|
||||
"sheet.personal_rules.title": "כללים אישיים",
|
||||
"sheet.personal_rules.unsupported": "לכלל הזה יש ערכים שהגרסה הזו אינה יכולה להשתמש בהם.",
|
||||
"shortcut.json_error": "הפרמטרים חייבים להיות JSON: {error}",
|
||||
"shortcut.no_command": "אין פקודה `{id}`; הטקסט שלפני ה־JSON חייב להיות מזהה פקודה",
|
||||
"shortcut.pin": "הצמדה לראש תיבת S",
|
||||
@@ -3455,6 +3546,7 @@
|
||||
"sketch_text.align_right": "ימין",
|
||||
"sketch_text.bold": "מודגש",
|
||||
"sketch_text.builtin_font": "{family} (מובנה)",
|
||||
"sketch_text.formula": "התחילו ב־= כדי לבנות את הטקסט מפרמטרים: ='W' + number(Width)",
|
||||
"sketch_text.italic": "נטוי",
|
||||
"sketch_text.loading_fonts": "מחפש גופנים מותקנים…",
|
||||
"sketch_text.on_path": "הטקסט עוקב אחר המסלול שלו: העקומה קובעת את מיקומו",
|
||||
@@ -3595,6 +3687,8 @@
|
||||
"ui.3d_mouse_section": "עכבר תלת־ממדי",
|
||||
"ui.3d_mouse_show_in_view": "הצגת העכבר התלת־ממדי בתצוגה התלת־ממדית",
|
||||
"ui.3d_mouse_show_in_view_tip": "תמונה של העכבר התלת־ממדי בפינה השמאלית התחתונה, שזזה כשמזיזים אותו.",
|
||||
"ui.3d_mouse_sketch_orbit": "סיבוב סקיצות בעכבר התלת־ממדי",
|
||||
"ui.3d_mouse_sketch_orbit_tip": "העכבר התלת־ממדי מסובב את התצוגה גם כשסקיצה פתוחה ונראית חזיתית, כמו שהעכבר יכול. כבוי: הוא מזיז ומגדיל את הסקיצה עד שתטה את התצוגה.",
|
||||
"ui.3d_mouse_skip": "ההתקן שלי לא יכול לעשות את זה",
|
||||
"ui.3d_mouse_skip_tip": "מדלג על התנועה הזאת בשני הכיוונים, והכיול משמיט אותה.",
|
||||
"ui.3d_mouse_speed": "מהירות עכבר תלת־ממדי",
|
||||
@@ -3623,6 +3717,8 @@
|
||||
"ui.3d_mouse_step_twist_counterclockwise": "יש לסובב את הכיפה נגד כיוון השעון, להחזיק ואז לשחרר.",
|
||||
"ui.3d_mouse_switch_cap": "כיול כהתקן חזרה למרכז",
|
||||
"ui.3d_mouse_switch_trackball": "כיול ככדור עקיבה",
|
||||
"ui.3d_mouse_use_direct": "שימוש במנהל ישיר",
|
||||
"ui.3d_mouse_use_direct_tip": "קריאת העכבר התלת־ממדי ישירות (hidraw) במקום דרך spacenavd.",
|
||||
"ui.3d_mouse_view_axis": "ציר מבט",
|
||||
"ui.3d_mouse_view_off": "כבוי",
|
||||
"ui.3d_mouse_view_off_tip": "הפרופיל של מנהל ההתקן לעכבר התלת־ממדי מכבה את הציר הזה, ולכן התנועה שלו לא מזיזה את המבט. בחירה כאן בתנועה של ציר אחר מחליפה בין השניים, והתנועה ההיא מפסיקה להזיז את המבט.",
|
||||
@@ -3756,6 +3852,8 @@
|
||||
"ui.agent_chat_remove": "הסרה מהתור",
|
||||
"ui.agent_chat_remove_file": "הסרה מההודעה הזו",
|
||||
"ui.agent_chat_remove_imported": "הסרה מההודעה הזו; החלק נשאר בתכן (ביטול מסיר אותו)",
|
||||
"ui.agent_chat_reply_cut_off": "התשובה הגיעה למגבלת האורך שלה לפני שהסתיימה, ולכן ייתכן שהיא חלקית. יש לבקש מהסוכן להמשיך, או לענות בקצרה יותר.",
|
||||
"ui.agent_chat_reply_declined": "המודל סירב לענות על ההודעה הזו.",
|
||||
"ui.agent_chat_retry": "ניסיון חוזר",
|
||||
"ui.agent_chat_right": "ימין",
|
||||
"ui.agent_chat_save": "שמירה",
|
||||
@@ -3939,6 +4037,7 @@
|
||||
"ui.circle": "מעגל",
|
||||
"ui.circular_pattern": "מערך מעגלי",
|
||||
"ui.clear": "ניקוי",
|
||||
"ui.clear_print_face": "נקה פאת הדפסה",
|
||||
"ui.clear_recent": "ניקוי האחרונים",
|
||||
"ui.clear_the_selection": "ניקוי הבחירה",
|
||||
"ui.click_selects_whole_bodies": "לחיצה בוחרת גופים שלמים",
|
||||
@@ -3973,6 +4072,15 @@
|
||||
"ui.constant": "קבוע",
|
||||
"ui.constraints_conflict": "התנגשות בין אילוצים",
|
||||
"ui.construction": "עזר",
|
||||
"ui.construction.angle_hint": "ישר עזר: בחירת נקודה ואז הקלדת זווית או בחירת כיוון",
|
||||
"ui.construction.bisector_hint": "חוצה זווית: בחירת שני ישרים נחתכים ואז הפתרון הרצוי",
|
||||
"ui.construction.common_hint": "משיק משותף: בחירת שני מעגלים/קשתות ואז הפתרון הרצוי",
|
||||
"ui.construction.contact_hint": "משיק: בחירת מעגל/קשת או ספליין/חתך חרוט פתוח ואז נקודת השקה (לעקומות חופשיות: קצוות בלבד)",
|
||||
"ui.construction.external_hint": "משיק: בחירת מעגל או קשת, נקודה חיצונית ואז הפתרון הרצוי",
|
||||
"ui.construction.free_hint": "ישר עזר: לחיצה על שתי נקודות; לחיצה ימנית להתחלה מחדש",
|
||||
"ui.construction.point_hint": "ישר עזר: לחיצה על נקודת המעבר",
|
||||
"ui.construction.reference_hint": "ישר עזר: בחירת ישר ואז נקודה; לחיצה ימנית להחלפת הייחוס",
|
||||
"ui.construction.ribbon_label": "קווי עזר",
|
||||
"ui.context_menu": "תפריט הקשר",
|
||||
"ui.continuity_g0": "G0 שבר",
|
||||
"ui.continuity_g1": "G1 משיק",
|
||||
@@ -4362,12 +4470,17 @@
|
||||
"ui.plugins_skipped": "לא ניתן היה לקרוא {count} תיקיות.",
|
||||
"ui.plugins_version": "{version}",
|
||||
"ui.point": "נקודה",
|
||||
"ui.point_cloud_add_filter": "הוספת מסנן…",
|
||||
"ui.point_cloud_auto_range": "טווח אוטומטי",
|
||||
"ui.point_cloud_color_by": "צביעה לפי",
|
||||
"ui.point_cloud_file_colors": "צבעי הקובץ",
|
||||
"ui.point_cloud_filter_by": "סינון לפי",
|
||||
"ui.point_cloud_height_high": "הגובה המרבי",
|
||||
"ui.point_cloud_height_low": "הגובה המזערי",
|
||||
"ui.point_cloud_legend_filter": "מוצגים ומוסתרים במקרא שלמעלה",
|
||||
"ui.point_cloud_no_file_colors": "אין בקובץ צבעים",
|
||||
"ui.point_cloud_pause": "השהיה",
|
||||
"ui.point_cloud_play": "הפעלה",
|
||||
"ui.point_cloud_single_color": "צבע אחיד",
|
||||
"ui.point_cloud_size": "גודל נקודה",
|
||||
"ui.point_cloud_units": "יחידות הקובץ",
|
||||
@@ -4376,6 +4489,11 @@
|
||||
"ui.polygon": "מצולע",
|
||||
"ui.preferences": "העדפות",
|
||||
"ui.press_a_key": "יש ללחוץ על מקש…",
|
||||
"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": "פרופיל",
|
||||
"ui.profile_plane": "מישור הפרופיל",
|
||||
"ui.profiles": "פרופילים",
|
||||
@@ -4439,6 +4557,7 @@
|
||||
"ui.revolve": "סיבוב",
|
||||
"ui.ribbon_exit": "יציאה",
|
||||
"ui.ribbon_labels": "הצגת טקסט מתחת לסמלי רצועת הכלים",
|
||||
"ui.ribbon_menus_skip_pinned": "השמטת פקודות מוצמדות מהתפריטים הנפתחים",
|
||||
"ui.ribbon_pin_footer": "פקודות מוצמדות מוצגות ברצועת הכלים. יש לרחף מעל שורה כדי להצמיד אותה או לבטל את הצמדתה.",
|
||||
"ui.ribbon_workspace": "סביבת עבודה",
|
||||
"ui.right": "ימין",
|
||||
@@ -4457,6 +4576,7 @@
|
||||
"ui.selectOther": "בחירת אחר…",
|
||||
"ui.selectTangentChain": "בחירת שרשרת משיקה",
|
||||
"ui.select_all": "בחירת הכול",
|
||||
"ui.select_components_by_face": "בחירת רכיבים (בחירת פאה בוחרת את הרכיב שלה)",
|
||||
"ui.selection": "בחירה",
|
||||
"ui.selection_modifiers": "מקשי שינוי לבחירה",
|
||||
"ui.selection_shift_add_only": "Shift+לחיצה מוסיף בלי להסיר",
|
||||
@@ -4498,6 +4618,11 @@
|
||||
"ui.sketch_intersect_selection_filter": "מסנן בחירה",
|
||||
"ui.sketch_modifiers": "מקשי שינוי בסקיצה",
|
||||
"ui.sketch_project_geometry": "גאומטריה",
|
||||
"ui.sketch_report.all_constrained": "הכול מאולץ במלואו.",
|
||||
"ui.sketch_report.dof": "דרגות חופש",
|
||||
"ui.sketch_report.loose": "לא מאולץ במלואו",
|
||||
"ui.sketch_report.status": "מצב",
|
||||
"ui.sketch_report.title": "דוח סקיצה",
|
||||
"ui.sketch_scale": "קנה מידה של סקיצה",
|
||||
"ui.sketches": "סקיצות",
|
||||
"ui.slice": "חתך",
|
||||
@@ -4602,10 +4727,19 @@
|
||||
"ui.view_centre": "מרכז המבט",
|
||||
"ui.viewport_status_clear": "כיבוי {name}",
|
||||
"ui.viewport_status_clear_hint": "כיבוי {name} · Esc",
|
||||
"ui.viewport_status_edges": "{count} קצוות",
|
||||
"ui.viewport_status_from": "מכיוון {direction}",
|
||||
"ui.viewport_status_key": "הצגת מקרא עבור {name}",
|
||||
"ui.viewport_status_minimum_radius": "רדיוס מינימלי",
|
||||
"ui.viewport_status_pairs": "{count} זוגות",
|
||||
"ui.viewport_status_section_hatch": "{setting} · קווקוו",
|
||||
"ui.viewport_status_section_plane": "המישור הנוכחי",
|
||||
"ui.viewport_status_section_tilted": "נטוי · {offset}",
|
||||
"ui.viewport_status_stripes": "{count} פסים",
|
||||
"ui.viewport_status_summary_clear": "כיבוי הכול",
|
||||
"ui.viewport_status_summary_heading": "בתצוגה זו",
|
||||
"ui.viewport_status_summary_label": "{name} ועוד {count} פריטים",
|
||||
"ui.viewport_status_summary_more": "+{count}",
|
||||
"ui.viewport_theme_follow": "לפי ערכת הנושא",
|
||||
"ui.visual_shaded_hidden_edges": "מוצלל עם מקצועות נסתרים",
|
||||
"ui.visual_shaded_with_edges": "מוצלל עם מקצועות",
|
||||
@@ -4642,7 +4776,9 @@
|
||||
"update.button": "עדכון",
|
||||
"update.button_version": "עדכון ל־{version}",
|
||||
"update.check_automatically": "בדיקת עדכונים אוטומטית",
|
||||
"update.check_for_updates": "בדיקת עדכונים",
|
||||
"update.check_now": "בדיקה עכשיו",
|
||||
"update.check_unavailable": "בדיקת עדכונים אינה זמינה עבור התקנה זו.",
|
||||
"update.checking": "בבדיקה…",
|
||||
"update.copy": "העתקה",
|
||||
"update.dmg_opened": "תמונת הדיסק פתוחה ואומתה: יש לגרור את SolidCraft לתיקיית היישומים במקום העותק הישן, ואז להפעיל מחדש את SolidCraft.",
|
||||
|
||||
@@ -206,6 +206,7 @@
|
||||
"Edges": "Bridovi",
|
||||
"Edit": "Uredi",
|
||||
"Edit Profile Sketch": "Uredi skicu profila",
|
||||
"Edit shortcuts": "Uredi prečace",
|
||||
"Enable rule": "Omogući pravilo",
|
||||
"Enabled": "Omogućeno",
|
||||
"End Radius": "Krajnji polumjer",
|
||||
@@ -468,6 +469,7 @@
|
||||
"Radius": "Polumjer",
|
||||
"Radius Fit": "Uklapanje polumjera",
|
||||
"Ratio": "Omjer",
|
||||
"Read-only list: shortcuts can't be changed here.": "Popis samo za čitanje: prečaci se ovdje ne mogu mijenjati.",
|
||||
"Reattach selected face": "Ponovno pričvrsti odabrano lice",
|
||||
"Recover": "Oporavi",
|
||||
"Recover unsaved design?": "Oporaviti nespremljeni dizajn?",
|
||||
@@ -652,6 +654,7 @@
|
||||
"Third point": "Treća točka",
|
||||
"This child is shared. Deleting it removes it from every copy of its parent.": "Ovaj podređeni element je dijeljen. Brisanjem se uklanja iz svake kopije njegova roditelja.",
|
||||
"This child is shared. Position changes apply to every copy of its parent.": "Ovaj podređeni element je dijeljen. Promjene položaja primjenjuju se na svaku kopiju njegova roditelja.",
|
||||
"This component is inside a subassembly placed more than once: aligning it moves it in every placement.": "Ova se komponenta nalazi unutar podsklopa postavljenog više puta: njezino poravnavanje pomiče je u svakom postavljanju.",
|
||||
"This face cannot provide a durable attachment. Choose another face or explicitly use a fixed plane.": "Ovo lice ne može dati trajno pričvršćenje. Odaberite drugo lice ili izričito upotrijebite fiksnu ravninu.",
|
||||
"This image is not attached to GitHub yet. Copy or save it, then attach it on GitHub.": "Ova slika još nije priložena na GitHubu. Kopirajte je ili spremite, a zatim je priložite na GitHubu.",
|
||||
"Thread depth": "Dubina navoja",
|
||||
@@ -772,6 +775,8 @@
|
||||
"at the face": "na licu",
|
||||
"bom.group_children": "Podređeni dijelovi u retku svojeg dijela",
|
||||
"browser.body_name": "Tijelo{number}",
|
||||
"browser.calibrate_click_a": "Kliknite sliku kako biste zadali točku A ili upišite njezine koordinate.",
|
||||
"browser.calibrate_click_b": "Kliknite sliku kako biste zadali točku B ili upišite njezine koordinate.",
|
||||
"browser.calibrate_title": "Kalibriraj: {name}",
|
||||
"browser.capture_many": "Pomaknute komponente bez zabilježenog položaja: {count}.",
|
||||
"browser.capture_note": "Zabilježi zadržava novi položaj; Vrati ga vraća nazad.",
|
||||
@@ -793,6 +798,11 @@
|
||||
"cam.roller": "Valjak",
|
||||
"cam.roller_pick": "Valjak (neobavezno)",
|
||||
"cam.tip_pick": "Vrh (neobavezno)",
|
||||
"camera.shot_pitch": "Nagib",
|
||||
"camera.shot_roll": "Valjanje",
|
||||
"camera.shot_yaw": "Skretanje",
|
||||
"canvas.decal_place_hint": "Kliknite lice kako biste postavili naljepnicu",
|
||||
"canvas.place_hint": "Kliknite ravninu ili lice kako biste postavili platno",
|
||||
"choose a profile": "odaberite profil",
|
||||
"choose the cam's and the follower's joints": "odaberite spojeve brijega i sljedbenika",
|
||||
"choose the joints to couple": "odaberite spojeve koje treba povezati",
|
||||
@@ -800,12 +810,16 @@
|
||||
"click sketch curves": "kliknite krivulje skice",
|
||||
"cmd.appearance_assign": "Izgled",
|
||||
"cmd.appearance_chain": "Lanac izgleda",
|
||||
"cmd.appearance_copy": "Kopiraj izgled",
|
||||
"cmd.appearance_favorite": "Omiljeni izgled",
|
||||
"cmd.appearance_library": "Biblioteka izgleda",
|
||||
"cmd.appearance_list": "Popis izgleda",
|
||||
"cmd.appearance_paint": "Oboji izgledom",
|
||||
"cmd.appearance_paste": "Zalijepi izgled",
|
||||
"cmd.appearance_pick_up": "Preuzmi izgled",
|
||||
"cmd.appearance_replace": "Zamijeni izgled",
|
||||
"cmd.assembly_bom": "Sastavnica",
|
||||
"cmd.body.print_face": "Ploha ispisa",
|
||||
"cmd.body_rename": "Preimenuj tijelo",
|
||||
"cmd.browser_collapse_all": "Sažmi sve",
|
||||
"cmd.browser_expand_all": "Proširi sve",
|
||||
@@ -813,6 +827,7 @@
|
||||
"cmd.browser_move": "Premjesti stavke stabla modela",
|
||||
"cmd.browser_order": "Poredaj stavke stabla modela",
|
||||
"cmd.browser_rename_group": "Preimenuj grupu",
|
||||
"cmd.browser_reparent": "Premjesti u komponentu",
|
||||
"cmd.browser_ungroup": "Razgrupiraj",
|
||||
"cmd.browser_visibility": "Prikaži/sakrij",
|
||||
"cmd.canvas_calibrate": "Kalibriraj platno",
|
||||
@@ -839,7 +854,9 @@
|
||||
"cmd.component_metadata": "Svojstva dijela",
|
||||
"cmd.component_metadata_set": "Promijeni svojstva dijela",
|
||||
"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_paste_from_design": "Zalijepi iz drugog dizajna",
|
||||
"cmd.component_paste_new": "Zalijepi kao novo",
|
||||
"cmd.component_properties": "Fizička svojstva komponente",
|
||||
"cmd.component_relink": "Ponovno poveži izvor",
|
||||
@@ -976,6 +993,7 @@
|
||||
"cmd.motion_trace": "Trag kretanja",
|
||||
"cmd.motion_trace_edit": "Uredi trag kretanja",
|
||||
"cmd.motion_trace_keep": "Zadrži trag kretanja",
|
||||
"cmd.occurrence_align": "Poravnaj primjerak",
|
||||
"cmd.occurrence_copy": "Zalijepi (primjerak)",
|
||||
"cmd.occurrence_drag": "Povuci primjerak",
|
||||
"cmd.occurrence_ground": "Učvrsti",
|
||||
@@ -1033,11 +1051,17 @@
|
||||
"cmd.render_library_surface_unbind": "Ukloni vezu površine",
|
||||
"cmd.render_library_targets": "Pronađi ciljeve biblioteke renderiranja",
|
||||
"cmd.render_lights_add": "Dodaj svjetlo",
|
||||
"cmd.render_lights_duplicate": "Dupliciraj svjetlo renderiranja",
|
||||
"cmd.render_lights_list": "Popis svjetala renderiranja",
|
||||
"cmd.render_lights_mute": "Isključi svjetlo",
|
||||
"cmd.render_lights_remove": "Ukloni svjetlo renderiranja",
|
||||
"cmd.render_lights_set": "Postavi svjetlo renderiranja",
|
||||
"cmd.render_lights_solo": "Samo ovo svjetlo",
|
||||
"cmd.render_optics_bind": "Spremi optičku vezu",
|
||||
"cmd.render_optics_unbind": "Ukloni optičku vezu",
|
||||
"cmd.render_output_size": "Postavi veličinu izlaza renderiranja",
|
||||
"cmd.render_preflight": "Provjeri zahtjev za renderiranje",
|
||||
"cmd.render_preflight_copy": "Kopiraj izvješće provjere renderiranja",
|
||||
"cmd.render_preset_apply": "Primijeni izvorni studio",
|
||||
"cmd.render_preset_resolve": "Pregledaj izvorni studio",
|
||||
"cmd.render_quality_prepare": "Pregledaj fiksni proračun uzorkovanja",
|
||||
@@ -1060,6 +1084,13 @@
|
||||
"cmd.render_start": "Renderiraj sliku",
|
||||
"cmd.render_status": "Napredak renderiranja",
|
||||
"cmd.select_clear": "Poništi odabir",
|
||||
"cmd.settings_profile_apply": "Primijeni profil postavki",
|
||||
"cmd.settings_profile_delete": "Izbriši profil postavki",
|
||||
"cmd.settings_profile_export": "Izvezi profil postavki",
|
||||
"cmd.settings_profile_import": "Uvezi profil postavki",
|
||||
"cmd.settings_profile_list": "Profili postavki",
|
||||
"cmd.settings_profile_rename": "Preimenuj profil postavki",
|
||||
"cmd.settings_profile_save": "Spremi profil postavki",
|
||||
"cmd.sheet_convert": "Pretvori u lim",
|
||||
"cmd.sheet_export_bend_table": "Izvezi tablicu savijanja",
|
||||
"cmd.sheet_export_dxf": "Izvezi razvijeni oblik kao DXF",
|
||||
@@ -1073,6 +1104,16 @@
|
||||
"cmd.sheet_refold": "Ponovno presavij",
|
||||
"cmd.sheet_rules": "Popis pravila za lim",
|
||||
"cmd.sheet_unfold": "Razvij",
|
||||
"cmd.sketch.construction_line": "Pomoćni pravac",
|
||||
"cmd.sketch.construction_line.angle": "Pomoćni pravac pod kutom",
|
||||
"cmd.sketch.construction_line.bisector": "Simetrala kuta",
|
||||
"cmd.sketch.construction_line.horizontal": "Vodoravni pomoćni pravac",
|
||||
"cmd.sketch.construction_line.parallel": "Paralelni pomoćni pravac",
|
||||
"cmd.sketch.construction_line.perpendicular": "Pomoćni pravac okomit na pravac",
|
||||
"cmd.sketch.construction_line.tangent_at": "Tangenta u točki krivulje",
|
||||
"cmd.sketch.construction_line.tangent_external": "Tangenta kroz vanjsku točku",
|
||||
"cmd.sketch.construction_line.tangent_two": "Zajednička tangenta",
|
||||
"cmd.sketch.construction_line.vertical": "Okomiti pomoćni pravac",
|
||||
"cmd.sketch_arc3d": "3D luk",
|
||||
"cmd.sketch_arc_center_point": "Luk iz središta",
|
||||
"cmd.sketch_arc_tangent": "Tangentni luk",
|
||||
@@ -1105,6 +1146,8 @@
|
||||
"cmd.sketch_drawing_bounds": "Veličina crteža",
|
||||
"cmd.sketch_edit": "Uredi skicu",
|
||||
"cmd.sketch_edit_text": "Uredi tekst",
|
||||
"cmd.sketch_equation_curve": "Krivulja iz jednadžbe",
|
||||
"cmd.sketch_equation_curve_edit": "Uredi krivulju iz jednadžbe",
|
||||
"cmd.sketch_export_dxf": "Spremi kao DXF",
|
||||
"cmd.sketch_finish_auto_constrain": "Završi s automatskim ograničenjima",
|
||||
"cmd.sketch_fit_curves_to_section": "Uklopi krivulje u presjek mreže",
|
||||
@@ -1201,6 +1244,7 @@
|
||||
"cmd.view.render_browser": "Stablo renderiranja",
|
||||
"cmd.view.render_dock": "Premjesti panel renderiranja",
|
||||
"cmd.view.render_focus": "Fokusni prikaz renderiranja",
|
||||
"cmd.view.status_items": "Statusne stavke prozora prikaza",
|
||||
"cmd.view_ambient_occlusion": "Ambijentalna okluzija",
|
||||
"cmd.view_angle_readout": "Kutovi prikaza",
|
||||
"cmd.view_apply": "Vrati imenovani prikaz",
|
||||
@@ -1222,6 +1266,7 @@
|
||||
"cmd.view_normal_to": "Okomito na odabir",
|
||||
"cmd.view_previous_tab": "Prethodna kartica",
|
||||
"cmd.view_rename": "Preimenuj imenovani prikaz",
|
||||
"cmd.view_render_frame": "Vodilice kadra renderiranja",
|
||||
"cmd.view_right": "Pogled zdesna",
|
||||
"cmd.view_save": "Novi imenovani prikaz",
|
||||
"cmd.view_shots_strip": "Traka kadrova",
|
||||
@@ -1232,10 +1277,14 @@
|
||||
"color.copy": "Kliknite za kopiranje vrijednosti boje",
|
||||
"color.edit": "Kliknite za uređivanje boje",
|
||||
"color.gamma": "Prikaz vrijednosti boje u gama prostoru 0-255",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "Unesite #rrggbb ili #rrggbbaa; šest znamenki zadržava neprozirnost",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
"color.hue": "Nijansa",
|
||||
"color.linear": "Prikaz vrijednosti boje u linearnom prostoru 0-1",
|
||||
"color.saturation_value": "Zasićenost i vrijednost",
|
||||
"color.selected": "Odabrana boja",
|
||||
"config.column_filter_empty": "Nema odgovarajućih stupaca",
|
||||
"contact.components_count": "Komponente: {count}",
|
||||
"contact.enabled_label": "omogućeno",
|
||||
"coupling.gear_1": "Zupčanik 1",
|
||||
@@ -1350,8 +1399,20 @@
|
||||
"help.material.assign": "Određuje od čega su odabrana tijela izrađena, npr. čelik, aluminij ili ABS. Upotrijebite ga kako bi masa i težina odgovarale stvarnom dijelu.",
|
||||
"help.mit": "MIT licenca",
|
||||
"help.notice": "NOTICE",
|
||||
"help.occurrence.move": "Pomiče dio sklopa samo u stupnjevima slobode koje dopuštaju njegovi spojevi. Odaberite lice dijela, a zatim povucite dostupnu strelicu ili luk.",
|
||||
"help.parameters.change": "Otvara tablicu imenovanih vrijednosti i izraza koji upravljaju kotama dizajna. Upotrijebite je kako biste promijenili mjeru na jednom mjestu, a sve značajke koje je koriste je slijede.",
|
||||
"help.pointcloud.insert": "Umetnite PLY, XYZ ili PTS skeniranje kao oblak točaka uz model. Koristite ga kao referencu pri izradi modela iz izmjerenih podataka.",
|
||||
"help.sketch.construction_line": "Izradite beskonačni pomoćni pravac kroz dvije točke. Upotrijebite ga kako biste rasporedili skicu bez razdvajanja profila.",
|
||||
"help.sketch.construction_line.angle": "Izradite beskonačni pomoćni pravac pod kutom od osi X skice. Odaberite točku i upišite kut ili odaberite smjer.",
|
||||
"help.sketch.construction_line.bisector": "Odaberite dvije ravne linije skice koje se sijeku, pa jednu od dviju simetrala kuta. Rezultat prati izvorne linije.",
|
||||
"help.sketch.construction_line.horizontal": "Izradite beskonačni vodoravni pomoćni pravac kroz točku. Njegovo ograničenje vodoravnosti ostaje na snazi pri kasnijim promjenama.",
|
||||
"help.sketch.construction_line.parallel": "Odaberite ravnu liniju skice, pa postavite paralelni beskonačni pomoćni pravac. Desnim klikom odaberite drugu referencu.",
|
||||
"help.sketch.construction_line.perpendicular": "Odaberite ravnu liniju skice, pa postavite beskonačni pomoćni pravac okomit na nju. Desnim klikom odaberite drugu referencu.",
|
||||
"help.sketch.construction_line.tangent_at": "Izradite tangentu u točki na kružnici ili kružnom luku, ili u krajnjoj točki otvorenog splinea ili konike. Odaberite krivulju, pa točku dodira.",
|
||||
"help.sketch.construction_line.tangent_external": "Odaberite kružnicu ili kružni luk, pa vanjsku točku i rješenje tangente. Pregledi prikazuju samo dodirne točke na nacrtanom luku.",
|
||||
"help.sketch.construction_line.tangent_two": "Odaberite dvije kružnice ili kružna luka, pa u pregledu odaberite zajedničku tangentu. Dodirne točke moraju ležati na nacrtanim krivuljama.",
|
||||
"help.sketch.construction_line.vertical": "Izradite beskonačni okomiti pomoćni pravac kroz točku. Njegovo ograničenje okomitosti ostaje na snazi pri kasnijim promjenama.",
|
||||
"help.sketch.equation_curve": "Crta krivulju prema formulama: x(t) i y(t) ili y = f(x), u zadanom rasponu. Formule mogu koristiti parametre i krivulja ih prati: upotrijebite je za sinusoidu, spiralu ili evolventu zuba zupčanika.",
|
||||
"help.solid.align": "Pomiče odabrana tijela tako da se točka, lice ili ravnina na njima poklopi s točkom, licem ili ravninom negdje drugdje. Upotrijebite ga kako biste postavili dio uz drugi bez mjerenja odmaka.",
|
||||
"help.solid.chamfer": "Odsijeca odabrane bridove ravnim skošenjem zadane udaljenosti. Koristite ga kako biste ublažili oštre bridove ili olakšali uvođenje zatika ili vijka.",
|
||||
"help.solid.combine": "Spaja tijela, oduzima ih od ciljnog tijela ili zadržava samo zajednički volumen, a može zadržati i alatna tijela. Koristite ga kako biste spojili dijelove, jednim oblikom odrezali drugi ili pronašli gdje se dva tijela preklapaju.",
|
||||
@@ -1480,6 +1541,21 @@
|
||||
"joint.one_component": "Ovi dijelovi su tijela jedne komponente: {bodies}. Spoj pomiče jednu komponentu u odnosu na drugu, pa svaki dio treba vlastitu komponentu.",
|
||||
"joint.rigid_group_one_component": "Sva tijela ({count}) nalaze se u jednoj komponenti (ili u samom dizajnu): najprije upotrijebite {command}.",
|
||||
"joint.rigid_group_select_components": "Odaberite dvije ili više komponenti.",
|
||||
"joint_move.blocked_hint": "Provjerite vrstu spoja (npr. Kruti), učvršćenje, granice spoja i vrijednosti kojima upravljaju parametri.",
|
||||
"joint_move.blocked_title": "Ovaj dio nije moguće pomaknuti",
|
||||
"joint_move.coarse": "Korak rotacije blizu osi (°)",
|
||||
"joint_move.distance": "Udaljenost od početka",
|
||||
"joint_move.done": "Gotovo",
|
||||
"joint_move.fine": "Korak rotacije daleko od osi (°)",
|
||||
"joint_move.fixed": "Ovaj dio nema pomičnu os spoja.",
|
||||
"joint_move.help": "Pomakni — povucite strelicu ili prsten. Esc poništava trenutno povlačenje.",
|
||||
"joint_move.legacy_hint": "Isključite kako biste koristili prethodni panel Pomakni.",
|
||||
"joint_move.precision": "Povucite izvan isprekidane vodilice za finu rotaciju.",
|
||||
"joint_move.rotation": "Rotacija od početka",
|
||||
"joint_move.select": "Odaberite lice ili jedan dio sklopa, a zatim odaberite Pomakni.",
|
||||
"joint_move.settings": "Pomicanje u sklopu",
|
||||
"joint_move.step": "Korak",
|
||||
"joint_move.use_handles": "Koristi ručice kretanja spojeva za Pomakni u sklopu",
|
||||
"keymap.command_id": "ID naredbe",
|
||||
"keymap.empty": "Nema odgovarajućih naredbi",
|
||||
"keymap.use_anyway": "Ipak upotrijebi {key} ovdje",
|
||||
@@ -1574,6 +1650,8 @@
|
||||
"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.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.",
|
||||
"nav.right_drag_orbits.label": "Povlačenje desnom tipkom orbitira",
|
||||
"nav.solidworks.help": "Povlačenje srednjom tipkom orbitira, Ctrl+srednja pomiče, Shift+srednja zumira, povlačenje desnom orbitira",
|
||||
"nav.tinkercad.help": "Povlačenje desnom tipkom orbitira, povlačenje srednjom ili Shift+desnom pomiče",
|
||||
"nav.touchpad.help": "Držite Shift i pomičite pokazivač za pomicanje prikaza; držite Alt/Option i pomičite ga za orbitiranje. Tipka miša nije potrebna. Okomito pomicanje zumira.",
|
||||
@@ -1595,6 +1673,9 @@
|
||||
"navbar.custom.customise": "Prilagodi navigacijsku traku…",
|
||||
"navbar.custom.design": "Dizajn",
|
||||
"navbar.custom.duplicate": "Ovo mjesto je već na traci.",
|
||||
"navbar.custom.edge": "Rub",
|
||||
"navbar.custom.edge_bottom": "Dolje",
|
||||
"navbar.custom.edge_top": "Gore",
|
||||
"navbar.custom.fade": "Izblijedi",
|
||||
"navbar.custom.full": "Uklonite jedno mjesto kako biste dodali drugo.",
|
||||
"navbar.custom.hidden_design": "Skriveno u Dizajnu",
|
||||
@@ -1701,6 +1782,7 @@
|
||||
"parts.dimension.thickness": "Debljina",
|
||||
"parts.dimension.width": "Širina",
|
||||
"parts.family.bearing": "Kuglični ležaj s dubokim utorom (ovojni oblik)",
|
||||
"parts.family.button_head_cap_screw": "Vijak s poluokruglom glavom i unutarnjim šesterokutom",
|
||||
"parts.family.dowel_pin": "Cilindrični zatik",
|
||||
"parts.family.hex_bolt": "Vijak sa šesterokutnom glavom",
|
||||
"parts.family.hex_nut": "Šesterokutna matica",
|
||||
@@ -1759,6 +1841,11 @@
|
||||
"prefs.bottom_centre": "Dolje u sredini",
|
||||
"prefs.bottom_right": "Dolje desno",
|
||||
"prefs.browser": "Stablo modela",
|
||||
"prefs.browser_eye_left": "Oko pored strelice za sažimanje",
|
||||
"prefs.browser_eye_left_note": "Redci stabla modela prikazuju oko za prikazivanje/skrivanje pored strelice za sažimanje, kao prije, umjesto u stupcu na desnom kraju redaka.",
|
||||
"prefs.browser_reveal_on_hover": "Prikaži pri prelasku mišem",
|
||||
"prefs.browser_show_note": "Prikazano drži stablo modela na zaslonu. Prikaži pri prelasku mišem skriva ga dok pokazivač ne dođe na njegovo mjesto ili na tanku karticu uz njegov lijevi rub, a ostaje otvoreno dok je pokazivač unutar njega ili dok je otvoren izbornik iz njega.",
|
||||
"prefs.browser_shown": "Prikazano",
|
||||
"prefs.built_in": "Ugrađeno",
|
||||
"prefs.button_left": "Povlačenje lijevom tipkom",
|
||||
"prefs.button_middle": "Povlačenje srednjom tipkom",
|
||||
@@ -1806,6 +1893,8 @@
|
||||
"prefs.graphics_card_restart": "Stupa na snagu nakon ponovnog pokretanja SolidCrafta. WGPU_ADAPTER_NAME ima prednost nad ovim odabirom.",
|
||||
"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",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out_note": "Simboli ograničenja nestaju iz prikaza kada je skica toliko umanjena da bi je zatrpali. Isključeno: ostaju fiksne veličine na zaslonu pri svakom zumiranju.",
|
||||
"prefs.history": "Vremenska traka",
|
||||
"prefs.history_origin_bodies": "Prikaži ishodište i tijela na vrhu",
|
||||
"prefs.history_origin_bodies_note": "Vremenska traka lijevo ili ispod stabla modela počinje mapom Tijela aktivne komponente i Ishodištem, s njihovim okom i izbornicima kao u stablu modela. Vremenska traka na dnu ostaje nepromijenjena.",
|
||||
@@ -1829,6 +1918,8 @@
|
||||
"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.linked_designs": "Povezani dizajni",
|
||||
"prefs.live_input": "Ulaz uživo",
|
||||
"prefs.live_preview": "Pregled uživo",
|
||||
@@ -1842,6 +1933,9 @@
|
||||
"prefs.max_file_size_step": "STEP i uvezene datoteke",
|
||||
"prefs.max_file_size_web": "Web aplikacija otvara STEP i uvezene datoteke do 512 MB te dizajne do 256 MB: kartica preglednika ne može primiti veće datoteke. Desktop aplikacija može podići ove granice.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "Velike ikone",
|
||||
"prefs.menu_list": "Popis",
|
||||
"prefs.menus": "Izbornici",
|
||||
"prefs.minutes": "minuta",
|
||||
"prefs.minutes_suffix": " min",
|
||||
"prefs.mouse": "Miš i dodirna pločica",
|
||||
@@ -1861,14 +1955,40 @@
|
||||
"prefs.origin_plane_names": "Nazivi ravnina ishodišta",
|
||||
"prefs.origin_plane_names_note": "Nazivi prikazani u stablu modela, dijalozima i popisima. {top} je XY, {front} je XZ, a {right} je YZ, sa Z prema gore. Naredbe, skripte i spremljeni dizajni zadržavaju XY, XZ i YZ.",
|
||||
"prefs.overall_speed": "Ukupna brzina",
|
||||
"prefs.persistent_ribbon_pressed": "Istakni aktivne naredbe na vrpci",
|
||||
"prefs.persistent_ribbon_pressed_help": "Ističe naredbe na vrpci dok su njihovi efekti uključeni. Isključite kako biste zadržali prethodni neutralni izgled i pomoć za naredbe.",
|
||||
"prefs.perspective_fov": "Vidno polje",
|
||||
"prefs.placement": "Postavljanje",
|
||||
"prefs.prefer_free_sketch_points": "Daj prednost pomičnim točkama skice kad se točke preklapaju",
|
||||
"prefs.preview_choices": "Pregled vaših odabira",
|
||||
"prefs.print_face_highlight": "Isticanje plohe ispisa",
|
||||
"prefs.print_face_highlight_tip": "Koliko se jako ploha ispisa tijela boji i obrubljuje u prikazu. 0 % ne prikazuje isticanje.",
|
||||
"prefs.profile_active": "Aktivni profil",
|
||||
"prefs.profile_applied": "Profil je primijenjen.",
|
||||
"prefs.profile_changed": "Postavke su se promijenile otkako je profil spremljen. Spremi ih zadržava u njemu.",
|
||||
"prefs.profile_deleted": "Profil je izbrisan.",
|
||||
"prefs.profile_export": "Izvezi…",
|
||||
"prefs.profile_exported": "Profil je izvezen.",
|
||||
"prefs.profile_imported": "Profil je uvezen. Odaberite ga pod Aktivni profil kako biste se prebacili na njega.",
|
||||
"prefs.profile_name_hint": "Naziv profila, npr. Prijenosnik",
|
||||
"prefs.profile_rename": "Preimenuj",
|
||||
"prefs.profile_renamed": "Profil je preimenovan.",
|
||||
"prefs.profile_save": "Spremi",
|
||||
"prefs.profile_saved": "Trenutne postavke spremljene su u profil.",
|
||||
"prefs.profile_skipped": "Profil je primijenjen; postavke koje su zadržale trenutnu vrijednost: {n} (pogledajte zapisnik).",
|
||||
"prefs.profiles": "Profili postavki",
|
||||
"prefs.profiles_note": "Prečaci, alatna traka, navigacija, radijalni izbornik i postavke sučelja, spremljeni zajedno pod jednim nazivom i mijenjani odjednom.",
|
||||
"prefs.project_options_dialog": "Dijalog opcija projiciranja",
|
||||
"prefs.project_options_dialog_note": "Uključeno: prije projiciranja odaberite Geometrija ili Tijela i Veza projekcije. Isključeno: zadržava se prethodni način rada s izravnim odabirom.",
|
||||
"prefs.quality_default": "zadano",
|
||||
"prefs.quality_fastest": "najbrže",
|
||||
"prefs.quality_slowest": "najsporije",
|
||||
"prefs.quality_smoother": "glađe",
|
||||
"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_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",
|
||||
"prefs.saving": "Automatsko spremanje i oporavak",
|
||||
"prefs.scale_note": "Također Ctrl + / Ctrl − u prozoru. Gumbi su 28 px pri 100 %.",
|
||||
@@ -1877,18 +1997,29 @@
|
||||
"prefs.search_empty": "Nema odgovarajućih postavki",
|
||||
"prefs.select": "Odabir",
|
||||
"prefs.select_one_item": "Odaberi jednu stavku",
|
||||
"prefs.select_other_hidden": "Odaberi skrivenu geometriju pomoću Odaberi drugo",
|
||||
"prefs.select_other_hidden_help": "Uključeno: obuhvaća skrivena tijela, skice i zaklonjene bridove u blizini pokazivača. Isključeno: koristi se izvorni popis vidljive geometrije ispod pokazivača.",
|
||||
"prefs.selecting": "Odabiranje",
|
||||
"prefs.shift": "Shift",
|
||||
"prefs.shift_add_ctrl_toggle": "Shift samo dodaje; Ctrl / Cmd uključuje i isključuje",
|
||||
"prefs.shift_add_note": "Uključeno: Shift dodaje, Ctrl / Cmd uključuje i isključuje. Isključeno (zadano): oba uključuju i isključuju.",
|
||||
"prefs.shortcuts": "Prečaci",
|
||||
"prefs.single_surface_analysis": "Koristi samo jednu analizu ploha",
|
||||
"prefs.single_surface_analysis_help": "Kad je isključeno, sjenčanje ploha i Kontinuitet mogu ostati uključeni zajedno. Kad je uključeno, zadržava se posljednja pokrenuta ili promijenjena analiza.",
|
||||
"prefs.sketch_align_points": "Poravnaj s drugim točkama skice tijekom crtanja",
|
||||
"prefs.sketch_align_points_note": "Tijekom crtanja točka se istodobno poravnava vodoravno s jednom točkom skice i okomito s drugom, uz točkastu vodilicu, a klik zadržava oba poravnanja. Isključeno: samo s prethodnom točkom, po jedna os.",
|
||||
"prefs.sketch_conflict_colour": "Geometrija u sukobu u boji sukoba",
|
||||
"prefs.sketch_conflict_colour_note": "Dok su ograničenja skice u sukobu, krivulje i točke koje drži neuspjelo ograničenje crtaju se i u boji sukoba, a ne samo njegov simbol ili kota. Isključeno: zadržavaju uobičajene boje.",
|
||||
"prefs.sketch_construction_lines": "Prošireni alati za pomoćne pravce",
|
||||
"prefs.sketch_exit_view": "Kad se skica završi",
|
||||
"prefs.sketch_exit_view.keep": "Zadrži trenutni prikaz",
|
||||
"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_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",
|
||||
"prefs.sketch_recent_snap_note": "Tijekom crtanja pokazivač se privlači ondje gdje se sijeku vodilice dvaju posljednjih središta kružnica ili središnjih točaka ravnih bridova. Isključeno: te vodilice služe samo za prikaz i pokazivač se ne pomiče.",
|
||||
"prefs.sketch_selection": "Skica i odabir",
|
||||
"prefs.sketch_selection_heading": "Skica i odabir",
|
||||
"prefs.sketch_selection_note": "Kako se skice privlače i kako klikovi odabiru.",
|
||||
@@ -1933,7 +2064,14 @@
|
||||
"prefs.toast_place": "Obavijesti",
|
||||
"prefs.toggle_item": "Dodaj stavku ili je ponovno ukloni",
|
||||
"prefs.toolbar": "Alatna traka",
|
||||
"prefs.toolbar_tabs": "Kartice alatne trake",
|
||||
"prefs.top": "Gore",
|
||||
"prefs.trackpad_zoom_modifier": "Zumiranje pomicanjem s dva prsta uz držanje tipke",
|
||||
"prefs.trackpad_zoom_modifier.alt": "⌥ / Alt",
|
||||
"prefs.trackpad_zoom_modifier.cmd": "⌘",
|
||||
"prefs.trackpad_zoom_modifier.ctrl": "Ctrl",
|
||||
"prefs.trackpad_zoom_modifier.help": "Držite samo ovu tipku kako biste okomitim pomicanjem zumirali prema pokazivaču. Isključeno zadržava postojeće ponašanje pomicanja.",
|
||||
"prefs.trackpad_zoom_modifier.off": "Isključeno",
|
||||
"prefs.translucent": "Prozirno",
|
||||
"prefs.under_browser": "Ispod stabla modela",
|
||||
"prefs.under_cursor": "Ispod pokazivača",
|
||||
@@ -1943,13 +2081,24 @@
|
||||
"prefs.view_confirm_corner": "U redu i Odustani u kutu prikaza",
|
||||
"prefs.view_confirm_corner_note": "Dok je otvoren dijalog ili skica, kvačica i križić gore desno u prikazu rade isto što i gumbi U redu i Odustani u dijalogu, odnosno završavaju i poništavaju skicu. Navigator prikaza pomiče se ispod njih.",
|
||||
"prefs.viewport": "Prozor prikaza",
|
||||
"prefs.viewport_analysis_status": "Prikaži status analize u prozoru prikaza",
|
||||
"prefs.viewport_analysis_status_help": "Prikazuje aktivne analize iznad prozora prikaza, otvara njihove legende iz svake stavke, a tipka Esc isključuje najnoviju analizu. Isključite kako biste vratili legende u kutovima i prethodno ponašanje tipke Esc.",
|
||||
"prefs.viewport_background": "Pozadina",
|
||||
"prefs.viewport_background_colour": "Boja",
|
||||
"prefs.viewport_background_theme": "Gradijent teme",
|
||||
"prefs.wheel": "Kotačić",
|
||||
"prefs.wheel_trackpad": "Kotačić i dodirna pločica",
|
||||
"prefs.workspace": "Radni prostor",
|
||||
"prefs.zoom_stop_at_model": "Zaustavi zumiranje na modelu",
|
||||
"prefs.zoom_stop_at_model_tip": "3D miš i aplikacija uređaja zumiraju prema površini u sredini prikaza; zumiranje povlačenjem zadržava točku na kojoj je povlačenje počelo. Isključeno: zumiranje može proći kroz model. Kotačić i štipanje zasebno koriste Zumiranje prema pokazivaču",
|
||||
"prefs.zoom_toward_cursor": "Zumiranje prema pokazivaču",
|
||||
"properties.title": "Svojstva: {name}",
|
||||
"recovery.days_ago": "prije {count} dana",
|
||||
"recovery.detail": "{file} · značajki: {features} · automatski spremljeno {when}",
|
||||
"recovery.discard_all": "Odbaci sve",
|
||||
"recovery.discard_all_detail": "Ovo se ne može poništiti.",
|
||||
"recovery.discard_all_title": "Odbaciti automatska spremanja ({count})?",
|
||||
"recovery.discard_all_title_one": "Odbaciti 1 automatsko spremanje?",
|
||||
"recovery.hours_ago": "prije {count} h",
|
||||
"recovery.just_now": "upravo",
|
||||
"recovery.min_ago": "prije {count} min",
|
||||
@@ -2034,6 +2183,7 @@
|
||||
"render.appearance_editor.weight": "Težina",
|
||||
"render.appearance_physics_separate": "Vizualni izgled ne dodjeljuje gustoću, toplinske podatke niti druga inženjerska svojstva.",
|
||||
"render.appearances": "Izgledi",
|
||||
"render.assign_help": "Kliknite za primjenu na odabir; povucite za pregled i ispustite ili, kad ništa nije odabrano, kliknite za bojenje.",
|
||||
"render.availability_native": "Izvorni sastav; pregledajte prije primjene",
|
||||
"render.availability_projection": "Podržana projekcija; pogledajte opseg i izostavke",
|
||||
"render.availability_reference": "Referentni podaci; nije automatska dodjela",
|
||||
@@ -2072,6 +2222,7 @@
|
||||
"render.bloom_threshold": "Prag linearnih svijetlih dijelova",
|
||||
"render.browse_capabilities": "Pregledaj potpuni katalog mogućnosti",
|
||||
"render.browse_environments": "Pregledaj izvorna HDR okruženja",
|
||||
"render.browse_looks": "Pregledaj…",
|
||||
"render.browse_native_scenes": "Pregledaj izvorne studije",
|
||||
"render.browse_physical": "Pregledaj fizičke reference",
|
||||
"render.browse_quality": "Pregledaj specifikacije kvalitete",
|
||||
@@ -2095,6 +2246,82 @@
|
||||
"render.check_opaque": "Prozirnost: samo neprozirne površine; alfa pozadine je podržana.",
|
||||
"render.check_sorted_alpha": "Prozirnost: sortirana aproksimacija alfe.",
|
||||
"render.check_transmission": "Prozirnost: fizičko propuštanje svjetlosti.",
|
||||
"render.children.absolute_floor": "Apsolutni donji prag",
|
||||
"render.children.adaptive": "Prilagodljivo",
|
||||
"render.children.add_look_reason": "Dodajte izgled kako biste uredili njegovo uključeno stanje i jačinu.",
|
||||
"render.children.all_shots": "Svi kadrovi",
|
||||
"render.children.bloom": "Sjaj",
|
||||
"render.children.check": "Provjera",
|
||||
"render.children.check_now": "Provjeri sada",
|
||||
"render.children.chromatic": "Kromatski pomak",
|
||||
"render.children.compact": "Kompaktno",
|
||||
"render.children.contrast": "Kontrast",
|
||||
"render.children.copy_image": "Kopiraj",
|
||||
"render.children.copy_job": "Kopiraj JSON posla",
|
||||
"render.children.copy_request": "Kopiraj JSON zahtjeva",
|
||||
"render.children.coverage_error": "Pogreška pokrivenosti",
|
||||
"render.children.cpu": "CPU praćenje zraka",
|
||||
"render.children.denoise": "Smanji šum na dovršenoj slici",
|
||||
"render.children.depth": "Dubina putanje",
|
||||
"render.children.distortion": "Izobličenje",
|
||||
"render.children.edge_samples": "Uzorci rubova",
|
||||
"render.children.effects_banner": "Ovi efekti zahtijevaju GPU praćenje putanja. Otvaranje ovoga nikad ne mijenja metodu.",
|
||||
"render.children.enable_adaptive": "Omogućite neovisne probne uzorke kako biste uredili prilagodljiva ograničenja.",
|
||||
"render.children.enable_bloom": "Omogućite sjaj kako biste uredili njegovu jačinu, prag i polumjer.",
|
||||
"render.children.finishing": "Završna obrada",
|
||||
"render.children.flare": "Odsjaj",
|
||||
"render.children.fresnel": "Fresnelovi slojevi",
|
||||
"render.children.ghosts": "Duhovi prstenova",
|
||||
"render.children.glass": "Prijenos kroz staklo",
|
||||
"render.children.go_to": "Idi na {editor}",
|
||||
"render.children.grain": "Zrno",
|
||||
"render.children.halo": "Oreol",
|
||||
"render.children.hdr": "HDR",
|
||||
"render.children.keep_master": "Zadrži sirovi linearni master",
|
||||
"render.children.last_result": "Posljednje: {number} · {width} × {height} · {phase}",
|
||||
"render.children.lens_effects": "Efekti objektiva",
|
||||
"render.children.look_summary": "{tone} · izgledi: {count}",
|
||||
"render.children.looks": "Kreativni izgledi",
|
||||
"render.children.memory": "Memorija",
|
||||
"render.children.method": "Metoda",
|
||||
"render.children.minimum": "Minimum",
|
||||
"render.children.needs_cpu": "Zahtijeva CPU praćenje zraka. Otvaranje ovoga nikad ne mijenja metodu.",
|
||||
"render.children.nested": "Potvrđeni ugniježđeni dielektrici",
|
||||
"render.children.not_checked": "Nije provjereno",
|
||||
"render.children.path": "Praćenje putanja",
|
||||
"render.children.pilots": "Probni uzorci",
|
||||
"render.children.preview": "Podaci pregleda",
|
||||
"render.children.progressive_reason": "Odaberite CPU progresivno kako biste uredili broj uzoraka i sjeme.",
|
||||
"render.children.radius": "Polumjer",
|
||||
"render.children.reflect_depth": "Dubina refleksije",
|
||||
"render.children.reflect_samples": "Uzorci refleksije",
|
||||
"render.children.regrade": "Ponovna gradacija",
|
||||
"render.children.relative_error": "Relativna pogreška",
|
||||
"render.children.render": "Renderiranje",
|
||||
"render.children.render_shot": "Renderiraj kadar",
|
||||
"render.children.renderer": "Iscrtavač",
|
||||
"render.children.renderers": "Iscrtavači",
|
||||
"render.children.request": "Ovaj zahtjev",
|
||||
"render.children.reset_finishing": "Vrati zadanu završnu obradu",
|
||||
"render.children.review_quality": "Pregledaj specifikaciju kvalitete…",
|
||||
"render.children.rgb_gain": "RGB pojačanje",
|
||||
"render.children.samples": "Uzorci",
|
||||
"render.children.sampling": "Uzorkovanje",
|
||||
"render.children.saturation": "Zasićenost",
|
||||
"render.children.scene_summary": "Tijela: {bodies} · trokuti: {triangles}",
|
||||
"render.children.seed": "Sjeme",
|
||||
"render.children.sharpening": "Izoštravanje",
|
||||
"render.children.single_image": "Jedna slika",
|
||||
"render.children.soft_focus": "Meki fokus",
|
||||
"render.children.streak": "Pruga",
|
||||
"render.children.strength": "Jačina",
|
||||
"render.children.studio": "Studio",
|
||||
"render.children.threshold": "Prag",
|
||||
"render.children.tone": "Ton",
|
||||
"render.children.under_hood": "Ispod haube",
|
||||
"render.children.vignette": "Vinjeta",
|
||||
"render.children.workstation": "Radna stanica",
|
||||
"render.choose_look": "Najprije odaberite izgled u Izgledima",
|
||||
"render.chromatic_offset": "Kromatski pomak rubova",
|
||||
"render.clear_stage": "Ukloni spremljenu scenu",
|
||||
"render.copy_request": "Kopiraj JSON",
|
||||
@@ -2346,8 +2573,33 @@
|
||||
"render.flare_strength": "Jačina odsjaja svijetlih dijelova",
|
||||
"render.flare_threshold": "Linearni prag odsjaja scene",
|
||||
"render.fold_browser": "Sažmi stablo modela",
|
||||
"render.frame_aspect": "Omjer",
|
||||
"render.frame_centre_mark": "Oznaka središta",
|
||||
"render.frame_copy_limit": "Nema dostupnog naziva za duplikat kadra",
|
||||
"render.frame_copy_name": "{name} kopija {number}",
|
||||
"render.frame_custom": "Prilagođeno",
|
||||
"render.frame_distance_unit": " × veličina",
|
||||
"render.frame_duplicate": "Dupliciraj",
|
||||
"render.frame_exposure": "Ekspozicija",
|
||||
"render.frame_fov": "Vidno polje",
|
||||
"render.frame_guides": "Vodilice",
|
||||
"render.frame_invalid_aspect": "Neispravan unaprijed zadani omjer",
|
||||
"render.frame_lens": "Vidno polje {fov}° · ≈ {mm} mm",
|
||||
"render.frame_name": "Naziv",
|
||||
"render.frame_output_size": "Veličina izlaza",
|
||||
"render.frame_photo": "3:2 · Fotografija",
|
||||
"render.frame_portrait": "4:5 · Portret",
|
||||
"render.frame_presets": "Unaprijed zadani kadrovi",
|
||||
"render.frame_presets_pending": "Stiže s uređivačem kadrova",
|
||||
"render.frame_projection": "Projekcija",
|
||||
"render.frame_render_shot": "Renderiraj ovaj kadar",
|
||||
"render.frame_rule_thirds": "Pravilo trećina",
|
||||
"render.frame_safe_area": "Sigurno područje",
|
||||
"render.frame_shot_title": "Kadar · {name}",
|
||||
"render.frame_square": "1:1 · Kvadrat",
|
||||
"render.frame_tall": "9:16 · Okomito",
|
||||
"render.frame_thirds": "Trećine",
|
||||
"render.frame_wide": "16:9 · Široki zaslon",
|
||||
"render.gpu_depth": "Maksimalna dubina putanje",
|
||||
"render.gpu_options_help": "Uzorci, dubina, sjeme i ekspozicija spremaju se s ovom postavkom. Sprema se samo aktivna metoda slike.",
|
||||
"render.gpu_path_failed": "GPU praćenje putanja nije dostupno",
|
||||
@@ -2375,6 +2627,7 @@
|
||||
"render.heading_results": "REZULTATI",
|
||||
"render.heading_shots": "KADROVI",
|
||||
"render.hide_shots_label": "Sakrij traku kadrova",
|
||||
"render.hover_look": "{object} · {look} · kliknite za odabir",
|
||||
"render.image_advanced": "Napredne GPU kontrole",
|
||||
"render.image_cancelled": "Renderiranje je prekinuto",
|
||||
"render.image_cancelling": "Prekidanje",
|
||||
@@ -2521,17 +2774,95 @@
|
||||
"render.library_uniform_ack": "Upotrijebi jednoličnu aproksimaciju bez prostornih ili naprednih efekata",
|
||||
"render.library_usable": "Izvršne projekcije",
|
||||
"render.library_users": "Korisnici",
|
||||
"render.light_aim": "Usmjeri prema",
|
||||
"render.light_anchor_pending": "Promjena koordinatnog sustava svjetla još nije podržana.",
|
||||
"render.light_azimuth": "Azimut",
|
||||
"render.light_backend_limit": "Odaberite CPU praćenje zraka ili izričiti GPU studio kako biste uređivali svjetla. Svjetla starog GPU studija ostaju samo za čitanje.",
|
||||
"render.light_behaviour": "PONAŠANJE",
|
||||
"render.light_default_fill": "Popunjavajuće",
|
||||
"render.light_default_key": "Glavno",
|
||||
"render.light_default_number": "Svjetlo {number}",
|
||||
"render.light_default_rim": "Rubno",
|
||||
"render.light_delete": "Izbriši",
|
||||
"render.light_distance": "Udaljenost",
|
||||
"render.light_duplicate": "Dupliciraj",
|
||||
"render.light_elevation": "Elevacija",
|
||||
"render.light_gpu_punctual": "Točkasta svjetla i reflektori dostupni su uz CPU praćenje zraka.",
|
||||
"render.light_hard_shadow": "Ovo svjetlo ima oštre sjene; mekoća emitera nije dostupna.",
|
||||
"render.light_intensity": "Intenzitet",
|
||||
"render.light_missing": "Ovo svjetlo više ne postoji. Odaberite svjetlo u stablu modela.",
|
||||
"render.light_place_highlight": "Postavi prema odsjaju",
|
||||
"render.light_place_pending": "Postavljanje prema odsjaju zahtijeva odabir svjetla u prozoru prikaza i još nije podržano.",
|
||||
"render.light_position": "POLOŽAJ",
|
||||
"render.light_preview_help": "Prikaz uživo je bez sjena; površinska svjetla koriste aproksimaciju s uzorkom u središtu. Renderiranje koristi potpuni model svjetla.",
|
||||
"render.light_product_centre": "Središte proizvoda",
|
||||
"render.light_size": "Veličina",
|
||||
"render.light_softness": "Mekoća",
|
||||
"render.light_temperature": "Temperatura boje",
|
||||
"render.light_temperature_help": "Približna nijansa crnog tijela; sprema se kao linearni RGB.",
|
||||
"render.light_type": "Vrsta",
|
||||
"render.light_type_area": "Površinsko",
|
||||
"render.light_type_point": "Točkasto",
|
||||
"render.light_type_spot": "Reflektor",
|
||||
"render.light_type_sun": "Sunce",
|
||||
"render.light_view_only": "Samo ovo svjetlo i isključivanje svjetla utječu na ovaj prikaz i njegova renderiranja; nikad se ne spremaju u dizajn.",
|
||||
"render.light_world_origin": "Ishodište svijeta",
|
||||
"render.lighting_aim_point": "Točka usmjeravanja",
|
||||
"render.lighting_apply": "Primijeni postavku",
|
||||
"render.lighting_area_samples": "Uzorci površinskog svjetla",
|
||||
"render.lighting_budget": "Proračun",
|
||||
"render.lighting_budget_help": "Brojevi su samo za čitanje. Dodajte, duplicirajte ili izbrišite svjetla kako biste ih promijenili; metoda određuje ograničenja.",
|
||||
"render.lighting_colour": "Boja",
|
||||
"render.lighting_consent": "Zamijeni usmjerena svjetla ovom postavkom",
|
||||
"render.lighting_custom": "Prilagođena svjetla",
|
||||
"render.lighting_custom_help": "Odabirom opcije Prilagođeno postavka se pretvara u svjetla koja se mogu uređivati, svako u svom retku u stablu.",
|
||||
"render.lighting_direction": "Smjer X Y Z",
|
||||
"render.lighting_direction_help": "Ili upišite smjer prema svjetlu (X Y Z).",
|
||||
"render.lighting_distance_unit": " × veličina",
|
||||
"render.lighting_edge_u": "Vektor stranice U",
|
||||
"render.lighting_edge_v": "Vektor stranice V",
|
||||
"render.lighting_falloff": "Slabljenje",
|
||||
"render.lighting_falloff_help": "Intenzitet je naveden za referentnu udaljenost.",
|
||||
"render.lighting_height": "Visina V",
|
||||
"render.lighting_minimum": "Minimum",
|
||||
"render.lighting_mute": "Isključi ovo svjetlo",
|
||||
"render.lighting_point": "Točka",
|
||||
"render.lighting_preset": "Postavka studija",
|
||||
"render.lighting_preset_cpu": "Usmjerene postavke studija zahtijevaju CPU praćenje zraka. GPU osvjetljenje koristi izričita svjetla vrste Sunce i Površinsko.",
|
||||
"render.lighting_preset_row": "Postavka",
|
||||
"render.lighting_radiance": "Radijancija",
|
||||
"render.lighting_reference": "Referenca",
|
||||
"render.lighting_review_help": "Zamjenjuje popis usmjerenih svjetala. Zadržava točkasta i površinska svjetla, okruženje, sjene, kameru i sve ostale postavke kadra.",
|
||||
"render.lighting_review_title": "Pregledaj postavku osvjetljenja",
|
||||
"render.lighting_samples_cpu": "Uzorci površinskog svjetla primjenjuju se na CPU praćenje zraka.",
|
||||
"render.lighting_samples_need_area": "Dodajte CPU površinsko svjetlo kako biste postavili uzorke površinskog svjetla.",
|
||||
"render.lighting_samples_reason": "Uzorci površinskog svjetla moraju biti 1, 4, 16 ili 64.",
|
||||
"render.lighting_shadow_cpu": "Prekidač sjena zahtijeva CPU praćenje zraka. GPU praćenje putanja uvijek baca sjene.",
|
||||
"render.lighting_shadows": "Baci sjene",
|
||||
"render.lighting_shadows_section": "Sjene i uzorkovanje",
|
||||
"render.lighting_shape": "Oblik",
|
||||
"render.lighting_solo": "Samo ovo svjetlo",
|
||||
"render.lighting_source": "Izvor",
|
||||
"render.lighting_source_section": "Izvor svjetla",
|
||||
"render.lighting_spot_preserved": "Ovo točkasto svjetlo zadržava spremljeni stožac reflektora dok mu se ne promijeni vrsta.",
|
||||
"render.lighting_sun_distance": "Smjer sunca ne ovisi o udaljenosti i točki usmjeravanja.",
|
||||
"render.lighting_unsupported": "nepodržano",
|
||||
"render.lighting_used": "{used} od {limit}",
|
||||
"render.lighting_width": "Širina U",
|
||||
"render.lighting_world_position": "Svjetski X Y Z",
|
||||
"render.lights_fixed_to": "Svjetla vezana za",
|
||||
"render.linear_help": "Zadržava 32-bitni float RGBA master prije uklanjanja šuma, ekspozicije, mapiranja tonova, filtara i pozadine. Linearni sRGB/D65; pomnožen pokrivenošću. Dodatna memorija izričito se odobrava. Bez sažimanja i dodatnih prolaza.",
|
||||
"render.linear_retain": "Zadrži sirovi linearni EXR",
|
||||
"render.linear_unavailable": "Odabrani dovršeni kadar nema zadržani EXR. Omogućite zadržavanje prije renderiranja; stariji PNG ne može vratiti sirovu radijanciju.",
|
||||
"render.look_add": "Dodaj izgled boje",
|
||||
"render.look_applied": "Primijenjeno: {name}",
|
||||
"render.look_bleach": "Po uzoru na bleach bypass",
|
||||
"render.look_cool": "Hladno",
|
||||
"render.look_cyanotype": "Po uzoru na cijanotipiju",
|
||||
"render.look_faded": "Izblijedjeli otisak",
|
||||
"render.look_material": "Materijal",
|
||||
"render.look_mono": "Jednobojno",
|
||||
"render.look_pasted": "Izgled je zalijepljen",
|
||||
"render.look_sepia": "Sepija",
|
||||
"render.look_strength": "Jačina izgleda",
|
||||
"render.look_teal_orange": "Tirkizno / narančasto",
|
||||
@@ -2607,9 +2938,11 @@
|
||||
"render.output_advanced": "Napredno",
|
||||
"render.output_backend_ready": "Spremno",
|
||||
"render.output_check": "Provjeri",
|
||||
"render.output_check_failed": "Provjera · pogreška",
|
||||
"render.output_check_help": "Pokrenite Provjeri da pregledate ovaj zahtjev bez pokretanja renderiranja.",
|
||||
"render.output_check_summary": "Provjera · Napomene: {count}",
|
||||
"render.output_copy": "Kopiraj",
|
||||
"render.output_copy_report": "Kopiraj izvješće",
|
||||
"render.output_destination": "Odredište",
|
||||
"render.output_destination_help": "Odaberite odredišnu datoteku pri izvozu dovršene slike.",
|
||||
"render.output_dimensions": "Veličina",
|
||||
@@ -2619,6 +2952,7 @@
|
||||
"render.output_fit": "Uklopi",
|
||||
"render.output_format": "Format",
|
||||
"render.output_format_status": "PNG je točna dovršena slika za prikaz. Neobavezni GPU EXR zadržava sirovu linearnu radijanciju scene u formatu FLOAT32 prije filtara prikaza. Izvoz nikad ne zamjenjuje predmemoriju pregleda ni drugi posao.",
|
||||
"render.output_grading": "Gradacija",
|
||||
"render.output_image": "Slika",
|
||||
"render.output_largest": "najviše {width} × {height}",
|
||||
"render.output_lock_ratio": "Zaključaj omjer stranica",
|
||||
@@ -2628,6 +2962,8 @@
|
||||
"render.output_quality_waiting": "čeka definiciju kvalitete",
|
||||
"render.output_ratio_refused": "Povezana dimenzija premašuje ograničenje veličine ove metode. Prethodna veličina zadržava se; otključajte omjer ili odaberite manju vrijednost.",
|
||||
"render.output_ready": "Spremno za renderiranje",
|
||||
"render.output_run_check": "Pokreni provjeru",
|
||||
"render.output_size_limit": "{method}: do {dimension} px po strani i {pixels} piksela. Najveća veličina koja stane: {width} × {height}.",
|
||||
"render.output_size_ok": "Veličina u redu · {width} × {height} px",
|
||||
"render.output_swap_orientation": "Zamijeni širinu i visinu",
|
||||
"render.output_tools_inspector": "Veličinu i format uredite u inspektoru Izlaz.",
|
||||
@@ -2644,6 +2980,7 @@
|
||||
"render.page_output": "Izlaz",
|
||||
"render.page_scene": "Scena",
|
||||
"render.paint_label": "Oboji",
|
||||
"render.painting_with": "Bojenje izgledom: {name}",
|
||||
"render.passive_stage": "Podloga i pasivna scena",
|
||||
"render.passive_stage_help": "Konačni pasivni paneli pripadaju samo renderiranim slikama. Ne dodaju CAD tijela ni masu. Postavljanje je fiksno nakon primjene; presijecanje granica proizvoda odbija se. CPU ne daje difuzno odbijeno osvjetljenje.",
|
||||
"render.perspective": "Perspektivna kamera",
|
||||
@@ -2665,6 +3002,7 @@
|
||||
"render.pick_help": "Odaberite Tijelo, Lice ili Komponenta. Kliknite prazan prostor kako biste očistili odabir; Shift ili Ctrl dodaje ili uključuje/isključuje. Isticanja se pojavljuju samo u živom prikazu proizvoda.",
|
||||
"render.pick_label": "Odaberi",
|
||||
"render.pick_up_label": "Preuzmi",
|
||||
"render.pick_up_prompt": "Kliknite objekt kako biste preuzeli njegov izgled",
|
||||
"render.pick_waiting": "Odabir proizvoda još nije podržan.",
|
||||
"render.post_contrast": "Kontrast",
|
||||
"render.post_gain_b": "Pojačanje plave",
|
||||
@@ -2704,13 +3042,17 @@
|
||||
"render.radial_distortion": "Radijalno izobličenje",
|
||||
"render.radial_fit": "Uklopi proizvod",
|
||||
"render.radial_save": "Spremi kadar",
|
||||
"render.recent_looks": "Nedavni izgledi",
|
||||
"render.regrade_action": "Ponovna gradacija sirovog mastera (bez uklanjanja šuma)",
|
||||
"render.regrade_help": "Izrađuje zaseban PNG iz odabranog dovršenog sirovog mastera, koristeći ove kontrole prikaza. Ne prati zrake, ne mijenja zabilježenu veličinu, kameru ni svjetla i ne prebrisuje EXR. Uklanjanje šuma ne primjenjuje se ponovno: sirovi masteri ne čuvaju njegove vodilje. Izvedena slika i dalje se može izvesti istim kontrolama posla.",
|
||||
"render.regrade_label": "Ponovna gradacija",
|
||||
"render.reload_settings": "Ponovno učitaj spremljene postavke",
|
||||
"render.remove_override": "Ukloni nadjačavanje",
|
||||
"render.replace_all": "Zamijeni sve",
|
||||
"render.replace_ground": "Potvrdi zamjenu postojeće scene",
|
||||
"render.replace_label": "Zamijeni",
|
||||
"render.replace_saved_surface": "Zamijeni postojeću spremljenu vezu za ovaj cilj",
|
||||
"render.replace_with": "Zamijeni s",
|
||||
"render.restore_camera": "Vrati spremljenu kameru",
|
||||
"render.result_actual": "100%",
|
||||
"render.result_actual_help": "Jedan piksel slike po pikselu zaslona.",
|
||||
@@ -2762,14 +3104,28 @@
|
||||
"render.saved_surface_remove": "Ukloni spremljenu vezu",
|
||||
"render.saved_surfaces": "Spremljene veze površina",
|
||||
"render.saved_surfaces_help": "Fiksirane ugrađene mape prate odabrani primjerak u lokalnim milimetrima. Zamijenjen ili nedostajući cilj pogreška je, nikad automatska ponovna dodjela.",
|
||||
"render.scene_all_shots": "Svi kadrovi ({count})",
|
||||
"render.scene_apply": "Primijeni pregledani studio",
|
||||
"render.scene_batch_reason": "Opcija Svi kadrovi zahtijeva red čekanja za renderiranje spremljenih kadrova, koji još nije dostupan.",
|
||||
"render.scene_check_now": "Provjeri sada",
|
||||
"render.scene_check_passed": "Provjera uspješna · napomene: {notes}",
|
||||
"render.scene_check_stale": "Provjera je zastarjela · provjerite ponovno",
|
||||
"render.scene_check_unchecked": "Još nije provjereno · 0 napomena",
|
||||
"render.scene_consent": "Potvrdi zamjenu osvjetljenja, okruženja, ekspozicije i scene",
|
||||
"render.scene_light_summary": "{environment} · svjetla: {lights}",
|
||||
"render.scene_look_summary": "Izgledi: {looks} · nadjačavanja: {overrides}",
|
||||
"render.scene_open_check": "Otvori provjeru",
|
||||
"render.scene_preserved_fields": "Kamera, uzorkovanje, dimenzije, optičke veze, CAD geometrija i inženjerski materijali čuvaju se.",
|
||||
"render.scene_preserves_design": "Sastavite osvjetljenje i pasivnu scenu za vidljivi predmet. Geometrija, inženjerski materijali i kamera ostaju odvojeni.",
|
||||
"render.scene_ready": "Spremno za renderiranje",
|
||||
"render.scene_reminder": "Scena čuva dizajn. Geometrija, inženjerska svojstva i CAD nikad se ne mijenjaju iz Renderiranja.",
|
||||
"render.scene_reminders": "Podsjetnici",
|
||||
"render.scene_renderer_ready": "{renderer}: spremno",
|
||||
"render.scene_replaces": "Zamjenjuje",
|
||||
"render.scene_review": "Razriješi i pregledaj",
|
||||
"render.scene_review_help": "Pregledajte potpunu izvornu kompoziciju prije primjene. Nevezane napredne SCENE specifikacije ne zamjenjuju se.",
|
||||
"render.scene_scope_details": "Opseg kompozicije",
|
||||
"render.scene_stages": "Faze",
|
||||
"render.search_appearances": "Pretraži izglede ({count})",
|
||||
"render.setup_legacy_environment": "Ova starija postavka koristi naslijeđenu rotaciju ili intenzitet okruženja. Te vrijednosti zadržavaju se; izričito ih pretvorite prije spremanja punog posla slike.",
|
||||
"render.setup_name": "Naziv postavke",
|
||||
@@ -2782,7 +3138,22 @@
|
||||
"render.shell_tab_camera": "KAMERA RENDERA",
|
||||
"render.shell_tab_lighting": "OSVJETLJENJE",
|
||||
"render.shell_tab_scene": "SCENA RENDERA",
|
||||
"render.shot_aperture": "Otvor blende",
|
||||
"render.shot_distance": "Udaljenost",
|
||||
"render.shot_dof": "Dubinska oštrina",
|
||||
"render.shot_duplicate": "Dupliciraj spremljeni kadar",
|
||||
"render.shot_edit_output": "Uredi u Izlazu",
|
||||
"render.shot_exposure_reason": "Ekspozicija zahtijeva CPU praćenje zraka ili GPU praćenje putanja.",
|
||||
"render.shot_exposure_section": "Ekspozicija",
|
||||
"render.shot_focus_distance": "Udaljenost fokusa",
|
||||
"render.shot_fov_reason": "Vidno polje zahtijeva perspektivnu projekciju.",
|
||||
"render.shot_frame_subject": "Uokviri predmet",
|
||||
"render.shot_invalid_camera": "Vrijednosti kamere moraju biti konačne i unutar raspona.",
|
||||
"render.shot_lens": "Objektiv",
|
||||
"render.shot_lens_enable_reason": "Omogućite dubinsku oštrinu kako biste uredili otvor blende i udaljenost fokusa.",
|
||||
"render.shot_lens_gpu_reason": "Dubinska oštrina zahtijeva GPU praćenje putanja.",
|
||||
"render.shot_lens_perspective_reason": "Dubinska oštrina zahtijeva perspektivnu projekciju.",
|
||||
"render.shot_look_selection": "Pogledaj odabir",
|
||||
"render.shot_manager": "Spremljeni kadrovi",
|
||||
"render.shot_new_name": "Naziv novog kadra",
|
||||
"render.shot_remove": "Ukloni spremljeni kadar",
|
||||
@@ -2790,6 +3161,9 @@
|
||||
"render.shot_remove_title": "Ukloniti spremljeni kadar?",
|
||||
"render.shot_rename": "Preimenuj spremljeni kadar",
|
||||
"render.shot_saved_only": "Najprije spremite trenutni nacrt. Dupliciranje i preimenovanje čuvaju točne spremljene opcije i reference na resurse.",
|
||||
"render.shot_selection_reason": "Odaberite tijelo ili lice prema kojem se usmjerava.",
|
||||
"render.shot_size": "Veličina",
|
||||
"render.shot_target": "Cilj",
|
||||
"render.shots_camera_changed": "Kamera je promijenjena otkako je ovaj kadar spremljen",
|
||||
"render.shots_name_help": "Upišite jedinstven naziv trenutnog kadra.",
|
||||
"render.shots_no_result": "Još nema renderiranja",
|
||||
@@ -3052,6 +3426,7 @@
|
||||
"select an axis": "odaberite os",
|
||||
"select an axis, a straight or circular edge, or a cylinder, cone or torus face first": "prvo odaberite os, ravan ili kružni brid ili lice valjka, stošca ili torusa",
|
||||
"select bodies first": "prvo odaberite tijela",
|
||||
"select components first": "najprije odaberite komponente",
|
||||
"select corner points, or the two lines of one corner": "odaberite točke kutova ili dvije linije jednog kuta",
|
||||
"select one corner point, or the two lines of one corner": "odaberite jednu točku kuta ili dvije linije jednog kuta",
|
||||
"select sheet edges first": "prvo odaberite bridove lima",
|
||||
@@ -3070,6 +3445,7 @@
|
||||
"settings.double_click_components": "Dvaput kliknite komponentu kako biste je aktivirali ili uredili povezanu komponentu na mjestu",
|
||||
"settings.frame_rate_details_note": "Dodaje graf vremena posljednjih okvira s brojem iscrtanih trokuta, histogram vremena okvira i što je GPU iscrtao. Kliknite broj okvira u sekundi u prikazu kako biste ih prikazali ili sakrili.",
|
||||
"settings.frame_rate_note": "Prikazuje broj okvira u sekundi u donjem desnom kutu prikaza.",
|
||||
"settings.insert_lists_open_designs": "Umetni dizajn najprije navodi dizajne otvorene u drugim karticama",
|
||||
"settings.measure_axis_distances": "Prikaži udaljenosti X/Y/Z",
|
||||
"settings.measure_axis_distances_help": "Prikazuj komponente udaljenosti po svjetskim osima uz ukupnu udaljenost dok je Mjerenje otvoreno.",
|
||||
"settings.msaa_applies_on_restart": "Zaglađivanje rubova primjenjuje se pri sljedećem pokretanju SolidCrafta",
|
||||
@@ -3094,6 +3470,10 @@
|
||||
"shortcut.unpin": "Otkvači",
|
||||
"sketch is missing": "skica nedostaje",
|
||||
"sketch.degrees_of_freedom": "Stupnjevi slobode: {count}",
|
||||
"sketch.dimension_added_driven": "Skica već određuje tu kotu, pa je dodana kao referentna kota",
|
||||
"sketch.dimension_applied": "Kota je primijenjena",
|
||||
"sketch.dimension_applied_defined": "Kota je primijenjena; definiran je parametar `{name}`",
|
||||
"sketch.dimension_applied_renamed": "Kota je primijenjena; parametar je nazvan `{name}`",
|
||||
"sketch.dof_left": "Preostali stupnjevi slobode: {count}",
|
||||
"sketch.dof_left_one": "Preostali stupnjevi slobode: {count}",
|
||||
"sketch.draw_curves": "Crtaj kao {linetype}",
|
||||
@@ -3128,6 +3508,8 @@
|
||||
"sketch_text.use_font_file": "Upotrijebi datoteku fonta {path}",
|
||||
"solid": "puno",
|
||||
"status.editing": "Uređivanje: {name}",
|
||||
"status.navigation": "Mapiranje miša: {mapping}",
|
||||
"status.navigation_tip": "Kako miš pomiče prikaz. Kliknite za odabir drugog mapiranja",
|
||||
"status.under_constrained": "Nedovoljno ograničeno (stupnjevi slobode: {count})",
|
||||
"status.under_constrained_one": "Nedovoljno ograničeno (stupanj slobode: {count})",
|
||||
"status.units_tip": "Jedinice dizajna. Kliknite za promjenu",
|
||||
@@ -3161,8 +3543,10 @@
|
||||
"timeline.delete_folders_tip": "Zadrži njihove značajke i ugniježđene mape u vremenskoj traci",
|
||||
"timeline.delete_folders_with_features_tip": "Izbriši mape sa svim značajkama u njima i njihove ugniježđene mape",
|
||||
"timeline.delete_many": "Izbriši odabrane značajke: {count}",
|
||||
"timeline.edited_rolled_back": "Značajka {name} je promijenjena, ali je nakon oznake vraćanja pa se ne gradi: pomaknite oznaku iza nje kako biste vidjeli rezultat",
|
||||
"timeline.feature_error": "Pogreška: {message}",
|
||||
"timeline.feature_hint": "Dvaput kliknite za uređivanje · povucite za promjenu redoslijeda · desni klik za više",
|
||||
"timeline.feature_rolled_back": "Vraćeno: nalazi se nakon oznake vraćanja, pa se ne gradi",
|
||||
"timeline.feature_warning": "Upozorenje: {message}",
|
||||
"timeline.group": "Nova mapa značajki",
|
||||
"timeline.move_many": "Premjesti stavke vremenske trake",
|
||||
@@ -3244,6 +3628,10 @@
|
||||
"ui.3d_mouse_offer": "Kalibracija 3D miša ispravlja zamijenjene ili obrnute osi i postavlja njegovu brzinu i osjećaj.",
|
||||
"ui.3d_mouse_offer_never": "Ne prikazuj više",
|
||||
"ui.3d_mouse_offer_swap": "Prebaci na Hidraw (izravno)",
|
||||
"ui.3d_mouse_orbit": "Orbitiranje",
|
||||
"ui.3d_mouse_orbit_classic": "Klasično",
|
||||
"ui.3d_mouse_orbit_tip": "Praćeno: 3D miš okreće prikaz oko dijela modela koji je u sredini prikaza kad okretanje počne, označenog blijedom kuglom. Klasično: oko središta prikaza",
|
||||
"ui.3d_mouse_orbit_traced": "Praćeno",
|
||||
"ui.3d_mouse_reading": "Očitanje",
|
||||
"ui.3d_mouse_reset_axes": "Vrati zadanu dodjelu osi",
|
||||
"ui.3d_mouse_reset_view": "Vrati prikaz",
|
||||
@@ -3280,6 +3668,8 @@
|
||||
"ui.3d_mouse_step_twist_counterclockwise": "Zakrenite kapicu suprotno od kazaljke na satu, držite je, a zatim pustite.",
|
||||
"ui.3d_mouse_switch_cap": "Kalibriraj kao Povratak u središte",
|
||||
"ui.3d_mouse_switch_trackball": "Kalibriraj kao kuglični miš",
|
||||
"ui.3d_mouse_use_direct": "Koristi izravni upravljački program",
|
||||
"ui.3d_mouse_use_direct_tip": "Čitaj 3D miš izravno (hidraw) umjesto preko spacenavd.",
|
||||
"ui.3d_mouse_view_axis": "Os prikaza",
|
||||
"ui.3d_mouse_view_off": "Isključeno",
|
||||
"ui.3d_mouse_view_off_tip": "Profil upravljačkog programa 3D miša isključuje ovu os, pa njezino kretanje ne pomiče prikaz. Odabir kretanja druge osi ovdje ih zamjenjuje, a to kretanje prestaje pomicati prikaz.",
|
||||
@@ -3596,6 +3986,7 @@
|
||||
"ui.circle": "Kružnica",
|
||||
"ui.circular_pattern": "Kružni niz",
|
||||
"ui.clear": "Očisti",
|
||||
"ui.clear_print_face": "Ukloni plohu ispisa",
|
||||
"ui.clear_recent": "Očisti nedavno",
|
||||
"ui.clear_the_selection": "Poništi odabir",
|
||||
"ui.click_selects_whole_bodies": "Klik odabire cijela tijela",
|
||||
@@ -3630,6 +4021,15 @@
|
||||
"ui.constant": "Konstantno",
|
||||
"ui.constraints_conflict": "Ograničenja su u sukobu",
|
||||
"ui.construction": "Pomoćna",
|
||||
"ui.construction.angle_hint": "Pomoćni pravac: kliknite točku, pa upišite kut ili odaberite smjer",
|
||||
"ui.construction.bisector_hint": "Simetrala: odaberite dvije ravne linije koje se sijeku, pa željeno rješenje",
|
||||
"ui.construction.common_hint": "Zajednička tangenta: odaberite dvije kružnice ili dva luka, pa željeno rješenje",
|
||||
"ui.construction.contact_hint": "Tangenta: odaberite kružnicu/luk ili otvoreni spline/koniku, pa točku dodira (krivulje slobodnog oblika: krajnje točke)",
|
||||
"ui.construction.external_hint": "Tangenta: odaberite kružnicu ili luk, vanjsku točku, pa željeno rješenje",
|
||||
"ui.construction.free_hint": "Pomoćni pravac: kliknite dvije točke; desnim klikom počnite ispočetka",
|
||||
"ui.construction.point_hint": "Pomoćni pravac: kliknite točku kroz koju prolazi",
|
||||
"ui.construction.reference_hint": "Pomoćni pravac: odaberite ravnu liniju, pa točku; desnim klikom promijenite referencu",
|
||||
"ui.construction.ribbon_label": "Pomoćne",
|
||||
"ui.context_menu": "Kontekstni izbornik",
|
||||
"ui.continuity_g0": "G0 pregib",
|
||||
"ui.continuity_g1": "G1 tangentno",
|
||||
@@ -3670,6 +4070,7 @@
|
||||
"ui.design_properties.part_number": "Broj dijela",
|
||||
"ui.design_properties.project": "Projekt",
|
||||
"ui.design_properties.revision": "Revizija",
|
||||
"ui.design_saved": "Spremljeno: {path}",
|
||||
"ui.diameter": "Promjer",
|
||||
"ui.dimension": "Kota",
|
||||
"ui.direct_interaction": "Izravna interakcija",
|
||||
@@ -3720,6 +4121,14 @@
|
||||
"ui.end_scale": "Krajnje skaliranje",
|
||||
"ui.entities": "Elementi",
|
||||
"ui.equal": "Jednakost",
|
||||
"ui.equation_end": "Kraj",
|
||||
"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_parametric": "Parametarski",
|
||||
"ui.equation_x_of_t": "x(t)",
|
||||
"ui.equation_y_of_t": "y(t)",
|
||||
"ui.equation_y_of_x": "y(x)",
|
||||
"ui.equivalent_radius": "Ekvivalentni polumjer",
|
||||
"ui.expand": "Proširi",
|
||||
"ui.exportDxf": "Izvezi DXF…",
|
||||
@@ -3785,6 +4194,7 @@
|
||||
"ui.heading_design": "DIZAJN",
|
||||
"ui.heading_draft": "NAGIB",
|
||||
"ui.heading_edit": "UREDI",
|
||||
"ui.heading_equation_curve": "KRIVULJA IZ JEDNADŽBE",
|
||||
"ui.heading_extrude": "EKSTRUDIRAJ",
|
||||
"ui.heading_fillet": "ZAOBLJENJE",
|
||||
"ui.heading_finish_editing": "ZAVRŠI UREĐIVANJE",
|
||||
@@ -3876,6 +4286,10 @@
|
||||
"ui.import_ellipsis": "Uvezi…",
|
||||
"ui.infinite": "Beskonačno",
|
||||
"ui.insert": "Umetni",
|
||||
"ui.insert_design.browse": "Pregledaj…",
|
||||
"ui.insert_design.open_designs": "Otvoreni dizajni",
|
||||
"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.intersect": "Presjek",
|
||||
"ui.isolate": "Izoliraj",
|
||||
@@ -4019,12 +4433,29 @@
|
||||
"ui.polygon": "Mnogokut",
|
||||
"ui.preferences": "Postavke",
|
||||
"ui.press_a_key": "pritisnite tipku…",
|
||||
"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",
|
||||
"ui.profile_plane": "Ravnina profila",
|
||||
"ui.profiles": "Profili",
|
||||
"ui.project": "Projiciraj",
|
||||
"ui.project_face_edges_sketch": "Projiciraj bridove lica u nove skice",
|
||||
"ui.properties": "Svojstva",
|
||||
"ui.properties.copy_matrix": "Kopiraj matricu",
|
||||
"ui.properties.first_moments": "Statički momenti",
|
||||
"ui.properties.inertia": "Moment tromosti",
|
||||
"ui.properties.inertia_note": "Oko središta mase, u svjetskim osima (g·{unit}²)",
|
||||
"ui.properties.inertia_unavailable": "Nije dostupno: mreža tijela nije zatvorena.",
|
||||
"ui.properties.inertia_x": "Ixx, Ixy, Ixz",
|
||||
"ui.properties.inertia_y": "Iyx, Iyy, Iyz",
|
||||
"ui.properties.inertia_z": "Izx, Izy, Izz",
|
||||
"ui.properties.measuring": "Mjerenje…",
|
||||
"ui.properties.measuring_failed": "Mjerenje je zaustavljeno prije završetka.",
|
||||
"ui.properties.principal_axis": "Glavna os {number}",
|
||||
"ui.properties.principal_moments": "Glavni momenti tromosti",
|
||||
"ui.pull_direction": "Smjer vađenja iz kalupa",
|
||||
"ui.quality": "Kvaliteta",
|
||||
"ui.quantity": "Broj",
|
||||
@@ -4070,6 +4501,7 @@
|
||||
"ui.revolve": "Rotiraj",
|
||||
"ui.ribbon_exit": "Izlaz",
|
||||
"ui.ribbon_labels": "Prikaži tekst ispod ikona na traci",
|
||||
"ui.ribbon_menus_skip_pinned": "Izostavi prikvačene naredbe iz padajućih izbornika",
|
||||
"ui.ribbon_pin_footer": "Prikvačene naredbe prikazuju se na traci. Zadržite pokazivač iznad retka kako biste ga prikvačili ili otkvačili.",
|
||||
"ui.ribbon_workspace": "Radni prostor",
|
||||
"ui.right": "Desno",
|
||||
@@ -4087,6 +4519,7 @@
|
||||
"ui.select": "Odaberi",
|
||||
"ui.selectOther": "Odaberi drugo…",
|
||||
"ui.selectTangentChain": "Odaberi tangentni lanac",
|
||||
"ui.select_all": "Odaberi sve",
|
||||
"ui.selection": "Odabir",
|
||||
"ui.selection_modifiers": "Modifikatori odabira",
|
||||
"ui.selection_shift_add_only": "Shift+klik dodaje bez uklanjanja",
|
||||
@@ -4123,7 +4556,11 @@
|
||||
"ui.sketch": "Skica",
|
||||
"ui.sketch_dimension": "Kota skice",
|
||||
"ui.sketch_grid": "Mreža skice",
|
||||
"ui.sketch_intersect_geometry": "Geometrija",
|
||||
"ui.sketch_intersect_projection_link": "Veza projekcije",
|
||||
"ui.sketch_intersect_selection_filter": "Filtar odabira",
|
||||
"ui.sketch_modifiers": "Modifikatori skice",
|
||||
"ui.sketch_project_geometry": "Geometrija",
|
||||
"ui.sketch_scale": "Skaliranje skice",
|
||||
"ui.sketches": "Skice",
|
||||
"ui.slice": "Presijeci",
|
||||
@@ -4170,6 +4607,11 @@
|
||||
"ui.symmetric": "Simetrično",
|
||||
"ui.symmetry": "Simetrija",
|
||||
"ui.system": "Sustav",
|
||||
"ui.tab_assemble": "Sklopi",
|
||||
"ui.tab_manage": "Upravljanje",
|
||||
"ui.tab_sheet_metal": "Lim",
|
||||
"ui.tab_view": "Prikaz",
|
||||
"ui.tab_weldments": "Zavarene konstrukcije",
|
||||
"ui.tangent": "Tangentno",
|
||||
"ui.taper_angle": "Kut suženja",
|
||||
"ui.tapped": "S navojem",
|
||||
@@ -4185,6 +4627,7 @@
|
||||
"ui.tip_angle": "Kut vrha",
|
||||
"ui.to_object": "Do objekta",
|
||||
"ui.tool_direction": "Smjer alata",
|
||||
"ui.toolbar_tabs_hidden": "Sve kartice alatne trake su skrivene. Kliknite + ili ovdje desnom tipkom kako biste prikazali jednu.",
|
||||
"ui.top": "Odozgo",
|
||||
"ui.top_diameter": "Gornji promjer",
|
||||
"ui.top_radius": "Gornji polumjer",
|
||||
@@ -4222,6 +4665,8 @@
|
||||
"ui.view_centre": "Središte prikaza",
|
||||
"ui.viewport_status_clear": "Isključi {name}",
|
||||
"ui.viewport_status_clear_hint": "Isključi {name} · Esc",
|
||||
"ui.viewport_status_edges": "Bridovi: {count}",
|
||||
"ui.viewport_status_from": "iz smjera {direction}",
|
||||
"ui.viewport_status_key": "Prikaži legendu za {name}",
|
||||
"ui.viewport_status_minimum_radius": "Minimalni polumjer",
|
||||
"ui.viewport_status_pairs": "Parovi: {count}",
|
||||
@@ -4231,6 +4676,7 @@
|
||||
"ui.visual_shaded_with_edges": "Osjenčano s bridovima",
|
||||
"ui.visual_style": "Vizualni stil",
|
||||
"ui.visual_wireframe": "Žičani model",
|
||||
"ui.wheel_zoom_speed": "Brzina zumiranja kotačićem miša",
|
||||
"ui.width": "Širina",
|
||||
"ui.window_system": "Sustav prozora",
|
||||
"ui.window_system_auto": "Automatski",
|
||||
@@ -4261,7 +4707,9 @@
|
||||
"update.button": "Ažuriraj",
|
||||
"update.button_version": "Ažuriraj na {version}",
|
||||
"update.check_automatically": "Automatski provjeravaj ažuriranja",
|
||||
"update.check_for_updates": "Provjeri ažuriranja",
|
||||
"update.check_now": "Provjeri sada",
|
||||
"update.check_unavailable": "Provjera ažuriranja nije dostupna za ovu instalaciju.",
|
||||
"update.checking": "Provjera…",
|
||||
"update.copy": "Kopiraj",
|
||||
"update.dmg_opened": "Slika diska je otvorena i provjerena: povucite SolidCraft u Programe, zamijenite staru kopiju, a zatim ponovno pokrenite SolidCraft.",
|
||||
@@ -4284,6 +4732,9 @@
|
||||
"update.prefs_managed": "Ovo je SolidCraft {version}, kojim upravlja {manager}. Provjera pita GitHub za najnovije izdanje i ne šalje ništa drugo; ažurirajte SolidCraft u {manager}.",
|
||||
"update.prefs_note": "Ovo je SolidCraft {version} ({kind}). Provjera pita GitHub za najnovije izdanje i ne šalje ništa drugo.",
|
||||
"update.prefs_web": "Ponovno učitajte stranicu kako biste dobili najnoviju web aplikaciju. Ako mreža nije dostupna ili se ažuriranje ne može preuzeti na vrijeme, otvara se spremljena verzija; preuzimanje u tijeku nastavlja se za sljedeće ponovno učitavanje.",
|
||||
"update.refresh_checking": "Provjera najnovijeg izdanja…",
|
||||
"update.refresh_fallback": "Nije moguće provjeriti postoji li novije izdanje; ažurira se na {version}, pronađeno ranije.",
|
||||
"update.refresh_selected": "Ažuriranje na {version}.",
|
||||
"update.release_page": "Otvori stranicu izdanja",
|
||||
"update.restart": "Ponovno pokreni sada",
|
||||
"update.restart_later": "Kasnije",
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
"BLEND SURFACE": "ブレンドサーフェス",
|
||||
"BOUNDARY SURFACE": "境界サーフェス",
|
||||
"BROWSER": "ブラウザ",
|
||||
"Back to the original order": "元の順序に戻す",
|
||||
"Ball": "ボール",
|
||||
"Bar": "平鋼",
|
||||
"Base Center": "底面の中心",
|
||||
@@ -595,6 +596,8 @@
|
||||
"Snap to face and edge centres": "面とエッジの中心にスナップ",
|
||||
"SolidCraft cannot submit reports directly. Submit on GitHub; no token is needed in the app.": "SolidCraftからレポートを直接送信することはできません。GitHubで送信してください。アプリにトークンは必要ありません。",
|
||||
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "これらのデザインが保存される前にSolidCraftが終了しました。最後の自動保存ファイルを開くことができます。",
|
||||
"Sort A to Z": "昇順で並べ替え",
|
||||
"Sort Z to A": "降順で並べ替え",
|
||||
"Spacing 2 must be a non-zero number": "間隔 2は0以外の数値にしてください",
|
||||
"Spacing must be a non-zero number": "間隔は0以外の数値にしてください",
|
||||
"Specialise": "特殊操作",
|
||||
@@ -775,6 +778,8 @@
|
||||
"at the face": "面の位置",
|
||||
"bom.group_children": "サブパーツを親パーツの行に含める",
|
||||
"browser.body_name": "ボディ{number}",
|
||||
"browser.calibrate_click_a": "画像をクリックして点Aを指定するか、座標を入力してください。",
|
||||
"browser.calibrate_click_b": "画像をクリックして点Bを指定するか、座標を入力してください。",
|
||||
"browser.calibrate_title": "尺度を設定: {name}",
|
||||
"browser.capture_many": "{count}個のコンポーネントが移動されましたが、位置がキャプチャされていません。",
|
||||
"browser.capture_note": "「位置をキャプチャ」で新しい位置を保持し、「位置を元に戻す」で以前の位置に戻します。",
|
||||
@@ -817,6 +822,7 @@
|
||||
"cmd.appearance_pick_up": "外観を取得",
|
||||
"cmd.appearance_replace": "外観を置換",
|
||||
"cmd.assembly_bom": "部品表",
|
||||
"cmd.body.print_face": "造形面",
|
||||
"cmd.body_rename": "ボディの名前を変更",
|
||||
"cmd.browser_collapse_all": "すべて折りたたむ",
|
||||
"cmd.browser_expand_all": "すべて展開",
|
||||
@@ -1101,6 +1107,16 @@
|
||||
"cmd.sheet_refold": "折り戻し",
|
||||
"cmd.sheet_rules": "板金ルールを一覧表示",
|
||||
"cmd.sheet_unfold": "展開",
|
||||
"cmd.sketch.construction_line": "補助直線",
|
||||
"cmd.sketch.construction_line.angle": "角度指定の補助直線",
|
||||
"cmd.sketch.construction_line.bisector": "角の二等分線",
|
||||
"cmd.sketch.construction_line.horizontal": "水平補助直線",
|
||||
"cmd.sketch.construction_line.parallel": "平行補助直線",
|
||||
"cmd.sketch.construction_line.perpendicular": "直交補助直線",
|
||||
"cmd.sketch.construction_line.tangent_at": "曲線上の点での接線",
|
||||
"cmd.sketch.construction_line.tangent_external": "外部点を通る接線",
|
||||
"cmd.sketch.construction_line.tangent_two": "共通接線",
|
||||
"cmd.sketch.construction_line.vertical": "垂直補助直線",
|
||||
"cmd.sketch_arc3d": "3D 円弧",
|
||||
"cmd.sketch_arc_center_point": "中心点円弧",
|
||||
"cmd.sketch_arc_tangent": "正接円弧",
|
||||
@@ -1389,7 +1405,18 @@
|
||||
"help.occurrence.move": "アセンブリ部品をジョイントが許す自由度だけで動かします。部品の面を選択してから、操作可能な矢印または円弧をドラッグしてください。",
|
||||
"help.parameters.change": "デザインの寸法を決める名前付きの値と式の表を開きます。1か所で寸法を変え、それを使うすべてのフィーチャに反映させるときに使います。",
|
||||
"help.pointcloud.insert": "PLY・XYZ・PTS形式のスキャンを点群としてモデルと一緒に挿入します。計測データを参考にモデリングするときに使います。",
|
||||
"help.sketch.construction_line": "2点を通る無限の補助直線を作成します。スケッチの輪郭を分割せずに作図できます。",
|
||||
"help.sketch.construction_line.angle": "スケッチのX軸に対して角度を指定し、無限の補助直線を作成します。点を選び、角度を入力するか方向を選びます。",
|
||||
"help.sketch.construction_line.bisector": "交差する2本のスケッチ直線を選び、2つの角の二等分線から選びます。結果は元の直線の編集に追従します。",
|
||||
"help.sketch.construction_line.horizontal": "指定点を通る無限の水平補助直線を作成します。後で編集しても水平拘束が維持されます。",
|
||||
"help.sketch.construction_line.parallel": "スケッチの直線を選び、平行な無限の補助直線を配置します。右クリックで参照を選び直します。",
|
||||
"help.sketch.construction_line.perpendicular": "スケッチの直線を選び、直交する無限の補助直線を配置します。右クリックで参照を選び直します。",
|
||||
"help.sketch.construction_line.tangent_at": "円や円弧上の点、または開いたスプラインや円錐曲線の端点に接線を作成します。曲線を選び、接点を選びます。",
|
||||
"help.sketch.construction_line.tangent_external": "円または円弧、外部点、接線の解を順に選びます。プレビューには描かれた円弧上の接点のみが表示されます。",
|
||||
"help.sketch.construction_line.tangent_two": "2つの円または円弧を選び、プレビューから共通接線を選びます。接点は描かれた曲線上にある必要があります。",
|
||||
"help.sketch.construction_line.vertical": "指定点を通る無限の垂直補助直線を作成します。後で編集しても垂直拘束が維持されます。",
|
||||
"help.sketch.equation_curve": "x(t) と y(t)、または y = f(x) の式から、範囲を指定して曲線を描きます。式にはパラメーターを使え、曲線はその変更に追従します。正弦波、らせん、歯車のインボリュートなどに使います。",
|
||||
"help.sketch.inspect": "開いているスケッチのレポートを開きます。残りの自由度と、完全に拘束されているかどうかを表示します。スケッチを終える前に、まだ動く部分を確認するときに使います。",
|
||||
"help.solid.align": "選択した立体を動かし、その点、面、平面を別の場所の点、面、平面に合わせます。距離を測らずに部品を別の部品に当てて配置するときに使います。",
|
||||
"help.solid.chamfer": "選択した辺を、指定した距離で平らに斜めに削ります。鋭い角を落とすときや、ピンやねじを入れやすくするときに使います。",
|
||||
"help.solid.combine": "複数の立体を結合したり、対象の立体から切り取ったり、共通する体積だけを残したりでき、工具の立体を残すこともできます。部品の結合、ある形での切り抜き、2つの立体が重なる範囲の確認に使います。",
|
||||
@@ -1592,6 +1619,10 @@
|
||||
"material.selection.mixed": "異なる材料",
|
||||
"measure.at": "位置",
|
||||
"measure.item": "項目 {number}",
|
||||
"measure.quick_copied": "測定値をコピーしました",
|
||||
"measure.quick_copy": "クリックで測定値をコピー",
|
||||
"measure.quick_edges": "エッジ {count} 本",
|
||||
"measure.quick_faces": "面 {count} 個",
|
||||
"messages.ago_h": "{n} 時間前",
|
||||
"messages.ago_min": "{n} 分前",
|
||||
"messages.ago_s": "{n} 秒前",
|
||||
@@ -1744,6 +1775,11 @@
|
||||
"not a move": "移動ではありません",
|
||||
"not a number": "数値ではありません",
|
||||
"open a sketch first": "先にスケッチを開いてください",
|
||||
"parameters.apply_live": "フィールドを離れるたびに変更を適用",
|
||||
"parameters.apply_live_tip": "オフ: 変更は「適用」または「OK」まで保留され、その後すべてをまとめて一度だけ再構築します。「キャンセル」または Esc で破棄します。",
|
||||
"parameters.held": "{count} 件の変更が適用待ちです",
|
||||
"parameters.held_hint": "式と単位を編集してください。変更は「適用」または「OK」まで保留されます。",
|
||||
"parameters.held_one": "1 件の変更が適用待ちです",
|
||||
"parts.automatic_name": "自動",
|
||||
"parts.dimension.across_flats": "二面幅",
|
||||
"parts.dimension.bore": "内径",
|
||||
@@ -1766,6 +1802,8 @@
|
||||
"parts.family.set_screw": "六角穴付き止めねじ 平先",
|
||||
"parts.family.socket_head_cap_screw": "六角穴付きボルト",
|
||||
"parts.family.washer": "平座金",
|
||||
"parts.holes": "穴 (省略可)",
|
||||
"parts.placement_hint": "1 つ以上の穴を選ぶとそれぞれに部品を取り付けます。選ばずに OK を押すと原点に配置します",
|
||||
"pattern.alignment_2": "配置 2",
|
||||
"pattern.alignment_hint": "個数には元の図形が含まれます。中央配置ではコピーを両側に配置し、反転で偶数個の場合に多く配置する側を切り替えます。",
|
||||
"pattern.direction_1": "方向 1",
|
||||
@@ -1818,6 +1856,8 @@
|
||||
"prefs.bottom_centre": "下中央",
|
||||
"prefs.bottom_right": "右下",
|
||||
"prefs.browser": "ブラウザー",
|
||||
"prefs.browser_eye_left": "表示切り替えを折りたたみ矢印の隣に表示",
|
||||
"prefs.browser_eye_left_note": "ブラウザーの各行の表示切り替えを、行の右端の列ではなく、以前のように折りたたみ矢印の隣に表示します。",
|
||||
"prefs.browser_reveal_on_hover": "ホバーで表示",
|
||||
"prefs.browser_show_note": "「表示」はブラウザーを常に画面に表示します。「ホバーで表示」はポインターがその位置か左端の細いタブに来るまで隠し、ポインターが中にある間やそのメニューが開いている間は表示したままにします。",
|
||||
"prefs.browser_shown": "表示",
|
||||
@@ -1868,6 +1908,8 @@
|
||||
"prefs.graphics_card_restart": "SolidCraft の再起動後に有効になります。WGPU_ADAPTER_NAME がこの選択より優先されます。",
|
||||
"prefs.graphics_note": "速度と滑らかさのバランスを調整します。各選択肢の効果をサンプルで確認できます。",
|
||||
"prefs.graphics_presets_note": "プリセットは以下の設定をまとめて変更します。",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "ズームアウト時に拘束グリフを隠す",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out_note": "スケッチを十分にズームアウトすると、拘束グリフは表示されず画面を散らかしません。オフ: ズームに関係なく固定の画面サイズのままです。",
|
||||
"prefs.history": "履歴",
|
||||
"prefs.history_origin_bodies": "原点とボディを先頭に表示",
|
||||
"prefs.history_origin_bodies_note": "左側またはブラウザー下の履歴の先頭に、アクティブなコンポーネントのボディフォルダーと原点を、ブラウザーと同じ表示切り替えとメニュー付きで表示します。下部の履歴は変わりません。",
|
||||
@@ -1906,6 +1948,9 @@
|
||||
"prefs.max_file_size_step": "STEP・インポートファイル",
|
||||
"prefs.max_file_size_web": "ウェブ版で開けるのは STEP・インポートファイルが 512 MB、デザインが 256 MB までです。ブラウザーのタブはそれ以上のファイルを保持できません。デスクトップ版では上限を上げられます。",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "大きなアイコン",
|
||||
"prefs.menu_list": "リスト",
|
||||
"prefs.menus": "メニュー",
|
||||
"prefs.minutes": "分",
|
||||
"prefs.minutes_suffix": " min",
|
||||
"prefs.mouse": "マウスとトラックパッド",
|
||||
@@ -1925,10 +1970,16 @@
|
||||
"prefs.origin_plane_names": "原点平面の名前",
|
||||
"prefs.origin_plane_names_note": "ブラウザー、ダイアログ、一覧に表示される名前です。{top} は XY、{front} は XZ、{right} は YZ です (Z が上)。コマンド、スクリプト、保存されたデザインでは XY、XZ、YZ のままです。",
|
||||
"prefs.overall_speed": "全体の速度",
|
||||
"prefs.parameter_list_rows": "パラメーター一覧の行数",
|
||||
"prefs.parameter_list_rows_tip": "空の値フィールドで下矢印を押すと、お気に入りを先頭にパラメーターをこの数まで一覧表示します。少なくすると大きな設計でも一覧が軽くなります。",
|
||||
"prefs.persistent_ribbon_pressed": "有効なリボンコマンドを強調表示",
|
||||
"prefs.persistent_ribbon_pressed_help": "効果が有効な間、リボンのコマンドを強調表示します。無効にすると、従来の中立的な表示とコマンドのヘルプを保持します。",
|
||||
"prefs.perspective_fov": "画角",
|
||||
"prefs.placement": "配置",
|
||||
"prefs.prefer_free_sketch_points": "スケッチ点が重なったときは移動可能な点を優先する",
|
||||
"prefs.preview_choices": "選択した設定のプレビュー",
|
||||
"prefs.print_face_highlight": "造形面の強調",
|
||||
"prefs.print_face_highlight_tip": "ビューでボディの造形面を色付けし輪郭を描く強さ。0 % で強調しません。",
|
||||
"prefs.profile_active": "使用中のプロファイル",
|
||||
"prefs.profile_applied": "プロファイルを適用しました。",
|
||||
"prefs.profile_changed": "プロファイルの保存後に設定が変更されています。「保存」でプロファイルに反映されます。",
|
||||
@@ -1950,6 +2001,8 @@
|
||||
"prefs.quality_fastest": "最速",
|
||||
"prefs.quality_slowest": "最も低速",
|
||||
"prefs.quality_smoother": "より滑らか",
|
||||
"prefs.quick_measure": "ビューの隅にクイック測定",
|
||||
"prefs.quick_measure_note": "選択したものの寸法をビューの右下、履歴の上に表示します: エッジの長さ(円形のものは Ø と R)、面の面積、点の位置、2 つの項目の距離と角度、複数のエッジや面の合計。クリックでコピーします。",
|
||||
"prefs.render_previous_geometry_budget": "レンダープレビューで以前のジオメトリ予算を使用",
|
||||
"prefs.render_previous_geometry_budget_help": "デバイスに基づく予算の代わりに、以前の固定ジオメトリ上限(64ビットデスクトップでは512 MiB、その他では128 MiB)に戻します。",
|
||||
"prefs.reports_saved": "{n} 件保存済み",
|
||||
@@ -1963,6 +2016,8 @@
|
||||
"prefs.search_empty": "一致する設定がありません",
|
||||
"prefs.select": "選択",
|
||||
"prefs.select_one_item": "項目を 1 つ選択",
|
||||
"prefs.select_other_hidden": "「他を選択」で非表示のジオメトリを選択",
|
||||
"prefs.select_other_hidden_help": "オン: ポインター付近の非表示のボディやスケッチ、遮られたエッジを含めます。オフ: ポインター直下の表示中のジオメトリだけを元のリストに表示します。",
|
||||
"prefs.selecting": "選択",
|
||||
"prefs.shift": "Shift",
|
||||
"prefs.shift_add_ctrl_toggle": "Shift は追加のみ、Ctrl / Cmd は選択を切り替え",
|
||||
@@ -1974,11 +2029,14 @@
|
||||
"prefs.sketch_align_points_note": "描画中、点が一つのスケッチ点と水平に、別の点と垂直に同時に揃い、点線のガイドが表示されます。クリックすると両方の整列が保持されます。オフ: 直前の点にのみ、一度に一方向だけ揃えます。",
|
||||
"prefs.sketch_conflict_colour": "競合するジオメトリを競合色で表示",
|
||||
"prefs.sketch_conflict_colour_note": "スケッチの拘束が競合している間、満たされない拘束のグリフや寸法だけでなく、その拘束が保持する曲線と点も競合色で描画します。オフ: 通常の色のままです。",
|
||||
"prefs.sketch_construction_lines": "拡張補助直線ツール",
|
||||
"prefs.sketch_exit_view": "スケッチ終了時のビュー",
|
||||
"prefs.sketch_exit_view.keep": "現在のビューを維持",
|
||||
"prefs.sketch_exit_view.previous": "前のビューに戻る",
|
||||
"prefs.sketch_exit_view.unless_rotated": "回転していなければ戻る",
|
||||
"prefs.sketch_exit_view_note": "「スケッチを終了」、キャンセル、Esc の 2 回押しの後にビューが移る先です。",
|
||||
"prefs.sketch_open_look_at": "スケッチを開いたときに正対させる",
|
||||
"prefs.sketch_open_look_at_note": "オフ: スケッチの作成や編集でビューを動かしません。必要なときは「正面視」でスケッチに正対します。",
|
||||
"prefs.sketch_recent_snap": "最近使った中心と中点へ自動スナップ",
|
||||
"prefs.sketch_recent_snap_note": "作図中、直前の2つの円の中心または直線辺の中点のガイドが交わる位置にカーソルをスナップします。オフ: ガイドは表示のみで、カーソルは移動しません。",
|
||||
"prefs.sketch_selection": "スケッチと選択",
|
||||
@@ -2038,13 +2096,21 @@
|
||||
"prefs.under_cursor": "カーソル下",
|
||||
"prefs.units_precision": "単位と精度",
|
||||
"prefs.updates": "更新",
|
||||
"prefs.value_boxes_in_view": "ビューでハンドルの横に値を表示",
|
||||
"prefs.value_boxes_in_view_tip": "コマンドの値(距離、半径など)をモデル上のハンドルの横のボックスに表示します。オフにすると、値はコマンドウィンドウに入力します。選択後はそのウィンドウにキーボード入力が入り、モデルを覆うものはありません。",
|
||||
"prefs.view": "ビューの動作",
|
||||
"prefs.view_confirm_corner": "ビューの隅に OK とキャンセル",
|
||||
"prefs.view_confirm_corner_note": "ダイアログまたはスケッチを開いている間、ビュー右上のチェックとバツが、ダイアログの OK とキャンセルと同じ操作を行うか、スケッチを終了またはキャンセルします。ビューナビゲーターはその下に移動します。",
|
||||
"prefs.viewport": "ビューポート",
|
||||
"prefs.viewport_analysis_status": "ビューポートの解析状態を表示",
|
||||
"prefs.viewport_analysis_status_help": "有効な解析をビューポートの上部に表示し、各項目から凡例を開きます。Esc で最新の解析を解除します。無効にすると隅の凡例と従来の Esc 動作に戻ります。",
|
||||
"prefs.viewport_background": "背景",
|
||||
"prefs.viewport_background_colour": "色",
|
||||
"prefs.viewport_background_theme": "テーマのグラデーション",
|
||||
"prefs.viewport_section_status": "ビューポートの断面状態を表示",
|
||||
"prefs.viewport_section_status_help": "ビューポートに断面設定を表示します。Escape または有効なリボンボタンで断面を解除します。無効にすると従来の断面操作を維持します。",
|
||||
"prefs.viewport_status_summary": "ビューポートの状態項目をまとめる",
|
||||
"prefs.viewport_status_summary_help": "3 個以上の状態項目を 1 つのサマリーバッジにまとめます。無効にすると、すべての項目を従来の行に表示します。",
|
||||
"prefs.wheel": "ホイール",
|
||||
"prefs.wheel_trackpad": "ホイールとトラックパッド",
|
||||
"prefs.workspace": "ワークスペース",
|
||||
@@ -2054,6 +2120,10 @@
|
||||
"properties.title": "プロパティ: {name}",
|
||||
"recovery.days_ago": "{count}日前",
|
||||
"recovery.detail": "{file} · {features}フィーチャー · 自動保存: {when}",
|
||||
"recovery.discard_all": "すべて破棄",
|
||||
"recovery.discard_all_detail": "この操作は元に戻せません。",
|
||||
"recovery.discard_all_title": "{count} 件の自動保存を破棄しますか?",
|
||||
"recovery.discard_all_title_one": "1 件の自動保存を破棄しますか?",
|
||||
"recovery.hours_ago": "{count}時間前",
|
||||
"recovery.just_now": "たった今",
|
||||
"recovery.min_ago": "{count}分前",
|
||||
@@ -2919,7 +2989,9 @@
|
||||
"render.output_ready": "レンダーの準備完了",
|
||||
"render.output_run_check": "チェックを実行",
|
||||
"render.output_size_limit": "{method}:各辺 {dimension} px、合計 {pixels} ピクセルまで。対応する最大サイズ:{width} × {height}。",
|
||||
"render.output_size_method_profile": "{width} × {height} をレンダリングするには、{method} と CPUリソースプロファイル「{profile}」を選ぶか、より小さいサイズを選んでください。",
|
||||
"render.output_size_ok": "サイズOK · {width} × {height} px",
|
||||
"render.output_size_profile": "{width} × {height} をレンダリングするには、CPUリソースプロファイル「{profile}」を選ぶか、より小さいサイズを選んでください。",
|
||||
"render.output_swap_orientation": "幅と高さを交換",
|
||||
"render.output_tools_inspector": "サイズと形式は出力インスペクターで編集してください。",
|
||||
"render.output_view_image": "画像を表示",
|
||||
@@ -3132,6 +3204,7 @@
|
||||
"render.size_custom": "カスタム…",
|
||||
"render.size_full_hd": "フルHD",
|
||||
"render.size_hd": "HD",
|
||||
"render.size_needs_profile": "{size}・「{profile}」が必要",
|
||||
"render.size_presets": "サイズプリセット",
|
||||
"render.size_presets_pending": "出力エディターとともに追加予定",
|
||||
"render.size_preview": "プレビュー",
|
||||
@@ -3419,6 +3492,24 @@
|
||||
"sheet.flange_type_contour": "輪郭",
|
||||
"sheet.flange_type_edge": "エッジ",
|
||||
"sheet.no_body": "板金ボディがありません。フランジ > Base で作成するか、板金に変換でプレートを板金に変換してください(広い平面を選択)。",
|
||||
"sheet.personal_rules.confirm_delete": "削除を確認",
|
||||
"sheet.personal_rules.delete": "削除",
|
||||
"sheet.personal_rules.delete_prompt": "この個人用ルールを削除しますか?デザインに既にあるルールは変更されません。",
|
||||
"sheet.personal_rules.full": "個人用ライブラリには既に64個のルールがあります。",
|
||||
"sheet.personal_rules.invalid_name": "改行や制御文字を含まない短い名前を入力してください。",
|
||||
"sheet.personal_rules.invalid_values": "ルールの値をすべて入力してください。各値はこのデザインで計算でき、板厚は正の値、K係数は0〜1である必要があります。",
|
||||
"sheet.personal_rules.missing": "選択した個人用ルールは利用できなくなりました。",
|
||||
"sheet.personal_rules.name": "ルール名",
|
||||
"sheet.personal_rules.name_exists": "同じ名前の個人用ルールが既にあります。別の名前を指定してください。",
|
||||
"sheet.personal_rules.name_hint": "一意の名前",
|
||||
"sheet.personal_rules.overwrite": "選択した個人用ルールの変更を保存",
|
||||
"sheet.personal_rules.rename": "名前を変更",
|
||||
"sheet.personal_rules.rule": "個人用ルール",
|
||||
"sheet.personal_rules.save": "新規保存",
|
||||
"sheet.personal_rules.scope": "すべてのデザインで使えるようにプロファイルに保存します。",
|
||||
"sheet.personal_rules.select": "ルールを選択",
|
||||
"sheet.personal_rules.title": "個人用ルール",
|
||||
"sheet.personal_rules.unsupported": "このルールには、このバージョンで使用できない値が含まれています。",
|
||||
"shortcut.json_error": "パラメータはJSON形式で指定してください: {error}",
|
||||
"shortcut.no_command": "コマンド`{id}`がありません。JSONの前のテキストはコマンドIDである必要があります",
|
||||
"shortcut.pin": "Sボックスの先頭にピン留め",
|
||||
@@ -3455,6 +3546,7 @@
|
||||
"sketch_text.align_right": "右",
|
||||
"sketch_text.bold": "太字",
|
||||
"sketch_text.builtin_font": "{family}(内蔵)",
|
||||
"sketch_text.formula": "= で始めるとパラメーターから文字列を組み立てられます: ='W' + number(Width)",
|
||||
"sketch_text.italic": "斜体",
|
||||
"sketch_text.loading_fonts": "インストール済みフォントを検索中…",
|
||||
"sketch_text.on_path": "文字はパスに沿います: 位置は曲線で決まります",
|
||||
@@ -3595,6 +3687,8 @@
|
||||
"ui.3d_mouse_section": "3Dマウス",
|
||||
"ui.3d_mouse_show_in_view": "3Dビューに3Dマウスを表示",
|
||||
"ui.3d_mouse_show_in_view_tip": "左下に3Dマウスの絵を表示し、操作に合わせて動かします。",
|
||||
"ui.3d_mouse_sketch_orbit": "3D マウスでスケッチを回転",
|
||||
"ui.3d_mouse_sketch_orbit_tip": "スケッチを開いて正面から見ているときも、マウスと同じように 3D マウスでビューを回転します。オフ: ビューを傾けるまで、スケッチのパンとズームのみ行います。",
|
||||
"ui.3d_mouse_skip": "このデバイスではできません",
|
||||
"ui.3d_mouse_skip_tip": "この動きを両方向とも飛ばし、キャリブレーションから外します。",
|
||||
"ui.3d_mouse_speed": "3D マウス速度",
|
||||
@@ -3623,6 +3717,8 @@
|
||||
"ui.3d_mouse_step_twist_counterclockwise": "キャップを反時計回りにひねって保持し、離してください。",
|
||||
"ui.3d_mouse_switch_cap": "センターに戻るタイプとしてキャリブレーション",
|
||||
"ui.3d_mouse_switch_trackball": "トラックボールとしてキャリブレーション",
|
||||
"ui.3d_mouse_use_direct": "直接ドライバーを使用",
|
||||
"ui.3d_mouse_use_direct_tip": "spacenavd を経由せず、3Dマウスを直接 (hidraw) 読み取ります。",
|
||||
"ui.3d_mouse_view_axis": "ビューの軸",
|
||||
"ui.3d_mouse_view_off": "オフ",
|
||||
"ui.3d_mouse_view_off_tip": "3D マウスのドライバーのプロファイルがこの軸をオフにしているため、この軸の動きではビューは動きません。ここで他の軸の動きを選ぶと 2 つが入れ替わり、その動きではビューが動かなくなります。",
|
||||
@@ -3756,6 +3852,8 @@
|
||||
"ui.agent_chat_remove": "待機から削除",
|
||||
"ui.agent_chat_remove_file": "このメッセージから外す",
|
||||
"ui.agent_chat_remove_imported": "このメッセージから外します。パーツはデザインに残ります (元に戻すで削除できます)",
|
||||
"ui.agent_chat_reply_cut_off": "応答が終わる前に長さの上限に達したため、不完全な可能性があります。続けるか、もっと短く答えるようエージェントに頼んでください。",
|
||||
"ui.agent_chat_reply_declined": "モデルはこのメッセージへの回答を断りました。",
|
||||
"ui.agent_chat_retry": "再試行",
|
||||
"ui.agent_chat_right": "右",
|
||||
"ui.agent_chat_save": "保存",
|
||||
@@ -3939,6 +4037,7 @@
|
||||
"ui.circle": "円",
|
||||
"ui.circular_pattern": "円形パターン",
|
||||
"ui.clear": "クリア",
|
||||
"ui.clear_print_face": "造形面を解除",
|
||||
"ui.clear_recent": "最近使った項目をクリア",
|
||||
"ui.clear_the_selection": "選択をクリア",
|
||||
"ui.click_selects_whole_bodies": "クリックでボディ全体を選択",
|
||||
@@ -3973,6 +4072,15 @@
|
||||
"ui.constant": "一定",
|
||||
"ui.constraints_conflict": "拘束が競合しています",
|
||||
"ui.construction": "コンストラクション",
|
||||
"ui.construction.angle_hint": "補助直線: 点をクリックし、角度を入力するか方向を選択",
|
||||
"ui.construction.bisector_hint": "二等分線: 交差する2直線を選び、必要な解を選択",
|
||||
"ui.construction.common_hint": "共通接線: 2つの円/円弧を選び、必要な解を選択",
|
||||
"ui.construction.contact_hint": "接線: 円/円弧または開いたスプライン/円錐曲線を選び、接点を選択 (自由曲線は端点のみ)",
|
||||
"ui.construction.external_hint": "接線: 円または円弧、外部点、必要な解の順に選択",
|
||||
"ui.construction.free_hint": "補助直線: 2点をクリック。右クリックで最初から",
|
||||
"ui.construction.point_hint": "補助直線: 通過点をクリック",
|
||||
"ui.construction.reference_hint": "補助直線: 直線、点の順に選択。右クリックで参照を変更",
|
||||
"ui.construction.ribbon_label": "補助線",
|
||||
"ui.context_menu": "コンテキスト メニュー",
|
||||
"ui.continuity_g0": "G0 折れ",
|
||||
"ui.continuity_g1": "G1 接線",
|
||||
@@ -4362,12 +4470,17 @@
|
||||
"ui.plugins_skipped": "{count} 個のフォルダを読めませんでした。",
|
||||
"ui.plugins_version": "{version}",
|
||||
"ui.point": "点",
|
||||
"ui.point_cloud_add_filter": "フィルターを追加…",
|
||||
"ui.point_cloud_auto_range": "範囲を自動設定",
|
||||
"ui.point_cloud_color_by": "色の付け方",
|
||||
"ui.point_cloud_file_colors": "ファイルの色",
|
||||
"ui.point_cloud_filter_by": "フィルター",
|
||||
"ui.point_cloud_height_high": "最高の高さ",
|
||||
"ui.point_cloud_height_low": "最低の高さ",
|
||||
"ui.point_cloud_legend_filter": "上の凡例で表示・非表示を切り替え",
|
||||
"ui.point_cloud_no_file_colors": "ファイルに色情報がありません",
|
||||
"ui.point_cloud_pause": "一時停止",
|
||||
"ui.point_cloud_play": "再生",
|
||||
"ui.point_cloud_single_color": "単色",
|
||||
"ui.point_cloud_size": "点のサイズ",
|
||||
"ui.point_cloud_units": "ファイルの単位",
|
||||
@@ -4376,6 +4489,11 @@
|
||||
"ui.polygon": "ポリゴン",
|
||||
"ui.preferences": "環境設定",
|
||||
"ui.press_a_key": "キーを押してください…",
|
||||
"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": "プロファイル",
|
||||
"ui.profile_plane": "プロファイル平面",
|
||||
"ui.profiles": "プロファイル",
|
||||
@@ -4439,6 +4557,7 @@
|
||||
"ui.revolve": "回転",
|
||||
"ui.ribbon_exit": "終了",
|
||||
"ui.ribbon_labels": "リボンのアイコンの下にテキストを表示",
|
||||
"ui.ribbon_menus_skip_pinned": "ピン留めしたコマンドをドロップダウンメニューに表示しない",
|
||||
"ui.ribbon_pin_footer": "ピン留めしたコマンドはリボンに表示されます。行にポインターを合わせて、ピン留めや解除ができます。",
|
||||
"ui.ribbon_workspace": "ワークスペース",
|
||||
"ui.right": "右側面",
|
||||
@@ -4457,6 +4576,7 @@
|
||||
"ui.selectOther": "その他を選択…",
|
||||
"ui.selectTangentChain": "接線チェーンを選択",
|
||||
"ui.select_all": "すべて選択",
|
||||
"ui.select_components_by_face": "コンポーネントを選択(面をクリックするとそのコンポーネントが選択されます)",
|
||||
"ui.selection": "選択",
|
||||
"ui.selection_modifiers": "選択修飾キー",
|
||||
"ui.selection_shift_add_only": "Shift+クリックで追加のみ(解除なし)",
|
||||
@@ -4498,6 +4618,11 @@
|
||||
"ui.sketch_intersect_selection_filter": "選択フィルター",
|
||||
"ui.sketch_modifiers": "スケッチ修飾キー",
|
||||
"ui.sketch_project_geometry": "ジオメトリ",
|
||||
"ui.sketch_report.all_constrained": "すべて完全に拘束されています。",
|
||||
"ui.sketch_report.dof": "自由度",
|
||||
"ui.sketch_report.loose": "完全には拘束されていません",
|
||||
"ui.sketch_report.status": "状態",
|
||||
"ui.sketch_report.title": "スケッチ レポート",
|
||||
"ui.sketch_scale": "スケッチ尺度",
|
||||
"ui.sketches": "スケッチ",
|
||||
"ui.slice": "スライス",
|
||||
@@ -4602,10 +4727,19 @@
|
||||
"ui.view_centre": "ビュー中心",
|
||||
"ui.viewport_status_clear": "{name}をオフにする",
|
||||
"ui.viewport_status_clear_hint": "{name}をオフにする · Esc",
|
||||
"ui.viewport_status_edges": "{count} エッジ",
|
||||
"ui.viewport_status_from": "方向 {direction}",
|
||||
"ui.viewport_status_key": "{name}の凡例を表示",
|
||||
"ui.viewport_status_minimum_radius": "最小半径",
|
||||
"ui.viewport_status_pairs": "{count} 組",
|
||||
"ui.viewport_status_section_hatch": "{setting} · ハッチング",
|
||||
"ui.viewport_status_section_plane": "現在の平面",
|
||||
"ui.viewport_status_section_tilted": "傾斜 · {offset}",
|
||||
"ui.viewport_status_stripes": "縞 {count} 本",
|
||||
"ui.viewport_status_summary_clear": "すべて解除",
|
||||
"ui.viewport_status_summary_heading": "このビュー内",
|
||||
"ui.viewport_status_summary_label": "{name} と他 {count} 項目",
|
||||
"ui.viewport_status_summary_more": "+{count}",
|
||||
"ui.viewport_theme_follow": "テーマに従う",
|
||||
"ui.visual_shaded_hidden_edges": "隠線付きシェーディング",
|
||||
"ui.visual_shaded_with_edges": "エッジ付きシェーディング",
|
||||
@@ -4642,7 +4776,9 @@
|
||||
"update.button": "アップデート",
|
||||
"update.button_version": "{version} にアップデート",
|
||||
"update.check_automatically": "アップデートを自動的に確認",
|
||||
"update.check_for_updates": "アップデートを確認",
|
||||
"update.check_now": "今すぐ確認",
|
||||
"update.check_unavailable": "このインストールではアップデートを確認できません。",
|
||||
"update.checking": "確認中…",
|
||||
"update.copy": "コピー",
|
||||
"update.dmg_opened": "ディスクイメージを検証して開きました: SolidCraft をアプリケーションフォルダにドラッグして古いものを置き換え、SolidCraft を再起動してください。",
|
||||
|
||||
@@ -711,6 +711,7 @@
|
||||
"cmd.appearance_pick_up": "모양 가져오기",
|
||||
"cmd.appearance_replace": "모양 바꾸기",
|
||||
"cmd.assembly_bom": "자재 명세서",
|
||||
"cmd.body.print_face": "출력 바닥면",
|
||||
"cmd.body_rename": "바디 이름 변경",
|
||||
"cmd.browser_group": "새 그룹",
|
||||
"cmd.browser_move": "브라우저 항목 이동",
|
||||
@@ -958,6 +959,16 @@
|
||||
"cmd.sheet_refold": "다시 접기",
|
||||
"cmd.sheet_rules": "판금 규칙 목록",
|
||||
"cmd.sheet_unfold": "펼치기",
|
||||
"cmd.sketch.construction_line": "보조 직선",
|
||||
"cmd.sketch.construction_line.angle": "각도 지정 보조 직선",
|
||||
"cmd.sketch.construction_line.bisector": "각의 이등분선",
|
||||
"cmd.sketch.construction_line.horizontal": "수평 보조 직선",
|
||||
"cmd.sketch.construction_line.parallel": "평행 보조 직선",
|
||||
"cmd.sketch.construction_line.perpendicular": "직교 보조 직선",
|
||||
"cmd.sketch.construction_line.tangent_at": "곡선 위 점에서의 접선",
|
||||
"cmd.sketch.construction_line.tangent_external": "외부 점을 지나는 접선",
|
||||
"cmd.sketch.construction_line.tangent_two": "공통 접선",
|
||||
"cmd.sketch.construction_line.vertical": "수직 보조 직선",
|
||||
"cmd.sketch_arc_center_point": "중심점 호",
|
||||
"cmd.sketch_arc_tangent": "접선 호",
|
||||
"cmd.sketch_arc_three_point": "3점 호",
|
||||
@@ -1199,6 +1210,16 @@
|
||||
"help.mit": "MIT 라이선스",
|
||||
"help.notice": "NOTICE",
|
||||
"help.pointcloud.insert": "PLY, XYZ 또는 PTS 스캔을 모델 옆에 포인트 클라우드로 삽입합니다. 측정 데이터를 참고하여 모델링할 때 사용합니다.",
|
||||
"help.sketch.construction_line": "두 점을 지나는 무한 보조 직선을 만듭니다. 스케치 프로파일을 나누지 않고 작도할 수 있습니다.",
|
||||
"help.sketch.construction_line.angle": "스케치 X축을 기준으로 지정한 각도의 무한 보조 직선을 만듭니다. 점을 선택한 뒤 각도를 입력하거나 방향을 선택합니다.",
|
||||
"help.sketch.construction_line.bisector": "교차하는 두 스케치 직선을 선택한 뒤 두 각의 이등분선 중 하나를 선택합니다. 결과는 원본 직선의 편집을 따릅니다.",
|
||||
"help.sketch.construction_line.horizontal": "한 점을 지나는 무한 수평 보조 직선을 만듭니다. 나중에 편집해도 수평 구속이 유지됩니다.",
|
||||
"help.sketch.construction_line.parallel": "스케치 직선을 선택한 뒤 평행한 무한 보조 직선을 배치합니다. 다른 참조를 선택하려면 마우스 오른쪽 버튼을 클릭합니다.",
|
||||
"help.sketch.construction_line.perpendicular": "스케치 직선을 선택한 뒤 직교하는 무한 보조 직선을 배치합니다. 다른 참조를 선택하려면 마우스 오른쪽 버튼을 클릭합니다.",
|
||||
"help.sketch.construction_line.tangent_at": "원이나 원호 위의 점, 또는 열린 스플라인이나 원뿔 곡선의 끝점에 접선을 만듭니다. 곡선을 선택한 뒤 접점을 선택합니다.",
|
||||
"help.sketch.construction_line.tangent_external": "원 또는 원호, 외부 점, 접선 해를 차례로 선택합니다. 미리보기에는 그려진 원호 위의 접점만 표시됩니다.",
|
||||
"help.sketch.construction_line.tangent_two": "두 원 또는 원호를 선택한 뒤 미리보기에서 공통 접선을 선택합니다. 접점은 그려진 곡선 위에 있어야 합니다.",
|
||||
"help.sketch.construction_line.vertical": "한 점을 지나는 무한 수직 보조 직선을 만듭니다. 나중에 편집해도 수직 구속이 유지됩니다.",
|
||||
"help.sketch.equation_curve": "범위에 걸쳐 수식으로 곡선을 그립니다: x(t)와 y(t), 또는 y = f(x). 수식에 매개변수를 쓸 수 있으며 곡선이 그 값을 따라갑니다. 사인파, 나선, 기어 이의 인벌류트에 사용합니다.",
|
||||
"help.solid.chamfer": "선택한 모서리를 지정한 거리만큼 평평하게 깎습니다. 날카로운 모서리를 다듬거나 핀 또는 나사가 쉽게 들어가도록 할 때 사용합니다.",
|
||||
"help.solid.extrude": "스케치 프로파일을 지정한 거리만큼 3D로 밀어내 재료를 더하거나 제거합니다. 평면 윤곽을 블록, 보스 또는 포켓으로 만들 때 사용합니다.",
|
||||
@@ -1505,6 +1526,8 @@
|
||||
"prefs.bottom_centre": "하단 가운데",
|
||||
"prefs.bottom_right": "오른쪽 아래",
|
||||
"prefs.browser": "브라우저",
|
||||
"prefs.browser_eye_left": "표시 아이콘을 접기 화살표 옆에 표시",
|
||||
"prefs.browser_eye_left_note": "브라우저 행의 표시 아이콘을 행 오른쪽 끝의 열이 아니라 이전처럼 접기 화살표 옆에 표시합니다.",
|
||||
"prefs.built_in": "기본 제공",
|
||||
"prefs.button_left": "왼쪽 드래그",
|
||||
"prefs.button_middle": "가운데 드래그",
|
||||
@@ -1548,6 +1571,8 @@
|
||||
"prefs.graphics": "그래픽",
|
||||
"prefs.graphics_note": "속도와 부드러움 중에서 선택합니다. 각 선택의 효과가 샘플에 표시됩니다.",
|
||||
"prefs.graphics_presets_note": "프리셋은 아래의 선택 항목을 한 번에 설정합니다.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "축소 시 구속 조건 기호 숨기기",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out_note": "스케치를 충분히 축소하면 구속 조건 기호가 가려지지 않도록 숨깁니다. 끄기: 확대/축소와 관계없이 고정된 화면 크기를 유지합니다.",
|
||||
"prefs.history": "이력",
|
||||
"prefs.history_origin_bodies": "원점과 바디를 맨 위에 표시",
|
||||
"prefs.history_origin_bodies_note": "왼쪽 또는 브라우저 아래의 히스토리가 활성 구성 요소의 바디 폴더와 원점으로 시작하며, 브라우저처럼 표시 아이콘과 메뉴가 있습니다. 아래쪽 히스토리는 바뀌지 않습니다.",
|
||||
@@ -1592,8 +1617,12 @@
|
||||
"prefs.origin_plane_names": "원점 평면 이름",
|
||||
"prefs.origin_plane_names_note": "브라우저, 대화 상자, 목록에 표시되는 이름입니다. {top}은 XY, {front}는 XZ, {right}는 YZ이며 Z가 위쪽입니다. 명령, 스크립트, 저장된 디자인은 XY, XZ, YZ를 유지합니다.",
|
||||
"prefs.overall_speed": "전체 속도",
|
||||
"prefs.persistent_ribbon_pressed": "활성 리본 명령 강조 표시",
|
||||
"prefs.persistent_ribbon_pressed_help": "효과가 켜져 있는 동안 리본 명령을 강조 표시합니다. 끄면 이전의 중립적인 모양과 명령 도움말을 유지합니다.",
|
||||
"prefs.perspective_fov": "시야각",
|
||||
"prefs.preview_choices": "선택 항목 미리보기",
|
||||
"prefs.print_face_highlight": "출력 바닥면 강조",
|
||||
"prefs.print_face_highlight_tip": "뷰에서 바디의 출력 바닥면을 색칠하고 윤곽을 그리는 강도입니다. 0 %이면 강조하지 않습니다.",
|
||||
"prefs.project_options_dialog": "Project 옵션 대화상자",
|
||||
"prefs.project_options_dialog_note": "켜기: 투영 전에 지오메트리 또는 바디와 투영 링크를 선택합니다. 끄기: 기존 직접 선택 작업 흐름을 유지합니다.",
|
||||
"prefs.quality_default": "기본값",
|
||||
@@ -1609,6 +1638,8 @@
|
||||
"prefs.search_empty": "일치하는 설정이 없습니다",
|
||||
"prefs.select": "선택",
|
||||
"prefs.select_one_item": "항목 하나 선택",
|
||||
"prefs.select_other_hidden": "다른 항목 선택으로 숨겨진 형상 선택",
|
||||
"prefs.select_other_hidden_help": "켜짐: 포인터 근처의 숨겨진 바디, 스케치 및 가려진 모서리를 포함합니다. 꺼짐: 포인터 바로 아래에 보이는 형상만 포함하는 기존 목록을 사용합니다.",
|
||||
"prefs.selecting": "선택",
|
||||
"prefs.shift": "Shift",
|
||||
"prefs.shift_add_ctrl_toggle": "Shift는 추가만, Ctrl / Cmd는 토글",
|
||||
@@ -1620,6 +1651,9 @@
|
||||
"prefs.sketch_align_points_note": "그리는 동안 점이 한 스케치 점과 수평으로, 다른 점과 수직으로 동시에 맞춰지고 점선 안내선이 표시됩니다. 클릭하면 두 정렬이 모두 유지됩니다. 끄기: 이전 점에만, 한 번에 한 축씩 맞춥니다.",
|
||||
"prefs.sketch_conflict_colour": "충돌하는 기하를 충돌 색상으로 표시",
|
||||
"prefs.sketch_conflict_colour_note": "스케치의 구속 조건이 충돌하는 동안 실패한 구속 조건의 글리프나 치수뿐 아니라 그 구속 조건이 잡고 있는 곡선과 점도 충돌 색상으로 그립니다. 끄기: 평소 색상을 유지합니다.",
|
||||
"prefs.sketch_construction_lines": "확장 보조 직선 도구",
|
||||
"prefs.sketch_open_look_at": "스케치를 열 때 정면으로 돌리기",
|
||||
"prefs.sketch_open_look_at_note": "끄기: 스케치를 만들거나 편집해도 뷰가 그대로입니다. 필요할 때 '바라보기'로 스케치를 정면으로 봅니다.",
|
||||
"prefs.sketch_recent_snap": "최근 중심과 중점에 자동 스냅",
|
||||
"prefs.sketch_recent_snap_note": "그리는 동안 최근에 사용한 두 원의 중심 또는 직선 모서리 중점의 안내선이 만나는 위치로 커서를 스냅합니다. 끄기: 안내선은 보기만 하고 커서는 움직이지 않습니다.",
|
||||
"prefs.sketch_selection": "스케치 및 선택",
|
||||
@@ -1670,10 +1704,18 @@
|
||||
"prefs.under_cursor": "커서 아래",
|
||||
"prefs.units_precision": "단위 및 정밀도",
|
||||
"prefs.updates": "업데이트",
|
||||
"prefs.value_boxes_in_view": "뷰에서 핸들 옆에 값 표시",
|
||||
"prefs.value_boxes_in_view_tip": "명령의 값(거리, 반경 등)을 모델 위 핸들 옆 상자에 표시합니다. 끄면 값은 명령 창에 입력하며, 선택 후 키보드 입력이 그 창으로 가고 모델을 가리는 것이 없습니다.",
|
||||
"prefs.view": "뷰 동작",
|
||||
"prefs.view_confirm_corner": "뷰 모서리에 확인 및 취소",
|
||||
"prefs.view_confirm_corner_note": "대화 상자나 스케치가 열려 있는 동안 뷰 오른쪽 위의 체크와 X 표시가 대화 상자의 확인과 취소와 같은 동작을 하거나 스케치를 마치고 취소합니다. 뷰 탐색기는 그 아래로 내려갑니다.",
|
||||
"prefs.viewport": "뷰포트",
|
||||
"prefs.viewport_analysis_status": "뷰포트 해석 상태 표시",
|
||||
"prefs.viewport_analysis_status_help": "활성 해석을 뷰포트 위에 표시하고 각 항목에서 범례를 엽니다. Esc로 최신 해석을 끕니다. 끄면 모서리 범례와 이전 Esc 동작으로 돌아갑니다.",
|
||||
"prefs.viewport_section_status": "뷰포트 단면 상태 표시",
|
||||
"prefs.viewport_section_status_help": "뷰포트에 단면 설정을 표시합니다. Escape 키와 활성 리본 버튼으로 단면을 끕니다. 비활성화하면 이전 단면 조작을 유지합니다.",
|
||||
"prefs.viewport_status_summary": "뷰포트 상태 항목 요약",
|
||||
"prefs.viewport_status_summary_help": "상태 항목이 3개 이상이면 하나의 요약 배지로 표시합니다. 비활성화하면 모든 항목을 기존 행에 표시합니다.",
|
||||
"prefs.wheel": "휠",
|
||||
"prefs.wheel_trackpad": "휠 및 트랙패드",
|
||||
"prefs.workspace": "작업 공간",
|
||||
@@ -1683,6 +1725,10 @@
|
||||
"properties.title": "속성: {name}",
|
||||
"recovery.days_ago": "{count}일 전",
|
||||
"recovery.detail": "{file} · 피처 {features}개 · {when} 자동 저장",
|
||||
"recovery.discard_all": "모두 버리기",
|
||||
"recovery.discard_all_detail": "이 작업은 되돌릴 수 없습니다.",
|
||||
"recovery.discard_all_title": "자동 저장 {count}개를 버리시겠습니까?",
|
||||
"recovery.discard_all_title_one": "자동 저장 1개를 버리시겠습니까?",
|
||||
"recovery.hours_ago": "{count}시간 전",
|
||||
"recovery.just_now": "방금",
|
||||
"recovery.min_ago": "{count}분 전",
|
||||
@@ -2234,7 +2280,9 @@
|
||||
"render.output_ready": "렌더링 준비 완료",
|
||||
"render.output_run_check": "검사 실행",
|
||||
"render.output_size_limit": "{method}: 한 변 최대 {dimension} px, 총 {pixels} 픽셀. 가능한 최대 크기: {width} × {height}.",
|
||||
"render.output_size_method_profile": "{width} × {height}을(를) 렌더링하려면 {method}와(과) {profile} CPU 리소스 프로필을 선택하거나 더 작은 크기를 선택하세요.",
|
||||
"render.output_size_ok": "크기 정상 · {width} × {height} px",
|
||||
"render.output_size_profile": "{width} × {height}을(를) 렌더링하려면 {profile} CPU 리소스 프로필이나 더 작은 크기를 선택하세요.",
|
||||
"render.output_tools_inspector": "출력 검사기에서 크기와 형식을 편집하세요.",
|
||||
"render.output_view_image": "이미지 보기",
|
||||
"render.paint_label": "칠하기",
|
||||
@@ -2366,6 +2414,7 @@
|
||||
"render.size_custom": "사용자 지정…",
|
||||
"render.size_full_hd": "풀 HD",
|
||||
"render.size_hd": "HD",
|
||||
"render.size_needs_profile": "{size} · {profile} 필요",
|
||||
"render.size_presets": "크기 프리셋",
|
||||
"render.size_presets_pending": "출력 편집기와 함께 제공 예정",
|
||||
"render.size_preview": "미리보기",
|
||||
@@ -2587,6 +2636,24 @@
|
||||
"sheet.flange_type_base": "베이스",
|
||||
"sheet.flange_type_contour": "윤곽",
|
||||
"sheet.flange_type_edge": "모서리",
|
||||
"sheet.personal_rules.confirm_delete": "삭제 확인",
|
||||
"sheet.personal_rules.delete": "삭제",
|
||||
"sheet.personal_rules.delete_prompt": "이 개인 규칙을 삭제할까요? 설계에 이미 있는 규칙은 바뀌지 않습니다.",
|
||||
"sheet.personal_rules.full": "개인 라이브러리에 이미 규칙이 64개 있습니다.",
|
||||
"sheet.personal_rules.invalid_name": "줄 바꿈이나 제어 문자가 없는 짧은 이름을 입력하세요.",
|
||||
"sheet.personal_rules.invalid_values": "규칙 값을 모두 입력하세요. 각 값은 이 설계에서 계산할 수 있어야 하며, 두께는 양수, K 계수는 0에서 1 사이여야 합니다.",
|
||||
"sheet.personal_rules.missing": "선택한 개인 규칙을 더 이상 사용할 수 없습니다.",
|
||||
"sheet.personal_rules.name": "규칙 이름",
|
||||
"sheet.personal_rules.name_exists": "같은 이름의 개인 규칙이 이미 있습니다. 다른 이름을 사용하세요.",
|
||||
"sheet.personal_rules.name_hint": "고유한 이름",
|
||||
"sheet.personal_rules.overwrite": "선택한 개인 규칙에 변경 사항 저장",
|
||||
"sheet.personal_rules.rename": "이름 바꾸기",
|
||||
"sheet.personal_rules.rule": "개인 규칙",
|
||||
"sheet.personal_rules.save": "새로 저장",
|
||||
"sheet.personal_rules.scope": "모든 설계에서 쓸 수 있도록 프로필에 저장됩니다.",
|
||||
"sheet.personal_rules.select": "규칙 선택",
|
||||
"sheet.personal_rules.title": "개인 규칙",
|
||||
"sheet.personal_rules.unsupported": "이 규칙에는 이 버전에서 사용할 수 없는 값이 있습니다.",
|
||||
"shortcut.json_error": "매개변수는 JSON 형식이어야 합니다: {error}",
|
||||
"shortcut.no_command": "`{id}` 명령이 없습니다. JSON 앞의 텍스트는 명령 ID여야 합니다",
|
||||
"shortcut.pin": "S 상자 맨 위에 고정",
|
||||
@@ -2771,6 +2838,8 @@
|
||||
"ui.3d_mouse_step_twist_counterclockwise": "캡을 시계 반대 방향으로 돌려 유지한 다음 놓으세요.",
|
||||
"ui.3d_mouse_switch_cap": "중앙 복귀형으로 보정",
|
||||
"ui.3d_mouse_switch_trackball": "트랙볼로 보정",
|
||||
"ui.3d_mouse_use_direct": "직접 드라이버 사용",
|
||||
"ui.3d_mouse_use_direct_tip": "spacenavd를 거치지 않고 3D 마우스를 직접(hidraw) 읽습니다.",
|
||||
"ui.3d_mouse_view_axis": "뷰 축",
|
||||
"ui.3d_mouse_view_off": "끔",
|
||||
"ui.3d_mouse_view_off_tip": "3D 마우스 드라이버 프로필이 이 축을 끄므로, 이 축의 동작으로는 뷰가 움직이지 않습니다. 여기서 다른 축의 동작을 고르면 둘이 바뀌고, 그 동작은 더 이상 뷰를 움직이지 않습니다.",
|
||||
@@ -2886,6 +2955,8 @@
|
||||
"ui.agent_chat_remove": "대기열에서 제거",
|
||||
"ui.agent_chat_remove_file": "이 메시지에서 빼기",
|
||||
"ui.agent_chat_remove_imported": "이 메시지에서 빼기. 부품은 디자인에 남습니다 (실행 취소하면 제거됨)",
|
||||
"ui.agent_chat_reply_cut_off": "응답이 끝나기 전에 길이 한도에 도달해 불완전할 수 있습니다. 에이전트에게 계속하거나 더 짧게 답하라고 요청하세요.",
|
||||
"ui.agent_chat_reply_declined": "모델이 이 메시지에 대한 답변을 거절했습니다.",
|
||||
"ui.agent_chat_right": "오른쪽",
|
||||
"ui.agent_chat_save": "저장",
|
||||
"ui.agent_chat_send": "보내기",
|
||||
@@ -3034,6 +3105,7 @@
|
||||
"ui.circle": "원",
|
||||
"ui.circular_pattern": "원형 패턴",
|
||||
"ui.clear": "지우기",
|
||||
"ui.clear_print_face": "출력 바닥면 해제",
|
||||
"ui.clear_recent": "최근 항목 지우기",
|
||||
"ui.clear_the_selection": "선택 해제",
|
||||
"ui.click_selects_whole_bodies": "클릭하면 바디 전체 선택",
|
||||
@@ -3068,6 +3140,15 @@
|
||||
"ui.constant": "상수",
|
||||
"ui.constraints_conflict": "구속 조건 충돌",
|
||||
"ui.construction": "구성선",
|
||||
"ui.construction.angle_hint": "보조 직선: 점 클릭 후 각도 입력 또는 방향 선택",
|
||||
"ui.construction.bisector_hint": "이등분선: 교차하는 두 직선 선택 후 원하는 해 선택",
|
||||
"ui.construction.common_hint": "공통 접선: 두 원/원호 선택 후 원하는 해 선택",
|
||||
"ui.construction.contact_hint": "접선: 원/원호 또는 열린 스플라인/원뿔 곡선 선택 후 접점 선택 (자유 곡선은 끝점만)",
|
||||
"ui.construction.external_hint": "접선: 원 또는 원호, 외부 점, 원하는 해를 차례로 선택",
|
||||
"ui.construction.free_hint": "보조 직선: 두 점 클릭; 마우스 오른쪽 버튼으로 다시 시작",
|
||||
"ui.construction.point_hint": "보조 직선: 통과할 점 클릭",
|
||||
"ui.construction.reference_hint": "보조 직선: 직선 선택 후 점 선택; 마우스 오른쪽 버튼으로 참조 변경",
|
||||
"ui.construction.ribbon_label": "보조선",
|
||||
"ui.context_menu": "컨텍스트 메뉴",
|
||||
"ui.continuity_g0": "G0 꺾임",
|
||||
"ui.continuity_g1": "G1 접선",
|
||||
@@ -3439,6 +3520,11 @@
|
||||
"ui.polygon": "다각형",
|
||||
"ui.preferences": "환경 설정",
|
||||
"ui.press_a_key": "키를 누르세요…",
|
||||
"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": "프로파일",
|
||||
"ui.profile_plane": "프로파일 평면",
|
||||
"ui.profiles": "프로파일",
|
||||
@@ -3515,6 +3601,7 @@
|
||||
"ui.selectOther": "기타 선택…",
|
||||
"ui.selectTangentChain": "접선 체인 선택",
|
||||
"ui.select_all": "모두 선택",
|
||||
"ui.select_components_by_face": "구성요소 선택(면을 클릭하면 해당 구성요소 선택)",
|
||||
"ui.selection": "선택",
|
||||
"ui.selection_modifiers": "선택 보조 키",
|
||||
"ui.selection_shift_add_only": "Shift+클릭은 추가만 함",
|
||||
@@ -3643,10 +3730,19 @@
|
||||
"ui.view_centre": "뷰 중심",
|
||||
"ui.viewport_status_clear": "{name} 끄기",
|
||||
"ui.viewport_status_clear_hint": "{name} 끄기 · Esc",
|
||||
"ui.viewport_status_edges": "모서리 {count}개",
|
||||
"ui.viewport_status_from": "{direction} 방향에서",
|
||||
"ui.viewport_status_key": "{name} 범례 표시",
|
||||
"ui.viewport_status_minimum_radius": "최소 반지름",
|
||||
"ui.viewport_status_pairs": "{count}쌍",
|
||||
"ui.viewport_status_section_hatch": "{setting} · 해칭",
|
||||
"ui.viewport_status_section_plane": "현재 평면",
|
||||
"ui.viewport_status_section_tilted": "기울어짐 · {offset}",
|
||||
"ui.viewport_status_stripes": "줄무늬 {count}개",
|
||||
"ui.viewport_status_summary_clear": "모두 끄기",
|
||||
"ui.viewport_status_summary_heading": "이 뷰에서",
|
||||
"ui.viewport_status_summary_label": "{name} 외 {count}개 항목",
|
||||
"ui.viewport_status_summary_more": "+{count}",
|
||||
"ui.viewport_theme_follow": "테마 따르기",
|
||||
"ui.visual_shaded_hidden_edges": "음영 처리 및 숨겨진 모서리",
|
||||
"ui.visual_shaded_with_edges": "음영 처리 및 모서리",
|
||||
@@ -3678,7 +3774,9 @@
|
||||
"update.button": "업데이트",
|
||||
"update.button_version": "{version}(으)로 업데이트",
|
||||
"update.check_automatically": "업데이트 자동 확인",
|
||||
"update.check_for_updates": "업데이트 확인",
|
||||
"update.check_now": "지금 확인",
|
||||
"update.check_unavailable": "이 설치에서는 업데이트를 확인할 수 없습니다.",
|
||||
"update.checking": "확인 중…",
|
||||
"update.copy": "복사",
|
||||
"update.dmg_opened": "디스크 이미지를 확인하고 열었습니다: SolidCraft를 응용 프로그램 폴더로 끌어 기존 버전을 바꾼 뒤 SolidCraft를 다시 시작하세요.",
|
||||
|
||||
@@ -606,6 +606,7 @@
|
||||
"cmd.appearance_library": "Biblioteca de aparências",
|
||||
"cmd.appearance_list": "Listar aparências",
|
||||
"cmd.assembly_bom": "Lista de materiais",
|
||||
"cmd.body.print_face": "Face de impressão",
|
||||
"cmd.body_rename": "Renomear corpo",
|
||||
"cmd.browser_group": "Novo grupo",
|
||||
"cmd.browser_move": "Mover itens do navegador",
|
||||
@@ -815,6 +816,16 @@
|
||||
"cmd.sheet_refold": "Redobrar",
|
||||
"cmd.sheet_rules": "Listar regras de chapa metálica",
|
||||
"cmd.sheet_unfold": "Desdobrar",
|
||||
"cmd.sketch.construction_line": "Reta de construção",
|
||||
"cmd.sketch.construction_line.angle": "Reta de construção em ângulo",
|
||||
"cmd.sketch.construction_line.bisector": "Bissetriz",
|
||||
"cmd.sketch.construction_line.horizontal": "Reta de construção horizontal",
|
||||
"cmd.sketch.construction_line.parallel": "Reta de construção paralela",
|
||||
"cmd.sketch.construction_line.perpendicular": "Reta de construção perpendicular",
|
||||
"cmd.sketch.construction_line.tangent_at": "Tangente em um ponto da curva",
|
||||
"cmd.sketch.construction_line.tangent_external": "Tangente por um ponto externo",
|
||||
"cmd.sketch.construction_line.tangent_two": "Tangente comum",
|
||||
"cmd.sketch.construction_line.vertical": "Reta de construção vertical",
|
||||
"cmd.sketch_arc_center_point": "Arco por ponto central",
|
||||
"cmd.sketch_arc_tangent": "Arco tangente",
|
||||
"cmd.sketch_arc_three_point": "Arco por 3 pontos",
|
||||
@@ -1282,6 +1293,8 @@
|
||||
"prefs.orientation": "Orientação",
|
||||
"prefs.overall_speed": "Velocidade geral",
|
||||
"prefs.preview_choices": "Prévia das suas escolhas",
|
||||
"prefs.print_face_highlight": "Destaque da face de impressão",
|
||||
"prefs.print_face_highlight_tip": "Intensidade da cor e do contorno da face de impressão de um corpo na vista. 0 % não mostra destaque.",
|
||||
"prefs.quality_default": "padrão",
|
||||
"prefs.quality_fastest": "mais rápido",
|
||||
"prefs.quality_slowest": "mais lento",
|
||||
@@ -1561,6 +1574,8 @@
|
||||
"ui.3d_mouse_step_twist_counterclockwise": "Gire a tampa no sentido anti-horário, segure e solte.",
|
||||
"ui.3d_mouse_switch_cap": "Calibrar como retorno ao centro",
|
||||
"ui.3d_mouse_switch_trackball": "Calibrar como trackball",
|
||||
"ui.3d_mouse_use_direct": "Usar driver direto",
|
||||
"ui.3d_mouse_use_direct_tip": "Ler o mouse 3D diretamente (hidraw) em vez de pelo spacenavd.",
|
||||
"ui.3d_mouse_view_axis": "Eixo da vista",
|
||||
"ui.3d_mouse_view_off": "Desligado",
|
||||
"ui.3d_mouse_view_off_tip": "O perfil do driver do mouse 3D desliga este eixo, então o movimento dele não move a vista. Escolher aqui o movimento de outro eixo troca os dois, e esse movimento deixa de mover a vista.",
|
||||
@@ -1830,6 +1845,7 @@
|
||||
"ui.circle": "Círculo",
|
||||
"ui.circular_pattern": "Padrão circular",
|
||||
"ui.clear": "Limpar",
|
||||
"ui.clear_print_face": "Limpar face de impressão",
|
||||
"ui.clear_recent": "Limpar recentes",
|
||||
"ui.clear_the_selection": "Limpar a seleção",
|
||||
"ui.click_selects_whole_bodies": "O clique seleciona corpos inteiros",
|
||||
@@ -1864,6 +1880,7 @@
|
||||
"ui.constant": "Constante",
|
||||
"ui.constraints_conflict": "Restrições em conflito",
|
||||
"ui.construction": "Construção",
|
||||
"ui.construction.ribbon_label": "Linhas aux.",
|
||||
"ui.context_menu": "Menu de contexto",
|
||||
"ui.continuity_g0": "G0 vinco",
|
||||
"ui.continuity_g1": "G1 tangente",
|
||||
@@ -2187,6 +2204,11 @@
|
||||
"ui.polygon": "Polígono",
|
||||
"ui.preferences": "Preferências",
|
||||
"ui.press_a_key": "pressione uma tecla…",
|
||||
"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",
|
||||
"ui.profile_plane": "Plano do perfil",
|
||||
"ui.profiles": "Perfis",
|
||||
@@ -2377,7 +2399,9 @@
|
||||
"update.button": "Atualizar",
|
||||
"update.button_version": "Atualizar para {version}",
|
||||
"update.check_automatically": "Verificar atualizações automaticamente",
|
||||
"update.check_for_updates": "Verificar atualizações",
|
||||
"update.check_now": "Verificar agora",
|
||||
"update.check_unavailable": "A verificação de atualizações não está disponível para esta instalação.",
|
||||
"update.checking": "Verificando…",
|
||||
"update.copy": "Copiar",
|
||||
"update.dmg_opened": "A imagem de disco está aberta e verificada: arraste o SolidCraft para Aplicativos, substituindo a cópia antiga, e reinicie o SolidCraft.",
|
||||
|
||||
@@ -206,6 +206,7 @@
|
||||
"Edges": "Ивице",
|
||||
"Edit": "Измени",
|
||||
"Edit Profile Sketch": "Измени скицу профила",
|
||||
"Edit shortcuts": "Измени пречице",
|
||||
"Enable rule": "Омогући правило",
|
||||
"Enabled": "Омогућено",
|
||||
"End Radius": "Крајњи полупречник",
|
||||
@@ -468,6 +469,7 @@
|
||||
"Radius": "Полупречник",
|
||||
"Radius Fit": "Уклапање полупречника",
|
||||
"Ratio": "Однос",
|
||||
"Read-only list: shortcuts can't be changed here.": "Списак само за читање: пречице се овде не могу мењати.",
|
||||
"Reattach selected face": "Поново причврсти изабрано лице",
|
||||
"Recover": "Опорави",
|
||||
"Recover unsaved design?": "Опоравити несачувани дизајн?",
|
||||
@@ -652,6 +654,7 @@
|
||||
"Third point": "Трећа тачка",
|
||||
"This child is shared. Deleting it removes it from every copy of its parent.": "Овај подређени елемент је дељен. Брисањем се уклања из сваке копије његовог родитеља.",
|
||||
"This child is shared. Position changes apply to every copy of its parent.": "Овај подређени елемент је дељен. Промене положаја примењују се на сваку копију његовог родитеља.",
|
||||
"This component is inside a subassembly placed more than once: aligning it moves it in every placement.": "Ова компонента је унутар подсклопа који је постављен више пута: њено поравнање је помера у сваком постављању.",
|
||||
"This face cannot provide a durable attachment. Choose another face or explicitly use a fixed plane.": "Ово лице не може дати трајно причвршћење. Изаберите друго лице или изричито користите фиксну раван.",
|
||||
"This image is not attached to GitHub yet. Copy or save it, then attach it on GitHub.": "Ова слика још није приложена на GitHub-у. Копирајте је или сачувајте, а затим је приложите на GitHub-у.",
|
||||
"Thread depth": "Дубина навоја",
|
||||
@@ -772,6 +775,8 @@
|
||||
"at the face": "на лицу",
|
||||
"bom.group_children": "Подделови у реду свог дела",
|
||||
"browser.body_name": "Тело{number}",
|
||||
"browser.calibrate_click_a": "Кликните слику да зададете тачку A или упишите њене координате.",
|
||||
"browser.calibrate_click_b": "Кликните слику да зададете тачку B или упишите њене координате.",
|
||||
"browser.calibrate_title": "Калибриши: {name}",
|
||||
"browser.capture_many": "Померене компоненте без забележеног положаја: {count}.",
|
||||
"browser.capture_note": "Забележи задржава нови положај; Врати га враћа назад.",
|
||||
@@ -793,6 +798,11 @@
|
||||
"cam.roller": "Ваљак",
|
||||
"cam.roller_pick": "Ваљак (необавезно)",
|
||||
"cam.tip_pick": "Врх (необавезно)",
|
||||
"camera.shot_pitch": "Нагиб",
|
||||
"camera.shot_roll": "Ваљање",
|
||||
"camera.shot_yaw": "Скретање",
|
||||
"canvas.decal_place_hint": "Кликните лице да поставите налепницу",
|
||||
"canvas.place_hint": "Кликните раван или лице да поставите платно",
|
||||
"choose a profile": "изаберите профил",
|
||||
"choose the cam's and the follower's joints": "изаберите спојеве брега и пратиоца",
|
||||
"choose the joints to couple": "изаберите спојеве за спрезање",
|
||||
@@ -800,12 +810,16 @@
|
||||
"click sketch curves": "кликните на криве скице",
|
||||
"cmd.appearance_assign": "Изглед",
|
||||
"cmd.appearance_chain": "Ланац изгледа",
|
||||
"cmd.appearance_copy": "Копирај изглед",
|
||||
"cmd.appearance_favorite": "Омиљени изглед",
|
||||
"cmd.appearance_library": "Библиотека изгледа",
|
||||
"cmd.appearance_list": "Списак изгледа",
|
||||
"cmd.appearance_paint": "Обоји изгледом",
|
||||
"cmd.appearance_paste": "Налепи изглед",
|
||||
"cmd.appearance_pick_up": "Преузми изглед",
|
||||
"cmd.appearance_replace": "Замени изглед",
|
||||
"cmd.assembly_bom": "Саставница",
|
||||
"cmd.body.print_face": "Површина за штампу",
|
||||
"cmd.body_rename": "Преименуј тело",
|
||||
"cmd.browser_collapse_all": "Скупи све",
|
||||
"cmd.browser_expand_all": "Прошири све",
|
||||
@@ -813,6 +827,7 @@
|
||||
"cmd.browser_move": "Премести ставке стабла модела",
|
||||
"cmd.browser_order": "Поређај ставке стабла модела",
|
||||
"cmd.browser_rename_group": "Преименуј групу",
|
||||
"cmd.browser_reparent": "Премести у компоненту",
|
||||
"cmd.browser_ungroup": "Разгрупиши",
|
||||
"cmd.browser_visibility": "Прикажи/сакриј",
|
||||
"cmd.canvas_calibrate": "Калибриши платно",
|
||||
@@ -839,7 +854,9 @@
|
||||
"cmd.component_metadata": "Својства дела",
|
||||
"cmd.component_metadata_set": "Промени својства дела",
|
||||
"cmd.component_move_bodies": "Премести тела у компоненту",
|
||||
"cmd.component_move_features": "Премести операције у компоненту",
|
||||
"cmd.component_move_sketches": "Премести скице у компоненту",
|
||||
"cmd.component_paste_from_design": "Налепи из другог дизајна",
|
||||
"cmd.component_paste_new": "Налепи као ново",
|
||||
"cmd.component_properties": "Физичка својства компоненте",
|
||||
"cmd.component_relink": "Поново повежи извор",
|
||||
@@ -976,6 +993,7 @@
|
||||
"cmd.motion_trace": "Траг кретања",
|
||||
"cmd.motion_trace_edit": "Измени траг кретања",
|
||||
"cmd.motion_trace_keep": "Задржи траг кретања",
|
||||
"cmd.occurrence_align": "Поравнај примерак",
|
||||
"cmd.occurrence_copy": "Налепи (примерак)",
|
||||
"cmd.occurrence_drag": "Превуци примерак",
|
||||
"cmd.occurrence_ground": "Учврсти",
|
||||
@@ -1033,11 +1051,17 @@
|
||||
"cmd.render_library_surface_unbind": "Уклони везу површине",
|
||||
"cmd.render_library_targets": "Пронађи циљеве библиотеке рендеровања",
|
||||
"cmd.render_lights_add": "Додај светло",
|
||||
"cmd.render_lights_duplicate": "Дуплицирај светло рендеровања",
|
||||
"cmd.render_lights_list": "Списак светала рендеровања",
|
||||
"cmd.render_lights_mute": "Искључи светло",
|
||||
"cmd.render_lights_remove": "Уклони светло рендеровања",
|
||||
"cmd.render_lights_set": "Подеси светло рендеровања",
|
||||
"cmd.render_lights_solo": "Само ово светло",
|
||||
"cmd.render_optics_bind": "Сачувај оптичку везу",
|
||||
"cmd.render_optics_unbind": "Уклони оптичку везу",
|
||||
"cmd.render_output_size": "Подеси величину излаза рендеровања",
|
||||
"cmd.render_preflight": "Провери захтев за рендеровање",
|
||||
"cmd.render_preflight_copy": "Копирај извештај провере рендеровања",
|
||||
"cmd.render_preset_apply": "Примени изворни студио",
|
||||
"cmd.render_preset_resolve": "Прегледај изворни студио",
|
||||
"cmd.render_quality_prepare": "Прегледај фиксни буџет узорковања",
|
||||
@@ -1060,6 +1084,13 @@
|
||||
"cmd.render_start": "Рендеруј слику",
|
||||
"cmd.render_status": "Напредак рендеровања",
|
||||
"cmd.select_clear": "Поништи избор",
|
||||
"cmd.settings_profile_apply": "Примени профил подешавања",
|
||||
"cmd.settings_profile_delete": "Избриши профил подешавања",
|
||||
"cmd.settings_profile_export": "Извези профил подешавања",
|
||||
"cmd.settings_profile_import": "Увези профил подешавања",
|
||||
"cmd.settings_profile_list": "Профили подешавања",
|
||||
"cmd.settings_profile_rename": "Преименуј профил подешавања",
|
||||
"cmd.settings_profile_save": "Сачувај профил подешавања",
|
||||
"cmd.sheet_convert": "Претвори у лим",
|
||||
"cmd.sheet_export_bend_table": "Извези табелу савијања",
|
||||
"cmd.sheet_export_dxf": "Извези развијени облик као DXF",
|
||||
@@ -1073,6 +1104,16 @@
|
||||
"cmd.sheet_refold": "Поново пресавиј",
|
||||
"cmd.sheet_rules": "Списак правила за лим",
|
||||
"cmd.sheet_unfold": "Развиј",
|
||||
"cmd.sketch.construction_line": "Помоћна права",
|
||||
"cmd.sketch.construction_line.angle": "Помоћна права под углом",
|
||||
"cmd.sketch.construction_line.bisector": "Симетрала угла",
|
||||
"cmd.sketch.construction_line.horizontal": "Хоризонтална помоћна права",
|
||||
"cmd.sketch.construction_line.parallel": "Паралелна помоћна права",
|
||||
"cmd.sketch.construction_line.perpendicular": "Нормална помоћна права",
|
||||
"cmd.sketch.construction_line.tangent_at": "Тангента у тачки криве",
|
||||
"cmd.sketch.construction_line.tangent_external": "Тангента кроз спољашњу тачку",
|
||||
"cmd.sketch.construction_line.tangent_two": "Заједничка тангента",
|
||||
"cmd.sketch.construction_line.vertical": "Вертикална помоћна права",
|
||||
"cmd.sketch_arc3d": "3D лук",
|
||||
"cmd.sketch_arc_center_point": "Лук из центра",
|
||||
"cmd.sketch_arc_tangent": "Тангентни лук",
|
||||
@@ -1105,6 +1146,8 @@
|
||||
"cmd.sketch_drawing_bounds": "Величина цртежа",
|
||||
"cmd.sketch_edit": "Измени скицу",
|
||||
"cmd.sketch_edit_text": "Измени текст",
|
||||
"cmd.sketch_equation_curve": "Крива по једначини",
|
||||
"cmd.sketch_equation_curve_edit": "Измени криву по једначини",
|
||||
"cmd.sketch_export_dxf": "Сачувај као DXF",
|
||||
"cmd.sketch_finish_auto_constrain": "Заврши са аутоматским ограничењима",
|
||||
"cmd.sketch_fit_curves_to_section": "Уклопи криве у пресек мреже",
|
||||
@@ -1201,6 +1244,7 @@
|
||||
"cmd.view.render_browser": "Стабло рендеровања",
|
||||
"cmd.view.render_dock": "Премести панел рендеровања",
|
||||
"cmd.view.render_focus": "Фокусни приказ рендеровања",
|
||||
"cmd.view.status_items": "Ставке статуса у прозору приказа",
|
||||
"cmd.view_ambient_occlusion": "Амбијентална оклузија",
|
||||
"cmd.view_angle_readout": "Углови приказа",
|
||||
"cmd.view_apply": "Врати именовани приказ",
|
||||
@@ -1222,6 +1266,7 @@
|
||||
"cmd.view_normal_to": "Нормално на избор",
|
||||
"cmd.view_previous_tab": "Претходна картица",
|
||||
"cmd.view_rename": "Преименуј именовани приказ",
|
||||
"cmd.view_render_frame": "Водичи кадра рендеровања",
|
||||
"cmd.view_right": "Поглед здесна",
|
||||
"cmd.view_save": "Нови именовани приказ",
|
||||
"cmd.view_shots_strip": "Трака кадрова",
|
||||
@@ -1232,10 +1277,14 @@
|
||||
"color.copy": "Кликните да копирате вредности боје",
|
||||
"color.edit": "Кликните да измените боју",
|
||||
"color.gamma": "Приказ вредности боје у гама простору 0-255",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "Унесите #rrggbb или #rrggbbaa; шест цифара чува непрозирност",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
"color.hue": "Нијанса",
|
||||
"color.linear": "Приказ вредности боје у линеарном простору 0-1",
|
||||
"color.saturation_value": "Засићеност и вредност",
|
||||
"color.selected": "Изабрана боја",
|
||||
"config.column_filter_empty": "Нема одговарајућих колона",
|
||||
"contact.components_count": "Компоненте: {count}",
|
||||
"contact.enabled_label": "омогућено",
|
||||
"coupling.gear_1": "Зупчаник 1",
|
||||
@@ -1350,8 +1399,20 @@
|
||||
"help.material.assign": "Одређује од чега су изабрана тела направљена, нпр. челик, алуминијум или ABS. Користите га да маса и тежина одговарају стварном делу.",
|
||||
"help.mit": "MIT лиценца",
|
||||
"help.notice": "NOTICE",
|
||||
"help.occurrence.move": "Помера део склопа само дуж слобода које дозвољавају његови спојеви. Изаберите лице дела, а затим превуците доступну стрелицу или лук.",
|
||||
"help.parameters.change": "Отвара табелу именованих вредности и израза који управљају котама дизајна. Користите је да промените меру на једном месту, а све операције које је користе је прате.",
|
||||
"help.pointcloud.insert": "Уметните PLY, XYZ или PTS скенирање као облак тачака поред модела. Користите га као референцу при изради модела на основу измерених података.",
|
||||
"help.sketch.construction_line": "Направите бесконачну помоћну праву кроз две тачке. Користите је да распоредите скицу без раздвајања профила.",
|
||||
"help.sketch.construction_line.angle": "Направите бесконачну помоћну праву под углом од осе X скице. Изаберите тачку и упишите угао или изаберите смер.",
|
||||
"help.sketch.construction_line.bisector": "Изаберите две праве линије скице које се секу, па једну од две симетрале угла. Резултат прати изворне линије.",
|
||||
"help.sketch.construction_line.horizontal": "Направите бесконачну хоризонталну помоћну праву кроз тачку. Њено ограничење хоризонталности остаје на снази при каснијим изменама.",
|
||||
"help.sketch.construction_line.parallel": "Изаберите праву линију скице, па поставите паралелну бесконачну помоћну праву. Десним кликом изаберите другу референцу.",
|
||||
"help.sketch.construction_line.perpendicular": "Изаберите праву линију скице, па поставите нормалну бесконачну помоћну праву. Десним кликом изаберите другу референцу.",
|
||||
"help.sketch.construction_line.tangent_at": "Направите тангенту у тачки на кругу или кружном луку, или у крајњој тачки отвореног сплајна или конике. Изаберите криву, па тачку додира.",
|
||||
"help.sketch.construction_line.tangent_external": "Изаберите круг или кружни лук, па спољашњу тачку и решење тангенте. Прегледи приказују само тачке додира на нацртаном луку.",
|
||||
"help.sketch.construction_line.tangent_two": "Изаберите два круга или кружна лука, па у прегледу изаберите заједничку тангенту. Тачке додира морају лежати на нацртаним кривама.",
|
||||
"help.sketch.construction_line.vertical": "Направите бесконачну вертикалну помоћну праву кроз тачку. Њено ограничење вертикалности остаје на снази при каснијим изменама.",
|
||||
"help.sketch.equation_curve": "Црта криву из формула: x(t) и y(t), или y = f(x), у задатом опсегу. Формуле могу да користе параметре и крива их прати: користите је за синусоиду, спиралу или евольвенту зупца зупчаника.",
|
||||
"help.solid.align": "Помера изабрана тела тако да се тачка, лице или раван на њима поклопи са тачком, лицем или равни негде другде. Користите га да поставите део уз други без мерења одмака.",
|
||||
"help.solid.chamfer": "Одсеца изабране ивице равним скошењем задатог растојања. Користите га да ублажите оштре ивице или олакшате увођење чивије или завртња.",
|
||||
"help.solid.combine": "Спаја тела, одузима их од циљног тела или задржава само заједничку запремину, а може да задржи и алатна тела. Користите га да спојите делове, да једним обликом одсечете други или да пронађете где се два тела преклапају.",
|
||||
@@ -1480,6 +1541,21 @@
|
||||
"joint.one_component": "Ови делови су тела једне компоненте: {bodies}. Спој помера једну компоненту у односу на другу, па сваки део треба своју компоненту.",
|
||||
"joint.rigid_group_one_component": "Сва тела ({count}) су у једној компоненти (или у самом дизајну): прво користите {command}.",
|
||||
"joint.rigid_group_select_components": "Изаберите две или више компоненти.",
|
||||
"joint_move.blocked_hint": "Проверите врсту споја (на пример Круто), учвршћивање, границе споја и вредности које покрећу параметри.",
|
||||
"joint_move.blocked_title": "Овај део се не може померити",
|
||||
"joint_move.coarse": "Корак ротације близу осе (°)",
|
||||
"joint_move.distance": "Растојање од почетка",
|
||||
"joint_move.done": "Готово",
|
||||
"joint_move.fine": "Корак ротације далеко од осе (°)",
|
||||
"joint_move.fixed": "Овај део нема покретну осу споја.",
|
||||
"joint_move.help": "Помери — превуците стрелицу или прстен. Esc отказује тренутно превлачење.",
|
||||
"joint_move.legacy_hint": "Искључите да бисте користили претходни панел Помери.",
|
||||
"joint_move.precision": "Повуците изван испрекиданог водича за фину ротацију.",
|
||||
"joint_move.rotation": "Ротација од почетка",
|
||||
"joint_move.select": "Изаберите лице или један део склопа, а затим изаберите Помери.",
|
||||
"joint_move.settings": "Померање у склопу",
|
||||
"joint_move.step": "Корак",
|
||||
"joint_move.use_handles": "Користи ручице кретања спојева за померање у склопу",
|
||||
"keymap.command_id": "ID команде",
|
||||
"keymap.empty": "Нема одговарајућих команди",
|
||||
"keymap.use_anyway": "Ипак користи {key} овде",
|
||||
@@ -1574,6 +1650,8 @@
|
||||
"nav.maya.help": "Alt+превлачење левим орбитира, Alt+средњи помера, Alt+десни зумира; превлачење средњим такође помера",
|
||||
"nav.openscad.help": "Превлачење левим дугметом по празном простору орбитира, Shift+превлачење левим бира оквиром, превлачење геометрије скице је мења; превлачење десним или средњим дугметом помера, Shift+десно или Shift+средње зумира",
|
||||
"nav.perspective.sketch": "Измена скице користи ортографски приказ. Завршите скицу да пребаците на перспективу.",
|
||||
"nav.right_drag_orbits.help": "Искључено: превлачење десним дугметом не дира камеру, а десно дугме само отвара контекстни мени. Избор SolidWorks мапирања миша га искључује.",
|
||||
"nav.right_drag_orbits.label": "Превлачење десним дугметом орбитира",
|
||||
"nav.solidworks.help": "Превлачење средњим дугметом орбитира, Ctrl+средње помера, Shift+средње зумира, превлачење десним орбитира",
|
||||
"nav.tinkercad.help": "Превлачење десним дугметом орбитира, превлачење средњим или Shift+десним помера",
|
||||
"nav.touchpad.help": "Држите Shift и померајте показивач за померање приказа; држите Alt/Option и померајте га за орбитирање. Дугме миша није потребно. Вертикално скроловање зумира.",
|
||||
@@ -1595,6 +1673,9 @@
|
||||
"navbar.custom.customise": "Прилагоди навигациону траку…",
|
||||
"navbar.custom.design": "Дизајн",
|
||||
"navbar.custom.duplicate": "Ово место је већ на траци.",
|
||||
"navbar.custom.edge": "Ивица",
|
||||
"navbar.custom.edge_bottom": "Доле",
|
||||
"navbar.custom.edge_top": "Горе",
|
||||
"navbar.custom.fade": "Избледи",
|
||||
"navbar.custom.full": "Уклоните једно место да бисте додали друго.",
|
||||
"navbar.custom.hidden_design": "Скривено у Дизајну",
|
||||
@@ -1701,6 +1782,7 @@
|
||||
"parts.dimension.thickness": "Дебљина",
|
||||
"parts.dimension.width": "Ширина",
|
||||
"parts.family.bearing": "Куглични лежај са дубоким жлебом (габарит)",
|
||||
"parts.family.button_head_cap_screw": "Завртањ са полуоблом главом и унутрашњим шестоугаоником",
|
||||
"parts.family.dowel_pin": "Паралелни клин (колчић)",
|
||||
"parts.family.hex_bolt": "Завртањ са шестоугаоном главом",
|
||||
"parts.family.hex_nut": "Шестоугаона навртка",
|
||||
@@ -1759,6 +1841,11 @@
|
||||
"prefs.bottom_centre": "Доле у средини",
|
||||
"prefs.bottom_right": "Доле десно",
|
||||
"prefs.browser": "Стабло модела",
|
||||
"prefs.browser_eye_left": "Око поред стрелице за скупљање",
|
||||
"prefs.browser_eye_left_note": "Редови стабла модела приказују око за приказивање/скривање поред стрелице за скупљање, као раније, уместо у колони на десном крају редова.",
|
||||
"prefs.browser_reveal_on_hover": "Прикажи при преласку мишем",
|
||||
"prefs.browser_show_note": "Приказано држи стабло модела на екрану. Прикажи при преласку мишем га скрива док показивач не дође на његово место или на танак језичак на његовој левој ивици, а остаје отворено док је показивач у њему или док је отворен мени из њега.",
|
||||
"prefs.browser_shown": "Приказано",
|
||||
"prefs.built_in": "Уграђено",
|
||||
"prefs.button_left": "Превлачење левим дугметом",
|
||||
"prefs.button_middle": "Превлачење средњим дугметом",
|
||||
@@ -1806,6 +1893,8 @@
|
||||
"prefs.graphics_card_restart": "Ступа на снагу након поновног покретања SolidCraft-а. WGPU_ADAPTER_NAME има предност над овим избором.",
|
||||
"prefs.graphics_note": "Жртвујте брзину за глаткоћу. Сваки избор показује свој учинак на примеру.",
|
||||
"prefs.graphics_presets_note": "Унапред подешене вредности подешавају све изборе испод одједном.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Сакриј симболе ограничења када је приказ умањен",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out_note": "Симболи ограничења нестају из приказа када је скица толико умањена да би је затрпали. Искључено: остају фиксне величине на екрану при сваком зумирању.",
|
||||
"prefs.history": "Временска трака",
|
||||
"prefs.history_origin_bodies": "Прикажи исходиште и тела на врху",
|
||||
"prefs.history_origin_bodies_note": "Временска трака лево или испод стабла модела почиње фасциклом Тела активне компоненте и Исходиштем, са њиховим оком и менијима као у стаблу модела. Временска трака на дну остаје непромењена.",
|
||||
@@ -1829,6 +1918,8 @@
|
||||
"prefs.layout_note": "Компактно причвршћује панеле уз ивице прозора. Провидност стабла модела доступна је у опцији Плутајуће. Страну Асистента промените на страници Асистент.",
|
||||
"prefs.left": "Лево",
|
||||
"prefs.left_click": "Леви клик",
|
||||
"prefs.line_drag_draws_one": "Притисак и превлачење цртају једну линију",
|
||||
"prefs.line_drag_draws_one_note": "У алату Линија, притисак, превлачење и пуштање цртају једну линију од места притиска до места пуштања и ту се завршава. Кликови и даље цртају ланац. Искључено: превлачење поставља тачку где се пусти, као и клик.",
|
||||
"prefs.linked_designs": "Повезани дизајни",
|
||||
"prefs.live_input": "Улаз уживо",
|
||||
"prefs.live_preview": "Преглед уживо",
|
||||
@@ -1842,6 +1933,9 @@
|
||||
"prefs.max_file_size_step": "STEP и датотеке за увоз",
|
||||
"prefs.max_file_size_web": "Веб апликација отвара STEP и датотеке за увоз до 512 MB и дизајне до 256 MB: картица прегледача не може да садржи веће датотеке. Десктоп апликација може да повећа ова ограничења.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "Велике иконе",
|
||||
"prefs.menu_list": "Списак",
|
||||
"prefs.menus": "Менији",
|
||||
"prefs.minutes": "минута",
|
||||
"prefs.minutes_suffix": " min",
|
||||
"prefs.mouse": "Миш и додирна табла",
|
||||
@@ -1861,14 +1955,40 @@
|
||||
"prefs.origin_plane_names": "Називи равни исходишта",
|
||||
"prefs.origin_plane_names_note": "Називи приказани у стаблу модела, дијалозима и листама. {top} је XY, {front} је XZ, а {right} је YZ, са Z нагоре. Команде, скрипте и сачувани дизајни задржавају XY, XZ и YZ.",
|
||||
"prefs.overall_speed": "Укупна брзина",
|
||||
"prefs.persistent_ribbon_pressed": "Истакни активне команде на траци",
|
||||
"prefs.persistent_ribbon_pressed_help": "Истиче команде на траци док су њихови ефекти укључени. Искључите да бисте задржали претходни неутрални изглед и помоћ за команде.",
|
||||
"prefs.perspective_fov": "Видно поље",
|
||||
"prefs.placement": "Постављање",
|
||||
"prefs.prefer_free_sketch_points": "Дај предност покретним тачкама скице када се тачке преклапају",
|
||||
"prefs.preview_choices": "Преглед ваших избора",
|
||||
"prefs.print_face_highlight": "Истицање површине за штампу",
|
||||
"prefs.print_face_highlight_tip": "Колико се јако површина за штампу тела боји и обрубљује у приказу. 0 % не приказује истицање.",
|
||||
"prefs.profile_active": "Активни профил",
|
||||
"prefs.profile_applied": "Профил је примењен.",
|
||||
"prefs.profile_changed": "Подешавања су промењена откако је профил сачуван. Притисните Сачувај да их задржите у њему.",
|
||||
"prefs.profile_deleted": "Профил је избрисан.",
|
||||
"prefs.profile_export": "Извези…",
|
||||
"prefs.profile_exported": "Профил је извезен.",
|
||||
"prefs.profile_imported": "Профил је увезен. Изаберите га под Активни профил да бисте прешли на њега.",
|
||||
"prefs.profile_name_hint": "Назив профила, нпр. Лаптоп",
|
||||
"prefs.profile_rename": "Преименуј",
|
||||
"prefs.profile_renamed": "Профил је преименован.",
|
||||
"prefs.profile_save": "Сачувај",
|
||||
"prefs.profile_saved": "Тренутна подешавања су сачувана у профил.",
|
||||
"prefs.profile_skipped": "Профил је примењен; подешавања која су задржала тренутну вредност: {n} (погледајте дневник).",
|
||||
"prefs.profiles": "Профили подешавања",
|
||||
"prefs.profiles_note": "Пречице, трака са алатима, навигација, радијални мени и подешавања интерфејса, чувани заједно под једним називом и мењани одједном.",
|
||||
"prefs.project_options_dialog": "Дијалог опција за Пројектуј",
|
||||
"prefs.project_options_dialog_note": "Укључено: пре пројектовања изаберите Геометрија или Тела и Веза пројекције. Искључено: задржава се претходни начин рада са директним избором.",
|
||||
"prefs.quality_default": "подразумевано",
|
||||
"prefs.quality_fastest": "најбрже",
|
||||
"prefs.quality_slowest": "најспорије",
|
||||
"prefs.quality_smoother": "глађе",
|
||||
"prefs.render_previous_geometry_budget": "Користи претходни буџет геометрије за преглед у Рендеровању",
|
||||
"prefs.render_previous_geometry_budget_help": "Враћа претходно фиксно ограничење геометрије (512 MiB на 64-битним десктоп рачунарима, 128 MiB другде) уместо буџета према уређају.",
|
||||
"prefs.reports_saved": "Сачувано: {n}",
|
||||
"prefs.ribbon_reveal": "Прикажи резултате претраге на траци",
|
||||
"prefs.ribbon_reveal_note": "Задржавање показивача изнад резултата у Претражи команде показује где се команда налази на траци: њену картицу, њено дугме или њен ред у падајућем менију панела. Команда покренута из претраге тамо трепери, а покренута команда из падајућег менија означава свој панел.",
|
||||
"prefs.right_click": "Десни клик",
|
||||
"prefs.saving": "Аутоматско чување и опоравак",
|
||||
"prefs.scale_note": "Такође Ctrl + / Ctrl − у прозору. Дугмад су 28 px при 100%.",
|
||||
@@ -1877,18 +1997,29 @@
|
||||
"prefs.search_empty": "Нема одговарајућих подешавања",
|
||||
"prefs.select": "Избор",
|
||||
"prefs.select_one_item": "Изабери једну ставку",
|
||||
"prefs.select_other_hidden": "Изабери скривену геометрију помоћу Изабери друго",
|
||||
"prefs.select_other_hidden_help": "Укључено: обухвата скривена тела, скице и заклоњене ивице у близини показивача. Искључено: користи се изворни списак видљиве геометрије испод показивача.",
|
||||
"prefs.selecting": "Бирање",
|
||||
"prefs.shift": "Shift",
|
||||
"prefs.shift_add_ctrl_toggle": "Shift само додаје; Ctrl / Cmd укључује и искључује",
|
||||
"prefs.shift_add_note": "Укључено: Shift додаје, Ctrl / Cmd укључује и искључује. Искључено (подразумевано): оба укључују и искључују.",
|
||||
"prefs.shortcuts": "Пречице",
|
||||
"prefs.single_surface_analysis": "Користи само једну анализу површи",
|
||||
"prefs.single_surface_analysis_help": "Када је искључено, сенчење површи и Континуитет могу остати укључени заједно. Када је укључено, остаје последња покренута или промењена анализа.",
|
||||
"prefs.sketch_align_points": "Поравнај са другим тачкама скице током цртања",
|
||||
"prefs.sketch_align_points_note": "Током цртања тачка се истовремено поравнава хоризонтално са једном тачком скице и вертикално са другом, уз тачкасти водич, а клик задржава оба поравнања. Искључено: само са претходном тачком, по једна оса.",
|
||||
"prefs.sketch_conflict_colour": "Геометрија у сукобу у боји сукоба",
|
||||
"prefs.sketch_conflict_colour_note": "Док су ограничења скице у сукобу, криве и тачке које држи ограничење које не успева исцртавају се такође у боји сукоба, а не само његов симбол или кота. Искључено: задржавају уобичајене боје.",
|
||||
"prefs.sketch_construction_lines": "Проширени алати за помоћне праве",
|
||||
"prefs.sketch_exit_view": "Када се скица заврши",
|
||||
"prefs.sketch_exit_view.keep": "Задржи тренутни приказ",
|
||||
"prefs.sketch_exit_view.previous": "Врати се на претходни приказ",
|
||||
"prefs.sketch_exit_view.unless_rotated": "Врати се осим ако је ротирано",
|
||||
"prefs.sketch_exit_view_note": "Куда приказ иде након Заврши скицу, Откажи или двоструког Esc.",
|
||||
"prefs.sketch_open_look_at": "Окрени камеру према скици када се отвори",
|
||||
"prefs.sketch_open_look_at_note": "Искључено: када направите или измените скицу, приказ остаје онакав какав јесте. Погледај окреће приказ према скици када то желите.",
|
||||
"prefs.sketch_recent_snap": "Аутоматски привлачи на недавне центре и средишње тачке",
|
||||
"prefs.sketch_recent_snap_note": "Током цртања показивач се привлачи тамо где се секу водичи двају последњих центара кругова или средишњих тачака правих ивица. Искључено: ти водичи служе само за приказ и показивач се не помера.",
|
||||
"prefs.sketch_selection": "Скица и избор",
|
||||
"prefs.sketch_selection_heading": "Скица и избор",
|
||||
"prefs.sketch_selection_note": "Како се скице привлаче и како кликови бирају.",
|
||||
@@ -1933,7 +2064,14 @@
|
||||
"prefs.toast_place": "Обавештења",
|
||||
"prefs.toggle_item": "Додај ставку или је поново уклони",
|
||||
"prefs.toolbar": "Трака са алатима",
|
||||
"prefs.toolbar_tabs": "Картице траке са алатима",
|
||||
"prefs.top": "Горе",
|
||||
"prefs.trackpad_zoom_modifier": "Зумирај скроловањем са два прста док држите",
|
||||
"prefs.trackpad_zoom_modifier.alt": "⌥ / Alt",
|
||||
"prefs.trackpad_zoom_modifier.cmd": "⌘",
|
||||
"prefs.trackpad_zoom_modifier.ctrl": "Ctrl",
|
||||
"prefs.trackpad_zoom_modifier.help": "Држите само овај тастер да зумирате према показивачу вертикалним скроловањем. Искључено задржава постојеће понашање скроловања.",
|
||||
"prefs.trackpad_zoom_modifier.off": "Искључено",
|
||||
"prefs.translucent": "Провидно",
|
||||
"prefs.under_browser": "Испод стабла модела",
|
||||
"prefs.under_cursor": "Испод показивача",
|
||||
@@ -1943,13 +2081,24 @@
|
||||
"prefs.view_confirm_corner": "У реду и Откажи у углу приказа",
|
||||
"prefs.view_confirm_corner_note": "Док је отворен дијалог или скица, квачица и крстић горе десно у приказу раде исто што и дугмад У реду и Откажи у дијалогу, односно завршавају и отказују скицу. Навигатор приказа помера се испод њих.",
|
||||
"prefs.viewport": "Прозор приказа",
|
||||
"prefs.viewport_analysis_status": "Прикажи статус анализе у прозору приказа",
|
||||
"prefs.viewport_analysis_status_help": "Приказује активне анализе изнад прозора приказа, отвара њихове легенде из сваке ставке, а тастер Esc искључује најновију анализу. Искључите да бисте вратили легенде у угловима и претходно понашање тастера Esc.",
|
||||
"prefs.viewport_background": "Позадина",
|
||||
"prefs.viewport_background_colour": "Боја",
|
||||
"prefs.viewport_background_theme": "Градијент теме",
|
||||
"prefs.wheel": "Точкић",
|
||||
"prefs.wheel_trackpad": "Точкић и додирна табла",
|
||||
"prefs.workspace": "Радни простор",
|
||||
"prefs.zoom_stop_at_model": "Заустави зумирање на моделу",
|
||||
"prefs.zoom_stop_at_model_tip": "3D миш и апликација уређаја зумирају према површини у средини приказа; зумирање превлачењем држи тачку где је превлачење почело. Искључено: зумирање може да прође кроз модел. Точкић и штипање засебно користе Зумирање према показивачу",
|
||||
"prefs.zoom_toward_cursor": "Зумирање према показивачу",
|
||||
"properties.title": "Својства: {name}",
|
||||
"recovery.days_ago": "пре {count} дана",
|
||||
"recovery.detail": "{file} · операција: {features} · аутоматски сачувано {when}",
|
||||
"recovery.discard_all": "Одбаци све",
|
||||
"recovery.discard_all_detail": "Ово се не може поништити.",
|
||||
"recovery.discard_all_title": "Одбацити аутоматска чувања ({count})?",
|
||||
"recovery.discard_all_title_one": "Одбацити 1 аутоматско чување?",
|
||||
"recovery.hours_ago": "пре {count} h",
|
||||
"recovery.just_now": "управо сада",
|
||||
"recovery.min_ago": "пре {count} min",
|
||||
@@ -2034,6 +2183,7 @@
|
||||
"render.appearance_editor.weight": "Тежина",
|
||||
"render.appearance_physics_separate": "Визуелни изглед не додељује густину, топлотне податке нити друга инжењерска својства.",
|
||||
"render.appearances": "Изгледи",
|
||||
"render.assign_help": "Кликните да примените на избор; превуците за преглед и пустите, или кликните без избора да бојите.",
|
||||
"render.availability_native": "Изворни састав; прегледајте пре примене",
|
||||
"render.availability_projection": "Подржана пројекција; погледајте опсег и изоставке",
|
||||
"render.availability_reference": "Референтни подаци; није аутоматска додела",
|
||||
@@ -2072,6 +2222,7 @@
|
||||
"render.bloom_threshold": "Праг линеарних светлих делова",
|
||||
"render.browse_capabilities": "Прегледај комплетан каталог могућности",
|
||||
"render.browse_environments": "Прегледај изворна HDR окружења",
|
||||
"render.browse_looks": "Прегледај…",
|
||||
"render.browse_native_scenes": "Прегледај изворне студије",
|
||||
"render.browse_physical": "Прегледај физичке референце",
|
||||
"render.browse_quality": "Прегледај спецификације квалитета",
|
||||
@@ -2095,6 +2246,82 @@
|
||||
"render.check_opaque": "Провидност: само непрозирне површине; алфа позадине је подржана.",
|
||||
"render.check_sorted_alpha": "Провидност: апроксимација сортираном алфом.",
|
||||
"render.check_transmission": "Провидност: физичко пропуштање.",
|
||||
"render.children.absolute_floor": "Апсолутни доњи праг",
|
||||
"render.children.adaptive": "Адаптивно",
|
||||
"render.children.add_look_reason": "Додајте изглед да бисте мењали да ли је омогућен и његову јачину.",
|
||||
"render.children.all_shots": "Сви кадрови",
|
||||
"render.children.bloom": "Сјај",
|
||||
"render.children.check": "Провера",
|
||||
"render.children.check_now": "Провери сада",
|
||||
"render.children.chromatic": "Хроматски помак",
|
||||
"render.children.compact": "Компактно",
|
||||
"render.children.contrast": "Контраст",
|
||||
"render.children.copy_image": "Копирај",
|
||||
"render.children.copy_job": "Копирај JSON посла",
|
||||
"render.children.copy_request": "Копирај JSON захтева",
|
||||
"render.children.coverage_error": "Грешка покривености",
|
||||
"render.children.cpu": "CPU праћење зрака",
|
||||
"render.children.denoise": "Смањи шум на завршеној слици",
|
||||
"render.children.depth": "Дубина путање",
|
||||
"render.children.distortion": "Изобличење",
|
||||
"render.children.edge_samples": "Узорци ивица",
|
||||
"render.children.effects_banner": "Ови ефекти захтевају GPU праћење путања. Отварање овога никад не мења методу.",
|
||||
"render.children.enable_adaptive": "Омогућите независне пробне узорке да бисте мењали адаптивне границе.",
|
||||
"render.children.enable_bloom": "Омогућите сјај да бисте мењали његову јачину, праг и полупречник.",
|
||||
"render.children.finishing": "Завршна обрада",
|
||||
"render.children.flare": "Одсјај",
|
||||
"render.children.fresnel": "Fresnel-ови слојеви",
|
||||
"render.children.ghosts": "Духови прстенова",
|
||||
"render.children.glass": "Пренос кроз стакло",
|
||||
"render.children.go_to": "Иди на {editor}",
|
||||
"render.children.grain": "Зрно",
|
||||
"render.children.halo": "Ореол",
|
||||
"render.children.hdr": "HDR",
|
||||
"render.children.keep_master": "Задржи сирови линеарни мастер",
|
||||
"render.children.last_result": "Последњи: {number} · {width} × {height} · {phase}",
|
||||
"render.children.lens_effects": "Ефекти објектива",
|
||||
"render.children.look_summary": "{tone} · изгледи: {count}",
|
||||
"render.children.looks": "Креативни изгледи",
|
||||
"render.children.memory": "Меморија",
|
||||
"render.children.method": "Метода",
|
||||
"render.children.minimum": "Минимум",
|
||||
"render.children.needs_cpu": "Потребно је CPU праћење зрака. Отварање овога никад не мења методу.",
|
||||
"render.children.nested": "Потврђени угнежђени диелектрици",
|
||||
"render.children.not_checked": "Није проверено",
|
||||
"render.children.path": "Праћење путања",
|
||||
"render.children.pilots": "Пробни узорци",
|
||||
"render.children.preview": "Подаци прегледа",
|
||||
"render.children.progressive_reason": "Изаберите Прогресивно да бисте мењали број узорака и семе.",
|
||||
"render.children.radius": "Полупречник",
|
||||
"render.children.reflect_depth": "Дубина рефлексије",
|
||||
"render.children.reflect_samples": "Узорци рефлексије",
|
||||
"render.children.regrade": "Поновна градација",
|
||||
"render.children.relative_error": "Релативна грешка",
|
||||
"render.children.render": "Рендеровање",
|
||||
"render.children.render_shot": "Рендеруј кадар",
|
||||
"render.children.renderer": "Рендерер",
|
||||
"render.children.renderers": "Рендерери",
|
||||
"render.children.request": "Овај захтев",
|
||||
"render.children.reset_finishing": "Ресетуј завршну обраду",
|
||||
"render.children.review_quality": "Прегледај спецификацију квалитета…",
|
||||
"render.children.rgb_gain": "RGB појачање",
|
||||
"render.children.samples": "Узорци",
|
||||
"render.children.sampling": "Узорковање",
|
||||
"render.children.saturation": "Засићеност",
|
||||
"render.children.scene_summary": "тела: {bodies} · троуглови: {triangles}",
|
||||
"render.children.seed": "Семе",
|
||||
"render.children.sharpening": "Изоштравање",
|
||||
"render.children.single_image": "Једна слика",
|
||||
"render.children.soft_focus": "Меки фокус",
|
||||
"render.children.streak": "Пруга",
|
||||
"render.children.strength": "Јачина",
|
||||
"render.children.studio": "Студио",
|
||||
"render.children.threshold": "Праг",
|
||||
"render.children.tone": "Тон",
|
||||
"render.children.under_hood": "Испод хаубе",
|
||||
"render.children.vignette": "Вињета",
|
||||
"render.children.workstation": "Радна станица",
|
||||
"render.choose_look": "Прво изаберите изглед у Изгледима",
|
||||
"render.chromatic_offset": "Хроматски помак ивица",
|
||||
"render.clear_stage": "Уклони сачувану сцену",
|
||||
"render.copy_request": "Копирај JSON",
|
||||
@@ -2346,8 +2573,33 @@
|
||||
"render.flare_strength": "Јачина одсјаја светлих делова",
|
||||
"render.flare_threshold": "Линеарни праг одсјаја сцене",
|
||||
"render.fold_browser": "Скупи стабло модела",
|
||||
"render.frame_aspect": "Однос страница",
|
||||
"render.frame_centre_mark": "Ознака центра",
|
||||
"render.frame_copy_limit": "Нема слободног назива за дупликат кадра",
|
||||
"render.frame_copy_name": "{name} копија {number}",
|
||||
"render.frame_custom": "Прилагођено",
|
||||
"render.frame_distance_unit": " × величина",
|
||||
"render.frame_duplicate": "Дуплицирај",
|
||||
"render.frame_exposure": "Експозиција",
|
||||
"render.frame_fov": "Видно поље",
|
||||
"render.frame_guides": "Водичи",
|
||||
"render.frame_invalid_aspect": "Неисправан унапред подешен однос страница",
|
||||
"render.frame_lens": "FOV {fov}° · ≈ {mm} mm",
|
||||
"render.frame_name": "Назив",
|
||||
"render.frame_output_size": "Величина излаза",
|
||||
"render.frame_photo": "3:2 · Фотографија",
|
||||
"render.frame_portrait": "4:5 · Портрет",
|
||||
"render.frame_presets": "Унапред подешени кадрови",
|
||||
"render.frame_presets_pending": "Стиже са уређивачем кадрова",
|
||||
"render.frame_projection": "Пројекција",
|
||||
"render.frame_render_shot": "Рендеруј овај кадар",
|
||||
"render.frame_rule_thirds": "Правило трећина",
|
||||
"render.frame_safe_area": "Безбедна зона",
|
||||
"render.frame_shot_title": "Кадар · {name}",
|
||||
"render.frame_square": "1:1 · Квадрат",
|
||||
"render.frame_tall": "9:16 · Усправно",
|
||||
"render.frame_thirds": "Трећине",
|
||||
"render.frame_wide": "16:9 · Широки екран",
|
||||
"render.gpu_depth": "Максимална дубина путање",
|
||||
"render.gpu_options_help": "Узорци, дубина, семе и експозиција чувају се са овим подешавањем. Чува се само активна метода слике.",
|
||||
"render.gpu_path_failed": "GPU праћење путања није доступно",
|
||||
@@ -2375,6 +2627,7 @@
|
||||
"render.heading_results": "РЕЗУЛТАТИ",
|
||||
"render.heading_shots": "КАДРОВИ",
|
||||
"render.hide_shots_label": "Сакриј траку кадрова",
|
||||
"render.hover_look": "{object} · {look} · кликните за избор",
|
||||
"render.image_advanced": "Напредне GPU контроле",
|
||||
"render.image_cancelled": "Рендеровање је отказано",
|
||||
"render.image_cancelling": "Отказивање",
|
||||
@@ -2521,17 +2774,95 @@
|
||||
"render.library_uniform_ack": "Користи једноличну апроксимацију без просторних или напредних ефеката",
|
||||
"render.library_usable": "Извршне пројекције",
|
||||
"render.library_users": "Корисници",
|
||||
"render.light_aim": "Усмери ка",
|
||||
"render.light_anchor_pending": "Промена координатног система светла још није подржана.",
|
||||
"render.light_azimuth": "Азимут",
|
||||
"render.light_backend_limit": "Изаберите CPU праћење зрака или изричити GPU студио да бисте мењали светла. Светла старог GPU студија остају само за читање.",
|
||||
"render.light_behaviour": "ПОНАШАЊЕ",
|
||||
"render.light_default_fill": "Допунско",
|
||||
"render.light_default_key": "Главно",
|
||||
"render.light_default_number": "Светло {number}",
|
||||
"render.light_default_rim": "Рубно",
|
||||
"render.light_delete": "Избриши",
|
||||
"render.light_distance": "Растојање",
|
||||
"render.light_duplicate": "Дуплицирај",
|
||||
"render.light_elevation": "Елевација",
|
||||
"render.light_gpu_punctual": "Тачкаста светла и рефлектори доступни су уз CPU праћење зрака.",
|
||||
"render.light_hard_shadow": "Ово светло има оштре сенке; мекоћа емитера није доступна.",
|
||||
"render.light_intensity": "Интензитет",
|
||||
"render.light_missing": "Ово светло више не постоји. Изаберите светло у стаблу модела.",
|
||||
"render.light_place_highlight": "Постави према одсјају",
|
||||
"render.light_place_pending": "Постављање према одсјају захтева избор светла у прозору приказа и још није подржано.",
|
||||
"render.light_position": "ПОЛОЖАЈ",
|
||||
"render.light_preview_help": "Приказ уживо је без сенки; површинска светла користе апроксимацију узорком из центра. Рендеровање користи потпуни модел светла.",
|
||||
"render.light_product_centre": "Центар производа",
|
||||
"render.light_size": "Величина",
|
||||
"render.light_softness": "Мекоћа",
|
||||
"render.light_temperature": "Температура боје",
|
||||
"render.light_temperature_help": "Приближна нијанса црног тела; чува се као линеарни RGB.",
|
||||
"render.light_type": "Врста",
|
||||
"render.light_type_area": "Површинско",
|
||||
"render.light_type_point": "Тачкасто",
|
||||
"render.light_type_spot": "Рефлектор",
|
||||
"render.light_type_sun": "Сунце",
|
||||
"render.light_view_only": "Само једно светло и искључена светла утичу на овај приказ и његове рендере; никад се не чувају у дизајну.",
|
||||
"render.light_world_origin": "Исходиште света",
|
||||
"render.lighting_aim_point": "Тачка циљања",
|
||||
"render.lighting_apply": "Примени подешавање",
|
||||
"render.lighting_area_samples": "Узорци површинског светла",
|
||||
"render.lighting_budget": "Буџет",
|
||||
"render.lighting_budget_help": "Бројеви су само за читање. Додајте, дуплицирајте или избришите светла да бисте их променили; метода одређује ограничења.",
|
||||
"render.lighting_colour": "Боја",
|
||||
"render.lighting_consent": "Замени усмерена светла овим подешавањем",
|
||||
"render.lighting_custom": "Прилагођена светла",
|
||||
"render.lighting_custom_help": "Избор опције Прилагођено претвара подешавање у светла која се могу мењати, свако у свом реду у стаблу.",
|
||||
"render.lighting_direction": "Смер X Y Z",
|
||||
"render.lighting_direction_help": "Или упишите смер према светлу (X Y Z).",
|
||||
"render.lighting_distance_unit": " × величина",
|
||||
"render.lighting_edge_u": "Вектор странице U",
|
||||
"render.lighting_edge_v": "Вектор странице V",
|
||||
"render.lighting_falloff": "Слабљење",
|
||||
"render.lighting_falloff_help": "Интензитет је наведен на референтном растојању.",
|
||||
"render.lighting_height": "Висина V",
|
||||
"render.lighting_minimum": "Минимум",
|
||||
"render.lighting_mute": "Искључи ово светло",
|
||||
"render.lighting_point": "Тачка",
|
||||
"render.lighting_preset": "Подешавање студија",
|
||||
"render.lighting_preset_cpu": "Подешавања студија са усмереним светлима захтевају CPU праћење зрака. GPU осветљење користи изричита сунчева и површинска светла.",
|
||||
"render.lighting_preset_row": "Подешавање",
|
||||
"render.lighting_radiance": "Радијанса",
|
||||
"render.lighting_reference": "Референца",
|
||||
"render.lighting_review_help": "Замењује списак усмерених светала. Задржава тачкаста и површинска светла, окружење, сенке, камеру и сва остала подешавања кадра.",
|
||||
"render.lighting_review_title": "Прегледај подешавање осветљења",
|
||||
"render.lighting_samples_cpu": "Узорци површинског светла важе за CPU праћење зрака.",
|
||||
"render.lighting_samples_need_area": "Додајте CPU површинско светло да бисте подесили узорке површинског светла.",
|
||||
"render.lighting_samples_reason": "Узорци површинског светла морају бити 1, 4, 16 или 64.",
|
||||
"render.lighting_shadow_cpu": "Прекидач сенки захтева CPU праћење зрака. GPU праћење путања увек баца сенке.",
|
||||
"render.lighting_shadows": "Баци сенке",
|
||||
"render.lighting_shadows_section": "Сенке и узорковање",
|
||||
"render.lighting_shape": "Облик",
|
||||
"render.lighting_solo": "Само ово светло",
|
||||
"render.lighting_source": "Извор",
|
||||
"render.lighting_source_section": "Извор светла",
|
||||
"render.lighting_spot_preserved": "Ово тачкасто светло задржава сачувани конус рефлектора док му се не промени врста.",
|
||||
"render.lighting_sun_distance": "Смер сунца не зависи од растојања и тачке циљања.",
|
||||
"render.lighting_unsupported": "неподржано",
|
||||
"render.lighting_used": "{used} од {limit}",
|
||||
"render.lighting_width": "Ширина U",
|
||||
"render.lighting_world_position": "Светски X Y Z",
|
||||
"render.lights_fixed_to": "Светла везана за",
|
||||
"render.linear_help": "Задржава 32-битни флоат RGBA мастер пре уклањања шума, експозиције, мапирања тонова, филтера и позадине. Линеарни sRGB/D65; помножен покривеношћу. Додатна меморија се изричито одобрава. Без компресије и додатних пролаза.",
|
||||
"render.linear_retain": "Задржи сирови линеарни EXR",
|
||||
"render.linear_unavailable": "Изабрани завршени кадар нема задржани EXR. Омогућите задржавање пре рендеровања; старији PNG не може да врати сирову радијансу.",
|
||||
"render.look_add": "Додај изглед боје",
|
||||
"render.look_applied": "Примењено: {name}",
|
||||
"render.look_bleach": "По узору на bleach bypass",
|
||||
"render.look_cool": "Хладно",
|
||||
"render.look_cyanotype": "По узору на цијанотипију",
|
||||
"render.look_faded": "Избледели отисак",
|
||||
"render.look_material": "Материјал",
|
||||
"render.look_mono": "Једнобојно",
|
||||
"render.look_pasted": "Изглед је налепљен",
|
||||
"render.look_sepia": "Сепија",
|
||||
"render.look_strength": "Јачина изгледа",
|
||||
"render.look_teal_orange": "Тиркизно / наранџасто",
|
||||
@@ -2607,9 +2938,11 @@
|
||||
"render.output_advanced": "Напредно",
|
||||
"render.output_backend_ready": "Спремно",
|
||||
"render.output_check": "Провери",
|
||||
"render.output_check_failed": "Провера · грешка",
|
||||
"render.output_check_help": "Покрените проверу да бисте прегледали овај захтев без покретања рендеровања.",
|
||||
"render.output_check_summary": "Провера · Напомене: {count}",
|
||||
"render.output_copy": "Копирај",
|
||||
"render.output_copy_report": "Копирај извештај",
|
||||
"render.output_destination": "Одредиште",
|
||||
"render.output_destination_help": "Одредишну датотеку изаберите при извозу завршене слике.",
|
||||
"render.output_dimensions": "Величина",
|
||||
@@ -2619,6 +2952,7 @@
|
||||
"render.output_fit": "Уклопи",
|
||||
"render.output_format": "Формат",
|
||||
"render.output_format_status": "PNG је тачна завршена слика за приказ. Необавезни GPU EXR задржава сирову линеарну радијансу сцене у формату FLOAT32 пре филтера приказа. Извоз никад не замењује кеш прегледа ни други посао.",
|
||||
"render.output_grading": "Градација",
|
||||
"render.output_image": "Слика",
|
||||
"render.output_largest": "Највише {width} × {height}",
|
||||
"render.output_lock_ratio": "Закључај однос страница",
|
||||
@@ -2628,6 +2962,8 @@
|
||||
"render.output_quality_waiting": "чека се уговор о квалитету",
|
||||
"render.output_ratio_refused": "Повезана димензија премашује ограничење величине ове методе. Претходна величина се задржава; откључајте однос или изаберите мању вредност.",
|
||||
"render.output_ready": "Спремно за рендеровање",
|
||||
"render.output_run_check": "Покрени проверу",
|
||||
"render.output_size_limit": "{method}: до {dimension} px по страни и {pixels} пиксела. Највећа величина која стаје: {width} × {height}.",
|
||||
"render.output_size_ok": "Величина је у реду · {width} × {height} px",
|
||||
"render.output_swap_orientation": "Замени ширину и висину",
|
||||
"render.output_tools_inspector": "Величину и формат измените у инспектору Излаз.",
|
||||
@@ -2644,6 +2980,7 @@
|
||||
"render.page_output": "Излаз",
|
||||
"render.page_scene": "Сцена",
|
||||
"render.paint_label": "Обоји",
|
||||
"render.painting_with": "Бојење изгледом {name}",
|
||||
"render.passive_stage": "Подлога и пасивна сцена",
|
||||
"render.passive_stage_help": "Коначни пасивни панели припадају само рендерованим сликама. Не додају CAD тела ни масу. Постављање је фиксно након примене; пресецање граница производа се одбија. CPU не даје дифузно одбијено осветљење.",
|
||||
"render.perspective": "Перспективна камера",
|
||||
@@ -2665,6 +3002,7 @@
|
||||
"render.pick_help": "Изаберите Тело, Лице или Компонента. Кликните празан простор да очистите избор; Shift или Ctrl додаје или укључује/искључује. Истицања се појављују само у живом приказу производа.",
|
||||
"render.pick_label": "Изабери",
|
||||
"render.pick_up_label": "Преузми",
|
||||
"render.pick_up_prompt": "Кликните објекат да преузмете његов изглед",
|
||||
"render.pick_waiting": "Избор производа још није подржан.",
|
||||
"render.post_contrast": "Контраст",
|
||||
"render.post_gain_b": "Појачање плаве",
|
||||
@@ -2704,13 +3042,17 @@
|
||||
"render.radial_distortion": "Радијално изобличење",
|
||||
"render.radial_fit": "Уклопи производ",
|
||||
"render.radial_save": "Сачувај кадар",
|
||||
"render.recent_looks": "Недавни изгледи",
|
||||
"render.regrade_action": "Поновна градација сировог мастера (без уклањања шума)",
|
||||
"render.regrade_help": "Прави засебан PNG из изабраног завршеног сировог мастера, користећи ове контроле приказа. Не прати зраке, не мења забележену величину, камеру ни светла и не преписује EXR. Уклањање шума се не примењује поново: сирови мастери не чувају његове водиље. Изведена слика се и даље може извести истим контролама посла.",
|
||||
"render.regrade_label": "Поновна градација",
|
||||
"render.reload_settings": "Поново учитај сачувана подешавања",
|
||||
"render.remove_override": "Уклони надјачавање",
|
||||
"render.replace_all": "Замени све",
|
||||
"render.replace_ground": "Потврди замену постојеће сцене",
|
||||
"render.replace_label": "Замени",
|
||||
"render.replace_saved_surface": "Замени постојећу сачувану везу за овај циљ",
|
||||
"render.replace_with": "Замени са",
|
||||
"render.restore_camera": "Врати сачувану камеру",
|
||||
"render.result_actual": "100%",
|
||||
"render.result_actual_help": "Један пиксел слике по пикселу екрана.",
|
||||
@@ -2762,14 +3104,28 @@
|
||||
"render.saved_surface_remove": "Уклони сачувану везу",
|
||||
"render.saved_surfaces": "Сачуване везе површина",
|
||||
"render.saved_surfaces_help": "Фиксиране уграђене мапе прате изабрани примерак у локалним милиметрима. Замењен или недостајући циљ је грешка, никад аутоматска поновна додела.",
|
||||
"render.scene_all_shots": "Сви кадрови ({count})",
|
||||
"render.scene_apply": "Примени прегледани студио",
|
||||
"render.scene_batch_reason": "За Све кадрове потребан је ред чекања за рендеровање сачуваних кадрова, који још није доступан.",
|
||||
"render.scene_check_now": "Провери сада",
|
||||
"render.scene_check_passed": "Провера је прошла · напомене: {notes}",
|
||||
"render.scene_check_stale": "Провера је застарела · проверите поново",
|
||||
"render.scene_check_unchecked": "Још није проверено · напомене: 0",
|
||||
"render.scene_consent": "Потврди замену осветљења, окружења, експозиције и сцене",
|
||||
"render.scene_light_summary": "{environment} · светла: {lights}",
|
||||
"render.scene_look_summary": "изгледи: {looks} · надјачавања: {overrides}",
|
||||
"render.scene_open_check": "Отвори проверу",
|
||||
"render.scene_preserved_fields": "Камера, узорковање, димензије, оптичке везе, CAD геометрија и инжењерски материјали се чувају.",
|
||||
"render.scene_preserves_design": "Саставите осветљење и пасивну сцену за видљиви предмет. Геометрија, инжењерски материјали и камера остају одвојени.",
|
||||
"render.scene_ready": "Спремно за рендеровање",
|
||||
"render.scene_reminder": "Сцена чува дизајн. Геометрија, инжењерска својства и CAD никад се не мењају из Рендеровања.",
|
||||
"render.scene_reminders": "Подсетници",
|
||||
"render.scene_renderer_ready": "Спремно: {renderer}",
|
||||
"render.scene_replaces": "Замењује",
|
||||
"render.scene_review": "Разреши и прегледај",
|
||||
"render.scene_review_help": "Прегледајте комплетну изворну композицију пре примене. Невезане напредне SCENE спецификације се не замењују.",
|
||||
"render.scene_scope_details": "Опсег композиције",
|
||||
"render.scene_stages": "Фазе",
|
||||
"render.search_appearances": "Претражи {count} изгледа",
|
||||
"render.setup_legacy_environment": "Ово старије подешавање користи наслеђену ротацију или интензитет окружења. Те вредности се задржавају; изричито их претворите пре чувања пуног посла слике.",
|
||||
"render.setup_name": "Назив подешавања",
|
||||
@@ -2782,7 +3138,22 @@
|
||||
"render.shell_tab_camera": "КАМЕРА РЕНДЕРА",
|
||||
"render.shell_tab_lighting": "ОСВЕТЉЕЊЕ",
|
||||
"render.shell_tab_scene": "СЦЕНА РЕНДЕРА",
|
||||
"render.shot_aperture": "Отвор бленде",
|
||||
"render.shot_distance": "Растојање",
|
||||
"render.shot_dof": "Дубинска оштрина",
|
||||
"render.shot_duplicate": "Дуплицирај сачувани кадар",
|
||||
"render.shot_edit_output": "Измени у Излазу",
|
||||
"render.shot_exposure_reason": "Експозиција захтева CPU праћење зрака или GPU праћење путања.",
|
||||
"render.shot_exposure_section": "Експозиција",
|
||||
"render.shot_focus_distance": "Растојање фокуса",
|
||||
"render.shot_fov_reason": "Видно поље захтева перспективну пројекцију.",
|
||||
"render.shot_frame_subject": "Уоквири предмет",
|
||||
"render.shot_invalid_camera": "Вредности камере морају бити коначне и у дозвољеном опсегу.",
|
||||
"render.shot_lens": "Објектив",
|
||||
"render.shot_lens_enable_reason": "Омогућите дубинску оштрину да бисте мењали отвор бленде и растојање фокуса.",
|
||||
"render.shot_lens_gpu_reason": "Дубинска оштрина захтева GPU праћење путања.",
|
||||
"render.shot_lens_perspective_reason": "Дубинска оштрина захтева перспективну пројекцију.",
|
||||
"render.shot_look_selection": "Усмери на избор",
|
||||
"render.shot_manager": "Сачувани кадрови",
|
||||
"render.shot_new_name": "Назив новог кадра",
|
||||
"render.shot_remove": "Уклони сачувани кадар",
|
||||
@@ -2790,6 +3161,9 @@
|
||||
"render.shot_remove_title": "Уклонити сачувани кадар?",
|
||||
"render.shot_rename": "Преименуј сачувани кадар",
|
||||
"render.shot_saved_only": "Прво сачувајте тренутни нацрт. Дуплицирање и преименовање чувају тачне сачуване опције и референце на ресурсе.",
|
||||
"render.shot_selection_reason": "Изаберите тело или лице на које ћете усмерити камеру.",
|
||||
"render.shot_size": "Величина",
|
||||
"render.shot_target": "Циљ",
|
||||
"render.shots_camera_changed": "Камера је промењена откако је овај кадар сачуван",
|
||||
"render.shots_name_help": "Унесите јединствен назив за тренутни кадар.",
|
||||
"render.shots_no_result": "Још нема рендера",
|
||||
@@ -3052,6 +3426,7 @@
|
||||
"select an axis": "изаберите осу",
|
||||
"select an axis, a straight or circular edge, or a cylinder, cone or torus face first": "прво изаберите осу, праву или кружну ивицу, или лице цилиндра, конуса или торуса",
|
||||
"select bodies first": "прво изаберите тела",
|
||||
"select components first": "прво изаберите компоненте",
|
||||
"select corner points, or the two lines of one corner": "изаберите тачке углова или две линије једног угла",
|
||||
"select one corner point, or the two lines of one corner": "изаберите једну тачку угла или две линије једног угла",
|
||||
"select sheet edges first": "прво изаберите ивице лима",
|
||||
@@ -3070,6 +3445,7 @@
|
||||
"settings.double_click_components": "Двоструки клик на компоненту је активира, а повезану компоненту мења на месту",
|
||||
"settings.frame_rate_details_note": "Додаје график времена последњих кадрова са бројем исцртаних троуглова, хистограм времена кадрова и шта је GPU нацртао. Кликните број кадрова у секунди у приказу да их прикажете или сакријете.",
|
||||
"settings.frame_rate_note": "Приказује број кадрова у секунди у доњем десном углу приказа.",
|
||||
"settings.insert_lists_open_designs": "Уметни дизајн прво наводи дизајне отворене у другим картицама",
|
||||
"settings.measure_axis_distances": "Прикажи растојања X/Y/Z",
|
||||
"settings.measure_axis_distances_help": "Приказуј компоненте растојања по светским осама уз укупно растојање док је Мерење отворено.",
|
||||
"settings.msaa_applies_on_restart": "Углађивање ивица примењује се при следећем покретању SolidCraft-а",
|
||||
@@ -3094,6 +3470,10 @@
|
||||
"shortcut.unpin": "Откачи",
|
||||
"sketch is missing": "скица недостаје",
|
||||
"sketch.degrees_of_freedom": "Степени слободе: {count}",
|
||||
"sketch.dimension_added_driven": "Скица већ одређује ту коту, па је додата као изведена (референтна) кота",
|
||||
"sketch.dimension_applied": "Кота је примењена",
|
||||
"sketch.dimension_applied_defined": "Кота је примењена; параметар `{name}` је дефинисан",
|
||||
"sketch.dimension_applied_renamed": "Кота је примењена; параметар је назван `{name}`",
|
||||
"sketch.dof_left": "Преостали степени слободе: {count}",
|
||||
"sketch.dof_left_one": "Преостали степени слободе: {count}",
|
||||
"sketch.draw_curves": "Цртај као {linetype}",
|
||||
@@ -3128,6 +3508,8 @@
|
||||
"sketch_text.use_font_file": "Користи датотеку фонта {path}",
|
||||
"solid": "пуно",
|
||||
"status.editing": "Измена: {name}",
|
||||
"status.navigation": "Мапирање миша: {mapping}",
|
||||
"status.navigation_tip": "Како миш помера приказ. Кликните да изаберете друго мапирање",
|
||||
"status.under_constrained": "Недовољно ограничено (степени слободе: {count})",
|
||||
"status.under_constrained_one": "Недовољно ограничено ({count} степен слободе)",
|
||||
"status.units_tip": "Јединице дизајна. Кликните да их промените",
|
||||
@@ -3161,8 +3543,10 @@
|
||||
"timeline.delete_folders_tip": "Задржи њихове операције и угнежђене фасцикле у временској траци",
|
||||
"timeline.delete_folders_with_features_tip": "Избриши фасцикле са свим операцијама у њима и њихове угнежђене фасцикле",
|
||||
"timeline.delete_many": "Избриши изабране операције: {count}",
|
||||
"timeline.edited_rolled_back": "Операција {name} је промењена, али је после ознаке враћања, па није изграђена: померите ознаку иза ње да видите резултат",
|
||||
"timeline.feature_error": "Грешка: {message}",
|
||||
"timeline.feature_hint": "Двапут кликните за измену · превуците за промену редоследа · десни клик за више",
|
||||
"timeline.feature_rolled_back": "Враћено уназад: после ознаке враћања, па није изграђено",
|
||||
"timeline.feature_warning": "Упозорење: {message}",
|
||||
"timeline.group": "Нова фасцикла операција",
|
||||
"timeline.move_many": "Премести ставке временске траке",
|
||||
@@ -3244,6 +3628,10 @@
|
||||
"ui.3d_mouse_offer": "Калибрација 3D миша исправља замењене или обрнуте осе и подешава његову брзину и осећај.",
|
||||
"ui.3d_mouse_offer_never": "Не приказуј више",
|
||||
"ui.3d_mouse_offer_swap": "Пребаци на Hidraw (директни драјвер)",
|
||||
"ui.3d_mouse_orbit": "Орбитирање",
|
||||
"ui.3d_mouse_orbit_classic": "Класично",
|
||||
"ui.3d_mouse_orbit_tip": "Праћено: 3D миш окреће приказ око дела модела који је у средини приказа када окретање почне, означеног бледом куглом. Класично: око центра приказа",
|
||||
"ui.3d_mouse_orbit_traced": "Праћено",
|
||||
"ui.3d_mouse_reading": "Очитање",
|
||||
"ui.3d_mouse_reset_axes": "Ресетуј доделу оса",
|
||||
"ui.3d_mouse_reset_view": "Ресетуј приказ",
|
||||
@@ -3280,6 +3668,8 @@
|
||||
"ui.3d_mouse_step_twist_counterclockwise": "Закрените капицу супротно од казаљке на сату, држите је, а затим пустите.",
|
||||
"ui.3d_mouse_switch_cap": "Калибриши као Повратак у центар",
|
||||
"ui.3d_mouse_switch_trackball": "Калибриши као трекбол",
|
||||
"ui.3d_mouse_use_direct": "Користи директни драјвер",
|
||||
"ui.3d_mouse_use_direct_tip": "Читај 3D миш директно (hidraw) уместо преко spacenavd.",
|
||||
"ui.3d_mouse_view_axis": "Оса приказа",
|
||||
"ui.3d_mouse_view_off": "Искључено",
|
||||
"ui.3d_mouse_view_off_tip": "Профил драјвера 3D миша искључује ову осу, па њено кретање не помера приказ. Избор кретања друге осе овде их замењује, а то кретање престаје да помера приказ.",
|
||||
@@ -3596,6 +3986,7 @@
|
||||
"ui.circle": "Круг",
|
||||
"ui.circular_pattern": "Кружни низ",
|
||||
"ui.clear": "Очисти",
|
||||
"ui.clear_print_face": "Уклони површину за штампу",
|
||||
"ui.clear_recent": "Очисти недавно",
|
||||
"ui.clear_the_selection": "Поништи избор",
|
||||
"ui.click_selects_whole_bodies": "Клик бира цела тела",
|
||||
@@ -3630,6 +4021,15 @@
|
||||
"ui.constant": "Константно",
|
||||
"ui.constraints_conflict": "Ограничења су у сукобу",
|
||||
"ui.construction": "Помоћна",
|
||||
"ui.construction.angle_hint": "Помоћна права: кликните тачку, па упишите угао или изаберите смер",
|
||||
"ui.construction.bisector_hint": "Симетрала: изаберите две праве линије које се секу, па жељено решење",
|
||||
"ui.construction.common_hint": "Заједничка тангента: изаберите два круга или лука, па жељено решење",
|
||||
"ui.construction.contact_hint": "Тангента: изаберите круг/лук или отворени сплајн/конику, па тачку додира (криве слободног облика: крајње тачке)",
|
||||
"ui.construction.external_hint": "Тангента: изаберите круг или лук, спољашњу тачку, па жељено решење",
|
||||
"ui.construction.free_hint": "Помоћна права: кликните две тачке; десним кликом почните испочетка",
|
||||
"ui.construction.point_hint": "Помоћна права: кликните тачку кроз коју пролази",
|
||||
"ui.construction.reference_hint": "Помоћна права: изаберите праву линију, па тачку; десним кликом промените референцу",
|
||||
"ui.construction.ribbon_label": "Пом. праве",
|
||||
"ui.context_menu": "Контекстни мени",
|
||||
"ui.continuity_g0": "G0 прегиб",
|
||||
"ui.continuity_g1": "G1 тангентно",
|
||||
@@ -3670,6 +4070,7 @@
|
||||
"ui.design_properties.part_number": "Број дела",
|
||||
"ui.design_properties.project": "Пројекат",
|
||||
"ui.design_properties.revision": "Ревизија",
|
||||
"ui.design_saved": "Сачувано: {path}",
|
||||
"ui.diameter": "Пречник",
|
||||
"ui.dimension": "Кота",
|
||||
"ui.direct_interaction": "Директна интеракција",
|
||||
@@ -3720,6 +4121,14 @@
|
||||
"ui.end_scale": "Крајње скалирање",
|
||||
"ui.entities": "Елементи",
|
||||
"ui.equal": "Једнакост",
|
||||
"ui.equation_end": "Крај",
|
||||
"ui.equation_explicit": "Експлицитни",
|
||||
"ui.equation_form": "Облик",
|
||||
"ui.equation_note": "Променљива је обичан број: sin(t) је узима у радијанима. Резултат без јединице је у јединици дизајна.",
|
||||
"ui.equation_parametric": "Параметарски",
|
||||
"ui.equation_x_of_t": "x(t)",
|
||||
"ui.equation_y_of_t": "y(t)",
|
||||
"ui.equation_y_of_x": "y(x)",
|
||||
"ui.equivalent_radius": "Еквивалентни полупречник",
|
||||
"ui.expand": "Прошири",
|
||||
"ui.exportDxf": "Извези DXF…",
|
||||
@@ -3785,6 +4194,7 @@
|
||||
"ui.heading_design": "ДИЗАЈН",
|
||||
"ui.heading_draft": "НАГИБ",
|
||||
"ui.heading_edit": "ИЗМЕНИ",
|
||||
"ui.heading_equation_curve": "КРИВА ПО ЈЕДНАЧИНИ",
|
||||
"ui.heading_extrude": "ЕКСТРУДИРАЈ",
|
||||
"ui.heading_fillet": "ЗАОБЉЕЊЕ",
|
||||
"ui.heading_finish_editing": "ЗАВРШИ ИЗМЕНУ",
|
||||
@@ -3876,6 +4286,10 @@
|
||||
"ui.import_ellipsis": "Увези…",
|
||||
"ui.infinite": "Бесконачно",
|
||||
"ui.insert": "Уметни",
|
||||
"ui.insert_design.browse": "Прегледај…",
|
||||
"ui.insert_design.open_designs": "Отворени дизајни",
|
||||
"ui.insert_design.saved_file": "Дизајн {name} има несачуване промене, па је уметнута његова сачувана датотека. Сачувајте га, а затим покрените Ажурирај везе да бисте унели промене.",
|
||||
"ui.insert_design.unsaved": "Несачуване промене",
|
||||
"ui.inspect": "Прегледај",
|
||||
"ui.intersect": "Пресек",
|
||||
"ui.isolate": "Изолуј",
|
||||
@@ -4019,12 +4433,29 @@
|
||||
"ui.polygon": "Полигон",
|
||||
"ui.preferences": "Подешавања",
|
||||
"ui.press_a_key": "притисните тастер…",
|
||||
"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": "Профил",
|
||||
"ui.profile_plane": "Раван профила",
|
||||
"ui.profiles": "Профили",
|
||||
"ui.project": "Пројектуј",
|
||||
"ui.project_face_edges_sketch": "Пројектуј ивице лица у нове скице",
|
||||
"ui.properties": "Својства",
|
||||
"ui.properties.copy_matrix": "Копирај матрицу",
|
||||
"ui.properties.first_moments": "Статички моменти",
|
||||
"ui.properties.inertia": "Момент инерције",
|
||||
"ui.properties.inertia_note": "У односу на центар масе, у светским осама (g·{unit}²)",
|
||||
"ui.properties.inertia_unavailable": "Није доступно: мрежа једног тела није затворена.",
|
||||
"ui.properties.inertia_x": "Ixx, Ixy, Ixz",
|
||||
"ui.properties.inertia_y": "Iyx, Iyy, Iyz",
|
||||
"ui.properties.inertia_z": "Izx, Izy, Izz",
|
||||
"ui.properties.measuring": "Мерење…",
|
||||
"ui.properties.measuring_failed": "Мерење је заустављено пре завршетка.",
|
||||
"ui.properties.principal_axis": "Главна оса {number}",
|
||||
"ui.properties.principal_moments": "Главни моменти",
|
||||
"ui.pull_direction": "Смер вађења из калупа",
|
||||
"ui.quality": "Квалитет",
|
||||
"ui.quantity": "Број",
|
||||
@@ -4070,6 +4501,7 @@
|
||||
"ui.revolve": "Ротирај",
|
||||
"ui.ribbon_exit": "Изађи",
|
||||
"ui.ribbon_labels": "Прикажи текст испод икона на траци",
|
||||
"ui.ribbon_menus_skip_pinned": "Изостави прикачене команде из падајућих менија",
|
||||
"ui.ribbon_pin_footer": "Прикачене команде приказују се на траци. Задржите показивач изнад реда да га прикачите или откачите.",
|
||||
"ui.ribbon_workspace": "Радни простор",
|
||||
"ui.right": "Десно",
|
||||
@@ -4087,6 +4519,7 @@
|
||||
"ui.select": "Изабери",
|
||||
"ui.selectOther": "Изабери друго…",
|
||||
"ui.selectTangentChain": "Изабери тангентни ланац",
|
||||
"ui.select_all": "Изабери све",
|
||||
"ui.selection": "Избор",
|
||||
"ui.selection_modifiers": "Модификатори избора",
|
||||
"ui.selection_shift_add_only": "Shift+клик додаје без уклањања",
|
||||
@@ -4123,7 +4556,11 @@
|
||||
"ui.sketch": "Скица",
|
||||
"ui.sketch_dimension": "Кота скице",
|
||||
"ui.sketch_grid": "Мрежа скице",
|
||||
"ui.sketch_intersect_geometry": "Геометрија",
|
||||
"ui.sketch_intersect_projection_link": "Веза пројекције",
|
||||
"ui.sketch_intersect_selection_filter": "Филтер избора",
|
||||
"ui.sketch_modifiers": "Модификатори скице",
|
||||
"ui.sketch_project_geometry": "Геометрија",
|
||||
"ui.sketch_scale": "Скалирање скице",
|
||||
"ui.sketches": "Скице",
|
||||
"ui.slice": "Пресеци",
|
||||
@@ -4170,6 +4607,11 @@
|
||||
"ui.symmetric": "Симетрично",
|
||||
"ui.symmetry": "Симетрија",
|
||||
"ui.system": "Систем",
|
||||
"ui.tab_assemble": "Склоп",
|
||||
"ui.tab_manage": "Управљање",
|
||||
"ui.tab_sheet_metal": "Лим",
|
||||
"ui.tab_view": "Приказ",
|
||||
"ui.tab_weldments": "Заварене конструкције",
|
||||
"ui.tangent": "Тангентно",
|
||||
"ui.taper_angle": "Угао сужења",
|
||||
"ui.tapped": "Са навојем",
|
||||
@@ -4185,6 +4627,7 @@
|
||||
"ui.tip_angle": "Угао врха",
|
||||
"ui.to_object": "До објекта",
|
||||
"ui.tool_direction": "Смер алата",
|
||||
"ui.toolbar_tabs_hidden": "Све картице траке са алатима су скривене. Кликните + или кликните десним дугметом овде да прикажете неку.",
|
||||
"ui.top": "Одозго",
|
||||
"ui.top_diameter": "Горњи пречник",
|
||||
"ui.top_radius": "Горњи полупречник",
|
||||
@@ -4222,6 +4665,8 @@
|
||||
"ui.view_centre": "Центар приказа",
|
||||
"ui.viewport_status_clear": "Искључи {name}",
|
||||
"ui.viewport_status_clear_hint": "Искључи {name} · Esc",
|
||||
"ui.viewport_status_edges": "Ивица: {count}",
|
||||
"ui.viewport_status_from": "из смера {direction}",
|
||||
"ui.viewport_status_key": "Прикажи легенду за {name}",
|
||||
"ui.viewport_status_minimum_radius": "Минимални полупречник",
|
||||
"ui.viewport_status_pairs": "Парова: {count}",
|
||||
@@ -4231,6 +4676,7 @@
|
||||
"ui.visual_shaded_with_edges": "Осенчено са ивицама",
|
||||
"ui.visual_style": "Визуелни стил",
|
||||
"ui.visual_wireframe": "Жичани модел",
|
||||
"ui.wheel_zoom_speed": "Брзина зумирања точкићем миша",
|
||||
"ui.width": "Ширина",
|
||||
"ui.window_system": "Систем прозора",
|
||||
"ui.window_system_auto": "Аутоматски",
|
||||
@@ -4261,7 +4707,9 @@
|
||||
"update.button": "Ажурирај",
|
||||
"update.button_version": "Ажурирај на {version}",
|
||||
"update.check_automatically": "Аутоматски проверавај ажурирања",
|
||||
"update.check_for_updates": "Провери ажурирања",
|
||||
"update.check_now": "Провери сада",
|
||||
"update.check_unavailable": "Провера ажурирања није доступна за ову инсталацију.",
|
||||
"update.checking": "Провера…",
|
||||
"update.copy": "Копирај",
|
||||
"update.dmg_opened": "Слика диска је отворена и проверена: превуците SolidCraft у Applications, замените стару копију, а затим поново покрените SolidCraft.",
|
||||
@@ -4284,6 +4732,9 @@
|
||||
"update.prefs_managed": "Ово је SolidCraft {version}, којим управља {manager}. Провера пита GitHub за најновије издање и не шаље ништа друго; ажурирајте SolidCraft у {manager}.",
|
||||
"update.prefs_note": "Ово је SolidCraft {version} ({kind}). Провера пита GitHub за најновије издање и не шаље ништа друго.",
|
||||
"update.prefs_web": "Поново учитајте страницу да добијете најновију веб апликацију. Ако мрежа није доступна или се ажурирање не може преузети на време, отвара се сачувана верзија; преузимање у току наставља се за следеће поновно учитавање.",
|
||||
"update.refresh_checking": "Провера најновијег издања…",
|
||||
"update.refresh_fallback": "Није било могуће проверити да ли постоји новије издање; ажурирање на верзију {version}, пронађену раније.",
|
||||
"update.refresh_selected": "Ажурирање на {version}.",
|
||||
"update.release_page": "Отвори страницу издања",
|
||||
"update.restart": "Поново покрени сада",
|
||||
"update.restart_later": "Касније",
|
||||
|
||||
@@ -181,6 +181,8 @@
|
||||
"Designs you open or save appear here.": "Açtığınız veya kaydettiğiniz tasarımlar burada görünür.",
|
||||
"Determined by To Object. Choose Angle to edit the angular extent.": "Nesneye kadar seçeneğiyle belirlenir. Açısal kapsamı düzenlemek için Açı seçeneğini seçin.",
|
||||
"Diameter": "Çap",
|
||||
"Did this assembly open the wrong way up?": "Bu montaj yanlış yönde mi açıldı?",
|
||||
"Did this part open the wrong way up?": "Bu parça yanlış yönde mi açıldı?",
|
||||
"Dimension new shapes": "Yeni şekillere ölçü ver",
|
||||
"Direction": "Yön",
|
||||
"Discard": "At",
|
||||
@@ -204,6 +206,7 @@
|
||||
"Edges": "Kenarlar",
|
||||
"Edit": "Düzenle",
|
||||
"Edit Profile Sketch": "Profil eskizini düzenle",
|
||||
"Edit shortcuts": "Kısayolları düzenle",
|
||||
"Enable rule": "Kuralı etkinleştir",
|
||||
"Enabled": "Etkin",
|
||||
"End Radius": "Bitiş yarıçapı",
|
||||
@@ -466,6 +469,7 @@
|
||||
"Radius": "Yarıçap",
|
||||
"Radius Fit": "Yarıçap uydurma",
|
||||
"Ratio": "Oran",
|
||||
"Read-only list: shortcuts can't be changed here.": "Salt okunur liste: kısayollar burada değiştirilemez.",
|
||||
"Reattach selected face": "Seçili yüze yeniden bağla",
|
||||
"Recover": "Kurtar",
|
||||
"Recover unsaved design?": "Kaydedilmemiş tasarım kurtarılsın mı?",
|
||||
@@ -599,6 +603,7 @@
|
||||
"Square": "Kare",
|
||||
"Stand it up": "Dik çevir",
|
||||
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "Dik çevir: X etrafında çeyrek tur; geri alınabilen tek bir Taşı özelliği olarak eklenir.",
|
||||
"Stand this assembly up?": "Bu montaj dik çevrilsin mi?",
|
||||
"Stand this part up?": "Bu parça dik çevrilsin mi?",
|
||||
"Start angle": "Başlangıç açısı",
|
||||
"Start bulge": "Başlangıç şişkinliği",
|
||||
@@ -649,6 +654,7 @@
|
||||
"Third point": "Üçüncü nokta",
|
||||
"This child is shared. Deleting it removes it from every copy of its parent.": "Bu alt öğe paylaşılıyor. Silindiğinde üst öğesinin tüm kopyalarından kaldırılır.",
|
||||
"This child is shared. Position changes apply to every copy of its parent.": "Bu alt öğe paylaşılıyor. Konum değişiklikleri üst öğesinin tüm kopyalarına uygulanır.",
|
||||
"This component is inside a subassembly placed more than once: aligning it moves it in every placement.": "Bu bileşen, birden fazla kez yerleştirilmiş bir alt montajın içinde: onu hizalamak, onu her yerleşimde taşır.",
|
||||
"This face cannot provide a durable attachment. Choose another face or explicitly use a fixed plane.": "Bu yüz kalıcı bir bağlama sağlayamaz. Başka bir yüz seçin veya açıkça sabit bir düzlem kullanın.",
|
||||
"This image is not attached to GitHub yet. Copy or save it, then attach it on GitHub.": "Bu görüntü henüz GitHub'a eklenmedi. Kopyalayın veya kaydedin, ardından GitHub'a ekleyin.",
|
||||
"Thread depth": "Diş derinliği",
|
||||
@@ -769,6 +775,8 @@
|
||||
"at the face": "yüzde",
|
||||
"bom.group_children": "Alt parçalar kendi parçalarının satırında",
|
||||
"browser.body_name": "Gövde {number}",
|
||||
"browser.calibrate_click_a": "A Noktası için görüntüye tıklayın veya koordinatlarını yazın.",
|
||||
"browser.calibrate_click_b": "B Noktası için görüntüye tıklayın veya koordinatlarını yazın.",
|
||||
"browser.calibrate_title": "Kalibre et: {name}",
|
||||
"browser.capture_many": "{count} bileşen taşındı ancak konumları yakalanmadı.",
|
||||
"browser.capture_note": "Konumu yakala yeni konumu korur; Konumu geri al önceki konuma döndürür.",
|
||||
@@ -790,6 +798,11 @@
|
||||
"cam.roller": "Makara",
|
||||
"cam.roller_pick": "Makara (isteğe bağlı)",
|
||||
"cam.tip_pick": "Uç (isteğe bağlı)",
|
||||
"camera.shot_pitch": "Yatıklık",
|
||||
"camera.shot_roll": "Yuvarlanma",
|
||||
"camera.shot_yaw": "Sapma",
|
||||
"canvas.decal_place_hint": "Çıkartmayı yerleştirmek için bir yüze tıklayın",
|
||||
"canvas.place_hint": "Tuvali yerleştirmek için bir düzleme veya yüze tıklayın",
|
||||
"choose a profile": "bir profil seçin",
|
||||
"choose the cam's and the follower's joints": "kamın ve izleyicinin bağlantılarını seçin",
|
||||
"choose the joints to couple": "eşlenecek bağlantıları seçin",
|
||||
@@ -797,12 +810,16 @@
|
||||
"click sketch curves": "eskiz eğrilerine tıklayın",
|
||||
"cmd.appearance_assign": "Görünüm",
|
||||
"cmd.appearance_chain": "Görünüm zinciri",
|
||||
"cmd.appearance_copy": "Görünümü Kopyala",
|
||||
"cmd.appearance_favorite": "Favori Görünüm",
|
||||
"cmd.appearance_library": "Görünüm kitaplığı",
|
||||
"cmd.appearance_list": "Görünümleri listele",
|
||||
"cmd.appearance_paint": "Görünümü Boya",
|
||||
"cmd.appearance_paste": "Görünümü Yapıştır",
|
||||
"cmd.appearance_pick_up": "Görünümü Al",
|
||||
"cmd.appearance_replace": "Görünümü Değiştir",
|
||||
"cmd.assembly_bom": "Malzeme listesi",
|
||||
"cmd.body.print_face": "Baskı yüzeyi",
|
||||
"cmd.body_rename": "Gövdeyi yeniden adlandır",
|
||||
"cmd.browser_collapse_all": "Tümünü Daralt",
|
||||
"cmd.browser_expand_all": "Tümünü Genişlet",
|
||||
@@ -810,6 +827,7 @@
|
||||
"cmd.browser_move": "Tarayıcı öğelerini taşı",
|
||||
"cmd.browser_order": "Tarayıcı öğelerini sırala",
|
||||
"cmd.browser_rename_group": "Grubu yeniden adlandır",
|
||||
"cmd.browser_reparent": "Bileşene Taşı",
|
||||
"cmd.browser_ungroup": "Grubu çöz",
|
||||
"cmd.browser_visibility": "Göster/Gizle",
|
||||
"cmd.canvas_calibrate": "Tuvali kalibre et",
|
||||
@@ -836,7 +854,9 @@
|
||||
"cmd.component_metadata": "Parça özellikleri",
|
||||
"cmd.component_metadata_set": "Parça özelliklerini değiştir",
|
||||
"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_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",
|
||||
"cmd.component_relink": "Kaynağı yeniden bağla",
|
||||
@@ -973,6 +993,7 @@
|
||||
"cmd.motion_trace": "Hareket izi",
|
||||
"cmd.motion_trace_edit": "Hareket İzini Düzenle",
|
||||
"cmd.motion_trace_keep": "Hareket izini koru",
|
||||
"cmd.occurrence_align": "Örneği Hizala",
|
||||
"cmd.occurrence_copy": "Yapıştır (örnek)",
|
||||
"cmd.occurrence_drag": "Tekrarı Taşı",
|
||||
"cmd.occurrence_ground": "Sabitle",
|
||||
@@ -1030,11 +1051,17 @@
|
||||
"cmd.render_library_surface_unbind": "Yüzey Bağlamını Kaldır",
|
||||
"cmd.render_library_targets": "Render Kitaplık Hedeflerini Bul",
|
||||
"cmd.render_lights_add": "Işık Ekle",
|
||||
"cmd.render_lights_duplicate": "Render Işığını Çoğalt",
|
||||
"cmd.render_lights_list": "Render Işıklarını Listele",
|
||||
"cmd.render_lights_mute": "Işığı Sustur",
|
||||
"cmd.render_lights_remove": "Render Işığını Kaldır",
|
||||
"cmd.render_lights_set": "Render Işığını Ayarla",
|
||||
"cmd.render_lights_solo": "Işığı Solo Yap",
|
||||
"cmd.render_optics_bind": "Optik Bağlamı Kaydet",
|
||||
"cmd.render_optics_unbind": "Optik Bağlamı Kaldır",
|
||||
"cmd.render_output_size": "Render Çıktı Boyutunu Ayarla",
|
||||
"cmd.render_preflight": "Render isteğini kontrol et",
|
||||
"cmd.render_preflight_copy": "Render Kontrol Raporunu Kopyala",
|
||||
"cmd.render_preset_apply": "Yerli Stüdyoyu Uygula",
|
||||
"cmd.render_preset_resolve": "Yerli Stüdyoyu İncele",
|
||||
"cmd.render_quality_prepare": "Sabit Örnekleme Bütçesini İncele",
|
||||
@@ -1057,6 +1084,13 @@
|
||||
"cmd.render_start": "Resmi render et",
|
||||
"cmd.render_status": "Render ilerlemesi",
|
||||
"cmd.select_clear": "Seçimi temizle",
|
||||
"cmd.settings_profile_apply": "Ayar Profilini Uygula",
|
||||
"cmd.settings_profile_delete": "Ayar Profilini Sil",
|
||||
"cmd.settings_profile_export": "Ayar Profilini Dışa Aktar",
|
||||
"cmd.settings_profile_import": "Ayar Profilini İçe Aktar",
|
||||
"cmd.settings_profile_list": "Ayar Profilleri",
|
||||
"cmd.settings_profile_rename": "Ayar Profilini Yeniden Adlandır",
|
||||
"cmd.settings_profile_save": "Ayar Profilini Kaydet",
|
||||
"cmd.sheet_convert": "Sac metale dönüştür",
|
||||
"cmd.sheet_export_bend_table": "Büküm tablosunu dışa aktar",
|
||||
"cmd.sheet_export_dxf": "Açınımı DXF olarak dışa aktar",
|
||||
@@ -1070,6 +1104,16 @@
|
||||
"cmd.sheet_refold": "Yeniden katla",
|
||||
"cmd.sheet_rules": "Sac metal kurallarını listele",
|
||||
"cmd.sheet_unfold": "Katlamayı aç",
|
||||
"cmd.sketch.construction_line": "Yardımcı doğru",
|
||||
"cmd.sketch.construction_line.angle": "Açılı yardımcı doğru",
|
||||
"cmd.sketch.construction_line.bisector": "Açıortay",
|
||||
"cmd.sketch.construction_line.horizontal": "Yatay yardımcı doğru",
|
||||
"cmd.sketch.construction_line.parallel": "Paralel yardımcı doğru",
|
||||
"cmd.sketch.construction_line.perpendicular": "Dik yardımcı doğru",
|
||||
"cmd.sketch.construction_line.tangent_at": "Eğri noktasındaki teğet",
|
||||
"cmd.sketch.construction_line.tangent_external": "Dış noktadan geçen teğet",
|
||||
"cmd.sketch.construction_line.tangent_two": "Ortak teğet",
|
||||
"cmd.sketch.construction_line.vertical": "Dikey yardımcı doğru",
|
||||
"cmd.sketch_arc3d": "3B Yay",
|
||||
"cmd.sketch_arc_center_point": "Merkez noktalı yay",
|
||||
"cmd.sketch_arc_tangent": "Teğet yay",
|
||||
@@ -1102,6 +1146,8 @@
|
||||
"cmd.sketch_drawing_bounds": "Çizim Boyutu",
|
||||
"cmd.sketch_edit": "Eskizi düzenle",
|
||||
"cmd.sketch_edit_text": "Metni düzenle",
|
||||
"cmd.sketch_equation_curve": "Denklem Eğrisi",
|
||||
"cmd.sketch_equation_curve_edit": "Denklem Eğrisini Düzenle",
|
||||
"cmd.sketch_export_dxf": "DXF olarak kaydet",
|
||||
"cmd.sketch_finish_auto_constrain": "Otomatik kısıtlamayla bitir",
|
||||
"cmd.sketch_fit_curves_to_section": "Eğrileri ağ kesitine uydur",
|
||||
@@ -1198,6 +1244,7 @@
|
||||
"cmd.view.render_browser": "Render tarayıcısı",
|
||||
"cmd.view.render_dock": "Render panelini taşı",
|
||||
"cmd.view.render_focus": "Render odak görünümü",
|
||||
"cmd.view.status_items": "Görünüm Alanı Durum Öğeleri",
|
||||
"cmd.view_ambient_occlusion": "Ortam tıkanması",
|
||||
"cmd.view_angle_readout": "Görünüm açıları",
|
||||
"cmd.view_apply": "Adlandırılmış görünümü geri yükle",
|
||||
@@ -1219,6 +1266,7 @@
|
||||
"cmd.view_normal_to": "Seçime Dik",
|
||||
"cmd.view_previous_tab": "Önceki Sekme",
|
||||
"cmd.view_rename": "Adlandırılmış görünümü yeniden adlandır",
|
||||
"cmd.view_render_frame": "Render Kadraj Kılavuzları",
|
||||
"cmd.view_right": "Sağ Görünüm",
|
||||
"cmd.view_save": "Yeni adlandırılmış görünüm",
|
||||
"cmd.view_shots_strip": "Çekim Şeridi",
|
||||
@@ -1229,10 +1277,14 @@
|
||||
"color.copy": "Renk değerlerini kopyalamak için tıklayın",
|
||||
"color.edit": "Rengi düzenlemek için tıklayın",
|
||||
"color.gamma": "Renk değerleri 0-255 gama uzayında gösteriliyor",
|
||||
"color.hex": "Hex",
|
||||
"color.hex_help": "#rrggbb veya #rrggbbaa girin; altı hane opaklığı korur",
|
||||
"color.hex_hint": "#rrggbb",
|
||||
"color.hue": "Ton",
|
||||
"color.linear": "Renk değerleri 0-1 doğrusal uzayda gösteriliyor",
|
||||
"color.saturation_value": "Doygunluk ve değer",
|
||||
"color.selected": "Seçili renk",
|
||||
"config.column_filter_empty": "Eşleşen sütun yok",
|
||||
"contact.components_count": "{count} bileşen",
|
||||
"contact.enabled_label": "etkin",
|
||||
"coupling.gear_1": "Dişli 1",
|
||||
@@ -1350,6 +1402,17 @@
|
||||
"help.occurrence.move": "Montaj parçasını yalnızca eklemlerinin izin verdiği serbestliklerde taşır. Bir parça yüzü seçtikten sonra kullanılabilir oku veya yayı sürükleyin.",
|
||||
"help.parameters.change": "Tasarımın ölçülerini yöneten adlandırılmış değerlerin ve ifadelerin tablosunu açar. Bir ölçüyü tek yerden değiştirip onu kullanan her özelliğin buna uymasını sağlamak için kullanın.",
|
||||
"help.pointcloud.insert": "PLY, XYZ veya PTS taramasını modelin yanına nokta bulutu olarak ekler. Ölçülmüş verilerden model oluştururken referans olarak kullanın.",
|
||||
"help.sketch.construction_line": "İki noktadan geçen sonsuz bir yardımcı doğru oluşturun. Profilleri bölmeden bir eskizi kurgulamak için kullanın.",
|
||||
"help.sketch.construction_line.angle": "Eskizin X ekseninden belirli bir açıyla sonsuz bir yardımcı doğru oluşturun. Noktayı seçip açıyı yazın veya yönünü seçin.",
|
||||
"help.sketch.construction_line.bisector": "Kesişen iki düz eskiz çizgisini seçin, ardından iki açıortaydan birini seçin. Sonuç, kaynak çizgilerini izler.",
|
||||
"help.sketch.construction_line.horizontal": "Bir noktadan geçen sonsuz yatay bir yardımcı doğru oluşturun. Yatay kısıtlaması sonraki düzenlemelerde de korunur.",
|
||||
"help.sketch.construction_line.parallel": "Düz bir eskiz çizgisi seçin, ardından paralel bir sonsuz yardımcı doğru yerleştirin. Başka bir referans seçmek için sağ tıklayın.",
|
||||
"help.sketch.construction_line.perpendicular": "Düz bir eskiz çizgisi seçin, ardından dik bir sonsuz yardımcı doğru yerleştirin. Başka bir referans seçmek için sağ tıklayın.",
|
||||
"help.sketch.construction_line.tangent_at": "Bir daire veya dairesel yay üzerindeki bir noktada ya da açık bir spline veya konik eğrinin uç noktasında teğet oluşturun. Önce eğriyi, sonra değme noktasını seçin.",
|
||||
"help.sketch.construction_line.tangent_external": "Bir daire veya dairesel yay seçin, ardından bir dış nokta ve bir teğet çözümü seçin. Önizlemeler yalnızca çizilen yay üzerindeki değme noktalarını gösterir.",
|
||||
"help.sketch.construction_line.tangent_two": "İki daire veya dairesel yay seçin, ardından önizlemede ortak bir teğet seçin. Değme noktaları çizilen eğriler üzerinde olmalıdır.",
|
||||
"help.sketch.construction_line.vertical": "Bir noktadan geçen sonsuz dikey bir yardımcı doğru oluşturun. Dikey kısıtlaması sonraki düzenlemelerde de korunur.",
|
||||
"help.sketch.equation_curve": "Formüllerden bir eğri çizer: bir aralık boyunca x(t) ve y(t) ya da y = f(x). Formüller parametre kullanabilir ve eğri onları izler: sinüs dalgası, spiral veya bir dişli dişinin evolventi için kullanın.",
|
||||
"help.solid.align": "Seçili gövdeleri, üzerlerindeki bir nokta, yüz veya düzlem başka bir yerdeki nokta, yüz veya düzlemle buluşacak şekilde taşır. Ofseti ölçmeden bir parçayı diğerinin yanına yerleştirmek için kullanın.",
|
||||
"help.solid.chamfer": "Seçili kenarları, belirlediğiniz mesafede düz bir pahla keser. Keskin kenarları kırmak veya bir pim ya da vidanın girişini kolaylaştırmak için kullanın.",
|
||||
"help.solid.combine": "Gövdeleri birleştirir, bir hedef gövdeden çıkarır veya yalnızca paylaştıkları hacmi tutar ve araç gövdelerini tutabilir. Parçaları kaynaştırmak, bir şekli diğeriyle kesmek veya iki gövdenin örtüştüğü yeri bulmak için kullanın.",
|
||||
@@ -1376,6 +1439,8 @@
|
||||
"help.timeline.compute_all": "Zaman çizelgesindeki her özelliği baştan yeniden oluşturur. Modeli tazelemek ve hangi özelliklerin başarısız olduğunu görmek için kullanın.",
|
||||
"help.timeline.delete": "Seçili özellikleri zaman çizelgesinden siler; bir eskizi silmek ondan oluşturulan özellikleri de siler. Artık istenmeyen bir adımı kaldırmak için kullanın.",
|
||||
"help.timeline.play": "Kalan özellikleri, her yeniden hesaplamadan sonra yarım saniye bekleyerek tek tek yeniden oynatır. Durdurmak için bu komutu yeniden çalıştırın veya Esc tuşuna basın; Sona git hemen atlar.",
|
||||
"history.sketch_under_constrained": "Eksik kısıtlanmış ({count} serbestlik derecesi)",
|
||||
"history.sketch_under_constrained_one": "Eksik kısıtlanmış ({count} serbestlik derecesi)",
|
||||
"hole_presets.confirm_delete": "Silmeyi onayla",
|
||||
"hole_presets.delete": "Sil",
|
||||
"hole_presets.delete_prompt": "Bu kişisel ön ayar silinsin mi? Mevcut delikler değişmez.",
|
||||
@@ -1585,6 +1650,8 @@
|
||||
"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.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.",
|
||||
"nav.right_drag_orbits.label": "Sağ sürükleme döndürür",
|
||||
"nav.solidworks.help": "Orta sürükleme döndürür, Ctrl+orta kaydırır, Shift+orta yakınlaştırır, sağ sürükleme döndürür",
|
||||
"nav.tinkercad.help": "Sağ sürükleme döndürür, orta veya Shift+sağ sürükleme kaydırır",
|
||||
"nav.touchpad.help": "Kaydırmak için Shift tuşunu basılı tutun ve imleci hareket ettirin; Alt/Option ile döndürün. Fare tuşu gerekmez. Dikey kaydırma yakınlaştırır.",
|
||||
@@ -1606,6 +1673,9 @@
|
||||
"navbar.custom.customise": "Gezinme çubuğunu özelleştir…",
|
||||
"navbar.custom.design": "Tasarım",
|
||||
"navbar.custom.duplicate": "Bu yuva zaten çubukta.",
|
||||
"navbar.custom.edge": "Kenar",
|
||||
"navbar.custom.edge_bottom": "Alt",
|
||||
"navbar.custom.edge_top": "Üst",
|
||||
"navbar.custom.fade": "Soldur",
|
||||
"navbar.custom.full": "Başka bir yuva eklemek için bir yuvayı kaldırın.",
|
||||
"navbar.custom.hidden_design": "Tasarımda gizli",
|
||||
@@ -1712,6 +1782,7 @@
|
||||
"parts.dimension.thickness": "Kalınlık",
|
||||
"parts.dimension.width": "Genişlik",
|
||||
"parts.family.bearing": "Derin oluklu bilyeli rulman (zarf)",
|
||||
"parts.family.button_head_cap_screw": "Mercimek başlı alyan cıvata",
|
||||
"parts.family.dowel_pin": "Silindirik pim (paralel)",
|
||||
"parts.family.hex_bolt": "Altıgen başlı cıvata / vida",
|
||||
"parts.family.hex_nut": "Altıgen somun",
|
||||
@@ -1770,6 +1841,11 @@
|
||||
"prefs.bottom_centre": "Alt orta",
|
||||
"prefs.bottom_right": "Sağ alt",
|
||||
"prefs.browser": "Tarayıcı",
|
||||
"prefs.browser_eye_left": "Göz, daraltma okunun yanında",
|
||||
"prefs.browser_eye_left_note": "Tarayıcı satırları göster/gizle gözünü, satırların sağ ucundaki bir sütun yerine eskiden olduğu gibi daraltma okunun yanında gösterir.",
|
||||
"prefs.browser_reveal_on_hover": "Üzerine gelince göster",
|
||||
"prefs.browser_show_note": "Görünür, tarayıcıyı ekranda tutar. Üzerine gelince göster, imleç onun yerine veya sol kenarındaki ince sekmeye gelene kadar onu gizli tutar; imleç içindeyken veya ondan açılan bir menü açıkken açık kalır.",
|
||||
"prefs.browser_shown": "Görünür",
|
||||
"prefs.built_in": "Yerleşik",
|
||||
"prefs.button_left": "Sol sürükleme",
|
||||
"prefs.button_middle": "Orta sürükleme",
|
||||
@@ -1817,6 +1893,8 @@
|
||||
"prefs.graphics_card_restart": "SolidCraft'ı yeniden başlattıktan sonra etkili olur. WGPU_ADAPTER_NAME bu seçimi geçersiz kılar.",
|
||||
"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",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out_note": "Eskiz, kalabalık yaratacak kadar uzaklaştırıldığında kısıtlama simgeleri görünümden çıkar. Kapalıyken: her yakınlaştırma düzeyinde sabit ekran boyutunda kalırlar.",
|
||||
"prefs.history": "Geçmiş",
|
||||
"prefs.history_origin_bodies": "Başlangıç ve gövdeleri üstte göster",
|
||||
"prefs.history_origin_bodies_note": "Soldaki veya tarayıcının altındaki Geçmiş, etkin bileşenin Gövdeler klasörü ve Başlangıç ile başlar; gözleri ve menüleri tarayıcıdaki gibidir. Alttaki Geçmiş değişmez.",
|
||||
@@ -1840,6 +1918,8 @@
|
||||
"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.linked_designs": "Bağlı tasarımlar",
|
||||
"prefs.live_input": "Canlı girdi",
|
||||
"prefs.live_preview": "Canlı önizleme",
|
||||
@@ -1853,6 +1933,9 @@
|
||||
"prefs.max_file_size_step": "STEP ve içe aktarma dosyaları",
|
||||
"prefs.max_file_size_web": "Web uygulaması 512 MB’a kadar STEP ve içe aktarma dosyalarını, 256 MB’a kadar tasarımları açar: tarayıcı sekmesi daha büyük dosyaları barındıramaz. Masaüstü uygulamasında bu sınırlar artırılabilir.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "Büyük simgeler",
|
||||
"prefs.menu_list": "Liste",
|
||||
"prefs.menus": "Menüler",
|
||||
"prefs.minutes": "dakika",
|
||||
"prefs.minutes_suffix": " min",
|
||||
"prefs.mouse": "Fare ve trackpad",
|
||||
@@ -1872,14 +1955,40 @@
|
||||
"prefs.origin_plane_names": "Orijin düzlemi adları",
|
||||
"prefs.origin_plane_names_note": "Tarayıcıda, diyaloglarda ve listelerde gösterilen adlar. Z yukarı olmak üzere {top} XY, {front} XZ ve {right} YZ'dir. Komutlar, betikler ve kayıtlı tasarımlar XY, XZ ve YZ adlarını korur.",
|
||||
"prefs.overall_speed": "Genel hız",
|
||||
"prefs.persistent_ribbon_pressed": "Etkin şerit komutlarını vurgula",
|
||||
"prefs.persistent_ribbon_pressed_help": "Şerit komutlarını, etkileri açık olduğu sürece vurgula. Önceki nötr görünümü ve komut yardımını korumak için kapatın.",
|
||||
"prefs.perspective_fov": "Görüş alanı",
|
||||
"prefs.placement": "Yerleşim",
|
||||
"prefs.prefer_free_sketch_points": "Noktalar örtüştüğünde hareketli eskiz noktalarını tercih et",
|
||||
"prefs.preview_choices": "Seçimlerinizin önizlemesi",
|
||||
"prefs.print_face_highlight": "Baskı yüzeyi vurgusu",
|
||||
"prefs.print_face_highlight_tip": "Bir gövdenin baskı yüzeyinin görünümde ne kadar güçlü renklendirilip çerçeveleneceği. %0 vurgu göstermez.",
|
||||
"prefs.profile_active": "Etkin profil",
|
||||
"prefs.profile_applied": "Profil uygulandı.",
|
||||
"prefs.profile_changed": "Profil kaydedildikten sonra ayarlar değişti. Kaydet, onları profilde saklar.",
|
||||
"prefs.profile_deleted": "Profil silindi.",
|
||||
"prefs.profile_export": "Dışa aktar…",
|
||||
"prefs.profile_exported": "Profil dışa aktarıldı.",
|
||||
"prefs.profile_imported": "Profil içe aktarıldı. Ona geçmek için Etkin profil altında seçin.",
|
||||
"prefs.profile_name_hint": "Profil adı, ör. Dizüstü",
|
||||
"prefs.profile_rename": "Yeniden adlandır",
|
||||
"prefs.profile_renamed": "Profil yeniden adlandırıldı.",
|
||||
"prefs.profile_save": "Kaydet",
|
||||
"prefs.profile_saved": "Geçerli ayarlar profile kaydedildi.",
|
||||
"prefs.profile_skipped": "Profil uygulandı; {n} ayar geçerli değerini korudu (günlüğe bakın).",
|
||||
"prefs.profiles": "Ayar profilleri",
|
||||
"prefs.profiles_note": "Kısayollar, araç çubuğu, gezinme, radyal menü ve arayüz ayarları; tek bir ad altında birlikte saklanır ve tek seferde değiştirilir.",
|
||||
"prefs.project_options_dialog": "Yansıtma seçenekleri diyaloğu",
|
||||
"prefs.project_options_dialog_note": "Açık: yansıtmadan önce Geometri veya Gövdeler ile Yansıtma bağını seçin. Kapalı: önceki doğrudan seçim iş akışını korur.",
|
||||
"prefs.quality_default": "varsayılan",
|
||||
"prefs.quality_fastest": "en hızlı",
|
||||
"prefs.quality_slowest": "en yavaş",
|
||||
"prefs.quality_smoother": "daha pürüzsüz",
|
||||
"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_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",
|
||||
"prefs.saving": "Otomatik kayıt ve kurtarma",
|
||||
"prefs.scale_note": "Pencerede ayrıca Ctrl + / Ctrl −. Düğmeler %100'de 28 px.",
|
||||
@@ -1888,21 +1997,34 @@
|
||||
"prefs.search_empty": "Eşleşen ayar yok",
|
||||
"prefs.select": "Seçim",
|
||||
"prefs.select_one_item": "Bir öğe seçin",
|
||||
"prefs.select_other_hidden": "Gizli geometriyi Diğerini seç ile seç",
|
||||
"prefs.select_other_hidden_help": "Açıkken: imlecin yakınındaki gizli gövdeleri, eskizleri ve örtülü kenarları da dahil eder. Kapalıyken: imlecin altındaki görünür geometrinin özgün listesini kullanır.",
|
||||
"prefs.selecting": "Seçme",
|
||||
"prefs.shift": "Shift",
|
||||
"prefs.shift_add_ctrl_toggle": "Shift yalnızca ekler; Ctrl / Cmd değiştirir",
|
||||
"prefs.shift_add_note": "Açık: Shift ekler, Ctrl / Cmd değiştirir. Kapalı (varsayılan): ikisi de değiştirir.",
|
||||
"prefs.shortcuts": "Kısayollar",
|
||||
"prefs.single_surface_analysis": "Tek bir yüzey analizi kullan",
|
||||
"prefs.single_surface_analysis_help": "Kapalıyken yüzey gölgelendirmesi ve Süreklilik birlikte açık kalabilir. Bunu açmak, en son başlatılan veya değiştirilen analizi tutar.",
|
||||
"prefs.sketch_align_points": "Çizerken diğer eskiz noktalarıyla hizala",
|
||||
"prefs.sketch_align_points_note": "Çizim sırasında bir nokta, noktalı bir kılavuzla aynı anda bir eskiz noktasıyla yatay, bir diğeriyle dikey olarak hizalanır ve tıklama her iki hizalamayı da korur. Kapalı: yalnızca önceki noktayla, her seferinde tek eksende.",
|
||||
"prefs.sketch_conflict_colour": "Çakışan geometri çakışma renginde",
|
||||
"prefs.sketch_conflict_colour_note": "Bir eskizin kısıtlamaları çakışırken, başarısız olan kısıtlamanın tuttuğu eğriler ve noktalar yalnızca simgesi veya ölçüsü değil, çakışma renginde de çizilir. Kapalıyken olağan renklerini korurlar.",
|
||||
"prefs.sketch_construction_lines": "Genişletilmiş yardımcı doğru araçları",
|
||||
"prefs.sketch_exit_view": "Bir eskiz bittiğinde",
|
||||
"prefs.sketch_exit_view.keep": "Geçerli görünümü koru",
|
||||
"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_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ış",
|
||||
"prefs.sketch_recent_snap_note": "Çizim sırasında imleç, son iki daire merkezi veya düz kenar orta noktasından gelen kılavuzların kesiştiği yere yapışır. Kapalıyken bu kılavuzlar yalnızca görsel kalır ve imleç hareket etmez.",
|
||||
"prefs.sketch_selection": "Eskiz ve seçim",
|
||||
"prefs.sketch_selection_heading": "Eskiz ve seçim",
|
||||
"prefs.sketch_selection_note": "Eskizlerin nasıl yapıştığı ve tıklamaların nasıl seçtiği.",
|
||||
"prefs.sketch_state_icons": "Eskiz simgeleri durumlarının renginde",
|
||||
"prefs.sketch_state_icons_note": "Geçmiş'te ve tarayıcıda bir eskizin simgesi durumunun rengini alır (eksik kısıtlanmış, tam kısıtlı veya çakışan) ve araç ipucu durumu belirtir. Kapalıyken: her eskizin simgesi aynıdır.",
|
||||
"prefs.sketch_tab_always": "Eskiz sekmesini hep göster",
|
||||
"prefs.sketch_tab_always_note": "Hiçbir eskiz açık değilken Eskiz sekmesi şeritte, Model’den hemen sonra kalır. Çizim araçları bu durumda seçili düzlemde veya yüzde ya da bir sonra seçtiğiniz düzlemde, araç hazır olarak bir eskiz başlatır.",
|
||||
"prefs.sketch_tracking_guides": "Merkez ve orta nokta hizalama kılavuzları",
|
||||
@@ -1912,6 +2034,11 @@
|
||||
"prefs.source": "Kaynak",
|
||||
"prefs.speed": "Hız",
|
||||
"prefs.stand_up_note": "Bir STEP veya IGES dosyası Z yukarı açıldığında, X etrafında çeyrek tur öner.",
|
||||
"prefs.stand_up_unknown": "Bilinmeyen programlardan gelen dosyalar için",
|
||||
"prefs.stand_up_unknown.ask": "Diyalogda sor",
|
||||
"prefs.stand_up_unknown.note": "Not göster",
|
||||
"prefs.stand_up_unknown.off": "Önerme",
|
||||
"prefs.stand_up_unknown_note": "Dosyanın başlığı, Y yukarı olarak kaydettiği bilinen bir program belirtmediğinde önerinin nasıl sunulacağı. Not yalnızca dosya sırtüstü yatıyor gibi göründüğünde gösterilir; Diyalogda sor, daha önce olduğu gibi böyle her dosya için sorar.",
|
||||
"prefs.status_bar": "Durum çubuğu",
|
||||
"prefs.status_bar_note": "Pencerenin alt kenarındaki ince bir çubuk, nelerin düzenlendiğini, açık eskizin durumunu rengiyle ve tasarımın birimlerini gösterir; birimlere tıklamak onları değiştirir. Bu durumda eskizin durumu artık görünümün köşesinde gösterilmez.",
|
||||
"prefs.status_done": "Tamam",
|
||||
@@ -1937,7 +2064,14 @@
|
||||
"prefs.toast_place": "Bildirimler",
|
||||
"prefs.toggle_item": "Öğeyi ekleyin veya yeniden çıkarın",
|
||||
"prefs.toolbar": "Araç çubuğu",
|
||||
"prefs.toolbar_tabs": "Araç çubuğu sekmeleri",
|
||||
"prefs.top": "Üst",
|
||||
"prefs.trackpad_zoom_modifier": "İki parmakla kaydırarak yakınlaştırma tuşu",
|
||||
"prefs.trackpad_zoom_modifier.alt": "⌥ / Alt",
|
||||
"prefs.trackpad_zoom_modifier.cmd": "⌘",
|
||||
"prefs.trackpad_zoom_modifier.ctrl": "Ctrl",
|
||||
"prefs.trackpad_zoom_modifier.help": "Dikey kaydırmayla imlecin olduğu yere yakınlaştırmak için yalnızca bu tuşu basılı tutun. Kapalı, mevcut kaydırma davranışını korur.",
|
||||
"prefs.trackpad_zoom_modifier.off": "Kapalı",
|
||||
"prefs.translucent": "Yarı saydam",
|
||||
"prefs.under_browser": "Tarayıcının altında",
|
||||
"prefs.under_cursor": "İmlecin altında",
|
||||
@@ -1947,13 +2081,24 @@
|
||||
"prefs.view_confirm_corner": "Görünümün köşesinde Tamam ve İptal",
|
||||
"prefs.view_confirm_corner_note": "Bir diyalog veya eskiz açıkken, görünümün sağ üstündeki bir onay işareti ve çarpı işareti, diyalogun Tamam ve İptal düğmelerinin ya da eskizi bitirip iptal etmenin işini yapar. Görünüm yönlendiricileri bunların altına iner.",
|
||||
"prefs.viewport": "Görünüm alanı",
|
||||
"prefs.viewport_analysis_status": "Görünüm alanı analiz durumunu göster",
|
||||
"prefs.viewport_analysis_status_help": "Etkin analizleri görünüm alanının üstünde gösterir, her öğeden göstergesini açar ve en yeni analizi kapatmak için Esc tuşunu kullanır. Köşe göstergelerini ve Esc'in önceki davranışını geri yüklemek için kapatın.",
|
||||
"prefs.viewport_background": "Arka plan",
|
||||
"prefs.viewport_background_colour": "Renk",
|
||||
"prefs.viewport_background_theme": "Temanın degradesi",
|
||||
"prefs.wheel": "Tekerlek",
|
||||
"prefs.wheel_trackpad": "Tekerlek ve trackpad",
|
||||
"prefs.workspace": "Çalışma alanı",
|
||||
"prefs.zoom_stop_at_model": "Yakınlaştırmayı modelde durdur",
|
||||
"prefs.zoom_stop_at_model_tip": "3B fare ve cihaz uygulaması görünümün ortasındaki yüzeye doğru yakınlaştırır; sürükleyerek yakınlaştırma, sürüklemenin başladığı noktayı sabit tutar. Kapalı: yakınlaştırma modelin içinden geçebilir. Tekerlek ve iki parmakla sıkıştırma ayrı olarak İmlece doğru yakınlaştır ayarını kullanır",
|
||||
"prefs.zoom_toward_cursor": "Yakınlaştırma, imlece doğru",
|
||||
"properties.title": "Özellikler: {name}",
|
||||
"recovery.days_ago": "{count} gün önce",
|
||||
"recovery.detail": "{file} · {features} özellik · otomatik kaydedilen: {when}",
|
||||
"recovery.discard_all": "Tümünü at",
|
||||
"recovery.discard_all_detail": "Bu işlem geri alınamaz.",
|
||||
"recovery.discard_all_title": "{count} otomatik kayıt atılsın mı?",
|
||||
"recovery.discard_all_title_one": "1 otomatik kayıt atılsın mı?",
|
||||
"recovery.hours_ago": "{count} sa. önce",
|
||||
"recovery.just_now": "az önce",
|
||||
"recovery.min_ago": "{count} dk. önce",
|
||||
@@ -2038,6 +2183,7 @@
|
||||
"render.appearance_editor.weight": "Ağırlık",
|
||||
"render.appearance_physics_separate": "Görsel görünüm yoğunluk, termal veri veya diğer mühendislik özelliklerini atamaz.",
|
||||
"render.appearances": "Görünümler",
|
||||
"render.assign_help": "Seçime uygulamak için tıklayın; önizleyip bırakmak için sürükleyin veya hiçbir şey seçili değilken boyamak için tıklayın.",
|
||||
"render.availability_native": "Yerli kompozisyon; uygulamadan önce inceleyin",
|
||||
"render.availability_projection": "Desteklenen yansıtma; kapsama ve atlamalara bakın",
|
||||
"render.availability_reference": "Referans verisi; otomatik atama değildir",
|
||||
@@ -2076,6 +2222,7 @@
|
||||
"render.bloom_threshold": "Doğrusal vurgu eşiği",
|
||||
"render.browse_capabilities": "Tam yetenek kataloğunu incele",
|
||||
"render.browse_environments": "Orijinal HDR ortamlarına göz at",
|
||||
"render.browse_looks": "Göz at…",
|
||||
"render.browse_native_scenes": "Yerli stüdyolara göz at",
|
||||
"render.browse_physical": "Fiziksel referansları incele",
|
||||
"render.browse_quality": "Kalite özelliklerini incele",
|
||||
@@ -2099,6 +2246,82 @@
|
||||
"render.check_opaque": "Saydamlık: yalnızca opak yüzeyler; arka plan alfası desteklenir.",
|
||||
"render.check_sorted_alpha": "Saydamlık: sıralı alfa yaklaşımı.",
|
||||
"render.check_transmission": "Saydamlık: fiziksel geçirgenlik.",
|
||||
"render.children.absolute_floor": "Mutlak taban",
|
||||
"render.children.adaptive": "Adaptif",
|
||||
"render.children.add_look_reason": "Etkin durumunu ve şiddetini düzenlemek için bir görünüm ekleyin.",
|
||||
"render.children.all_shots": "Tüm çekimler",
|
||||
"render.children.bloom": "Bloom",
|
||||
"render.children.check": "Kontrol",
|
||||
"render.children.check_now": "Şimdi kontrol et",
|
||||
"render.children.chromatic": "Kromatik",
|
||||
"render.children.compact": "Sıkışık",
|
||||
"render.children.contrast": "Kontrast",
|
||||
"render.children.copy_image": "Kopyala",
|
||||
"render.children.copy_job": "İş JSON'unu kopyala",
|
||||
"render.children.copy_request": "İstek JSON'unu kopyala",
|
||||
"render.children.coverage_error": "Kapsama hatası",
|
||||
"render.children.cpu": "CPU ışın izleme",
|
||||
"render.children.denoise": "Bitmiş görüntüdeki gürültüyü azalt",
|
||||
"render.children.depth": "Yol derinliği",
|
||||
"render.children.distortion": "Bozulma",
|
||||
"render.children.edge_samples": "Kenar örnekleri",
|
||||
"render.children.effects_banner": "Bu efektler GPU yol izleme gerektirir. Bunu açmak yöntemi asla değiştirmez.",
|
||||
"render.children.enable_adaptive": "Adaptif sınırları düzenlemek için bağımsız pilotları etkinleştirin.",
|
||||
"render.children.enable_bloom": "Şiddetini, eşiğini ve yarıçapını düzenlemek için bloom'u etkinleştirin.",
|
||||
"render.children.finishing": "Sonlandırma",
|
||||
"render.children.flare": "Flare",
|
||||
"render.children.fresnel": "Fresnel tabakaları",
|
||||
"render.children.ghosts": "Hayalet halkalar",
|
||||
"render.children.glass": "Cam taşınımı",
|
||||
"render.children.go_to": "{editor} bölümüne git",
|
||||
"render.children.grain": "Gren",
|
||||
"render.children.halo": "Hale",
|
||||
"render.children.hdr": "HDR",
|
||||
"render.children.keep_master": "Ham doğrusal ana görüntüyü sakla",
|
||||
"render.children.last_result": "Son: {number} · {width} × {height} · {phase}",
|
||||
"render.children.lens_effects": "Mercek efektleri",
|
||||
"render.children.look_summary": "{tone} · {count} görünüm",
|
||||
"render.children.looks": "Yaratıcı görünümler",
|
||||
"render.children.memory": "Bellek",
|
||||
"render.children.method": "Yöntem",
|
||||
"render.children.minimum": "En az",
|
||||
"render.children.needs_cpu": "CPU ışın izleme gerektirir. Bunu açmak yöntemi asla değiştirmez.",
|
||||
"render.children.nested": "Sertifikalı iç içe dielektrikler",
|
||||
"render.children.not_checked": "Kontrol edilmedi",
|
||||
"render.children.path": "Yol izleme",
|
||||
"render.children.pilots": "Pilot örnekler",
|
||||
"render.children.preview": "Önizleme yükü",
|
||||
"render.children.progressive_reason": "Örnek sayısını ve tohumunu düzenlemek için Artımlı'yı seçin.",
|
||||
"render.children.radius": "Yarıçap",
|
||||
"render.children.reflect_depth": "Yansıma derinliği",
|
||||
"render.children.reflect_samples": "Yansıma örnekleri",
|
||||
"render.children.regrade": "Yeniden derecelendir",
|
||||
"render.children.relative_error": "Göreceli hata",
|
||||
"render.children.render": "Render",
|
||||
"render.children.render_shot": "Çekimi render et",
|
||||
"render.children.renderer": "İşleyici",
|
||||
"render.children.renderers": "İşleyiciler",
|
||||
"render.children.request": "Bu istek",
|
||||
"render.children.reset_finishing": "Sonlandırmayı sıfırla",
|
||||
"render.children.review_quality": "Bir kalite özelliğini incele…",
|
||||
"render.children.rgb_gain": "RGB kazancı",
|
||||
"render.children.samples": "Örnekler",
|
||||
"render.children.sampling": "Örnekleme",
|
||||
"render.children.saturation": "Doygunluk",
|
||||
"render.children.scene_summary": "{bodies} gövde · {triangles} üçgen",
|
||||
"render.children.seed": "Tohum",
|
||||
"render.children.sharpening": "Keskinleştirme",
|
||||
"render.children.single_image": "Tek görüntü",
|
||||
"render.children.soft_focus": "Yumuşak odak",
|
||||
"render.children.streak": "Çizgi",
|
||||
"render.children.strength": "Şiddet",
|
||||
"render.children.studio": "Stüdyo",
|
||||
"render.children.threshold": "Eşik",
|
||||
"render.children.tone": "Ton",
|
||||
"render.children.under_hood": "Perde arkası",
|
||||
"render.children.vignette": "Vinyet",
|
||||
"render.children.workstation": "İş istasyonu",
|
||||
"render.choose_look": "Önce Görünümler'de bir görünüm seçin",
|
||||
"render.chromatic_offset": "Kromatik kenar kayması",
|
||||
"render.clear_stage": "Kaydedilmiş sahneyi kaldır",
|
||||
"render.copy_request": "JSON kopyala",
|
||||
@@ -2350,8 +2573,33 @@
|
||||
"render.flare_strength": "Vurgu flare şiddeti",
|
||||
"render.flare_threshold": "Sahne-doğrusal flare eşiği",
|
||||
"render.fold_browser": "Tarayıcıyı daralt",
|
||||
"render.frame_aspect": "En-boy oranı",
|
||||
"render.frame_centre_mark": "Merkez işareti",
|
||||
"render.frame_copy_limit": "Kullanılabilir bir kopya çekim adı yok",
|
||||
"render.frame_copy_name": "{name} kopya {number}",
|
||||
"render.frame_custom": "Özel",
|
||||
"render.frame_distance_unit": " × boyut",
|
||||
"render.frame_duplicate": "Çoğalt",
|
||||
"render.frame_exposure": "Pozlama",
|
||||
"render.frame_fov": "Görüş alanı",
|
||||
"render.frame_guides": "Kılavuzlar",
|
||||
"render.frame_invalid_aspect": "Geçersiz en-boy oranı ön ayarı",
|
||||
"render.frame_lens": "FOV {fov}° · ≈ {mm} mm",
|
||||
"render.frame_name": "Ad",
|
||||
"render.frame_output_size": "Çıktı boyutu",
|
||||
"render.frame_photo": "3:2 · Fotoğraf",
|
||||
"render.frame_portrait": "4:5 · Portre",
|
||||
"render.frame_presets": "Kadraj ön ayarları",
|
||||
"render.frame_presets_pending": "Çekim düzenleyicisiyle gelecek",
|
||||
"render.frame_projection": "Projeksiyon",
|
||||
"render.frame_render_shot": "Bu çekimi render et",
|
||||
"render.frame_rule_thirds": "Üçler kuralı",
|
||||
"render.frame_safe_area": "Güvenli alan",
|
||||
"render.frame_shot_title": "Çekim · {name}",
|
||||
"render.frame_square": "1:1 · Kare",
|
||||
"render.frame_tall": "9:16 · Dikey",
|
||||
"render.frame_thirds": "Üçte birler",
|
||||
"render.frame_wide": "16:9 · Geniş ekran",
|
||||
"render.gpu_depth": "En fazla yol derinliği",
|
||||
"render.gpu_options_help": "Örnekler, derinlik, tohum ve pozlama bu kurulumla kaydedilir. Yalnızca etkin görüntü yöntemi kaydedilir.",
|
||||
"render.gpu_path_failed": "GPU yol izleyici kullanılamıyor",
|
||||
@@ -2379,6 +2627,7 @@
|
||||
"render.heading_results": "SONUÇLAR",
|
||||
"render.heading_shots": "ÇEKİMLER",
|
||||
"render.hide_shots_label": "Çekim şeridini gizle",
|
||||
"render.hover_look": "{object} · {look} · seçmek için tıklayın",
|
||||
"render.image_advanced": "Gelişmiş GPU kontrolleri",
|
||||
"render.image_cancelled": "Render iptal edildi",
|
||||
"render.image_cancelling": "İptal ediliyor",
|
||||
@@ -2525,17 +2774,95 @@
|
||||
"render.library_uniform_ack": "Uzamsal veya gelişmiş efektler olmadan tek biçimli yaklaşımı kullan",
|
||||
"render.library_usable": "Çalıştırılabilir yansıtmalar",
|
||||
"render.library_users": "Kullanan gövdeler",
|
||||
"render.light_aim": "Şuna yönelt",
|
||||
"render.light_anchor_pending": "Işık koordinat çerçevesini değiştirmek henüz desteklenmiyor.",
|
||||
"render.light_azimuth": "Azimut",
|
||||
"render.light_backend_limit": "Işıkları düzenlemek için CPU ışın izleme veya açık bir GPU stüdyosu seçin. Eski GPU stüdyo ışıkları salt okunur kalır.",
|
||||
"render.light_behaviour": "DAVRANIŞ",
|
||||
"render.light_default_fill": "Dolgu ışığı",
|
||||
"render.light_default_key": "Ana ışık",
|
||||
"render.light_default_number": "Işık {number}",
|
||||
"render.light_default_rim": "Kenar ışığı",
|
||||
"render.light_delete": "Sil",
|
||||
"render.light_distance": "Mesafe",
|
||||
"render.light_duplicate": "Çoğalt",
|
||||
"render.light_elevation": "Yükseklik açısı",
|
||||
"render.light_gpu_punctual": "Nokta ve Spot ışıklar CPU ışın izleme ile kullanılabilir.",
|
||||
"render.light_hard_shadow": "Bu ışığın gölgeleri serttir; ışık veren yumuşaklığı kullanılamıyor.",
|
||||
"render.light_intensity": "Şiddet",
|
||||
"render.light_missing": "Bu ışık artık yok. Tarayıcıda bir ışık seçin.",
|
||||
"render.light_place_highlight": "Vurguya göre yerleştir",
|
||||
"render.light_place_pending": "Vurguya göre yerleştirme, görünüm alanında ışık seçmeyi gerektirir ve henüz desteklenmiyor.",
|
||||
"render.light_position": "KONUM",
|
||||
"render.light_preview_help": "Canlı görünüm gölgesizdir; Alan ışıkları merkez örnekli bir yaklaşım kullanır. Render, tam ışık modelini kullanır.",
|
||||
"render.light_product_centre": "Ürün merkezi",
|
||||
"render.light_size": "Boyut",
|
||||
"render.light_softness": "Yumuşaklık",
|
||||
"render.light_temperature": "Renk sıcaklığı",
|
||||
"render.light_temperature_help": "Yaklaşık kara cisim tonu; doğrusal RGB olarak kaydedilir.",
|
||||
"render.light_type": "Tür",
|
||||
"render.light_type_area": "Alan",
|
||||
"render.light_type_point": "Nokta",
|
||||
"render.light_type_spot": "Spot",
|
||||
"render.light_type_sun": "Güneş",
|
||||
"render.light_view_only": "Solo ve susturma bu görünümü ve render'larını etkiler; tasarıma asla kaydedilmez.",
|
||||
"render.light_world_origin": "Dünya orijini",
|
||||
"render.lighting_aim_point": "Hedef noktası",
|
||||
"render.lighting_apply": "Ön ayarı uygula",
|
||||
"render.lighting_area_samples": "Alan örnekleri",
|
||||
"render.lighting_budget": "Bütçe",
|
||||
"render.lighting_budget_help": "Sayılar salt okunurdur. Değiştirmek için ışık ekleyin, çoğaltın veya silin; sınırları yöntem belirler.",
|
||||
"render.lighting_colour": "Renk",
|
||||
"render.lighting_consent": "Yönlü ışıkları bu ön ayarla değiştir",
|
||||
"render.lighting_custom": "Özel ışıklar",
|
||||
"render.lighting_custom_help": "Özel'i seçmek, ön ayarı düzenlenebilir ışıklara dönüştürür; ağaçta her biri için bir satır.",
|
||||
"render.lighting_direction": "Yön X Y Z",
|
||||
"render.lighting_direction_help": "Ya da ışığa doğru bir yön yazın (X Y Z).",
|
||||
"render.lighting_distance_unit": " × boyut",
|
||||
"render.lighting_edge_u": "Kenar U vektörü",
|
||||
"render.lighting_edge_v": "Kenar V vektörü",
|
||||
"render.lighting_falloff": "Zayıflama",
|
||||
"render.lighting_falloff_help": "Şiddet, referans mesafede belirtilir.",
|
||||
"render.lighting_height": "Yükseklik V",
|
||||
"render.lighting_minimum": "En az",
|
||||
"render.lighting_mute": "Bu ışığı sustur",
|
||||
"render.lighting_point": "Nokta",
|
||||
"render.lighting_preset": "Stüdyo ön ayarı",
|
||||
"render.lighting_preset_cpu": "Yönlü stüdyo ön ayarları CPU ışın izleme gerektirir. GPU aydınlatması açık Güneş ve Alan ışıkları kullanır.",
|
||||
"render.lighting_preset_row": "Ön ayar",
|
||||
"render.lighting_radiance": "Işınım",
|
||||
"render.lighting_reference": "Referans",
|
||||
"render.lighting_review_help": "Yönlü ışık listesinin yerini alır. Nokta ve alan ışıklarını, ortamı, gölgeleri, kamerayı ve diğer tüm çekim ayarlarını korur.",
|
||||
"render.lighting_review_title": "Aydınlatma ön ayarını incele",
|
||||
"render.lighting_samples_cpu": "Alan ışığı örnekleri CPU ışın izlemeye uygulanır.",
|
||||
"render.lighting_samples_need_area": "Alan örneklerini ayarlamak için bir CPU alan ışığı ekleyin.",
|
||||
"render.lighting_samples_reason": "Alan örnekleri 1, 4, 16 veya 64 olmalıdır.",
|
||||
"render.lighting_shadow_cpu": "Gölge anahtarı CPU ışın izleme gerektirir. GPU yol izleme her zaman gölge düşürür.",
|
||||
"render.lighting_shadows": "Gölge düşürsün",
|
||||
"render.lighting_shadows_section": "Gölgeler ve örnekleme",
|
||||
"render.lighting_shape": "Şekil",
|
||||
"render.lighting_solo": "Bu ışığı solo yap",
|
||||
"render.lighting_source": "Kaynak",
|
||||
"render.lighting_source_section": "Işık kaynağı",
|
||||
"render.lighting_spot_preserved": "Bu nokta ışık, türü değiştirilene kadar kaydedilmiş spot konisini korur.",
|
||||
"render.lighting_sun_distance": "Güneş yönü mesafeden ve hedef noktasından bağımsızdır.",
|
||||
"render.lighting_unsupported": "desteklenmiyor",
|
||||
"render.lighting_used": "{used} / {limit}",
|
||||
"render.lighting_width": "Genişlik U",
|
||||
"render.lighting_world_position": "Dünya X Y Z",
|
||||
"render.lights_fixed_to": "Işıklar şuna sabit",
|
||||
"render.linear_help": "Giderme, pozlama, ton eşleme, filtreler ve arka plandan önce 32 bit float RGBA ana görüntüyü saklar. Doğrusal sRGB/D65; kapsama ile ön çarpımlı. Ek bellek açıkça kabul edilir. Sıkıştırma veya ek geçiş yoktur.",
|
||||
"render.linear_retain": "Ham doğrusal EXR'ı sakla",
|
||||
"render.linear_unavailable": "Seçilen tamamlanmış karede saklanmış EXR yok. Render'dan önce saklamayı etkinleştirin; eski bir PNG ham ışınımı geri getiremez.",
|
||||
"render.look_add": "Renk görünümü ekle",
|
||||
"render.look_applied": "{name} uygulandı",
|
||||
"render.look_bleach": "Bleach-bypass esinli",
|
||||
"render.look_cool": "Soğuk",
|
||||
"render.look_cyanotype": "Siyanotip esinli",
|
||||
"render.look_faded": "Solmuş baskı",
|
||||
"render.look_material": "Malzeme",
|
||||
"render.look_mono": "Tek renk",
|
||||
"render.look_pasted": "Görünüm yapıştırıldı",
|
||||
"render.look_sepia": "Sepya",
|
||||
"render.look_strength": "Görünüm şiddeti",
|
||||
"render.look_teal_orange": "Turkuaz / turuncu",
|
||||
@@ -2611,9 +2938,11 @@
|
||||
"render.output_advanced": "Gelişmiş",
|
||||
"render.output_backend_ready": "Hazır",
|
||||
"render.output_check": "Kontrol",
|
||||
"render.output_check_failed": "Kontrol · hata",
|
||||
"render.output_check_help": "Render başlatmadan bu isteği gözden geçirmek için Kontrol'ü çalıştırın.",
|
||||
"render.output_check_summary": "Kontrol · Notlar: {count}",
|
||||
"render.output_copy": "Kopyala",
|
||||
"render.output_copy_report": "Raporu kopyala",
|
||||
"render.output_destination": "Hedef",
|
||||
"render.output_destination_help": "Tamamlanan görüntüyü dışa aktarırken hedef dosyayı seçin.",
|
||||
"render.output_dimensions": "Boyut",
|
||||
@@ -2623,6 +2952,7 @@
|
||||
"render.output_fit": "Sığdır",
|
||||
"render.output_format": "Biçim",
|
||||
"render.output_format_status": "PNG, tamamlanmış tam görüntü görüntüsüdür. İsteğe bağlı GPU EXR, görüntü filtrelerinden önce ham sahne-doğrusal FLOAT32 ışınımını saklar. Dışa aktarma, önizleme önbelleğini veya başka bir işi asla ikame etmez.",
|
||||
"render.output_grading": "Derecelendirme",
|
||||
"render.output_image": "Görüntü",
|
||||
"render.output_largest": "En çok {width} × {height}",
|
||||
"render.output_lock_ratio": "En-boy oranını kilitle",
|
||||
@@ -2632,6 +2962,8 @@
|
||||
"render.output_quality_waiting": "kalite sözleşmesi bekleniyor",
|
||||
"render.output_ratio_refused": "Bağlı boyut, bu yöntemin boyut sınırını aşıyor. Önceki boyut korunur; oranı kilitten çıkarın veya daha küçük bir değer seçin.",
|
||||
"render.output_ready": "Render için hazır",
|
||||
"render.output_run_check": "Kontrolü çalıştır",
|
||||
"render.output_size_limit": "{method}: kenar başına en fazla {dimension} px ve {pixels} piksel. Sığan en büyük boyut: {width} × {height}.",
|
||||
"render.output_size_ok": "Boyut uygun · {width} × {height} px",
|
||||
"render.output_swap_orientation": "Genişlik ve yüksekliği değiştir",
|
||||
"render.output_tools_inspector": "Boyutu ve biçimi Çıktı denetçisinde düzenleyin.",
|
||||
@@ -2648,6 +2980,7 @@
|
||||
"render.page_output": "Çıktı",
|
||||
"render.page_scene": "Sahne",
|
||||
"render.paint_label": "Boya",
|
||||
"render.painting_with": "{name} ile boyanıyor",
|
||||
"render.passive_stage": "Zemin ve pasif sahne",
|
||||
"render.passive_stage_help": "Sonlu pasif paneller yalnızca render görüntülerine aittir. CAD gövdesi veya kütle eklemezler. Uygulamadan sonra yerleşim sabittir; ürün sınırlarıyla kesişen reddedilir. CPU dağınık sekme aydınlatması sağlamaz.",
|
||||
"render.perspective": "Perspektif kamera",
|
||||
@@ -2669,6 +3002,7 @@
|
||||
"render.pick_help": "Gövde, Yüz veya Bileşen seçin. Temizlemek için boş alana tıklayın; Shift veya Ctrl ekler ya da seçimi tersine çevirir. Vurgular yalnızca canlı ürün görünümünde görünür.",
|
||||
"render.pick_label": "Seç",
|
||||
"render.pick_up_label": "Al",
|
||||
"render.pick_up_prompt": "Görünümünü almak için bir nesneye tıklayın",
|
||||
"render.pick_waiting": "Ürün seçimi henüz desteklenmiyor.",
|
||||
"render.post_contrast": "Kontrast",
|
||||
"render.post_gain_b": "Mavi kazancı",
|
||||
@@ -2708,13 +3042,17 @@
|
||||
"render.radial_distortion": "Radyal bozulma",
|
||||
"render.radial_fit": "Ürünü Sığdır",
|
||||
"render.radial_save": "Çekimi Kaydet",
|
||||
"render.recent_looks": "Son görünümler",
|
||||
"render.regrade_action": "Ham ana görüntüyü yeniden derecelendir (giderme yok)",
|
||||
"render.regrade_help": "Bu sunum kontrollerini kullanarak seçilen tamamlanmış ham ana görüntüden ayrı bir PNG oluşturur. Işın izlemez, yakalanan boyutu/kamerayı/ışıkları değiştirmez veya EXR'ın üzerine yazmaz. Giderme yeniden uygulanmaz: ham ana görüntüler onun kılavuzlarını saklamaz. Türev, aynı iş kontrollerinden dışa aktarılabilir kalır.",
|
||||
"render.regrade_label": "Yeniden derecelendir",
|
||||
"render.reload_settings": "Kaydedilmiş ayarları yükle",
|
||||
"render.remove_override": "Geçersiz Kılmayı Kaldır",
|
||||
"render.replace_all": "Tümünü Değiştir",
|
||||
"render.replace_ground": "Mevcut sahnenin değiştirilmesini onayla",
|
||||
"render.replace_label": "Değiştir",
|
||||
"render.replace_saved_surface": "Bu hedef için mevcut kaydedilmiş bağlamın yerini al",
|
||||
"render.replace_with": "Şununla değiştir",
|
||||
"render.restore_camera": "Kaydedilmiş kamerayı yükle",
|
||||
"render.result_actual": "%100",
|
||||
"render.result_actual_help": "Ekran pikseli başına bir görüntü pikseli.",
|
||||
@@ -2766,14 +3104,28 @@
|
||||
"render.saved_surface_remove": "Kaydedilmiş bağlamı kaldır",
|
||||
"render.saved_surfaces": "Kaydedilmiş yüzey bağlamları",
|
||||
"render.saved_surfaces_help": "Sabitlenmiş yerleşik haritalar, yerel milimetrelerde seçilen tekrarı izler. Değiştirilmiş veya eksik hedef bir hatadır, asla otomatik yeniden atama değildir.",
|
||||
"render.scene_all_shots": "Tüm çekimler ({count})",
|
||||
"render.scene_apply": "İncelenen stüdyoyu uygula",
|
||||
"render.scene_batch_reason": "Tüm çekimler, henüz kullanılamayan kayıtlı çekim render kuyruğunu gerektirir.",
|
||||
"render.scene_check_now": "Şimdi kontrol et",
|
||||
"render.scene_check_passed": "Kontrol geçti · {notes} not",
|
||||
"render.scene_check_stale": "Kontrol güncel değil · yeniden kontrol edin",
|
||||
"render.scene_check_unchecked": "Henüz kontrol edilmedi · 0 not",
|
||||
"render.scene_consent": "Aydınlatma, ortam, pozlama ve sahne değiştirmeyi onayla",
|
||||
"render.scene_light_summary": "{environment} · {lights} ışık",
|
||||
"render.scene_look_summary": "{looks} görünüm · {overrides} geçersiz kılma",
|
||||
"render.scene_open_check": "Kontrolü Aç",
|
||||
"render.scene_preserved_fields": "Kamera, örnekleme, boyutlar, optik bağlamlar, CAD geometrisi ve mühendislik malzemeleri korunur.",
|
||||
"render.scene_preserves_design": "Görünen konu için aydınlatma ve pasif bir sahne oluşturun. Geometri, mühendislik malzemeleri ve kamera ayrı kalır.",
|
||||
"render.scene_ready": "Render için hazır",
|
||||
"render.scene_reminder": "Sahne tasarımı korur. Geometri, mühendislik özellikleri ve CAD, Render'dan asla değiştirilmez.",
|
||||
"render.scene_reminders": "Hatırlatmalar",
|
||||
"render.scene_renderer_ready": "{renderer} hazır",
|
||||
"render.scene_replaces": "Şunların yerini alır",
|
||||
"render.scene_review": "Çöz ve incele",
|
||||
"render.scene_review_help": "Uygulamadan önce tam yerli kompozisyonu inceleyin. Bağlanmamış gelişmiş SCENE özellikleri ikame edilmez.",
|
||||
"render.scene_scope_details": "Kompozisyon kapsama alanı",
|
||||
"render.scene_stages": "Aşamalar",
|
||||
"render.search_appearances": "{count} görünümde ara",
|
||||
"render.setup_legacy_environment": "Bu eski kurulum, eski ortam dönüşü veya şiddeti kullanıyor. O değerler korunur; tam bir görüntü işi kaydetmeden önce onları açıkça dönüştürün.",
|
||||
"render.setup_name": "Kurulumun adı",
|
||||
@@ -2786,7 +3138,22 @@
|
||||
"render.shell_tab_camera": "RENDER KAMERASI",
|
||||
"render.shell_tab_lighting": "AYDINLATMA",
|
||||
"render.shell_tab_scene": "RENDER SAHNESİ",
|
||||
"render.shot_aperture": "Diyafram",
|
||||
"render.shot_distance": "Mesafe",
|
||||
"render.shot_dof": "Alan derinliği",
|
||||
"render.shot_duplicate": "Kaydedilmiş çekimi çoğalt",
|
||||
"render.shot_edit_output": "Çıktı'da düzenle",
|
||||
"render.shot_exposure_reason": "Pozlama, CPU ışın izleme veya GPU yol izleme gerektirir.",
|
||||
"render.shot_exposure_section": "Pozlama",
|
||||
"render.shot_focus_distance": "Odak mesafesi",
|
||||
"render.shot_fov_reason": "Görüş alanı perspektif projeksiyon gerektirir.",
|
||||
"render.shot_frame_subject": "Konuyu çerçevele",
|
||||
"render.shot_invalid_camera": "Kamera değerleri sonlu ve aralık içinde olmalıdır.",
|
||||
"render.shot_lens": "Mercek",
|
||||
"render.shot_lens_enable_reason": "Diyaframı ve odak mesafesini düzenlemek için alan derinliğini etkinleştirin.",
|
||||
"render.shot_lens_gpu_reason": "Alan derinliği GPU yol izleme gerektirir.",
|
||||
"render.shot_lens_perspective_reason": "Alan derinliği perspektif projeksiyon gerektirir.",
|
||||
"render.shot_look_selection": "Seçime bak",
|
||||
"render.shot_manager": "Kaydedilmiş çekimler",
|
||||
"render.shot_new_name": "Yeni çekim adı",
|
||||
"render.shot_remove": "Kaydedilmiş çekimi kaldır",
|
||||
@@ -2794,6 +3161,9 @@
|
||||
"render.shot_remove_title": "Kaydedilmiş çekim kaldırılsın mı?",
|
||||
"render.shot_rename": "Kaydedilmiş çekimi yeniden adlandır",
|
||||
"render.shot_saved_only": "Önce geçerli taslağı kaydedin. Çoğalt ve yeniden adlandır, tam kaydedilmiş seçenekleri ve kaynak referanslarını korur.",
|
||||
"render.shot_selection_reason": "Hedeflemek için bir gövde veya yüz seçin.",
|
||||
"render.shot_size": "Boyut",
|
||||
"render.shot_target": "Hedef",
|
||||
"render.shots_camera_changed": "Bu çekim kaydedildikten sonra kamera değişti",
|
||||
"render.shots_name_help": "Geçerli çekim için benzersiz bir ad girin.",
|
||||
"render.shots_no_result": "Henüz render yok",
|
||||
@@ -3016,6 +3386,13 @@
|
||||
"render.wait_pick_up": "henüz desteklenmiyor: ürün seçimi ve görünüm örnekleme",
|
||||
"render.wait_replace": "henüz desteklenmiyor: bir görünümün tüm kullanımlarını değiştirme",
|
||||
"render.wait_solo": "henüz desteklenmiyor: ışık görünürlüğü ve solo denetimleri",
|
||||
"render.wear_applies_to": "Uygulandığı yer",
|
||||
"render.wear_layer_count": "{count} katman",
|
||||
"render.wear_layer_one": "{count} katman",
|
||||
"render.wear_layers": "Katmanlar",
|
||||
"render.wear_no_layers": "Katman yok",
|
||||
"render.wear_selected_layer": "Seçili katman",
|
||||
"render.wear_title": "Aşınma",
|
||||
"sample.bracket.about": "2 mm sac metal kuralına göre kenar flanşları ve bir kenet içeren taban flanşı",
|
||||
"sample.bracket.name": "Sac metal köşebent",
|
||||
"sample.enclosure.about": "Dudak, vida göbekleri, dayanak ve geçmeli bağlantı içeren, içi boşaltılmış blok",
|
||||
@@ -3049,6 +3426,7 @@
|
||||
"select an axis": "bir eksen seçin",
|
||||
"select an axis, a straight or circular edge, or a cylinder, cone or torus face first": "önce bir eksen, düz veya dairesel bir kenar ya da silindir, koni veya torus yüzü seçin",
|
||||
"select bodies first": "önce gövdeleri seçin",
|
||||
"select components first": "önce bileşenleri seçin",
|
||||
"select corner points, or the two lines of one corner": "köşe noktalarını veya bir köşenin iki çizgisini seçin",
|
||||
"select one corner point, or the two lines of one corner": "bir köşe noktasını veya bir köşenin iki çizgisini seçin",
|
||||
"select sheet edges first": "önce sac kenarlarını seçin",
|
||||
@@ -3067,6 +3445,7 @@
|
||||
"settings.double_click_components": "Bir bileşeni etkinleştirmek veya bağlı bir bileşeni yerinde düzenlemek için çift tıklayın",
|
||||
"settings.frame_rate_details_note": "Son karelerin sürelerinin grafiğini üzerine çizilen üçgenlerle, bir kare süresi histogramını ve GPU'nun çizdiklerini ekler. Göstermek veya gizlemek için görünümdeki kare hızına tıklayın.",
|
||||
"settings.frame_rate_note": "Görünümün sağ alt köşesinde saniyedeki kare sayısını gösterir.",
|
||||
"settings.insert_lists_open_designs": "Tasarım ekle, diğer sekmelerde açık olan tasarımları önce listeler",
|
||||
"settings.measure_axis_distances": "X/Y/Z mesafelerini göster",
|
||||
"settings.measure_axis_distances_help": "Ölç açıkken toplam mesafenin yanında dünya ekseni bileşen ölçülerini gösterir.",
|
||||
"settings.msaa_applies_on_restart": "Kenar yumuşatma, SolidCraft bir sonraki başlatıldığında uygulanır",
|
||||
@@ -3091,6 +3470,10 @@
|
||||
"shortcut.unpin": "Sabitlemeyi kaldır",
|
||||
"sketch is missing": "eskiz eksik",
|
||||
"sketch.degrees_of_freedom": "{count} serbestlik derecesi",
|
||||
"sketch.dimension_added_driven": "Eskiz bu ölçüyü zaten belirliyor, bu yüzden türetilmiş (referans) ölçü olarak eklendi",
|
||||
"sketch.dimension_applied": "Ölçü uygulandı",
|
||||
"sketch.dimension_applied_defined": "Ölçü uygulandı; `{name}` parametresi tanımlandı",
|
||||
"sketch.dimension_applied_renamed": "Ölçü uygulandı; parametreye `{name}` adı verildi",
|
||||
"sketch.dof_left": "Kalan serbestlik derecesi: {count}",
|
||||
"sketch.dof_left_one": "Kalan serbestlik derecesi: {count}",
|
||||
"sketch.draw_curves": "{linetype} olarak çiz",
|
||||
@@ -3125,6 +3508,8 @@
|
||||
"sketch_text.use_font_file": "{path} yazı tipi dosyasını kullan",
|
||||
"solid": "katı",
|
||||
"status.editing": "{name} düzenleniyor",
|
||||
"status.navigation": "Fare eşlemesi: {mapping}",
|
||||
"status.navigation_tip": "Farenin görünümü nasıl hareket ettirdiği. Başka bir eşleme seçmek için tıklayın",
|
||||
"status.under_constrained": "Eksik kısıtlanmış ({count} serbestlik derecesi)",
|
||||
"status.under_constrained_one": "Eksik kısıtlanmış ({count} serbestlik derecesi)",
|
||||
"status.units_tip": "Tasarımın birimleri. Değiştirmek için tıklayın",
|
||||
@@ -3158,16 +3543,20 @@
|
||||
"timeline.delete_folders_tip": "Özelliklerini ve iç içe klasörlerini geçmişte tut",
|
||||
"timeline.delete_folders_with_features_tip": "Klasörleri, içlerindeki her özellikle ve iç içe klasörleriyle birlikte sil",
|
||||
"timeline.delete_many": "{count} Seçili Özelliği Sil",
|
||||
"timeline.edited_rolled_back": "{name} değişti, ancak geçmiş işaretçisinden sonra olduğu için oluşturulmadı: sonucu görmek için işaretçiyi onun ötesine taşıyın",
|
||||
"timeline.feature_error": "Hata: {message}",
|
||||
"timeline.feature_hint": "Düzenlemek için çift tıklayın · sıralamak için sürükleyin · daha fazlası için sağ tıklayın",
|
||||
"timeline.feature_rolled_back": "Geri sarıldı: geçmiş işaretçisinden sonra, bu yüzden oluşturulmadı",
|
||||
"timeline.feature_warning": "Uyarı: {message}",
|
||||
"timeline.group": "Yeni özellik klasörü oluştur",
|
||||
"timeline.move_many": "Zaman çizelgesi öğelerini taşı",
|
||||
"timeline.move_to_group": "Özellikleri klasöre taşı",
|
||||
"timeline.rename_group": "Özellik klasörünü yeniden adlandır",
|
||||
"timeline.reorder_many": "Özellikleri yeniden sırala",
|
||||
"timeline.suppress_features": "Özellikleri bastır",
|
||||
"timeline.suppress_folders_tip": "Klasörlerdeki ve iç içe klasörlerindeki her özelliği bastır veya bastırmayı kaldır",
|
||||
"timeline.ungroup": "Özellik klasörünü kaldır",
|
||||
"timeline.unsuppress_features": "Özelliklerin bastırmasını kaldır",
|
||||
"to the face": "yüze kadar",
|
||||
"to the next face": "sonraki yüze kadar",
|
||||
"toolbar.customize_note": "{tab} sekmesi. Bir komuta araç çubuğu düğmesi eklemek için komutu işaretleyin. Ayrıca panelin açılır menüsündeki bir satırı araç çubuğuna sürükleyebilir, bir düğmeyi kaldırmak için araç çubuğunun dışına sürükleyebilir veya açılır menü satırındaki raptiyeyi kullanabilirsiniz.",
|
||||
@@ -3239,6 +3628,10 @@
|
||||
"ui.3d_mouse_offer": "3D farenizi kalibre etmek, yer değiştirmiş veya ters dönmüş eksenleri düzeltir; hızını ve hissini ayarlar.",
|
||||
"ui.3d_mouse_offer_never": "Bir daha gösterme",
|
||||
"ui.3d_mouse_offer_swap": "Hidraw'a geç (doğrudan sürücü)",
|
||||
"ui.3d_mouse_orbit": "Döndürme",
|
||||
"ui.3d_mouse_orbit_classic": "Klasik",
|
||||
"ui.3d_mouse_orbit_tip": "İzlenen: 3B fare, dönüş başladığında görünümü, görünümün ortasındaki model kısmının etrafında döndürür; bu kısım soluk bir topla işaretlenir. Klasik: görünümün merkezi etrafında",
|
||||
"ui.3d_mouse_orbit_traced": "İzlenen",
|
||||
"ui.3d_mouse_reading": "Okunan değer",
|
||||
"ui.3d_mouse_reset_axes": "Eksen atamasını sıfırla",
|
||||
"ui.3d_mouse_reset_view": "Görünümü sıfırla",
|
||||
@@ -3275,6 +3668,8 @@
|
||||
"ui.3d_mouse_step_twist_counterclockwise": "Başlığı saat yönünün tersine çevirin, tutun, sonra bırakın.",
|
||||
"ui.3d_mouse_switch_cap": "Merkeze dönüş olarak kalibre et",
|
||||
"ui.3d_mouse_switch_trackball": "İztopu olarak kalibre et",
|
||||
"ui.3d_mouse_use_direct": "Doğrudan sürücüyü kullan",
|
||||
"ui.3d_mouse_use_direct_tip": "3D fareyi spacenavd yerine doğrudan (hidraw) oku.",
|
||||
"ui.3d_mouse_view_axis": "Görünüm ekseni",
|
||||
"ui.3d_mouse_view_off": "Kapalı",
|
||||
"ui.3d_mouse_view_off_tip": "3D fare sürücüsünün profili bu ekseni kapatıyor, bu yüzden hareketi görünümü oynatmaz. Burada başka bir eksenin hareketini seçerseniz ikisi yer değiştirir ve o hareket görünümü oynatmayı bırakır.",
|
||||
@@ -3581,6 +3976,8 @@
|
||||
"ui.cancel": "İptal",
|
||||
"ui.canvases": "Tuvaller",
|
||||
"ui.center": "Merkez",
|
||||
"ui.center_arc_follows_cursor": "Merkez noktalı yay imlecin yönünü izler",
|
||||
"ui.center_arc_follows_cursor_tip": "Merkez noktalı yay, imlecin merkezinin etrafında dönüş yönüne göre saat yönünde veya saat yönünün tersine ilerler. Kapalıyken: başlangıçtan itibaren her zaman saat yönünün tersine.",
|
||||
"ui.centerline": "Merkez çizgisi",
|
||||
"ui.chain": "Zincir",
|
||||
"ui.chamfer": "Pah",
|
||||
@@ -3589,6 +3986,7 @@
|
||||
"ui.circle": "Daire",
|
||||
"ui.circular_pattern": "Dairesel desen",
|
||||
"ui.clear": "Temizle",
|
||||
"ui.clear_print_face": "Baskı yüzeyini temizle",
|
||||
"ui.clear_recent": "Son kullanılanları temizle",
|
||||
"ui.clear_the_selection": "Seçimi temizle",
|
||||
"ui.click_selects_whole_bodies": "Tıklama tüm gövdeyi seçer",
|
||||
@@ -3623,6 +4021,15 @@
|
||||
"ui.constant": "Sabit",
|
||||
"ui.constraints_conflict": "Kısıtlamalar çakışıyor",
|
||||
"ui.construction": "Yapı çizgisi",
|
||||
"ui.construction.angle_hint": "Yardımcı doğru: bir noktaya tıklayın, ardından bir açı yazın veya bir yön seçin",
|
||||
"ui.construction.bisector_hint": "Açıortay: kesişen iki düz çizgiyi, ardından istenen çözümü seçin",
|
||||
"ui.construction.common_hint": "Ortak teğet: iki daire/yay, ardından istenen çözümü seçin",
|
||||
"ui.construction.contact_hint": "Teğet: bir daire/yay veya açık spline/konik eğri, ardından değme noktasını seçin (serbest biçimli eğriler: uç noktalar)",
|
||||
"ui.construction.external_hint": "Teğet: bir daire veya yay, bir dış nokta, ardından istenen çözümü seçin",
|
||||
"ui.construction.free_hint": "Yardımcı doğru: iki noktaya tıklayın; yeniden başlamak için sağ tıklayın",
|
||||
"ui.construction.point_hint": "Yardımcı doğru: geçeceği noktaya tıklayın",
|
||||
"ui.construction.reference_hint": "Yardımcı doğru: düz bir çizgi, ardından bir nokta seçin; referansı değiştirmek için sağ tıklayın",
|
||||
"ui.construction.ribbon_label": "Yardımcılar",
|
||||
"ui.context_menu": "Bağlam menüsü",
|
||||
"ui.continuity_g0": "G0 kırık",
|
||||
"ui.continuity_g1": "G1 teğet",
|
||||
@@ -3714,6 +4121,14 @@
|
||||
"ui.end_scale": "Bitiş ölçeği",
|
||||
"ui.entities": "Öğeler",
|
||||
"ui.equal": "Eşit",
|
||||
"ui.equation_end": "Bitiş",
|
||||
"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_parametric": "Parametrik",
|
||||
"ui.equation_x_of_t": "x(t)",
|
||||
"ui.equation_y_of_t": "y(t)",
|
||||
"ui.equation_y_of_x": "y(x)",
|
||||
"ui.equivalent_radius": "Eşdeğer yarıçap",
|
||||
"ui.expand": "Genişlet",
|
||||
"ui.exportDxf": "DXF olarak dışa aktar…",
|
||||
@@ -3779,6 +4194,7 @@
|
||||
"ui.heading_design": "TASARIM",
|
||||
"ui.heading_draft": "KONİKLİK",
|
||||
"ui.heading_edit": "DÜZENLE",
|
||||
"ui.heading_equation_curve": "DENKLEM EĞRİSİ",
|
||||
"ui.heading_extrude": "EKSTRÜZYON",
|
||||
"ui.heading_fillet": "YUVARLATMA",
|
||||
"ui.heading_finish_editing": "DÜZENLEMEYİ BİTİR",
|
||||
@@ -3870,6 +4286,10 @@
|
||||
"ui.import_ellipsis": "İçe aktar…",
|
||||
"ui.infinite": "Sonsuz",
|
||||
"ui.insert": "Ekle",
|
||||
"ui.insert_design.browse": "Göz at…",
|
||||
"ui.insert_design.open_designs": "Açık tasarımlar",
|
||||
"ui.insert_design.saved_file": "{name} kaydedilmemiş değişiklikler içeriyor, bu yüzden kaydedilmiş dosyası eklendi. Değişiklikleri almak için onu kaydedin, ardından Bağları güncelle'yi çalıştırın.",
|
||||
"ui.insert_design.unsaved": "Kaydedilmemiş değişiklikler",
|
||||
"ui.inspect": "İncele",
|
||||
"ui.intersect": "Kesişim",
|
||||
"ui.isolate": "Yalıt",
|
||||
@@ -4013,12 +4433,29 @@
|
||||
"ui.polygon": "Çokgen",
|
||||
"ui.preferences": "Ayarlar",
|
||||
"ui.press_a_key": "bir tuşa basın…",
|
||||
"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",
|
||||
"ui.profile_plane": "Profil düzlemi",
|
||||
"ui.profiles": "Profiller",
|
||||
"ui.project": "Yansıt",
|
||||
"ui.project_face_edges_sketch": "Yüz kenarlarını yeni eskizlere yansıt",
|
||||
"ui.properties": "Özellikler",
|
||||
"ui.properties.copy_matrix": "Matrisi kopyala",
|
||||
"ui.properties.first_moments": "Birinci momentler",
|
||||
"ui.properties.inertia": "Eylemsizlik momenti",
|
||||
"ui.properties.inertia_note": "Kütle merkezine göre, dünya eksenlerinde (g·{unit}²)",
|
||||
"ui.properties.inertia_unavailable": "Kullanılamıyor: bir gövdenin ağı kapalı değil.",
|
||||
"ui.properties.inertia_x": "Ixx, Ixy, Ixz",
|
||||
"ui.properties.inertia_y": "Iyx, Iyy, Iyz",
|
||||
"ui.properties.inertia_z": "Izx, Izy, Izz",
|
||||
"ui.properties.measuring": "Ölçülüyor…",
|
||||
"ui.properties.measuring_failed": "Ölçüm tamamlanmadan durdu.",
|
||||
"ui.properties.principal_axis": "Asal eksen {number}",
|
||||
"ui.properties.principal_moments": "Asal momentler",
|
||||
"ui.pull_direction": "Kalıptan çıkarma yönü",
|
||||
"ui.quality": "Kalite",
|
||||
"ui.quantity": "Adet",
|
||||
@@ -4064,6 +4501,7 @@
|
||||
"ui.revolve": "Döndür",
|
||||
"ui.ribbon_exit": "Çıkış",
|
||||
"ui.ribbon_labels": "Şerit simgelerinin altında metin göster",
|
||||
"ui.ribbon_menus_skip_pinned": "Sabitlenen komutları açılır menülerin dışında bırak",
|
||||
"ui.ribbon_pin_footer": "Sabitlenen komutlar şeritte görünür. Sabitlemek veya kaldırmak için bir satırın üzerine gelin.",
|
||||
"ui.ribbon_workspace": "Çalışma alanı",
|
||||
"ui.right": "Sağ",
|
||||
@@ -4118,7 +4556,11 @@
|
||||
"ui.sketch": "Eskiz",
|
||||
"ui.sketch_dimension": "Eskiz ölçüsü",
|
||||
"ui.sketch_grid": "Eskiz ızgarası",
|
||||
"ui.sketch_intersect_geometry": "Geometri",
|
||||
"ui.sketch_intersect_projection_link": "Yansıtma bağı",
|
||||
"ui.sketch_intersect_selection_filter": "Seçim filtresi",
|
||||
"ui.sketch_modifiers": "Eskiz değiştiricileri",
|
||||
"ui.sketch_project_geometry": "Geometri",
|
||||
"ui.sketch_scale": "Eskiz ölçeği",
|
||||
"ui.sketches": "Eskizler",
|
||||
"ui.slice": "Dilim",
|
||||
@@ -4169,6 +4611,7 @@
|
||||
"ui.tab_manage": "Yönet",
|
||||
"ui.tab_sheet_metal": "Sac Metal",
|
||||
"ui.tab_view": "Görünüm",
|
||||
"ui.tab_weldments": "Kaynak",
|
||||
"ui.tangent": "Teğet",
|
||||
"ui.taper_angle": "Konik açı",
|
||||
"ui.tapped": "Kılavuzlu",
|
||||
@@ -4184,6 +4627,7 @@
|
||||
"ui.tip_angle": "Uç açısı",
|
||||
"ui.to_object": "Nesneye kadar",
|
||||
"ui.tool_direction": "Takım yönü",
|
||||
"ui.toolbar_tabs_hidden": "Tüm araç çubuğu sekmeleri gizli. Birini göstermek için + işaretine tıklayın veya buraya sağ tıklayın.",
|
||||
"ui.top": "Üst",
|
||||
"ui.top_diameter": "Üst çap",
|
||||
"ui.top_radius": "Üst yarıçap",
|
||||
@@ -4221,6 +4665,8 @@
|
||||
"ui.view_centre": "Görünüm merkezi",
|
||||
"ui.viewport_status_clear": "Kapat: {name}",
|
||||
"ui.viewport_status_clear_hint": "Kapat: {name} · Esc",
|
||||
"ui.viewport_status_edges": "{count} kenar",
|
||||
"ui.viewport_status_from": "{direction} yönünden",
|
||||
"ui.viewport_status_key": "Göstergeyi göster: {name}",
|
||||
"ui.viewport_status_minimum_radius": "Minimum yarıçap",
|
||||
"ui.viewport_status_pairs": "{count} çift",
|
||||
@@ -4230,6 +4676,7 @@
|
||||
"ui.visual_shaded_with_edges": "Kenarlarla gölgeli",
|
||||
"ui.visual_style": "Görsel stil",
|
||||
"ui.visual_wireframe": "Tel kafes",
|
||||
"ui.wheel_zoom_speed": "Fare tekerleği yakınlaştırma hızı",
|
||||
"ui.width": "Genişlik",
|
||||
"ui.window_system": "Pencere sistemi",
|
||||
"ui.window_system_auto": "Otomatik",
|
||||
@@ -4260,7 +4707,9 @@
|
||||
"update.button": "Güncelle",
|
||||
"update.button_version": "{version} sürümüne güncelle",
|
||||
"update.check_automatically": "Güncellemeleri otomatik denetle",
|
||||
"update.check_for_updates": "Güncellemeleri denetle",
|
||||
"update.check_now": "Şimdi denetle",
|
||||
"update.check_unavailable": "Bu kurulum için güncelleme denetimi kullanılamıyor.",
|
||||
"update.checking": "Denetleniyor…",
|
||||
"update.copy": "Kopyala",
|
||||
"update.dmg_opened": "Disk görüntüsü açıldı ve doğrulandı: SolidCraft'ı “Uygulamalar” klasörüne sürükleyin, eski sürümü değiştirin ve SolidCraft'ı yeniden başlatın.",
|
||||
@@ -4274,13 +4723,18 @@
|
||||
"update.installed": "SolidCraft {version} yüklendi. Kullanmak için yeniden başlatın.",
|
||||
"update.installer_started": "Yükleme programı, SolidCraft kapatıldığında başlar.",
|
||||
"update.later": "Daha sonra hatırlat",
|
||||
"update.managed": "Güncelleme mevcut: SolidCraft'ı {manager} üzerinden güncelleyin",
|
||||
"update.no_notes": "Bu sürüm için not bulunmuyor.",
|
||||
"update.package_hint": "Bu kurulum bir {kind} kaynağından geliyor. Yeni paketi indirip şu komutla yükleyin:",
|
||||
"update.prefs_dev": "Bu bir geliştirme derlemesi ({version}): güncelleme aramaz.",
|
||||
"update.prefs_flatpak": "Bu kurulum ({version}) Flatpak tarafından güncellenir: {command} komutunu çalıştırın veya yazılım yöneticisini kullanın. SolidCraft kendisi denetlemez.",
|
||||
"update.prefs_label": "Güncellemeler",
|
||||
"update.prefs_managed": "Bu, {manager} tarafından yönetilen SolidCraft {version}. Denetim, GitHub'a en yeni sürümü sorar ve başka hiçbir şey göndermez; SolidCraft'ı {manager} üzerinden güncelleyin.",
|
||||
"update.prefs_note": "Bu, SolidCraft {version} ({kind}). Denetim, GitHub'a en yeni sürümü sorar ve başka hiçbir şey göndermez.",
|
||||
"update.prefs_web": "En yeni web uygulamasını almak için sayfayı yeniden yükleyin. Ağ kullanılamazsa veya güncelleme indirmesi zamanında bitiremezse bunun yerine kaydedilmiş sürüm açılır; süren bir indirme bir sonraki yeniden yükleme için devam eder.",
|
||||
"update.refresh_checking": "En yeni sürüm denetleniyor…",
|
||||
"update.refresh_fallback": "Daha yeni bir sürüm denetlenemedi; daha önce bulunan {version} sürümüne güncelleniyor.",
|
||||
"update.refresh_selected": "{version} sürümüne güncelleniyor.",
|
||||
"update.release_page": "Sürüm sayfasını aç",
|
||||
"update.restart": "Şimdi yeniden başlat",
|
||||
"update.restart_later": "Daha sonra",
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
"BLEND SURFACE": "MẶT NỐI MƯỢT",
|
||||
"BOUNDARY SURFACE": "BỀ MẶT BIÊN DẠNG",
|
||||
"BROWSER": "CÂY ĐỐI TƯỢNG",
|
||||
"Back to the original order": "Trở về thứ tự ban đầu",
|
||||
"Ball": "Cầu",
|
||||
"Bar": "Thép thanh",
|
||||
"Base Center": "Tâm đáy",
|
||||
@@ -595,6 +596,8 @@
|
||||
"Snap to face and edge centres": "Bắt vào tâm mặt và tâm cạnh",
|
||||
"SolidCraft cannot submit reports directly. Submit on GitHub; no token is needed in the app.": "SolidCraft không thể gửi báo cáo trực tiếp. Hãy gửi trên GitHub; ứng dụng không cần mã truy cập.",
|
||||
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft đã đóng trước khi các thiết kế này được lưu. Bạn có thể mở bản tự động lưu gần nhất của chúng.",
|
||||
"Sort A to Z": "Sắp xếp từ A đến Z",
|
||||
"Sort Z to A": "Sắp xếp từ Z đến A",
|
||||
"Spacing 2 must be a non-zero number": "Khoảng cách 2 phải là một số khác 0",
|
||||
"Spacing must be a non-zero number": "Khoảng cách phải là một số khác 0",
|
||||
"Specialise": "Chuyên biệt hóa",
|
||||
@@ -775,6 +778,8 @@
|
||||
"at the face": "tại bề mặt",
|
||||
"bom.group_children": "Chi tiết con trong hàng của chi tiết cha",
|
||||
"browser.body_name": "Thân khối{number}",
|
||||
"browser.calibrate_click_a": "Nhấp vào ảnh để chọn điểm A, hoặc nhập tọa độ của điểm đó.",
|
||||
"browser.calibrate_click_b": "Nhấp vào ảnh để chọn điểm B, hoặc nhập tọa độ của điểm đó.",
|
||||
"browser.calibrate_title": "Hiệu chuẩn: {name}",
|
||||
"browser.capture_many": "{count} chi tiết đã được di chuyển nhưng vị trí chưa được ghi lại.",
|
||||
"browser.capture_note": "Ghi vị trí sẽ giữ vị trí mới; Hoàn nguyên sẽ đặt lại như cũ.",
|
||||
@@ -817,6 +822,7 @@
|
||||
"cmd.appearance_pick_up": "Lấy diện mạo",
|
||||
"cmd.appearance_replace": "Thay diện mạo",
|
||||
"cmd.assembly_bom": "Bảng kê vật tư (BOM)",
|
||||
"cmd.body.print_face": "Mặt in",
|
||||
"cmd.body_rename": "Đổi tên thân khối",
|
||||
"cmd.browser_collapse_all": "Thu gọn tất cả",
|
||||
"cmd.browser_expand_all": "Mở rộng tất cả",
|
||||
@@ -1101,6 +1107,16 @@
|
||||
"cmd.sheet_refold": "Gập lại tấm kim loại",
|
||||
"cmd.sheet_rules": "Danh sách quy tắc kim loại tấm",
|
||||
"cmd.sheet_unfold": "Trải phẳng tạm thời",
|
||||
"cmd.sketch.construction_line": "Đường thẳng dựng hình",
|
||||
"cmd.sketch.construction_line.angle": "Đường dựng hình theo góc",
|
||||
"cmd.sketch.construction_line.bisector": "Đường phân giác",
|
||||
"cmd.sketch.construction_line.horizontal": "Đường dựng hình nằm ngang",
|
||||
"cmd.sketch.construction_line.parallel": "Đường dựng hình song song",
|
||||
"cmd.sketch.construction_line.perpendicular": "Đường dựng hình vuông góc",
|
||||
"cmd.sketch.construction_line.tangent_at": "Tiếp tuyến tại điểm trên đường cong",
|
||||
"cmd.sketch.construction_line.tangent_external": "Tiếp tuyến qua điểm ngoài",
|
||||
"cmd.sketch.construction_line.tangent_two": "Tiếp tuyến chung",
|
||||
"cmd.sketch.construction_line.vertical": "Đường dựng hình thẳng đứng",
|
||||
"cmd.sketch_arc3d": "Cung tròn 3D",
|
||||
"cmd.sketch_arc_center_point": "Cung tròn xác định bởi tâm và điểm",
|
||||
"cmd.sketch_arc_tangent": "Cung tròn tiếp tuyến",
|
||||
@@ -1389,7 +1405,18 @@
|
||||
"help.occurrence.move": "Chỉ di chuyển chi tiết lắp ráp theo các bậc tự do mà khớp nối cho phép. Chọn một mặt của chi tiết, rồi kéo mũi tên hoặc cung tròn có thể sử dụng.",
|
||||
"help.parameters.change": "Mở bảng các giá trị có tên và biểu thức điều khiển kích thước của thiết kế. Dùng để sửa một kích thước ở một chỗ và mọi tính năng dùng nó sẽ cập nhật theo.",
|
||||
"help.pointcloud.insert": "Chèn bản quét PLY, XYZ hoặc PTS dưới dạng đám mây điểm bên cạnh mô hình. Dùng làm tham chiếu khi dựng mô hình từ dữ liệu đo.",
|
||||
"help.sketch.construction_line": "Tạo đường thẳng dựng hình vô hạn qua hai điểm để bố trí phác thảo mà không chia tách biên dạng.",
|
||||
"help.sketch.construction_line.angle": "Tạo đường dựng hình vô hạn theo góc so với trục X của phác thảo. Chọn điểm rồi nhập góc hoặc chọn hướng.",
|
||||
"help.sketch.construction_line.bisector": "Chọn hai đường thẳng giao nhau trong phác thảo rồi chọn một trong hai đường phân giác. Kết quả thay đổi theo các đường gốc.",
|
||||
"help.sketch.construction_line.horizontal": "Tạo đường dựng hình nằm ngang vô hạn qua một điểm. Ràng buộc ngang được giữ khi chỉnh sửa sau này.",
|
||||
"help.sketch.construction_line.parallel": "Chọn đường thẳng trong phác thảo rồi đặt đường dựng hình vô hạn song song. Nhấp chuột phải để đổi tham chiếu.",
|
||||
"help.sketch.construction_line.perpendicular": "Chọn đường thẳng trong phác thảo rồi đặt đường dựng hình vô hạn vuông góc. Nhấp chuột phải để đổi tham chiếu.",
|
||||
"help.sketch.construction_line.tangent_at": "Tạo tiếp tuyến tại một điểm trên đường tròn hoặc cung tròn, hoặc tại đầu mút của spline hay đường conic hở. Chọn đường cong rồi chọn tiếp điểm.",
|
||||
"help.sketch.construction_line.tangent_external": "Chọn đường tròn hoặc cung tròn, một điểm ngoài rồi chọn nghiệm tiếp tuyến. Bản xem trước chỉ hiển thị tiếp điểm trên cung đã vẽ.",
|
||||
"help.sketch.construction_line.tangent_two": "Chọn hai đường tròn hoặc cung tròn rồi chọn tiếp tuyến chung trong bản xem trước. Tiếp điểm phải nằm trên các đường cong đã vẽ.",
|
||||
"help.sketch.construction_line.vertical": "Tạo đường dựng hình thẳng đứng vô hạn qua một điểm. Ràng buộc đứng được giữ khi chỉnh sửa sau này.",
|
||||
"help.sketch.equation_curve": "Vẽ một đường cong từ công thức trên một khoảng: x(t) và y(t), hoặc y = f(x). Công thức có thể dùng tham số và đường cong thay đổi theo: dùng cho sóng sin, đường xoắn ốc hay đường thân khai của răng bánh răng.",
|
||||
"help.sketch.inspect": "Mở báo cáo về bản phác thảo đang mở: còn bao nhiêu bậc tự do và đã ràng buộc hoàn toàn chưa. Dùng nó để biết phần nào vẫn còn di chuyển được trước khi hoàn tất bản phác thảo.",
|
||||
"help.solid.align": "Di chuyển các khối đã chọn để một điểm, mặt hoặc mặt phẳng trên chúng gặp một điểm, mặt hoặc mặt phẳng ở nơi khác. Dùng để đặt chi tiết áp vào chi tiết khác mà không cần đo khoảng cách.",
|
||||
"help.solid.chamfer": "Vát các cạnh đã chọn theo khoảng cách bạn thiết lập. Dùng để triệt tiêu cạnh sắc hoặc giúp lắp chốt, bu lông dễ dàng hơn.",
|
||||
"help.solid.combine": "Hợp nhất các khối, cắt trừ khỏi khối đích, hoặc chỉ giữ lại phần thể tích giao nhau, đồng thời có thể giữ lại các khối công cụ. Dùng để gộp chi tiết, dùng hình này cắt hình kia hoặc tìm vị trí hai khối giao nhau.",
|
||||
@@ -1592,6 +1619,10 @@
|
||||
"material.selection.mixed": "Các vật liệu khác nhau",
|
||||
"measure.at": "Tại",
|
||||
"measure.item": "Mục {number}",
|
||||
"measure.quick_copied": "Đã sao chép số đo",
|
||||
"measure.quick_copy": "Nhấp để sao chép số đo",
|
||||
"measure.quick_edges": "{count} cạnh",
|
||||
"measure.quick_faces": "{count} mặt",
|
||||
"messages.ago_h": "{n} giờ trước",
|
||||
"messages.ago_min": "{n} phút trước",
|
||||
"messages.ago_s": "{n} giây trước",
|
||||
@@ -1744,6 +1775,11 @@
|
||||
"not a move": "không phải một phép di chuyển",
|
||||
"not a number": "không phải là số",
|
||||
"open a sketch first": "hãy mở một phác thảo trước",
|
||||
"parameters.apply_live": "Áp dụng mỗi thay đổi khi rời khỏi ô của nó",
|
||||
"parameters.apply_live_tip": "Tắt: các thay đổi chờ đến khi nhấn Áp dụng hoặc OK, rồi thiết kế được dựng lại một lần cho tất cả. Hủy hoặc Escape sẽ bỏ chúng.",
|
||||
"parameters.held": "{count} thay đổi đang chờ Áp dụng",
|
||||
"parameters.held_hint": "Sửa biểu thức và đơn vị. Thay đổi chờ đến khi nhấn Áp dụng hoặc OK.",
|
||||
"parameters.held_one": "1 thay đổi đang chờ Áp dụng",
|
||||
"parts.automatic_name": "tự động",
|
||||
"parts.dimension.across_flats": "Cỡ cờ lê",
|
||||
"parts.dimension.bore": "Đường kính lỗ",
|
||||
@@ -1766,6 +1802,8 @@
|
||||
"parts.family.set_screw": "Vít trí lục giác chìm, đầu bằng",
|
||||
"parts.family.socket_head_cap_screw": "Bu lông lục giác chìm đầu trụ",
|
||||
"parts.family.washer": "Vòng đệm phẳng",
|
||||
"parts.holes": "Lỗ (tùy chọn)",
|
||||
"parts.placement_hint": "chọn một hoặc nhiều lỗ để lắp một chi tiết vào mỗi lỗ, hoặc nhấn OK để đặt tại gốc tọa độ",
|
||||
"pattern.alignment_2": "Căn chỉnh 2",
|
||||
"pattern.alignment_hint": "Số lượng bao gồm cả bản gốc. Căn giữa sẽ cân bằng các bản sao; Đổi hướng sẽ chọn phía bổ sung cho tổng số chẵn.",
|
||||
"pattern.direction_1": "Hướng 1",
|
||||
@@ -1818,6 +1856,8 @@
|
||||
"prefs.bottom_centre": "Giữa phía dưới",
|
||||
"prefs.bottom_right": "Góc dưới bên phải",
|
||||
"prefs.browser": "Cây thư mục",
|
||||
"prefs.browser_eye_left": "Biểu tượng mắt cạnh mũi tên thu gọn",
|
||||
"prefs.browser_eye_left_note": "Các hàng trong cây thư mục hiển thị biểu tượng mắt bật/tắt cạnh mũi tên thu gọn như trước đây, thay vì trong một cột ở đầu bên phải của các hàng.",
|
||||
"prefs.browser_reveal_on_hover": "Hiện khi di chuột",
|
||||
"prefs.browser_show_note": "Hiển thị giữ trình duyệt trên màn hình. Hiện khi di chuột ẩn nó cho đến khi con trỏ đến vị trí của nó hoặc thẻ mảnh ở cạnh trái, và nó vẫn mở khi con trỏ ở bên trong hoặc một menu của nó đang mở.",
|
||||
"prefs.browser_shown": "Hiển thị",
|
||||
@@ -1868,6 +1908,8 @@
|
||||
"prefs.graphics_card_restart": "Có hiệu lực sau khi khởi động lại SolidCraft. Biến môi trường WGPU_ADAPTER_NAME sẽ ghi đè lựa chọn này.",
|
||||
"prefs.graphics_note": "Cân đối giữa tốc độ và độ mượt mà. Mỗi tùy chọn đều hiển thị hiệu ứng trực quan trên mẫu thử.",
|
||||
"prefs.graphics_presets_note": "Các cấu hình định sẵn sẽ thiết lập đồng thời các tùy chọn bên dưới.",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "Ẩn biểu tượng ràng buộc khi thu nhỏ",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out_note": "Biểu tượng ràng buộc biến mất khi bản phác được thu nhỏ đủ xa để không làm rối khung nhìn. Tắt: chúng giữ kích thước màn hình cố định ở mọi mức thu phóng.",
|
||||
"prefs.history": "Lịch sử",
|
||||
"prefs.history_origin_bodies": "Hiển thị gốc tọa độ và thân khối ở trên cùng",
|
||||
"prefs.history_origin_bodies_note": "Cây Lịch sử ở bên trái hoặc dưới cây thư mục sẽ bắt đầu với thư mục Thân khối và Gốc tọa độ của cụm chi tiết đang kích hoạt, cùng biểu tượng mắt bật/tắt và trình đơn tương tự như trong cây thư mục. Dòng Lịch sử ở cạnh dưới không thay đổi.",
|
||||
@@ -1906,6 +1948,9 @@
|
||||
"prefs.max_file_size_step": "Tệp STEP và tệp nhập",
|
||||
"prefs.max_file_size_web": "Ứng dụng web mở tệp STEP và tệp nhập tối đa 512 MB, thiết kế tối đa 256 MB: thẻ trình duyệt không chứa được tệp lớn hơn. Ứng dụng máy tính cho phép tăng các giới hạn này.",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "Biểu tượng lớn",
|
||||
"prefs.menu_list": "Danh sách",
|
||||
"prefs.menus": "Menu",
|
||||
"prefs.minutes": "phút",
|
||||
"prefs.minutes_suffix": " phút",
|
||||
"prefs.mouse": "Chuột và bàn di cảm ứng",
|
||||
@@ -1925,10 +1970,16 @@
|
||||
"prefs.origin_plane_names": "Tên các mặt phẳng gốc",
|
||||
"prefs.origin_plane_names_note": "Tên hiển thị trong trình duyệt, hộp thoại và danh sách. {top} là XY, {front} là XZ và {right} là YZ, với Z hướng lên. Lệnh, script và thiết kế đã lưu vẫn dùng XY, XZ và YZ.",
|
||||
"prefs.overall_speed": "Tốc độ tổng thể",
|
||||
"prefs.parameter_list_rows": "Số dòng danh sách tham số",
|
||||
"prefs.parameter_list_rows_tip": "Phím mũi tên xuống trong ô giá trị trống liệt kê các tham số, mục yêu thích trước, tối đa số này. Ít hơn giúp danh sách nhẹ trong thiết kế lớn.",
|
||||
"prefs.persistent_ribbon_pressed": "Tô sáng lệnh đang bật trên dải băng",
|
||||
"prefs.persistent_ribbon_pressed_help": "Tô sáng các lệnh trên dải băng khi hiệu ứng của chúng đang bật. Tắt tùy chọn này để giữ giao diện trung tính và trợ giúp lệnh như trước.",
|
||||
"prefs.perspective_fov": "Trường nhìn",
|
||||
"prefs.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.print_face_highlight": "Tô sáng mặt in",
|
||||
"prefs.print_face_highlight_tip": "Mức tô màu và viền mặt in của thân trong khung nhìn. 0 % không tô sáng.",
|
||||
"prefs.profile_active": "Hồ sơ đang dùng",
|
||||
"prefs.profile_applied": "Đã áp dụng hồ sơ.",
|
||||
"prefs.profile_changed": "Cài đặt đã thay đổi kể từ khi lưu hồ sơ. “Lưu” sẽ ghi chúng vào hồ sơ.",
|
||||
@@ -1950,6 +2001,8 @@
|
||||
"prefs.quality_fastest": "nhanh nhất",
|
||||
"prefs.quality_slowest": "chậm nhất (chất lượng cao nhất)",
|
||||
"prefs.quality_smoother": "mượt mà hơn",
|
||||
"prefs.quick_measure": "Đo nhanh ở góc khung nhìn",
|
||||
"prefs.quick_measure_note": "Hiển thị kích thước của đối tượng được chọn ở góc dưới bên phải khung nhìn, phía trên Lịch sử: chiều dài của cạnh (hoặc Ø và R với cạnh tròn), diện tích của mặt, vị trí của điểm, khoảng cách và góc giữa hai đối tượng, tổng của nhiều cạnh hoặc mặt. Nhấp để sao chép.",
|
||||
"prefs.render_previous_geometry_budget": "Dùng ngân sách hình học trước đây cho bản xem trước kết xuất",
|
||||
"prefs.render_previous_geometry_budget_help": "Khôi phục giới hạn hình học cố định trước đây (512 MiB trên máy tính 64 bit, 128 MiB trên các nền tảng khác) thay cho ngân sách dựa trên thiết bị.",
|
||||
"prefs.reports_saved": "Đã lưu {n}",
|
||||
@@ -1963,6 +2016,8 @@
|
||||
"prefs.search_empty": "Không có cài đặt nào khớp",
|
||||
"prefs.select": "Chọn",
|
||||
"prefs.select_one_item": "Chọn một mục",
|
||||
"prefs.select_other_hidden": "Chọn hình học ẩn bằng Chọn đối tượng khác",
|
||||
"prefs.select_other_hidden_help": "Bật: bao gồm các khối, bản phác thảo ẩn và cạnh bị che gần con trỏ. Tắt: dùng danh sách ban đầu chỉ gồm hình học đang hiển thị ngay dưới con trỏ.",
|
||||
"prefs.selecting": "Đang chọn",
|
||||
"prefs.shift": "Shift",
|
||||
"prefs.shift_add_ctrl_toggle": "Shift chỉ thêm; Ctrl / Cmd đảo trạng thái chọn",
|
||||
@@ -1974,11 +2029,14 @@
|
||||
"prefs.sketch_align_points_note": "Khi vẽ, một điểm được căn ngang theo một điểm phác thảo và căn dọc theo điểm khác cùng lúc, kèm đường dẫn chấm; cú nhấp giữ cả hai ràng buộc căn. Tắt: chỉ căn theo điểm trước, mỗi lần một trục.",
|
||||
"prefs.sketch_conflict_colour": "Hình học xung đột theo màu xung đột",
|
||||
"prefs.sketch_conflict_colour_note": "Khi các ràng buộc của bản phác xung đột, các đường cong và điểm mà một ràng buộc lỗi giữ cũng được vẽ bằng màu xung đột, không chỉ ký hiệu hay kích thước của nó. Tắt: chúng giữ màu thường.",
|
||||
"prefs.sketch_construction_lines": "Công cụ đường dựng hình mở rộng",
|
||||
"prefs.sketch_exit_view": "Khi kết thúc phác thảo",
|
||||
"prefs.sketch_exit_view.keep": "Giữ góc nhìn hiện tại",
|
||||
"prefs.sketch_exit_view.previous": "Trở về góc nhìn trước đó",
|
||||
"prefs.sketch_exit_view.unless_rotated": "Trở về góc nhìn trước trừ khi đã xoay",
|
||||
"prefs.sketch_exit_view_note": "Góc nhìn sẽ chuyển đến đâu sau khi bấm Hoàn tất phác thảo, Hủy bỏ hoặc bấm phím Esc hai lần.",
|
||||
"prefs.sketch_open_look_at": "Xoay góc nhìn trực diện phác thảo khi mở",
|
||||
"prefs.sketch_open_look_at_note": "Tắt: tạo hoặc chỉnh sửa phác thảo sẽ giữ nguyên góc nhìn. Dùng Nhìn trực diện để nhìn thẳng vào phác thảo khi cần.",
|
||||
"prefs.sketch_recent_snap": "Tự động bắt điểm vào tâm và trung điểm gần đây",
|
||||
"prefs.sketch_recent_snap_note": "Khi vẽ, con trỏ được bắt vào nơi các đường dẫn của hai tâm đường tròn hoặc trung điểm cạnh thẳng gần nhất cắt nhau. Tắt: các đường dẫn chỉ để xem và con trỏ không di chuyển.",
|
||||
"prefs.sketch_selection": "Phác thảo & chọn đối tượng",
|
||||
@@ -2038,13 +2096,21 @@
|
||||
"prefs.under_cursor": "Dưới con trỏ chuột",
|
||||
"prefs.units_precision": "Đơn vị và độ chính xác",
|
||||
"prefs.updates": "Cập nhật ứng dụng",
|
||||
"prefs.value_boxes_in_view": "Hiện giá trị cạnh tay nắm trong khung nhìn",
|
||||
"prefs.value_boxes_in_view_tip": "Giá trị của lệnh (Khoảng cách, Bán kính…) trong một ô cạnh tay nắm, phía trên mô hình. Tắt: bạn nhập giá trị trong cửa sổ lệnh, cửa sổ này nhận bàn phím sau khi chọn, và không có gì che mô hình.",
|
||||
"prefs.view": "Hành vi góc nhìn",
|
||||
"prefs.view_confirm_corner": "Nút Đồng ý và Hủy bỏ ở góc khung nhìn",
|
||||
"prefs.view_confirm_corner_note": "Khi hộp thoại hoặc môi trường phác thảo đang mở, dấu tích và dấu chéo ở góc trên bên phải khung nhìn có chức năng tương tự nút Đồng ý và Hủy bỏ của hộp thoại, hoặc để hoàn tất và hủy phác thảo. Khối điều hướng góc nhìn sẽ dời xuống bên dưới chúng.",
|
||||
"prefs.viewport": "Khung nhìn 3D",
|
||||
"prefs.viewport_analysis_status": "Hiện trạng thái phân tích trong khung nhìn",
|
||||
"prefs.viewport_analysis_status_help": "Hiện các phân tích đang bật phía trên khung nhìn và mở chú giải từ từng mục. Esc tắt phân tích mới nhất. Tắt tùy chọn để khôi phục chú giải ở góc và hành vi Esc trước đây.",
|
||||
"prefs.viewport_background": "Nền",
|
||||
"prefs.viewport_background_colour": "Màu",
|
||||
"prefs.viewport_background_theme": "Dải màu của chủ đề",
|
||||
"prefs.viewport_section_status": "Hiện trạng thái mặt cắt trong khung nhìn",
|
||||
"prefs.viewport_section_status_help": "Hiện thiết lập mặt cắt trong khung nhìn. Escape và nút dải công cụ đang bật sẽ tắt mặt cắt. Tắt tùy chọn để giữ cách điều khiển mặt cắt trước đây.",
|
||||
"prefs.viewport_status_summary": "Tóm tắt các mục trạng thái khung nhìn",
|
||||
"prefs.viewport_status_summary_help": "Gộp từ ba mục trạng thái khung nhìn trở lên thành một huy hiệu tóm tắt. Tắt để giữ mọi mục trong hàng đầy đủ.",
|
||||
"prefs.wheel": "Con lăn chuột",
|
||||
"prefs.wheel_trackpad": "Con lăn chuột và bàn di cảm ứng",
|
||||
"prefs.workspace": "Không gian làm việc",
|
||||
@@ -2054,6 +2120,10 @@
|
||||
"properties.title": "Thuộc tính: {name}",
|
||||
"recovery.days_ago": "{count} ngày trước",
|
||||
"recovery.detail": "{file} · {features} tính năng · tự động lưu {when}",
|
||||
"recovery.discard_all": "Hủy bỏ tất cả",
|
||||
"recovery.discard_all_detail": "Không thể hoàn tác thao tác này.",
|
||||
"recovery.discard_all_title": "Hủy bỏ {count} bản tự động lưu?",
|
||||
"recovery.discard_all_title_one": "Hủy bỏ 1 bản tự động lưu?",
|
||||
"recovery.hours_ago": "{count} giờ trước",
|
||||
"recovery.just_now": "vừa xong",
|
||||
"recovery.min_ago": "{count} phút trước",
|
||||
@@ -2919,7 +2989,9 @@
|
||||
"render.output_ready": "Sẵn sàng kết xuất",
|
||||
"render.output_run_check": "Chạy kiểm tra",
|
||||
"render.output_size_limit": "{method}: tối đa {dimension} px mỗi cạnh và {pixels} điểm ảnh. Kích thước lớn nhất vừa khít: {width} × {height}.",
|
||||
"render.output_size_method_profile": "Để kết xuất {width} × {height}, hãy chọn {method} với cấu hình tài nguyên CPU {profile} hoặc một kích thước nhỏ hơn.",
|
||||
"render.output_size_ok": "Kích thước ổn · {width} × {height} px",
|
||||
"render.output_size_profile": "Để kết xuất {width} × {height}, hãy chọn cấu hình tài nguyên CPU {profile} hoặc một kích thước nhỏ hơn.",
|
||||
"render.output_swap_orientation": "Hoán đổi chiều rộng và chiều cao",
|
||||
"render.output_tools_inspector": "Chỉnh kích thước và định dạng trong bảng thuộc tính Đầu ra.",
|
||||
"render.output_view_image": "Xem ảnh",
|
||||
@@ -3132,6 +3204,7 @@
|
||||
"render.size_custom": "Tùy chỉnh…",
|
||||
"render.size_full_hd": "Full HD",
|
||||
"render.size_hd": "HD",
|
||||
"render.size_needs_profile": "{size} · cần {profile}",
|
||||
"render.size_presets": "Cỡ đặt sẵn",
|
||||
"render.size_presets_pending": "Sẽ có cùng trình chỉnh Đầu ra",
|
||||
"render.size_preview": "Xem trước",
|
||||
@@ -3419,6 +3492,24 @@
|
||||
"sheet.flange_type_contour": "Đường viền",
|
||||
"sheet.flange_type_edge": "Cạnh",
|
||||
"sheet.no_body": "chưa có khối tấm kim loại: hãy bắt đầu bằng Cánh gập > Cơ sở (Base), hoặc chuyển một tấm phẳng thành tấm kim loại bằng lệnh 'Chuyển thành tấm kim loại' (chọn mặt phẳng lớn của nó)",
|
||||
"sheet.personal_rules.confirm_delete": "Xác nhận xóa",
|
||||
"sheet.personal_rules.delete": "Xóa",
|
||||
"sheet.personal_rules.delete_prompt": "Xóa quy tắc cá nhân này? Các quy tắc đã có trong thiết kế sẽ không thay đổi.",
|
||||
"sheet.personal_rules.full": "Thư viện cá nhân đã chứa tối đa 64 quy tắc.",
|
||||
"sheet.personal_rules.invalid_name": "Nhập một tên ngắn gọn, không chứa ký tự xuống dòng hoặc ký tự điều khiển.",
|
||||
"sheet.personal_rules.invalid_values": "Hãy điền đủ các giá trị của quy tắc: mỗi giá trị phải tính được trong thiết kế này, với độ dày dương và hệ số K từ 0 đến 1.",
|
||||
"sheet.personal_rules.missing": "Quy tắc cá nhân đã chọn không còn khả dụng.",
|
||||
"sheet.personal_rules.name": "Tên quy tắc",
|
||||
"sheet.personal_rules.name_exists": "Quy tắc cá nhân với tên này đã tồn tại. Vui lòng sử dụng tên khác.",
|
||||
"sheet.personal_rules.name_hint": "Tên duy nhất",
|
||||
"sheet.personal_rules.overwrite": "Lưu các thay đổi vào quy tắc cá nhân đã chọn",
|
||||
"sheet.personal_rules.rename": "Đổi tên",
|
||||
"sheet.personal_rules.rule": "Quy tắc cá nhân",
|
||||
"sheet.personal_rules.save": "Lưu mới",
|
||||
"sheet.personal_rules.scope": "Được lưu trong hồ sơ cá nhân của bạn cho tất cả thiết kế.",
|
||||
"sheet.personal_rules.select": "Chọn một quy tắc",
|
||||
"sheet.personal_rules.title": "Quy tắc cá nhân",
|
||||
"sheet.personal_rules.unsupported": "Quy tắc này có các giá trị mà phiên bản hiện tại không thể sử dụng.",
|
||||
"shortcut.json_error": "tham số phải là JSON: {error}",
|
||||
"shortcut.no_command": "không có lệnh `{id}`; văn bản trước JSON phải là mã lệnh",
|
||||
"shortcut.pin": "Ghim lên đầu hộp S",
|
||||
@@ -3455,6 +3546,7 @@
|
||||
"sketch_text.align_right": "Phải",
|
||||
"sketch_text.bold": "In đậm",
|
||||
"sketch_text.builtin_font": "{family} (tích hợp sẵn)",
|
||||
"sketch_text.formula": "Bắt đầu bằng = để tạo chữ từ các tham số: ='W' + number(Width)",
|
||||
"sketch_text.italic": "In nghiêng",
|
||||
"sketch_text.loading_fonts": "Đang tìm kiếm các phông chữ đã cài đặt…",
|
||||
"sketch_text.on_path": "Văn bản đi theo đường dẫn: đường cong định vị văn bản",
|
||||
@@ -3595,6 +3687,8 @@
|
||||
"ui.3d_mouse_section": "Chuột 3D",
|
||||
"ui.3d_mouse_show_in_view": "Hiện chuột 3D trong khung nhìn 3D",
|
||||
"ui.3d_mouse_show_in_view_tip": "Hình chuột 3D của bạn ở góc dưới bên trái, chuyển động theo bạn.",
|
||||
"ui.3d_mouse_sketch_orbit": "Xoay bản phác thảo bằng chuột 3D",
|
||||
"ui.3d_mouse_sketch_orbit_tip": "Chuột 3D cũng xoay khung nhìn khi bản phác thảo đang mở và được nhìn thẳng, như chuột thường. Tắt: chỉ di chuyển và thu phóng bản phác thảo cho đến khi bạn nghiêng khung nhìn.",
|
||||
"ui.3d_mouse_skip": "Thiết bị của tôi không làm được điều này",
|
||||
"ui.3d_mouse_skip_tip": "Bỏ qua chuyển động này theo cả hai chiều; việc hiệu chuẩn sẽ không dùng nó.",
|
||||
"ui.3d_mouse_speed": "Tốc độ chuột 3D",
|
||||
@@ -3623,6 +3717,8 @@
|
||||
"ui.3d_mouse_step_twist_counterclockwise": "Xoay núm điều khiển ngược chiều kim đồng hồ, giữ nguyên, rồi thả ra.",
|
||||
"ui.3d_mouse_switch_cap": "Hiệu chuẩn theo kiểu trở về giữa",
|
||||
"ui.3d_mouse_switch_trackball": "Hiệu chuẩn như bi xoay",
|
||||
"ui.3d_mouse_use_direct": "Dùng trình điều khiển trực tiếp",
|
||||
"ui.3d_mouse_use_direct_tip": "Đọc chuột 3D trực tiếp (hidraw) thay vì qua spacenavd.",
|
||||
"ui.3d_mouse_view_axis": "Trục khung nhìn",
|
||||
"ui.3d_mouse_view_off": "Tắt",
|
||||
"ui.3d_mouse_view_off_tip": "Hồ sơ của trình điều khiển chuột 3D tắt trục này, nên chuyển động của nó không làm khung nhìn di chuyển. Chọn chuyển động của trục khác ở đây sẽ hoán đổi hai trục, và chuyển động đó không còn di chuyển khung nhìn.",
|
||||
@@ -3756,6 +3852,8 @@
|
||||
"ui.agent_chat_remove": "Xóa khỏi hàng đợi",
|
||||
"ui.agent_chat_remove_file": "Bỏ tệp này khỏi tin nhắn",
|
||||
"ui.agent_chat_remove_imported": "Gỡ khỏi tin nhắn này; chi tiết vẫn nằm trong thiết kế (sử dụng Hoàn tác để xóa)",
|
||||
"ui.agent_chat_reply_cut_off": "Câu trả lời đã chạm giới hạn độ dài trước khi xong nên có thể chưa đầy đủ. Hãy yêu cầu AI Agent tiếp tục, hoặc trả lời ngắn hơn.",
|
||||
"ui.agent_chat_reply_declined": "Mô hình đã từ chối trả lời tin nhắn này.",
|
||||
"ui.agent_chat_retry": "Thử lại",
|
||||
"ui.agent_chat_right": "Phải (Hình chiếu cạnh)",
|
||||
"ui.agent_chat_save": "Lưu",
|
||||
@@ -3939,6 +4037,7 @@
|
||||
"ui.circle": "Hình tròn",
|
||||
"ui.circular_pattern": "Mảng tròn quanh trục",
|
||||
"ui.clear": "Xóa",
|
||||
"ui.clear_print_face": "Xóa mặt in",
|
||||
"ui.clear_recent": "Xóa danh sách gần đây",
|
||||
"ui.clear_the_selection": "Hủy chọn",
|
||||
"ui.click_selects_whole_bodies": "Nhấp chuột chọn toàn bộ thân khối",
|
||||
@@ -3973,6 +4072,15 @@
|
||||
"ui.constant": "Hằng số",
|
||||
"ui.constraints_conflict": "Xung đột ràng buộc",
|
||||
"ui.construction": "Đường gióng dựng hình",
|
||||
"ui.construction.angle_hint": "Đường dựng hình: nhấp một điểm rồi nhập góc hoặc chọn hướng",
|
||||
"ui.construction.bisector_hint": "Phân giác: chọn hai đường thẳng giao nhau rồi chọn nghiệm mong muốn",
|
||||
"ui.construction.common_hint": "Tiếp tuyến chung: chọn hai đường tròn/cung rồi chọn nghiệm mong muốn",
|
||||
"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.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.context_menu": "Menu ngữ cảnh",
|
||||
"ui.continuity_g0": "Nếp gập G0",
|
||||
"ui.continuity_g1": "G1 tiếp tuyến",
|
||||
@@ -4362,12 +4470,17 @@
|
||||
"ui.plugins_skipped": "Không thể đọc {count} thư mục.",
|
||||
"ui.plugins_version": "{version}",
|
||||
"ui.point": "Điểm",
|
||||
"ui.point_cloud_add_filter": "Thêm bộ lọc…",
|
||||
"ui.point_cloud_auto_range": "Phạm vi tự động",
|
||||
"ui.point_cloud_color_by": "Tô màu theo",
|
||||
"ui.point_cloud_file_colors": "Màu trong tệp",
|
||||
"ui.point_cloud_filter_by": "Lọc theo",
|
||||
"ui.point_cloud_height_high": "Cao nhất",
|
||||
"ui.point_cloud_height_low": "Thấp nhất",
|
||||
"ui.point_cloud_legend_filter": "Hiện và ẩn trong chú giải ở trên",
|
||||
"ui.point_cloud_no_file_colors": "Tệp không có màu",
|
||||
"ui.point_cloud_pause": "Tạm dừng",
|
||||
"ui.point_cloud_play": "Phát",
|
||||
"ui.point_cloud_single_color": "Một màu",
|
||||
"ui.point_cloud_size": "Kích thước điểm",
|
||||
"ui.point_cloud_units": "Đơn vị của tệp",
|
||||
@@ -4376,6 +4489,11 @@
|
||||
"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_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",
|
||||
"ui.profile_plane": "Mặt phẳng biên dạng",
|
||||
"ui.profiles": "Các biên dạng",
|
||||
@@ -4439,6 +4557,7 @@
|
||||
"ui.revolve": "Xoay tròn tạo khối",
|
||||
"ui.ribbon_exit": "Thoát",
|
||||
"ui.ribbon_labels": "Hiển thị văn bản bên dưới biểu tượng dải băng",
|
||||
"ui.ribbon_menus_skip_pinned": "Không lặp lại các lệnh đã ghim trong menu thả xuống",
|
||||
"ui.ribbon_pin_footer": "Các lệnh đã ghim hiển thị trên ruy-băng. Rê chuột lên một hàng để ghim hoặc bỏ ghim.",
|
||||
"ui.ribbon_workspace": "Không gian làm việc",
|
||||
"ui.right": "Phải (Hình chiếu cạnh)",
|
||||
@@ -4457,6 +4576,7 @@
|
||||
"ui.selectOther": "Chọn đối tượng khác…",
|
||||
"ui.selectTangentChain": "Chọn chuỗi tiếp tuyến",
|
||||
"ui.select_all": "Chọn tất cả",
|
||||
"ui.select_components_by_face": "Chọn thành phần (chọn một mặt sẽ chọn thành phần chứa mặt đó)",
|
||||
"ui.selection": "Vùng chọn",
|
||||
"ui.selection_modifiers": "Phím bổ trợ chọn",
|
||||
"ui.selection_shift_add_only": "Shift+nhấp chỉ thêm, không bỏ chọn",
|
||||
@@ -4498,6 +4618,11 @@
|
||||
"ui.sketch_intersect_selection_filter": "Bộ lọc lựa chọn",
|
||||
"ui.sketch_modifiers": "Phím bổ trợ phác thảo",
|
||||
"ui.sketch_project_geometry": "Hình học",
|
||||
"ui.sketch_report.all_constrained": "Mọi thứ đã được ràng buộc hoàn toàn.",
|
||||
"ui.sketch_report.dof": "Bậc tự do",
|
||||
"ui.sketch_report.loose": "Chưa ràng buộc hoàn toàn",
|
||||
"ui.sketch_report.status": "Trạng thái",
|
||||
"ui.sketch_report.title": "Báo cáo phác thảo",
|
||||
"ui.sketch_scale": "Tỷ lệ phác thảo",
|
||||
"ui.sketches": "Các phác thảo",
|
||||
"ui.slice": "Cắt lát",
|
||||
@@ -4602,10 +4727,19 @@
|
||||
"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",
|
||||
"ui.viewport_status_edges": "{count} cạnh",
|
||||
"ui.viewport_status_from": "từ {direction}",
|
||||
"ui.viewport_status_key": "Hiển thị chú giải cho {name}",
|
||||
"ui.viewport_status_minimum_radius": "Bán kính nhỏ nhất",
|
||||
"ui.viewport_status_pairs": "{count} cặp",
|
||||
"ui.viewport_status_section_hatch": "{setting} · gạch mặt cắt",
|
||||
"ui.viewport_status_section_plane": "Mặt phẳng hiện tại",
|
||||
"ui.viewport_status_section_tilted": "nghiêng · {offset}",
|
||||
"ui.viewport_status_stripes": "{count} sọc",
|
||||
"ui.viewport_status_summary_clear": "Tắt tất cả",
|
||||
"ui.viewport_status_summary_heading": "Trong khung nhìn này",
|
||||
"ui.viewport_status_summary_label": "{name} và {count} mục khác",
|
||||
"ui.viewport_status_summary_more": "+{count}",
|
||||
"ui.viewport_theme_follow": "Theo chủ đề giao diện",
|
||||
"ui.visual_shaded_hidden_edges": "Tô bóng kèm nét khuất",
|
||||
"ui.visual_shaded_with_edges": "Tô bóng kèm hiển thị cạnh",
|
||||
@@ -4642,7 +4776,9 @@
|
||||
"update.button": "Cập nhật",
|
||||
"update.button_version": "Cập nhật lên {version}",
|
||||
"update.check_automatically": "Tự động kiểm tra bản cập nhật",
|
||||
"update.check_for_updates": "Kiểm tra bản cập nhật",
|
||||
"update.check_now": "Kiểm tra ngay",
|
||||
"update.check_unavailable": "Không thể kiểm tra bản cập nhật cho bản cài đặt này.",
|
||||
"update.checking": "Đang kiểm tra…",
|
||||
"update.copy": "Sao chép",
|
||||
"update.dmg_opened": "Ảnh đĩa đã mở và được xác thực: kéo SolidCraft vào thư mục Applications để thay thế bản cũ, sau đó khởi động lại SolidCraft.",
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
"BLEND SURFACE": "混合曲面",
|
||||
"BOUNDARY SURFACE": "边界曲面",
|
||||
"BROWSER": "浏览器",
|
||||
"Back to the original order": "恢复原始顺序",
|
||||
"Ball": "球",
|
||||
"Bar": "扁钢",
|
||||
"Base Center": "底面中心",
|
||||
@@ -595,6 +596,8 @@
|
||||
"Snap to face and edge centres": "捕捉到面和边的中心",
|
||||
"SolidCraft cannot submit reports directly. Submit on GitHub; no token is needed in the app.": "SolidCraft 无法直接提交报告。请在 GitHub 上提交;应用中不需要令牌。",
|
||||
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft 在这些设计保存前已关闭。可以打开它们的上次自动保存。",
|
||||
"Sort A to Z": "升序排序",
|
||||
"Sort Z to A": "降序排序",
|
||||
"Spacing 2 must be a non-zero number": "间距 2 必须是非零数字",
|
||||
"Spacing must be a non-zero number": "间距必须是非零数字",
|
||||
"Specialise": "专用操作",
|
||||
@@ -775,6 +778,8 @@
|
||||
"at the face": "在面上",
|
||||
"bom.group_children": "子零件并入所属零件的行",
|
||||
"browser.body_name": "实体{number}",
|
||||
"browser.calibrate_click_a": "点击图像选取点 A,或输入其坐标。",
|
||||
"browser.calibrate_click_b": "点击图像选取点 B,或输入其坐标。",
|
||||
"browser.calibrate_title": "校准:{name}",
|
||||
"browser.capture_many": "{count} 个组件已移动,但尚未捕获其位置。",
|
||||
"browser.capture_note": "“捕获位置”保留新位置;“恢复位置”还原原先的位置。",
|
||||
@@ -817,6 +822,7 @@
|
||||
"cmd.appearance_pick_up": "拾取外观",
|
||||
"cmd.appearance_replace": "替换外观",
|
||||
"cmd.assembly_bom": "物料清单",
|
||||
"cmd.body.print_face": "打印底面",
|
||||
"cmd.body_rename": "重命名实体",
|
||||
"cmd.browser_collapse_all": "全部折叠",
|
||||
"cmd.browser_expand_all": "全部展开",
|
||||
@@ -1101,6 +1107,16 @@
|
||||
"cmd.sheet_refold": "重新折弯",
|
||||
"cmd.sheet_rules": "列出钣金规则",
|
||||
"cmd.sheet_unfold": "展开",
|
||||
"cmd.sketch.construction_line": "构造直线",
|
||||
"cmd.sketch.construction_line.angle": "指定角度构造直线",
|
||||
"cmd.sketch.construction_line.bisector": "角平分线",
|
||||
"cmd.sketch.construction_line.horizontal": "水平构造直线",
|
||||
"cmd.sketch.construction_line.parallel": "平行构造直线",
|
||||
"cmd.sketch.construction_line.perpendicular": "垂直构造直线",
|
||||
"cmd.sketch.construction_line.tangent_at": "曲线上点处的切线",
|
||||
"cmd.sketch.construction_line.tangent_external": "过外部点的切线",
|
||||
"cmd.sketch.construction_line.tangent_two": "公切线",
|
||||
"cmd.sketch.construction_line.vertical": "竖直构造直线",
|
||||
"cmd.sketch_arc3d": "三维圆弧",
|
||||
"cmd.sketch_arc_center_point": "中心点圆弧",
|
||||
"cmd.sketch_arc_tangent": "相切圆弧",
|
||||
@@ -1389,7 +1405,18 @@
|
||||
"help.occurrence.move": "仅沿关节允许的自由度移动装配零部件。选中零部件的面后,拖动可用的箭头或圆弧。",
|
||||
"help.parameters.change": "打开驱动设计尺寸的命名数值和表达式表。用于在一处修改尺寸,让所有引用它的特征随之更新。",
|
||||
"help.pointcloud.insert": "将 PLY、XYZ 或 PTS 扫描作为点云插入到模型旁。根据测量数据建模时,可将其用作参考。",
|
||||
"help.sketch.construction_line": "创建经过两点的无限构造直线,用于辅助绘制草图而不分割轮廓。",
|
||||
"help.sketch.construction_line.angle": "创建与草图X轴成指定角度的无限构造直线。选择一点后输入角度或选择方向。",
|
||||
"help.sketch.construction_line.bisector": "选择两条相交的草图直线,再选择所需的角平分线。结果随原直线的编辑而变化。",
|
||||
"help.sketch.construction_line.horizontal": "创建经过指定点的无限水平构造直线。后续编辑时保持水平约束。",
|
||||
"help.sketch.construction_line.parallel": "选择草图直线,然后放置与其平行的无限构造直线。右键单击可更换参照。",
|
||||
"help.sketch.construction_line.perpendicular": "选择草图直线,然后放置与其垂直的无限构造直线。右键单击可更换参照。",
|
||||
"help.sketch.construction_line.tangent_at": "在圆或圆弧上的点处,或开放样条曲线、圆锥曲线的端点处创建切线。先选曲线,再选接点。",
|
||||
"help.sketch.construction_line.tangent_external": "依次选择圆或圆弧、外部点和所需切线。预览仅显示接点位于已绘圆弧上的解。",
|
||||
"help.sketch.construction_line.tangent_two": "选择两个圆或圆弧,再从预览中选择公切线。接点必须位于已绘曲线上。",
|
||||
"help.sketch.construction_line.vertical": "创建经过指定点的无限竖直构造直线。后续编辑时保持竖直约束。",
|
||||
"help.sketch.equation_curve": "在一个范围内按公式绘制曲线:x(t) 和 y(t),或 y = f(x)。公式可以使用参数,曲线随参数变化。适用于正弦波、螺旋线或齿轮的渐开线。",
|
||||
"help.sketch.inspect": "打开当前草图的报告:显示剩余的自由度以及是否已完全约束。在完成草图之前,用它找出仍然可以移动的部分。",
|
||||
"help.solid.align": "移动所选实体,使其上的点、面或平面与别处的点、面或平面对齐。用于把一个零件贴靠到另一个零件上,而无需测量距离。",
|
||||
"help.solid.chamfer": "用指定的距离在所选边上切出一个平直的斜面。用于去除锐边,或使销钉、螺钉更容易插入。",
|
||||
"help.solid.combine": "将多个实体合并、从目标实体中切除,或只保留它们共有的体积,还可以保留工具实体。用于合并零件、用一个形体切割另一个形体,或找出两个实体重叠的位置。",
|
||||
@@ -1592,6 +1619,10 @@
|
||||
"material.selection.mixed": "不同材料",
|
||||
"measure.at": "位置",
|
||||
"measure.item": "项目 {number}",
|
||||
"measure.quick_copied": "已复制测量值",
|
||||
"measure.quick_copy": "点击复制测量值",
|
||||
"measure.quick_edges": "{count} 条边",
|
||||
"measure.quick_faces": "{count} 个面",
|
||||
"messages.ago_h": "{n} 小时前",
|
||||
"messages.ago_min": "{n} 分钟前",
|
||||
"messages.ago_s": "{n} 秒前",
|
||||
@@ -1744,6 +1775,11 @@
|
||||
"not a move": "不是移动",
|
||||
"not a number": "不是数字",
|
||||
"open a sketch first": "请先打开一个草图",
|
||||
"parameters.apply_live": "离开字段时立即应用每项更改",
|
||||
"parameters.apply_live_tip": "关闭时:更改会等到“应用”或“确定”,然后设计只为所有更改重建一次。“取消”或 Esc 会放弃这些更改。",
|
||||
"parameters.held": "{count} 项更改等待应用",
|
||||
"parameters.held_hint": "编辑表达式和单位。更改将等到“应用”或“确定”时生效。",
|
||||
"parameters.held_one": "1 项更改等待应用",
|
||||
"parts.automatic_name": "自动",
|
||||
"parts.dimension.across_flats": "对边宽度",
|
||||
"parts.dimension.bore": "内径",
|
||||
@@ -1766,6 +1802,8 @@
|
||||
"parts.family.set_screw": "内六角平端紧定螺钉",
|
||||
"parts.family.socket_head_cap_screw": "内六角圆柱头螺钉",
|
||||
"parts.family.washer": "平垫圈",
|
||||
"parts.holes": "孔(可选)",
|
||||
"parts.placement_hint": "选择一个或多个孔,在每个孔中各装一个零件;或按确定将其放在原点",
|
||||
"pattern.alignment_2": "对齐 2",
|
||||
"pattern.alignment_hint": "数量包含原始图形。居中时副本分布在两侧;数量为偶数时,反转可切换多一个副本的一侧。",
|
||||
"pattern.direction_1": "方向 1",
|
||||
@@ -1818,6 +1856,8 @@
|
||||
"prefs.bottom_centre": "底部居中",
|
||||
"prefs.bottom_right": "右下角",
|
||||
"prefs.browser": "浏览器",
|
||||
"prefs.browser_eye_left": "显示切换位于折叠箭头旁",
|
||||
"prefs.browser_eye_left_note": "浏览器各行的显示切换像以前一样位于折叠箭头旁,而不是在行右端的一列中。",
|
||||
"prefs.browser_reveal_on_hover": "悬停时显示",
|
||||
"prefs.browser_show_note": "“显示”让浏览器始终留在屏幕上。“悬停时显示”会将其隐藏,直到指针移到它的位置或其左边缘的细标签上;指针在其中或其菜单打开时,它保持打开。",
|
||||
"prefs.browser_shown": "显示",
|
||||
@@ -1868,6 +1908,8 @@
|
||||
"prefs.graphics_card_restart": "重启 SolidCraft 后生效。WGPU_ADAPTER_NAME 优先于此选择。",
|
||||
"prefs.graphics_note": "在速度与平滑度之间取舍。每个选项均以示例展示效果。",
|
||||
"prefs.graphics_presets_note": "预设会同时设置以下选项。",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out": "缩小时隐藏约束符号",
|
||||
"prefs.hide_constraint_glyphs_zoomed_out_note": "草图缩小到一定程度时隐藏约束符号,以免遮挡草图。关闭:任意缩放比例下符号仍保持固定屏幕大小。",
|
||||
"prefs.history": "历史",
|
||||
"prefs.history_origin_bodies": "在顶部显示原点和实体",
|
||||
"prefs.history_origin_bodies_note": "左侧或浏览器下方的历史以活动组件的实体文件夹和原点开头,并带有与浏览器相同的显示切换和菜单。底部的历史保持不变。",
|
||||
@@ -1906,6 +1948,9 @@
|
||||
"prefs.max_file_size_step": "STEP 和导入文件",
|
||||
"prefs.max_file_size_web": "网页版可打开最大 512 MB 的 STEP 和导入文件,以及最大 256 MB 的设计:浏览器标签页无法容纳更大的文件。桌面版可提高这些上限。",
|
||||
"prefs.megabytes": "MB",
|
||||
"prefs.menu_grid": "大图标",
|
||||
"prefs.menu_list": "列表",
|
||||
"prefs.menus": "菜单",
|
||||
"prefs.minutes": "分钟",
|
||||
"prefs.minutes_suffix": " min",
|
||||
"prefs.mouse": "鼠标与触控板",
|
||||
@@ -1925,10 +1970,16 @@
|
||||
"prefs.origin_plane_names": "原点平面名称",
|
||||
"prefs.origin_plane_names_note": "浏览器、对话框和列表中显示的名称。{top} 即 XY,{front} 即 XZ,{right} 即 YZ(Z 轴朝上)。命令、脚本和已保存的设计仍使用 XY、XZ 和 YZ。",
|
||||
"prefs.overall_speed": "整体速度",
|
||||
"prefs.parameter_list_rows": "参数列表行数",
|
||||
"prefs.parameter_list_rows_tip": "在空的数值框中按下箭头会列出参数(收藏的在前),最多列出此数量。数量越少,大型设计中的列表越轻。",
|
||||
"prefs.persistent_ribbon_pressed": "突出显示已启用的功能区命令",
|
||||
"prefs.persistent_ribbon_pressed_help": "在效果启用时突出显示相应的功能区命令。关闭此选项可保留原来的中性外观和命令帮助。",
|
||||
"prefs.perspective_fov": "视场角",
|
||||
"prefs.placement": "放置",
|
||||
"prefs.prefer_free_sketch_points": "草图点重合时优先选择可移动的点",
|
||||
"prefs.preview_choices": "所选设置预览",
|
||||
"prefs.print_face_highlight": "打印底面高亮",
|
||||
"prefs.print_face_highlight_tip": "视图中实体打印底面的着色和轮廓强度。0 % 不显示高亮。",
|
||||
"prefs.profile_active": "当前配置文件",
|
||||
"prefs.profile_applied": "已应用配置文件。",
|
||||
"prefs.profile_changed": "保存配置文件后设置已更改。“保存”会将其写入配置文件。",
|
||||
@@ -1950,6 +2001,8 @@
|
||||
"prefs.quality_fastest": "最快",
|
||||
"prefs.quality_slowest": "最慢",
|
||||
"prefs.quality_smoother": "更平滑",
|
||||
"prefs.quick_measure": "在视图角落显示快速测量",
|
||||
"prefs.quick_measure_note": "在视图右下角、历史记录上方显示所选对象的尺寸:边的长度(圆形边显示 Ø 和 R)、面的面积、点的位置、两个对象之间的距离和角度、多条边或多个面的总和。点击即可复制。",
|
||||
"prefs.render_previous_geometry_budget": "使用先前的渲染预览几何内存预算",
|
||||
"prefs.render_previous_geometry_budget_help": "恢复先前的固定几何内存上限(64位桌面为512 MiB,其他平台为128 MiB),替代基于设备的预算。",
|
||||
"prefs.reports_saved": "已保存 {n} 份",
|
||||
@@ -1963,6 +2016,8 @@
|
||||
"prefs.search_empty": "没有匹配的设置",
|
||||
"prefs.select": "选择",
|
||||
"prefs.select_one_item": "选择一个项目",
|
||||
"prefs.select_other_hidden": "使用“选择其他”选择隐藏几何体",
|
||||
"prefs.select_other_hidden_help": "开启:包括指针附近的隐藏实体、草图和被遮挡的边。关闭:恢复原来的列表,仅包含指针正下方的可见几何体。",
|
||||
"prefs.selecting": "选择",
|
||||
"prefs.shift": "Shift",
|
||||
"prefs.shift_add_ctrl_toggle": "Shift 仅添加;Ctrl / Cmd 切换选择",
|
||||
@@ -1974,11 +2029,14 @@
|
||||
"prefs.sketch_align_points_note": "绘制时,点可同时与一个草图点水平对齐、与另一个草图点竖直对齐,并显示虚线参考线;单击后保留两个对齐约束。关闭:仅与上一点对齐,每次一个方向。",
|
||||
"prefs.sketch_conflict_colour": "以冲突颜色显示冲突的几何图形",
|
||||
"prefs.sketch_conflict_colour_note": "草图约束冲突时,不仅失败约束的符号或尺寸,该约束所约束的曲线和点也以冲突颜色绘制。关闭:它们保持平常的颜色。",
|
||||
"prefs.sketch_construction_lines": "扩展构造直线工具",
|
||||
"prefs.sketch_exit_view": "草图结束时的视图",
|
||||
"prefs.sketch_exit_view.keep": "保持当前视图",
|
||||
"prefs.sketch_exit_view.previous": "返回之前的视图",
|
||||
"prefs.sketch_exit_view.unless_rotated": "未旋转时返回",
|
||||
"prefs.sketch_exit_view_note": "“完成草图”、取消或连按两次 Esc 后视图的去向。",
|
||||
"prefs.sketch_open_look_at": "打开草图时转向正对草图",
|
||||
"prefs.sketch_open_look_at_note": "关闭:创建或编辑草图时视图保持不变。需要时用“朝向”正对草图。",
|
||||
"prefs.sketch_recent_snap": "自动吸附到最近的圆心和中点",
|
||||
"prefs.sketch_recent_snap_note": "绘图时,将光标吸附到最近两个圆心或直线边中点的辅助线相交处。关闭:辅助线仅作显示,光标不会移动。",
|
||||
"prefs.sketch_selection": "草图与选择",
|
||||
@@ -2038,13 +2096,21 @@
|
||||
"prefs.under_cursor": "光标下方",
|
||||
"prefs.units_precision": "单位与精度",
|
||||
"prefs.updates": "更新",
|
||||
"prefs.value_boxes_in_view": "在视图中于手柄旁显示数值",
|
||||
"prefs.value_boxes_in_view_tip": "命令的数值(距离、半径等)显示在模型上方手柄旁的框中。关闭后,在命令窗口中输入数值,选取后键盘输入会进入该窗口,模型不会被遮挡。",
|
||||
"prefs.view": "视图行为",
|
||||
"prefs.view_confirm_corner": "在视图角落显示确定和取消",
|
||||
"prefs.view_confirm_corner_note": "打开对话框或草图时,视图右上角的勾号和叉号与对话框的确定和取消作用相同,或完成和取消草图。视图导航器会下移到它们下方。",
|
||||
"prefs.viewport": "视口",
|
||||
"prefs.viewport_analysis_status": "显示视口分析状态",
|
||||
"prefs.viewport_analysis_status_help": "在视口上方显示启用的分析,从各项目打开图例,并按 Esc 关闭最近启用的分析。关闭此选项可恢复角落图例和原来的 Esc 行为。",
|
||||
"prefs.viewport_background": "背景",
|
||||
"prefs.viewport_background_colour": "颜色",
|
||||
"prefs.viewport_background_theme": "主题渐变",
|
||||
"prefs.viewport_section_status": "显示视口剖面状态",
|
||||
"prefs.viewport_section_status_help": "在视口中显示剖面设置。按 Escape 或单击活动的功能区按钮可关闭剖面。禁用以保留原有剖面控件。",
|
||||
"prefs.viewport_status_summary": "汇总视口状态项",
|
||||
"prefs.viewport_status_summary_help": "将三个或更多视口状态项折叠为一个汇总徽章。禁用后,所有项目仍显示在完整的一行中。",
|
||||
"prefs.wheel": "滚轮",
|
||||
"prefs.wheel_trackpad": "滚轮与触控板",
|
||||
"prefs.workspace": "工作区",
|
||||
@@ -2054,6 +2120,10 @@
|
||||
"properties.title": "属性:{name}",
|
||||
"recovery.days_ago": "{count} 天前",
|
||||
"recovery.detail": "{file} · {features} 个特征 · 自动保存于 {when}",
|
||||
"recovery.discard_all": "全部放弃",
|
||||
"recovery.discard_all_detail": "此操作无法撤消。",
|
||||
"recovery.discard_all_title": "放弃 {count} 个自动保存?",
|
||||
"recovery.discard_all_title_one": "放弃 1 个自动保存?",
|
||||
"recovery.hours_ago": "{count} 小时前",
|
||||
"recovery.just_now": "刚刚",
|
||||
"recovery.min_ago": "{count} 分钟前",
|
||||
@@ -2919,7 +2989,9 @@
|
||||
"render.output_ready": "已就绪,可渲染",
|
||||
"render.output_run_check": "运行检查",
|
||||
"render.output_size_limit": "{method}:每边最多 {dimension} px,总计 {pixels} 像素。可用的最大尺寸:{width} × {height}。",
|
||||
"render.output_size_method_profile": "要渲染 {width} × {height},请选择 {method} 和“{profile}”CPU 资源配置,或选择更小的尺寸。",
|
||||
"render.output_size_ok": "尺寸正常 · {width} × {height} px",
|
||||
"render.output_size_profile": "要渲染 {width} × {height},请选择“{profile}”CPU 资源配置,或选择更小的尺寸。",
|
||||
"render.output_swap_orientation": "交换宽度和高度",
|
||||
"render.output_tools_inspector": "请在输出检查器中编辑尺寸和格式。",
|
||||
"render.output_view_image": "查看图像",
|
||||
@@ -3132,6 +3204,7 @@
|
||||
"render.size_custom": "自定义…",
|
||||
"render.size_full_hd": "全高清",
|
||||
"render.size_hd": "HD",
|
||||
"render.size_needs_profile": "{size} · 需要“{profile}”",
|
||||
"render.size_presets": "尺寸预设",
|
||||
"render.size_presets_pending": "随输出编辑器推出",
|
||||
"render.size_preview": "预览",
|
||||
@@ -3419,6 +3492,24 @@
|
||||
"sheet.flange_type_contour": "轮廓",
|
||||
"sheet.flange_type_edge": "边线",
|
||||
"sheet.no_body": "没有钣金实体:请使用法兰 > Base 创建,或使用转换为钣金将板材转换为钣金(选择其较大的平面)。",
|
||||
"sheet.personal_rules.confirm_delete": "确认删除",
|
||||
"sheet.personal_rules.delete": "删除",
|
||||
"sheet.personal_rules.delete_prompt": "删除此个人规则?设计中已有的规则不会改变。",
|
||||
"sheet.personal_rules.full": "个人库中已有64条规则。",
|
||||
"sheet.personal_rules.invalid_name": "请输入不含换行符或控制字符的简短名称。",
|
||||
"sheet.personal_rules.invalid_values": "请填写完整的规则值:每个值都必须能在此设计中计算,厚度为正,K 因子在 0 到 1 之间。",
|
||||
"sheet.personal_rules.missing": "所选个人规则已不可用。",
|
||||
"sheet.personal_rules.name": "规则名称",
|
||||
"sheet.personal_rules.name_exists": "同名个人规则已存在,请使用其他名称。",
|
||||
"sheet.personal_rules.name_hint": "唯一名称",
|
||||
"sheet.personal_rules.overwrite": "保存对所选个人规则的修改",
|
||||
"sheet.personal_rules.rename": "重命名",
|
||||
"sheet.personal_rules.rule": "个人规则",
|
||||
"sheet.personal_rules.save": "另存为新规则",
|
||||
"sheet.personal_rules.scope": "保存在您的配置中,适用于所有设计。",
|
||||
"sheet.personal_rules.select": "选择规则",
|
||||
"sheet.personal_rules.title": "个人规则",
|
||||
"sheet.personal_rules.unsupported": "此规则包含当前版本无法使用的值。",
|
||||
"shortcut.json_error": "参数必须采用 JSON 格式:{error}",
|
||||
"shortcut.no_command": "没有命令 `{id}`;JSON 前的文本必须是命令 ID",
|
||||
"shortcut.pin": "固定到 S 框顶部",
|
||||
@@ -3455,6 +3546,7 @@
|
||||
"sketch_text.align_right": "右对齐",
|
||||
"sketch_text.bold": "粗体",
|
||||
"sketch_text.builtin_font": "{family}(内置)",
|
||||
"sketch_text.formula": "以 = 开头即可用参数组成文字:='W' + number(Width)",
|
||||
"sketch_text.italic": "斜体",
|
||||
"sketch_text.loading_fonts": "正在查找已安装字体…",
|
||||
"sketch_text.on_path": "文字沿路径排列:由曲线放置",
|
||||
@@ -3595,6 +3687,8 @@
|
||||
"ui.3d_mouse_section": "3D 鼠标",
|
||||
"ui.3d_mouse_show_in_view": "在 3D 视图中显示 3D 鼠标",
|
||||
"ui.3d_mouse_show_in_view_tip": "在左下角显示你的 3D 鼠标图示,随你的操作而动。",
|
||||
"ui.3d_mouse_sketch_orbit": "用 3D 鼠标旋转草图",
|
||||
"ui.3d_mouse_sketch_orbit_tip": "草图打开并正对视图时,3D 鼠标也能旋转视图,与鼠标一样。关闭:在你倾斜视图之前,只平移和缩放草图。",
|
||||
"ui.3d_mouse_skip": "我的设备做不到",
|
||||
"ui.3d_mouse_skip_tip": "在两个方向上都跳过这个动作,校准时不包含它。",
|
||||
"ui.3d_mouse_speed": "3D 鼠标速度",
|
||||
@@ -3623,6 +3717,8 @@
|
||||
"ui.3d_mouse_step_twist_counterclockwise": "逆时针扭转摇杆帽,保持住,然后松开。",
|
||||
"ui.3d_mouse_switch_cap": "按回中类型校准",
|
||||
"ui.3d_mouse_switch_trackball": "按轨迹球校准",
|
||||
"ui.3d_mouse_use_direct": "使用直接驱动",
|
||||
"ui.3d_mouse_use_direct_tip": "直接读取 3D 鼠标(hidraw),不经过 spacenavd。",
|
||||
"ui.3d_mouse_view_axis": "视图轴",
|
||||
"ui.3d_mouse_view_off": "关闭",
|
||||
"ui.3d_mouse_view_off_tip": "3D 鼠标驱动程序的配置文件关闭了此轴,因此它的动作不会移动视图。在此选择另一个轴的动作会将两者互换,该动作将不再移动视图。",
|
||||
@@ -3756,6 +3852,8 @@
|
||||
"ui.agent_chat_remove": "从队列中移除",
|
||||
"ui.agent_chat_remove_file": "从这条消息中移除",
|
||||
"ui.agent_chat_remove_imported": "从这条消息中移除;零件仍保留在设计中(撤销可将其删除)",
|
||||
"ui.agent_chat_reply_cut_off": "回复在完成前达到了长度上限,可能不完整。请让智能体继续,或请它回答得更简短。",
|
||||
"ui.agent_chat_reply_declined": "模型拒绝回答这条消息。",
|
||||
"ui.agent_chat_retry": "重试",
|
||||
"ui.agent_chat_right": "右侧",
|
||||
"ui.agent_chat_save": "保存",
|
||||
@@ -3939,6 +4037,7 @@
|
||||
"ui.circle": "圆",
|
||||
"ui.circular_pattern": "圆形阵列",
|
||||
"ui.clear": "清除",
|
||||
"ui.clear_print_face": "清除打印底面",
|
||||
"ui.clear_recent": "清除最近",
|
||||
"ui.clear_the_selection": "清除选择",
|
||||
"ui.click_selects_whole_bodies": "单击选择整个实体",
|
||||
@@ -3973,6 +4072,15 @@
|
||||
"ui.constant": "恒定",
|
||||
"ui.constraints_conflict": "约束冲突",
|
||||
"ui.construction": "构造",
|
||||
"ui.construction.angle_hint": "构造直线:单击一点,然后输入角度或选择方向",
|
||||
"ui.construction.bisector_hint": "角平分线:选择两条相交直线,再选择所需解",
|
||||
"ui.construction.common_hint": "公切线:选择两个圆/圆弧,再选择所需解",
|
||||
"ui.construction.contact_hint": "切线:先选圆/圆弧或开放样条曲线/圆锥曲线,再选接点(自由曲线仅支持端点)",
|
||||
"ui.construction.external_hint": "切线:依次选择圆或圆弧、外部点和所需解",
|
||||
"ui.construction.free_hint": "构造直线:单击两点;右键单击重新开始",
|
||||
"ui.construction.point_hint": "构造直线:单击要经过的点",
|
||||
"ui.construction.reference_hint": "构造直线:先选直线,再选一点;右键单击更换参照",
|
||||
"ui.construction.ribbon_label": "辅助线",
|
||||
"ui.context_menu": "上下文菜单",
|
||||
"ui.continuity_g0": "G0 折痕",
|
||||
"ui.continuity_g1": "G1 相切",
|
||||
@@ -4362,12 +4470,17 @@
|
||||
"ui.plugins_skipped": "有 {count} 个文件夹无法读取。",
|
||||
"ui.plugins_version": "{version}",
|
||||
"ui.point": "点",
|
||||
"ui.point_cloud_add_filter": "添加筛选…",
|
||||
"ui.point_cloud_auto_range": "自动范围",
|
||||
"ui.point_cloud_color_by": "着色方式",
|
||||
"ui.point_cloud_file_colors": "文件颜色",
|
||||
"ui.point_cloud_filter_by": "筛选依据",
|
||||
"ui.point_cloud_height_high": "最高高度",
|
||||
"ui.point_cloud_height_low": "最低高度",
|
||||
"ui.point_cloud_legend_filter": "在上方图例中显示或隐藏",
|
||||
"ui.point_cloud_no_file_colors": "文件中没有颜色",
|
||||
"ui.point_cloud_pause": "暂停",
|
||||
"ui.point_cloud_play": "播放",
|
||||
"ui.point_cloud_single_color": "单色",
|
||||
"ui.point_cloud_size": "点大小",
|
||||
"ui.point_cloud_units": "文件单位",
|
||||
@@ -4376,6 +4489,11 @@
|
||||
"ui.polygon": "多边形",
|
||||
"ui.preferences": "首选项",
|
||||
"ui.press_a_key": "按一个键…",
|
||||
"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": "轮廓",
|
||||
"ui.profile_plane": "轮廓平面",
|
||||
"ui.profiles": "轮廓",
|
||||
@@ -4439,6 +4557,7 @@
|
||||
"ui.revolve": "旋转",
|
||||
"ui.ribbon_exit": "退出",
|
||||
"ui.ribbon_labels": "显示功能区图标下的文字",
|
||||
"ui.ribbon_menus_skip_pinned": "下拉菜单中不再列出已固定的命令",
|
||||
"ui.ribbon_pin_footer": "固定的命令显示在功能区中。将指针悬停在行上可固定或取消固定。",
|
||||
"ui.ribbon_workspace": "工作区",
|
||||
"ui.right": "右视",
|
||||
@@ -4457,6 +4576,7 @@
|
||||
"ui.selectOther": "选择其他…",
|
||||
"ui.selectTangentChain": "选择相切链",
|
||||
"ui.select_all": "全选",
|
||||
"ui.select_components_by_face": "选择组件(单击面即选择其所属组件)",
|
||||
"ui.selection": "选择",
|
||||
"ui.selection_modifiers": "选择修饰键",
|
||||
"ui.selection_shift_add_only": "Shift+点击仅添加、不取消选择",
|
||||
@@ -4498,6 +4618,11 @@
|
||||
"ui.sketch_intersect_selection_filter": "选择过滤器",
|
||||
"ui.sketch_modifiers": "草图修饰键",
|
||||
"ui.sketch_project_geometry": "几何体",
|
||||
"ui.sketch_report.all_constrained": "全部已完全约束。",
|
||||
"ui.sketch_report.dof": "自由度",
|
||||
"ui.sketch_report.loose": "未完全约束",
|
||||
"ui.sketch_report.status": "状态",
|
||||
"ui.sketch_report.title": "草图报告",
|
||||
"ui.sketch_scale": "草图比例",
|
||||
"ui.sketches": "草图",
|
||||
"ui.slice": "切片",
|
||||
@@ -4602,10 +4727,19 @@
|
||||
"ui.view_centre": "视图中心",
|
||||
"ui.viewport_status_clear": "关闭{name}",
|
||||
"ui.viewport_status_clear_hint": "关闭{name} · Esc",
|
||||
"ui.viewport_status_edges": "{count} 条边",
|
||||
"ui.viewport_status_from": "从 {direction}",
|
||||
"ui.viewport_status_key": "显示{name}的图例",
|
||||
"ui.viewport_status_minimum_radius": "最小半径",
|
||||
"ui.viewport_status_pairs": "{count} 对",
|
||||
"ui.viewport_status_section_hatch": "{setting} · 填充线",
|
||||
"ui.viewport_status_section_plane": "当前平面",
|
||||
"ui.viewport_status_section_tilted": "倾斜 · {offset}",
|
||||
"ui.viewport_status_stripes": "{count} 条条纹",
|
||||
"ui.viewport_status_summary_clear": "全部关闭",
|
||||
"ui.viewport_status_summary_heading": "此视图中",
|
||||
"ui.viewport_status_summary_label": "{name} 和其他 {count} 项",
|
||||
"ui.viewport_status_summary_more": "+{count}",
|
||||
"ui.viewport_theme_follow": "跟随主题",
|
||||
"ui.visual_shaded_hidden_edges": "带隐藏边的着色",
|
||||
"ui.visual_shaded_with_edges": "带边着色",
|
||||
@@ -4642,7 +4776,9 @@
|
||||
"update.button": "更新",
|
||||
"update.button_version": "更新到 {version}",
|
||||
"update.check_automatically": "自动检查更新",
|
||||
"update.check_for_updates": "检查更新",
|
||||
"update.check_now": "立即检查",
|
||||
"update.check_unavailable": "此安装版本无法检查更新。",
|
||||
"update.checking": "正在检查…",
|
||||
"update.copy": "复制",
|
||||
"update.dmg_opened": "磁盘映像已校验并打开:将 SolidCraft 拖到“应用程序”替换旧版本,然后重启 SolidCraft。",
|
||||
|
||||
@@ -243,6 +243,44 @@ fn pushed(z: f32) -> f32 {
|
||||
return clamp(z - select(-push, push, reversed()), 0.0, 1.0);
|
||||
}
|
||||
|
||||
/// A far wall seen through the cut where another body's face lying on it was drawn in front of
|
||||
/// it (a shaft in its bore, #9407): its cap colour again, at its depth pulled toward the eye by
|
||||
/// `touch_depth`, so it wins over that face, which is faceted another way.
|
||||
@fragment
|
||||
fn fs_solid_touch(i: TOut, @builtin(front_facing) front: bool) -> CutOut {
|
||||
var o: CutOut;
|
||||
// Derivatives (in `touch_depth`) first, before `solid` may discard.
|
||||
o.depth = touch_depth(i.wp, i.pos.z);
|
||||
o.c = solid(i, front);
|
||||
return o;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_solid_touch_flat(i: FOut, @builtin(front_facing) front: bool) -> CutOut {
|
||||
let t = flat_fragment(i, front);
|
||||
var o: CutOut;
|
||||
o.depth = touch_depth(t.wp, t.pos.z);
|
||||
o.c = solid(t, front);
|
||||
return o;
|
||||
}
|
||||
|
||||
/// The depth of `wp` (depth `z`) pulled toward the eye by what may part two faceted copies of
|
||||
/// one surface: 3 pixels' width along the view ray (display refinement keeps a facet's sag about
|
||||
/// a pixel) and 2 pixels of the face's own depth slope (more where it turns from the eye).
|
||||
fn touch_depth(wp: vec3<f32>, z: f32) -> f32 {
|
||||
let px = min(length(dpdx(wp)), length(dpdy(wp)));
|
||||
let slope = max(abs(dpdx(z)), abs(dpdy(z)));
|
||||
var dir = -u.back.xyz;
|
||||
if (u.eye.w > 0.5) { dir = wp - u.eye.xyz; }
|
||||
let len = length(dir);
|
||||
if (len < 1e-12) { return z; }
|
||||
let c = u.vp * vec4<f32>(wp - dir * (3.0 * px / len), 1.0);
|
||||
if (c.w <= 0.0) { return z; }
|
||||
let pulled = c.z / c.w;
|
||||
if (reversed()) { return clamp(max(pulled, z) + 2.0 * slope, 0.0, 1.0); }
|
||||
return clamp(min(pulled, z) - 2.0 * slope, 0.0, 1.0);
|
||||
}
|
||||
|
||||
/// Depth of the section plane on the view ray through `wp` (a far wall seen through the cut),
|
||||
/// never behind the wall itself (`z`).
|
||||
fn cap_depth(wp: vec3<f32>, z: f32) -> f32 {
|
||||
@@ -697,38 +735,96 @@ fn fs_line_all(i: LOut) -> @location(0) vec4<f32> {
|
||||
return out_color(i.c);
|
||||
}
|
||||
|
||||
// Point clouds: each point is a round dot a few pixels across, facing the screen, depth-tested
|
||||
// and written like the model's faces, so the model hides the points behind it and they hide it.
|
||||
// The colour's alpha carries the dot's size (eighths of a pixel).
|
||||
// Point clouds (`view/clouds.rs`): each point is a round dot a few pixels across, facing the
|
||||
// screen, depth-tested and written like the model's faces, so the model hides the points behind
|
||||
// it and they hide it. The cloud's style colours them by their own colour, a ramp or a palette,
|
||||
// and shows only those whose filtered values lie in its ranges or sets.
|
||||
|
||||
struct POut {
|
||||
struct Cloud {
|
||||
style: vec4<f32>, // x: colour (0 own, 1 ramp, 2 palette), y: dot size (px), z, w: ramp range
|
||||
filters: array<vec4<f32>, 4>, // x: kind (0 off, 1 range y…z, 2 whole values y + i for the bits i of w)
|
||||
};
|
||||
@group(1) @binding(3) var<uniform> cloud: Cloud;
|
||||
|
||||
struct CIn {
|
||||
@location(0) p: vec3<f32>,
|
||||
@location(1) v: f32,
|
||||
@location(2) f: vec4<f32>,
|
||||
@location(3) rgb: vec4<f32>,
|
||||
};
|
||||
|
||||
struct COut {
|
||||
@builtin(position) pos: vec4<f32>,
|
||||
@location(0) c: vec4<f32>,
|
||||
@location(1) wp: vec3<f32>,
|
||||
@location(2) at: vec2<f32>,
|
||||
@location(0) wp: vec3<f32>,
|
||||
@location(1) v: f32,
|
||||
@location(2) f: vec4<f32>,
|
||||
@location(3) rgb: vec3<f32>,
|
||||
@location(4) at: vec2<f32>, // the place on its disc
|
||||
@location(5) @interpolate(flat) has: u32,
|
||||
};
|
||||
|
||||
@vertex
|
||||
fn vs_point(@builtin(vertex_index) vi: u32, @location(0) p: vec3<f32>, @location(1) c: vec4<f32>) -> POut {
|
||||
fn vs_cloud_dot(@builtin(vertex_index) vi: u32, i: CIn) -> COut {
|
||||
var xs = array<f32, 6>(-1.0, 1.0, 1.0, -1.0, 1.0, -1.0);
|
||||
var ys = array<f32, 6>(-1.0, -1.0, 1.0, -1.0, 1.0, 1.0);
|
||||
let at = vec2<f32>(xs[vi], ys[vi]);
|
||||
let size = max(c.a * 255.0 / 8.0, 1.0);
|
||||
let centre = u.vp * vec4<f32>(p, 1.0);
|
||||
let half = u.screen.xy * 0.5;
|
||||
var o: POut;
|
||||
o.pos = vec4<f32>(centre.xy + at * size * 0.5 / half * centre.w, centre.z, centre.w);
|
||||
let size = max(cloud.style.y, 1.0);
|
||||
let centre = u.vp * vec4<f32>(i.p, 1.0);
|
||||
var o: COut;
|
||||
o.pos = vec4<f32>(centre.xy + at * size / u.screen.xy * centre.w, centre.z, centre.w);
|
||||
// Behind the eye: nothing.
|
||||
if (near_z(centre) < 0.0) { o.pos = vec4<f32>(0.0, 0.0, 0.0, -1.0); }
|
||||
o.c = vec4<f32>(c.rgb, 1.0);
|
||||
o.wp = p;
|
||||
o.wp = i.p;
|
||||
o.v = i.v;
|
||||
o.f = i.f;
|
||||
o.rgb = i.rgb.rgb;
|
||||
o.has = u32(round(i.rgb.a * 255.0));
|
||||
// Dots under two pixels are squares: a disc that small loses its centre pixel.
|
||||
o.at = at * select(1.0, 0.0, size < 2.0);
|
||||
return o;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_point(i: POut) -> @location(0) vec4<f32> {
|
||||
if (clipped(i.wp) || dot(i.at, i.at) > 1.0) { discard; }
|
||||
return out_color(i.c);
|
||||
/// Whether filter `f` keeps the value `v`.
|
||||
fn cloud_keeps(f: vec4<f32>, v: f32) -> bool {
|
||||
if (f.x < 0.5) { return true; }
|
||||
if (f.x < 1.5) { return v >= f.y && v <= f.z; }
|
||||
let k = i32(round(v)) - i32(round(f.y));
|
||||
return k >= 0 && k < 32 && ((bitcast<u32>(f.w) >> u32(k)) & 1u) != 0u;
|
||||
}
|
||||
|
||||
fn cloud_ramp(t: f32) -> vec3<f32> {
|
||||
var stops = array<vec3<f32>, 5>(
|
||||
vec3<f32>(40.0, 70.0, 200.0), vec3<f32>(30.0, 170.0, 230.0), vec3<f32>(60.0, 200.0, 90.0),
|
||||
vec3<f32>(240.0, 220.0, 60.0), vec3<f32>(230.0, 70.0, 50.0));
|
||||
let k = clamp(t, 0.0, 1.0) * 4.0;
|
||||
let j = min(u32(k), 3u);
|
||||
return mix(stops[j], stops[j + 1u], k - f32(j)) / 255.0;
|
||||
}
|
||||
|
||||
fn cloud_palette(v: f32) -> vec3<f32> {
|
||||
var colours = array<vec3<f32>, 10>(
|
||||
vec3<f32>(240.0, 140.0, 40.0), vec3<f32>(245.0, 205.0, 65.0), vec3<f32>(230.0, 65.0, 65.0),
|
||||
vec3<f32>(165.0, 75.0, 190.0), vec3<f32>(65.0, 190.0, 115.0), vec3<f32>(75.0, 150.0, 220.0),
|
||||
vec3<f32>(240.0, 120.0, 180.0), vec3<f32>(140.0, 105.0, 75.0), vec3<f32>(120.0, 215.0, 215.0),
|
||||
vec3<f32>(190.0, 190.0, 80.0));
|
||||
if (v < -0.5) { return vec3<f32>(150.0, 160.0, 176.0) / 255.0; }
|
||||
return colours[u32(round(v)) % 10u] / 255.0;
|
||||
}
|
||||
|
||||
|
||||
@fragment
|
||||
fn fs_cloud(o: COut) -> @location(0) vec4<f32> {
|
||||
var out = clipped(o.wp) || dot(o.at, o.at) > 1.0;
|
||||
for (var k = 0u; k < 4u; k++) {
|
||||
out = out || (((o.has >> k) & 1u) != 0u && !cloud_keeps(cloud.filters[k], o.f[k]));
|
||||
}
|
||||
if (out) { discard; }
|
||||
var c = o.rgb;
|
||||
let mode = cloud.style.x;
|
||||
if ((o.has & 16u) != 0u && mode > 1.5) {
|
||||
c = cloud_palette(o.v);
|
||||
} else if ((o.has & 16u) != 0u && mode > 0.5) {
|
||||
c = cloud_ramp((o.v - cloud.style.z) / max(cloud.style.w - cloud.style.z, 1e-12));
|
||||
}
|
||||
return out_color(vec4<f32>(c, 1.0));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
//! The browser's groups as a tree: how deep a group sits, who holds whom, and pruning what a
|
||||
//! saved design says about the browser down to what still exists.
|
||||
use crate::document::{BROWSER_FOLDERS, BrowserEntry, BrowserGroup, Document, MAX_GROUP_NESTING};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
impl Document {
|
||||
pub fn browser_group(&self, id: u64) -> Option<&BrowserGroup> {
|
||||
self.browser_groups.iter().find(|g| g.id == id)
|
||||
}
|
||||
|
||||
/// The level of a group: 1 for a top-level group, 2 for a child of one, and so on. A chain
|
||||
/// that loops or runs past the limit counts as one level too deep.
|
||||
pub fn group_level(&self, id: u64) -> usize {
|
||||
let mut level = 0;
|
||||
let mut at = Some(id);
|
||||
while let Some(g) = at {
|
||||
level += 1;
|
||||
if level > MAX_GROUP_NESTING {
|
||||
return level;
|
||||
}
|
||||
at = self.browser_group(g).and_then(|g| g.parent);
|
||||
}
|
||||
level
|
||||
}
|
||||
|
||||
/// Whether `ancestor` is `id` or holds it, at any depth.
|
||||
pub fn group_within(&self, id: u64, ancestor: u64) -> bool {
|
||||
let mut at = Some(id);
|
||||
for _ in 0..=MAX_GROUP_NESTING {
|
||||
match at {
|
||||
Some(g) if g == ancestor => return true,
|
||||
Some(g) => at = self.browser_group(g).and_then(|g| g.parent),
|
||||
None => return false,
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// How many levels the group and the groups under it take: 1 for a group with no child group.
|
||||
pub fn group_height(&self, id: u64) -> usize {
|
||||
fn height(d: &Document, id: u64, left: usize) -> usize {
|
||||
if left == 0 {
|
||||
return 1;
|
||||
}
|
||||
1 + d.browser_groups.iter().filter(|g| g.parent == Some(id)).map(|g| height(d, g.id, left - 1)).max().unwrap_or(0)
|
||||
}
|
||||
height(self, id, MAX_GROUP_NESTING + 1)
|
||||
}
|
||||
|
||||
/// The entries of a holder's list in display order: a group's contents, or the folder's top
|
||||
/// level when `holder` is `None`. Groups the list names come where it names them. The child
|
||||
/// groups it does not name (in creation order) go just after the last group it names, or
|
||||
/// first when it names none, so they sit above the items as they always have. Entries for
|
||||
/// groups that are not the holder's children are dropped.
|
||||
pub fn ordered_entries(&self, component: u64, folder: &str, holder: Option<u64>) -> Vec<BrowserEntry> {
|
||||
let listed: &[BrowserEntry] = match holder {
|
||||
Some(g) => self.browser_group(g).map_or(&[], |g| &g.items),
|
||||
None => self.browser_order.get(&format!("{component}/{folder}")).map_or(&[], Vec::as_slice),
|
||||
};
|
||||
let mine: Vec<u64> =
|
||||
self.browser_groups.iter().filter(|g| g.component == component && g.folder == folder && g.parent == holder).map(|g| g.id).collect();
|
||||
let mut out: Vec<BrowserEntry> = Vec::with_capacity(listed.len() + mine.len());
|
||||
for e in listed {
|
||||
match e {
|
||||
BrowserEntry::Group(id) if !mine.contains(id) || out.contains(e) => {}
|
||||
_ => out.push(e.clone()),
|
||||
}
|
||||
}
|
||||
let at = out.iter().rposition(|e| matches!(e, BrowserEntry::Group(_))).map_or(0, |i| i + 1);
|
||||
let unlisted: Vec<BrowserEntry> = mine.iter().map(|id| BrowserEntry::Group(*id)).filter(|e| !out.contains(e)).collect();
|
||||
out.splice(at..at, unlisted);
|
||||
out
|
||||
}
|
||||
|
||||
/// The items in a group and in the groups inside it.
|
||||
pub fn group_item_keys(&self, id: u64) -> Vec<String> {
|
||||
let inside = self.browser_groups.iter().filter(|g| self.group_within(g.id, id));
|
||||
inside.flat_map(|g| g.item_keys().map(str::to_string)).collect()
|
||||
}
|
||||
|
||||
/// The group holding the item directly, in a component's folder.
|
||||
pub fn group_of_item(&self, component: u64, folder: &str, key: &str) -> Option<&BrowserGroup> {
|
||||
self.browser_groups.iter().find(|g| g.component == component && g.folder == folder && g.has_item(key))
|
||||
}
|
||||
|
||||
/// Only the browser's groups and order changed (a group made, nested, moved into, ordered):
|
||||
/// nothing is evaluated and no placement moves. The same whole-Document proof as
|
||||
/// [`Document::differs_only_in_render`]; `next_id` may differ, as a new group takes an id.
|
||||
pub fn differs_only_in_browser(&self, previous: &Document) -> bool {
|
||||
if self.browser_groups == previous.browser_groups && self.browser_order == previous.browser_order {
|
||||
return false;
|
||||
}
|
||||
let mut normalized = previous.clone();
|
||||
normalized.browser_groups.clone_from(&self.browser_groups);
|
||||
normalized.browser_order.clone_from(&self.browser_order);
|
||||
normalized.next_id = self.next_id;
|
||||
*self == normalized
|
||||
}
|
||||
|
||||
/// What [`Document::browser_pruned`] depends on, cheaply: it can only find something to drop
|
||||
/// after a feature, placement, component or group was added or removed. Ids are never
|
||||
/// reused, so a removal followed by an addition still moves `next_id`.
|
||||
pub fn browser_stamp(&self) -> [u64; 6] {
|
||||
let n = |len: usize| u64::try_from(len).unwrap_or(u64::MAX);
|
||||
[
|
||||
self.next_id,
|
||||
n(self.features.len()),
|
||||
n(self.occurrences.len()),
|
||||
n(self.components.len()),
|
||||
n(self.browser_groups.len()),
|
||||
n(self.browser_order.len()),
|
||||
]
|
||||
}
|
||||
|
||||
/// The groups and order lists with what they name that is gone dropped, or `None` when
|
||||
/// nothing is: groups of a missing component or folder, a parent that is missing, elsewhere
|
||||
/// or loops, entries for groups and sketches, construction items and placements that no
|
||||
/// longer exist, repeats, and order lists of missing components. Body names are left alone:
|
||||
/// only an evaluated model knows them.
|
||||
pub fn browser_pruned(&self) -> Option<(Vec<BrowserGroup>, BTreeMap<String, Vec<BrowserEntry>>)> {
|
||||
if self.browser_groups.is_empty() && self.browser_order.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let components: BTreeSet<u64> = self.components.iter().map(|c| c.id).collect();
|
||||
let features: BTreeSet<u64> = self.features.iter().map(|f| f.id).collect();
|
||||
let placed_in: BTreeMap<u64, u64> = self.occurrences.iter().map(|o| (o.id, o.parent)).collect();
|
||||
let component_exists = |c: u64| c == 0 || components.contains(&c);
|
||||
let placed = |component: u64, key: &str| key.parse::<u64>().ok().and_then(|o| placed_in.get(&o)) == Some(&component);
|
||||
// Body names cannot be checked here; the other folders hold feature ids.
|
||||
let item_exists = |folder: &str, component: u64, key: &str| match folder {
|
||||
"bodies" => true,
|
||||
"components" => placed(component, key),
|
||||
_ => key.parse::<u64>().is_ok_and(|id| features.contains(&id)),
|
||||
};
|
||||
let mut groups = self.browser_groups.clone();
|
||||
let mut order = self.browser_order.clone();
|
||||
groups.retain(|g| component_exists(g.component) && BROWSER_FOLDERS.contains(&g.folder.as_str()));
|
||||
let known: BTreeMap<u64, (u64, String)> = groups.iter().map(|g| (g.id, (g.component, g.folder.clone()))).collect();
|
||||
let parents: BTreeMap<u64, Option<u64>> = groups
|
||||
.iter()
|
||||
.map(|g| (g.id, g.parent.filter(|p| *p != g.id && known.get(p).is_some_and(|(c, f)| *c == g.component && *f == g.folder))))
|
||||
.collect();
|
||||
// A loop, or a chain too deep, is cut at the group that makes it.
|
||||
let too_deep = |id: u64| std::iter::successors(Some(id), |g| parents.get(g).copied().flatten()).nth(MAX_GROUP_NESTING).is_some();
|
||||
let kept: BTreeMap<u64, Option<u64>> = parents.iter().map(|(id, p)| (*id, if too_deep(*id) { None } else { *p })).collect();
|
||||
for g in &mut groups {
|
||||
g.parent = kept[&g.id];
|
||||
}
|
||||
let mut taken: BTreeSet<(u64, String, String)> = BTreeSet::new();
|
||||
for g in &mut groups {
|
||||
let (component, folder, id) = (g.component, g.folder.clone(), g.id);
|
||||
let mut seen = BTreeSet::new();
|
||||
g.items.retain(|e| match e {
|
||||
BrowserEntry::Group(c) => kept.get(c) == Some(&Some(id)) && seen.insert(*c),
|
||||
BrowserEntry::Item(k) => item_exists(&folder, component, k) && taken.insert((component, folder.clone(), k.clone())),
|
||||
});
|
||||
}
|
||||
let top = |c: u64, folder: &str, id: u64| kept.get(&id) == Some(&None) && known.get(&id).is_some_and(|(kc, kf)| *kc == c && kf == folder);
|
||||
order.retain(|key, list| {
|
||||
let Some((c, folder)) = key.split_once('/') else { return false };
|
||||
let Some(c) = c.parse::<u64>().ok().filter(|c| component_exists(*c)) else { return false };
|
||||
if !BROWSER_FOLDERS.contains(&folder) {
|
||||
return false;
|
||||
}
|
||||
let mut seen = BTreeSet::new();
|
||||
list.retain(|e| match e {
|
||||
BrowserEntry::Group(id) => top(c, folder, *id) && seen.insert(e.clone()),
|
||||
BrowserEntry::Item(k) => item_exists(folder, c, k) && seen.insert(e.clone()),
|
||||
});
|
||||
!list.is_empty()
|
||||
});
|
||||
(groups != self.browser_groups || order != self.browser_order).then_some((groups, order))
|
||||
}
|
||||
|
||||
/// Put placements of one component in the order the browser shows them: the saved
|
||||
/// `components` order, then the rest as they were.
|
||||
pub fn sort_placements(&self, parent: u64, ids: &mut [u64]) {
|
||||
let Some(order) = self.browser_order.get(&format!("{parent}/components")) else { return };
|
||||
let rank: BTreeMap<&str, usize> = order.iter().enumerate().filter_map(|(i, e)| Some((e.as_item()?, i))).collect();
|
||||
ids.sort_by_cached_key(|id| rank.get(id.to_string().as_str()).copied().unwrap_or(usize::MAX));
|
||||
}
|
||||
}
|
||||
@@ -176,6 +176,8 @@ pub(crate) fn remap_link(
|
||||
remap_link(b, ids, names, bodies, depth + 1);
|
||||
}
|
||||
LinkSource::Plane { name } => mapped_name(name, names),
|
||||
// A construction axis, by name (#9324); the origin axes are no feature's.
|
||||
LinkSource::Axis { name } if !matches!(name.as_str(), "X" | "Y" | "Z") => mapped_name(name, names),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,10 +344,13 @@ fn model_line(st: &ModelState, g: &GeoRef) -> Result<(Vec3, Vec3)> {
|
||||
_ => Err(bad("the face is not a cylinder, cone or torus")),
|
||||
}
|
||||
}
|
||||
_ => Err(bad("pick a straight or circular edge, or a cylinder, cone or torus face")),
|
||||
_ => Err(bad(MODEL_AXIS_REFS)),
|
||||
}
|
||||
}
|
||||
|
||||
/// What [`model_axis`] says about a reference that is neither an edge nor a face.
|
||||
pub const MODEL_AXIS_REFS: &str = "pick a straight or circular edge, or a cylinder, cone or torus face";
|
||||
|
||||
/// The axis a feature turns about when an edge or face of the model is picked for it (a revolve,
|
||||
/// a circular pattern, a plane at an angle), as [`model_line`] finds it. The direction is
|
||||
/// oriented the same way however the edge or surface runs (its largest component positive), so
|
||||
|
||||
@@ -2736,6 +2736,9 @@ pub struct Document {
|
||||
/// Physical material per body (by body name); others use the default.
|
||||
#[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")]
|
||||
pub materials: std::collections::BTreeMap<String, String>,
|
||||
/// The flat face per body (by body name) that a slicer export lays on the print bed.
|
||||
#[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")]
|
||||
pub print_faces: std::collections::BTreeMap<String, crate::config::ConfigFaceTarget>,
|
||||
/// Physical materials defined in this design (`material.create`), next to the built-in ones.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub user_materials: Vec<UserMaterial>,
|
||||
@@ -2756,9 +2759,11 @@ pub struct Document {
|
||||
/// order; feature ids remain the source of truth when features are renamed or reordered.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub timeline_groups: Vec<TimelineGroup>,
|
||||
/// Browser item order per component folder (`"<component>/<folder>"` → item keys).
|
||||
/// Browser order per component folder (`"<component>/<folder>"` → groups and item keys).
|
||||
/// The `components` folder orders a component's sub-components (by placement id); it is not
|
||||
/// drawn as a folder.
|
||||
#[serde(default, skip_serializing_if = "std::collections::BTreeMap::is_empty")]
|
||||
pub browser_order: std::collections::BTreeMap<String, Vec<String>>,
|
||||
pub browser_order: std::collections::BTreeMap<String, Vec<BrowserEntry>>,
|
||||
/// Named views: saved cameras.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub named_views: Vec<NamedView>,
|
||||
@@ -2847,8 +2852,69 @@ pub struct BrowserGroup {
|
||||
#[serde(default)]
|
||||
pub component: u64,
|
||||
pub folder: String,
|
||||
/// The group that holds this one; `None` is the top level of its folder.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub parent: Option<u64>,
|
||||
/// The group's items and child groups in display order. A child group missing here still
|
||||
/// belongs to it (its `parent` says so) and is listed after the ones that are here.
|
||||
#[serde(default)]
|
||||
pub items: Vec<String>,
|
||||
pub items: Vec<BrowserEntry>,
|
||||
}
|
||||
|
||||
/// One line of a browser order list: a group (a number in a saved design) or an item (a string:
|
||||
/// an item key or body name, so a body named `"1"` never clashes with group 1).
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum BrowserEntry {
|
||||
Group(u64),
|
||||
Item(String),
|
||||
}
|
||||
|
||||
impl From<&str> for BrowserEntry {
|
||||
fn from(key: &str) -> Self {
|
||||
BrowserEntry::Item(key.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for BrowserEntry {
|
||||
fn from(key: String) -> Self {
|
||||
BrowserEntry::Item(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl BrowserEntry {
|
||||
pub fn item(key: &str) -> Self {
|
||||
BrowserEntry::Item(key.to_string())
|
||||
}
|
||||
|
||||
pub fn as_item(&self) -> Option<&str> {
|
||||
match self {
|
||||
BrowserEntry::Item(k) => Some(k),
|
||||
BrowserEntry::Group(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_item(&self, key: &str) -> bool {
|
||||
self.as_item() == Some(key)
|
||||
}
|
||||
}
|
||||
|
||||
/// How many groups may sit inside one another (a top-level group is level 1).
|
||||
pub const MAX_GROUP_NESTING: usize = 8;
|
||||
|
||||
/// The folders a browser group or order list can be for. `components` only orders sub-components.
|
||||
pub const BROWSER_FOLDERS: [&str; 4] = ["bodies", "sketches", "construction", "components"];
|
||||
|
||||
impl BrowserGroup {
|
||||
/// The keys of the items directly in this group, in order.
|
||||
pub fn item_keys(&self) -> impl Iterator<Item = &str> {
|
||||
self.items.iter().filter_map(BrowserEntry::as_item)
|
||||
}
|
||||
|
||||
/// Whether this group holds the item directly.
|
||||
pub fn has_item(&self, key: &str) -> bool {
|
||||
self.items.iter().any(|e| e.is_item(key))
|
||||
}
|
||||
}
|
||||
|
||||
/// A saved history folder. Folders may nest; each feature or joint belongs to at most one folder.
|
||||
@@ -2982,6 +3048,7 @@ impl Document {
|
||||
body_components: Default::default(),
|
||||
body_offsets: Default::default(),
|
||||
materials: Default::default(),
|
||||
print_faces: Default::default(),
|
||||
user_materials: Vec::new(),
|
||||
appearances: Default::default(),
|
||||
favorites: Default::default(),
|
||||
@@ -2997,7 +3064,7 @@ impl Document {
|
||||
}
|
||||
|
||||
/// Rename body `old` to `new` where the design keeps things by body name outside its
|
||||
/// features: materials, appearances, plastic rules, configuration columns, offsets, components and the
|
||||
/// features: materials, print faces, appearances, plastic rules, configuration columns, offsets, components and the
|
||||
/// browser's groups and order. (The features are the rename command's to change.)
|
||||
///
|
||||
/// A name the design numbered (`Body1`) is not used again after the rename: the design's
|
||||
@@ -3013,6 +3080,10 @@ impl Document {
|
||||
if let Some(m) = self.materials.remove(old) {
|
||||
self.materials.insert(new.to_string(), m);
|
||||
}
|
||||
if let Some(mut face) = self.print_faces.remove(old) {
|
||||
face.body = new.to_string();
|
||||
self.print_faces.insert(new.to_string(), face);
|
||||
}
|
||||
if let Some(rule) = self.plastic.assigned.remove(old) {
|
||||
self.plastic.assigned.insert(new.to_string(), rule);
|
||||
}
|
||||
@@ -3039,15 +3110,16 @@ impl Document {
|
||||
if let Some(c) = self.body_components.remove(old) {
|
||||
self.body_components.insert(new.to_string(), c);
|
||||
}
|
||||
for g in self.browser_groups.iter_mut().filter(|g| g.folder == "bodies") {
|
||||
for k in g.items.iter_mut().filter(|k| *k == old) {
|
||||
*k = new.to_string();
|
||||
let rename = |e: &mut BrowserEntry| {
|
||||
if e.is_item(old) {
|
||||
*e = BrowserEntry::item(new);
|
||||
}
|
||||
};
|
||||
for g in self.browser_groups.iter_mut().filter(|g| g.folder == "bodies") {
|
||||
g.items.iter_mut().for_each(rename);
|
||||
}
|
||||
for (_, v) in self.browser_order.iter_mut().filter(|(k, _)| k.ends_with("/bodies")) {
|
||||
for k in v.iter_mut().filter(|k| *k == old) {
|
||||
*k = new.to_string();
|
||||
}
|
||||
v.iter_mut().for_each(rename);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3366,10 +3438,14 @@ impl Document {
|
||||
/// Read a design file's JSON (any format this build knows; older ones are upgraded).
|
||||
pub fn from_json(s: &str) -> Result<Document> {
|
||||
crate::render_resources::check_json_budget(s)?;
|
||||
let d = crate::format::read(s)?;
|
||||
let mut d = crate::format::read(s)?;
|
||||
if d.features.len() > MAX_FEATURES || d.params.len() > MAX_PARAMS {
|
||||
return Err(DocError::Invalid("document too large".into()));
|
||||
}
|
||||
// A design from elsewhere may nest groups too deep, loop, or name things that are gone.
|
||||
if let Some((groups, order)) = d.browser_pruned() {
|
||||
(d.browser_groups, d.browser_order) = (groups, order);
|
||||
}
|
||||
Ok(d)
|
||||
}
|
||||
pub fn to_json(&self) -> String {
|
||||
@@ -4014,7 +4090,7 @@ impl Document {
|
||||
return Err(DocError::Invalid("a text's height must be positive".into()));
|
||||
}
|
||||
if let Some(e) = &exprs.text {
|
||||
let words = self.eval_words(e).map_err(|error| DocError::Expr(format!("text: {error}")))?;
|
||||
let words = self.eval_words(e, vals).map_err(|error| DocError::Expr(format!("text: {error}")))?;
|
||||
if words.trim().is_empty() || words.chars().count() > solidcraft_sketch::MAX_TEXT_CHARS {
|
||||
return Err(DocError::Invalid(format!("text: `{e}` gives no words, or too many")));
|
||||
}
|
||||
|
||||
@@ -792,6 +792,11 @@ fn split_piece_of(name: &str) -> Option<&str> {
|
||||
(!n.starts_with('0') && n.parse::<u32>().is_ok()).then_some(base)
|
||||
}
|
||||
|
||||
/// The warning of a sketch whose profile search was cut short (#6896).
|
||||
fn profiles_cut_short_note() -> String {
|
||||
format!("some profiles may be missing: a group of touching curves is cut into more than {} pieces", solidcraft_sketch::MAX_PROFILE_EDGES)
|
||||
}
|
||||
|
||||
/// Add `note` to a feature's warning.
|
||||
fn append_warning(warning: &mut Option<String>, note: String) {
|
||||
*warning = Some(match warning.take() {
|
||||
@@ -2153,7 +2158,14 @@ impl Model {
|
||||
feature_component.entry(f.id).or_insert(f.component);
|
||||
}
|
||||
let watched = crate::progress::begin(doc.features.len());
|
||||
// The features that failed so far in this pass, by id: their names and errors (#9075).
|
||||
let mut failed: std::collections::HashMap<u64, (String, String)> = std::collections::HashMap::new();
|
||||
for (i, f) in doc.features.iter().enumerate() {
|
||||
if let Some(r) = out.last()
|
||||
&& let Some(e) = &r.error
|
||||
{
|
||||
failed.insert(r.id, (r.name.clone(), e.clone()));
|
||||
}
|
||||
self.placement_local = self.placement_local && placement_reuse::local_geometry(f, &feature_component);
|
||||
let rolled_back = i >= marker;
|
||||
// Retain at most one active result per rolled-back feature, even across several
|
||||
@@ -2213,6 +2225,33 @@ impl Model {
|
||||
if state.reserved_body_names != reserved {
|
||||
Arc::make_mut(&mut state).reserved_body_names.clone_from(&reserved);
|
||||
}
|
||||
// A sketch the feature is built from failed earlier in this pass: the feature fails naming
|
||||
// that sketch, with its error, as one using a failed parameter does (#9075), not as a
|
||||
// sketch that must come earlier in the timeline. Never cached: the cache key holds
|
||||
// neither the sketch's name nor its error (a rename, another failing value).
|
||||
let failed_sketch = if failed.is_empty() {
|
||||
None
|
||||
} else {
|
||||
f.kind.input_sketches().into_iter().find_map(|id| failed.get(&id)).map(|(name, e)| format!("sketch `{name}` failed: {e}"))
|
||||
};
|
||||
if let Some(e) = failed_sketch {
|
||||
recomputed += 1;
|
||||
reuse = false;
|
||||
out.push(FeatureResult {
|
||||
id: f.id,
|
||||
name: f.name.clone(),
|
||||
error: Some(e),
|
||||
warning: None,
|
||||
boolean_diagnostics: Default::default(),
|
||||
ms: 0.0,
|
||||
skipped: false,
|
||||
stale: false,
|
||||
fingerprint: fp,
|
||||
unkept: false,
|
||||
state: state.clone(),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
// The same feature on the same inputs: its earlier result.
|
||||
let key =
|
||||
cache_key(f, fp, fingerprint(doc, 0, f, &vals, false, &self.step_digests) ^ placement_digest(doc, f, Some(&state)), ctx, &state);
|
||||
@@ -7223,7 +7262,13 @@ fn eval_feature(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature, st:
|
||||
*warning = Some(warns.join("; "));
|
||||
}
|
||||
sk.validate_periodic_splines()?;
|
||||
let profiles = find_profiles(&sk);
|
||||
// A group of touching curves too large to search whole says so, rather than
|
||||
// leaving profiles out silently (#6896).
|
||||
let search = solidcraft_sketch::find_profiles_checked(&sk);
|
||||
if search.incomplete {
|
||||
append_warning(warning, profiles_cut_short_note());
|
||||
}
|
||||
let profiles = search.profiles;
|
||||
let ends = solidcraft_sketch::loose_ends(&sk);
|
||||
st.sketches.retain(|s| s.feature != f.id);
|
||||
st.sketches.push(Arc::new(SolvedSketch { feature: f.id, name: f.name.clone(), plane, sketch: sk, report, profiles, ends }));
|
||||
|
||||
@@ -647,3 +647,65 @@ fn shared_evaluation_refreshes_live_sketches_and_keeps_unknown_features_conserva
|
||||
assert!(model.results.iter().all(|r| r.error.is_none()));
|
||||
assert!(model.evaluate_shared(&doc), "an unclassified feature must keep the ordinary evaluation path");
|
||||
}
|
||||
|
||||
/// A saved model writes each set of sketch keys once, as the keys it sets and drops relative to
|
||||
/// the set written before it (every state repeating its whole set made a 5,484-feature design's
|
||||
/// model too large for its file, which then kept only some states and evaluated the rest on
|
||||
/// opening). Every state reads back the keys it was evaluated with, also in a model thinned to
|
||||
/// fewer states; a set that names no set before it is refused.
|
||||
#[test]
|
||||
fn saved_states_read_back_their_sketch_keys() {
|
||||
let mut doc = Document::new("Sketch keys");
|
||||
for i in 0..4 {
|
||||
doc.add_feature(line_sketch(PlaneRef::Origin { name: "XY".into() }, Vec2::new(f64::from(i) + 1.0, 1.0)), None).unwrap();
|
||||
for j in 0..3 {
|
||||
let corner = point_expr(Vec3::new(f64::from(i) * 20.0, f64::from(j) * 20.0, 0.0));
|
||||
let kind = FeatureKind::Box { corner, length: "5".into(), width: "5".into(), height: "5".into(), operation: Operation::NewBody };
|
||||
doc.add_feature(kind, None).unwrap();
|
||||
}
|
||||
}
|
||||
let mut model = Model::new();
|
||||
model.evaluate(&doc);
|
||||
let keys = |m: &Model| m.results.iter().map(|r| r.state.sketch_keys.clone()).collect::<Vec<_>>();
|
||||
assert_eq!(model.state().sketch_keys.len(), 4);
|
||||
let saved = model.saved(&doc).unwrap();
|
||||
assert_eq!(saved.records["sketch_key_sets"].as_array().unwrap().len(), 4, "one set per sketch");
|
||||
assert_eq!(saved.records["states"].as_array().unwrap().len(), doc.features.len());
|
||||
assert_eq!(keys(&Model::adopt(&doc, &saved).unwrap()), keys(&model));
|
||||
// The final state alone (every body costs more than the budget): the sets are all kept.
|
||||
let pieces: Vec<Vec<(usize, usize)>> = (0..saved.bodies.len()).map(|i| vec![(i, 1 << 20)]).collect();
|
||||
let (thinned, _, _) = saved.thinned(&pieces, 0).unwrap();
|
||||
assert_eq!(thinned.records["states"].as_array().unwrap().len(), 1);
|
||||
assert_eq!(thinned.final_state().unwrap().sketch_keys, model.state().sketch_keys);
|
||||
assert_eq!(keys(&Model::adopt(&doc, &thinned).unwrap()), keys(&model));
|
||||
// Sets and drops read as written; a base that is not an earlier set is refused.
|
||||
let with_sets = |sets: serde_json::Value| SavedModel {
|
||||
records: {
|
||||
let mut r = saved.records.clone();
|
||||
r["sketch_key_sets"] = sets;
|
||||
r
|
||||
},
|
||||
bodies: saved.bodies.clone(),
|
||||
};
|
||||
let first = *model.state().sketch_keys.keys().next().unwrap();
|
||||
let dropped = with_sets(serde_json::json!([
|
||||
{"set": [[first, "0000000000000001"], [7, "0000000000000002"]], "drop": []},
|
||||
{"base": 0, "set": [], "drop": []},
|
||||
{"base": 1, "set": [[8, "0000000000000003"]], "drop": [first]},
|
||||
{"base": 0, "set": [[7, "00000000000000ff"]], "drop": []},
|
||||
]));
|
||||
let want: BTreeMap<u64, u64> = [(7, 0xff), (first, 1)].into();
|
||||
assert_eq!(dropped.final_state().unwrap().sketch_keys, want);
|
||||
let dropped = with_sets(serde_json::json!([
|
||||
{"set": [[first, "0000000000000001"], [7, "0000000000000002"]], "drop": []},
|
||||
{"base": 0, "set": [], "drop": []},
|
||||
{"base": 0, "set": [], "drop": []},
|
||||
{"base": 1, "set": [[8, "0000000000000003"]], "drop": [first]},
|
||||
]));
|
||||
assert_eq!(dropped.final_state().unwrap().sketch_keys, [(7, 2), (8, 3)].into());
|
||||
for base in [3, 4] {
|
||||
let sets =
|
||||
serde_json::json!([{"set": [], "drop": []}, {"set": [], "drop": []}, {"set": [], "drop": []}, {"base": base, "set": [], "drop": []}]);
|
||||
assert!(with_sets(sets).final_state().is_err(), "base {base}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,6 +161,12 @@ pub const FUNCTIONS: &[(&str, &str)] = &[
|
||||
("ceil", "ceil(x)"),
|
||||
("round", "round(x)"),
|
||||
("exp", "exp(x)"),
|
||||
("sinh", "sinh(x)"),
|
||||
("cosh", "cosh(x)"),
|
||||
("tanh", "tanh(x)"),
|
||||
("asinh", "asinh(x)"),
|
||||
("acosh", "acosh(x), x ≥ 1"),
|
||||
("atanh", "atanh(x), −1 < x < 1"),
|
||||
("ln", "ln(x)"),
|
||||
("log", "log(x) (base 10)"),
|
||||
("log10", "log10(x)"),
|
||||
@@ -344,8 +350,9 @@ fn lex_spans(s: &str) -> Result<Vec<(Tok, usize, usize)>, DocError> {
|
||||
let at = |i: usize| cs.get(i).map(|x| x.0).unwrap_or(s.len());
|
||||
let mut i = 0;
|
||||
let mut out = Vec::new();
|
||||
// Parentheses open at this point: a comma outside them can only be a decimal comma.
|
||||
let mut depth = 0usize;
|
||||
// Parentheses open at this point, innermost last, true for a function call's: a comma directly
|
||||
// in a call separates its arguments, and anywhere else can only be a decimal comma (#9408).
|
||||
let mut parens: Vec<bool> = Vec::new();
|
||||
// A single leading `=` marks the text as a formula (`=d3`, #4681) and is not part of it.
|
||||
if let Some(k) = cs.iter().position(|(_, c)| !c.is_whitespace())
|
||||
&& cs.get(k).is_some_and(|(_, c)| *c == '=')
|
||||
@@ -363,7 +370,7 @@ fn lex_spans(s: &str) -> Result<Vec<(Tok, usize, usize)>, DocError> {
|
||||
while let Some(&(_, c)) = cs.get(i) {
|
||||
if c.is_whitespace() {
|
||||
i += 1;
|
||||
} else if c.is_ascii_digit() || c == '.' || leading_decimal_comma(&cs, i, depth, out.last()) {
|
||||
} else if c.is_ascii_digit() || c == '.' || leading_decimal_comma(&cs, i, in_call(&parens), out.last()) {
|
||||
let st = i;
|
||||
// A leading decimal comma (`,5` for 0,5, #2966) starts the number as `.5` would.
|
||||
let leading_comma = c == ',';
|
||||
@@ -374,11 +381,17 @@ fn lex_spans(s: &str) -> Result<Vec<(Tok, usize, usize)>, DocError> {
|
||||
i += 1;
|
||||
}
|
||||
// A decimal comma (`2,5 mm`, as typed with a numeric keypad in many locales, #1347):
|
||||
// a comma between digits, in a number with no point, outside any parentheses (inside
|
||||
// them a comma separates a function's arguments: `max(1,2)`).
|
||||
// a comma between digits, in a number with no point, not directly in a function call
|
||||
// (there a comma separates arguments: `max(1,2)`).
|
||||
let digit = |k: usize| cs.get(k).is_some_and(|(_, c)| c.is_ascii_digit());
|
||||
let has_point = cs.get(st..i).is_some_and(|ds| ds.iter().any(|(_, c)| *c == '.'));
|
||||
if depth == 0 && !leading_comma && !has_point && digit(i.wrapping_sub(1)) && cs.get(i).is_some_and(|(_, c)| *c == ',') && digit(i + 1) {
|
||||
if !in_call(&parens)
|
||||
&& !leading_comma
|
||||
&& !has_point
|
||||
&& digit(i.wrapping_sub(1))
|
||||
&& cs.get(i).is_some_and(|(_, c)| *c == ',')
|
||||
&& digit(i + 1)
|
||||
{
|
||||
i += 1;
|
||||
while digit(i) {
|
||||
i += 1;
|
||||
@@ -400,7 +413,7 @@ fn lex_spans(s: &str) -> Result<Vec<(Tok, usize, usize)>, DocError> {
|
||||
// A mixed number, as imperial sizes are written (`2 1/8"` is 2.125 in, #1802): a whole
|
||||
// number, then a proper fraction of whole numbers, is one value, not `2 × 1/8`.
|
||||
if t.chars().all(|c| c.is_ascii_digit())
|
||||
&& let Some((fraction, end)) = mixed_fraction(&cs, i, depth)
|
||||
&& let Some((fraction, end)) = mixed_fraction(&cs, i, in_call(&parens))
|
||||
{
|
||||
out.push((Tok::Num(v + fraction), at(st), at(end)));
|
||||
i = end;
|
||||
@@ -438,8 +451,10 @@ fn lex_spans(s: &str) -> Result<Vec<(Tok, usize, usize)>, DocError> {
|
||||
i += 1;
|
||||
} else if "+-*/^(),%".contains(c) {
|
||||
match c {
|
||||
'(' => depth += 1,
|
||||
')' => depth = depth.saturating_sub(1),
|
||||
'(' => parens.push(matches!(out.last(), Some((Tok::Id(_), _, _)))),
|
||||
')' => {
|
||||
parens.pop();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
out.push((Tok::Op(c), at(i), at(i + 1)));
|
||||
@@ -451,27 +466,32 @@ fn lex_spans(s: &str) -> Result<Vec<(Tok, usize, usize)>, DocError> {
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// Whether the innermost open parenthesis is a function call's.
|
||||
fn in_call(parens: &[bool]) -> bool {
|
||||
parens.last() == Some(&true)
|
||||
}
|
||||
|
||||
/// Whether the comma at `i` starts a number, as a leading decimal comma (`,5` for 0,5, #2966).
|
||||
/// It must be outside parentheses (inside them a comma separates arguments, as for any decimal
|
||||
/// comma), be followed by a digit, and stand where an operand is expected: at the start or right
|
||||
/// after `+ - * / ^`. A comma after a number, name, `)` or `%` keeps its meaning (`2 ,5` and
|
||||
/// `1,5,3` stay errors).
|
||||
fn leading_decimal_comma(cs: &[(usize, char)], i: usize, depth: usize, prev: Option<&(Tok, usize, usize)>) -> bool {
|
||||
depth == 0
|
||||
/// It must not be directly in a function call (`in_call`: there a comma separates arguments, as
|
||||
/// for any decimal comma), be followed by a digit, and stand where an operand is expected: at the
|
||||
/// start or right after `( + - * / ^`. A comma after a number, name, `)` or `%` keeps its meaning
|
||||
/// (`2 ,5` and `1,5,3` stay errors).
|
||||
fn leading_decimal_comma(cs: &[(usize, char)], i: usize, in_call: bool, prev: Option<&(Tok, usize, usize)>) -> bool {
|
||||
!in_call
|
||||
&& cs.get(i).is_some_and(|(_, c)| *c == ',')
|
||||
&& cs.get(i + 1).is_some_and(|(_, c)| c.is_ascii_digit())
|
||||
&& match prev {
|
||||
None => true,
|
||||
Some((Tok::Op(op), _, _)) => "+-*/^".contains(*op),
|
||||
Some((Tok::Op(op), _, _)) => "(+-*/^".contains(*op),
|
||||
Some(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// The proper fraction of a mixed number whose whole part ends at `i`: spaces, digits, `/`,
|
||||
/// digits, with nothing after them that would continue the number (`2 1/8.5`, `2 1/8e3`, a
|
||||
/// decimal comma outside parentheses: `2 1/8,5`). Its value and where it ends. `depth`: the
|
||||
/// parentheses open there (inside them a comma separates arguments: `max(2 1/8,1)`).
|
||||
fn mixed_fraction(cs: &[(usize, char)], mut i: usize, depth: usize) -> Option<(f64, usize)> {
|
||||
/// decimal comma: `2 1/8,5`). Its value and where it ends. `in_call`: directly in a function call
|
||||
/// there (a comma separates arguments: `max(2 1/8,1)`).
|
||||
fn mixed_fraction(cs: &[(usize, char)], mut i: usize, in_call: bool) -> Option<(f64, usize)> {
|
||||
let digits = |from: usize| cs.get(from..).map_or(0, |rest| rest.iter().take_while(|(_, c)| c.is_ascii_digit()).count());
|
||||
let space = i;
|
||||
while cs.get(i).is_some_and(|(_, c)| *c == ' ' || *c == '\t') {
|
||||
@@ -485,7 +505,7 @@ fn mixed_fraction(cs: &[(usize, char)], mut i: usize, depth: usize) -> Option<(f
|
||||
let end = i + numerator + 1 + denominator;
|
||||
let next = |k: usize| cs.get(end + k).map(|(_, c)| *c);
|
||||
let continues = matches!(next(0), Some('.'))
|
||||
|| (depth == 0 && matches!(next(0), Some(',')) && next(1).is_some_and(|c| c.is_ascii_digit()))
|
||||
|| (!in_call && matches!(next(0), Some(',')) && next(1).is_some_and(|c| c.is_ascii_digit()))
|
||||
|| (matches!(next(0), Some('e' | 'E')) && next(1).is_some_and(|c| c.is_ascii_digit() || c == '-' || c == '+'));
|
||||
if denominator == 0 || continues {
|
||||
return None;
|
||||
@@ -589,9 +609,11 @@ pub fn is_literal(s: &str) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Is the expression a bare number without a unit, like `20` or `-1.5`?
|
||||
/// Is the expression a bare number without a unit, like `20` or `-1.5`? A leading `=` only marks
|
||||
/// a formula (#4681), so `=20` is the same bare number.
|
||||
pub fn is_bare_number(s: &str) -> bool {
|
||||
is_literal(s) && s.trim().trim_start_matches(['-', '+']).trim().parse::<f64>().is_ok()
|
||||
let number = s.trim().strip_prefix('=').unwrap_or(s);
|
||||
is_literal(s) && number.trim().trim_start_matches(['-', '+']).trim().parse::<f64>().is_ok()
|
||||
}
|
||||
|
||||
/// Is `s` shaped like a parameter name: a letter or `_`, then letters, digits and `_`? The shape
|
||||
@@ -943,6 +965,24 @@ fn call(f: &str, args: &[Value], d: Defaults) -> Result<Value, DocError> {
|
||||
unit_fn(r * if scale != 0.0 { scale } else { 1.0 }, a)
|
||||
}
|
||||
"exp" => Value::num(num(one()?)?.exp()),
|
||||
"sinh" => Value::num(num(one()?)?.sinh()),
|
||||
"cosh" => Value::num(num(one()?)?.cosh()),
|
||||
"tanh" => Value::num(num(one()?)?.tanh()),
|
||||
"asinh" => Value::num(num(one()?)?.asinh()),
|
||||
"acosh" => {
|
||||
let x = num(one()?)?;
|
||||
if !(x >= 1.0) {
|
||||
return Err(DocError::Expr("`acosh` needs a number of at least 1".into()));
|
||||
}
|
||||
Value::num(x.acosh())
|
||||
}
|
||||
"atanh" => {
|
||||
let x = num(one()?)?;
|
||||
if !(x > -1.0 && x < 1.0) {
|
||||
return Err(DocError::Expr("`atanh` needs a number between -1 and 1, both excluded".into()));
|
||||
}
|
||||
Value::num(x.atanh())
|
||||
}
|
||||
"ln" | "log" | "log10" => {
|
||||
let x = num(one()?)?;
|
||||
if x <= 0.0 {
|
||||
@@ -1214,7 +1254,13 @@ mod tests {
|
||||
// Inside parentheses a comma still separates a function's arguments.
|
||||
assert_eq!(ev("max(1,2)", Kind::Unitless).unwrap(), 2.0);
|
||||
assert_eq!(ev("max(1 mm, 3, 2)", Kind::Length).unwrap(), 3.0);
|
||||
assert_eq!(ev("(2,5)", Kind::Unitless).ok(), None, "a comma inside parentheses is not a decimal comma");
|
||||
// Plain parentheses have no arguments to separate (#9408), also inside a call.
|
||||
assert_eq!(ev("(2,5)", Kind::Unitless).unwrap(), 2.5);
|
||||
assert_eq!(ev("-(width-0,5)/2", Kind::Length).unwrap(), -19.75);
|
||||
assert_eq!(ev("(,5)", Kind::Unitless).unwrap(), 0.5);
|
||||
assert_eq!(ev("(2 1/8,5)", Kind::Unitless).unwrap(), 2.0 / 8.5);
|
||||
assert_eq!(ev("max((1,5), 1)", Kind::Unitless).unwrap(), 1.5);
|
||||
assert_eq!(ev("(max(1,2))", Kind::Unitless).unwrap(), 2.0);
|
||||
// Not between two digits, or a second comma: still an error.
|
||||
for bad in ["2,", "2 ,5", "2, 5", "1,5,3", "1.5,3", ",5,3", ",5.3", "width ,5", "(1) ,5"] {
|
||||
assert!(ev(bad, Kind::Unitless).is_err(), "{bad}");
|
||||
@@ -1328,6 +1374,15 @@ mod tests {
|
||||
assert!((ev("atan2(1; 1)", Kind::Angle).unwrap() - std::f64::consts::FRAC_PI_4).abs() < 1e-12);
|
||||
assert!((ev("exp(ln(5))", Kind::Unitless).unwrap() - 5.0).abs() < 1e-12);
|
||||
assert!((ev("log(1000)", Kind::Unitless).unwrap() - 3.0).abs() < 1e-12);
|
||||
// Hyperbolic functions (#9332): their definitions in exp, identities and inverses.
|
||||
assert!((ev("cosh(1.5) - (exp(1.5) + exp(-1.5)) / 2", Kind::Unitless).unwrap()).abs() < 1e-12);
|
||||
assert!((ev("sinh(1.5) - (exp(1.5) - exp(-1.5)) / 2", Kind::Unitless).unwrap()).abs() < 1e-12);
|
||||
assert!((ev("tanh(0.7) - sinh(0.7) / cosh(0.7)", Kind::Unitless).unwrap()).abs() < 1e-12);
|
||||
assert!((ev("cosh(2) ^ 2 - sinh(2) ^ 2", Kind::Unitless).unwrap() - 1.0).abs() < 1e-12);
|
||||
assert!((ev("asinh(sinh(1.25))", Kind::Unitless).unwrap() - 1.25).abs() < 1e-12);
|
||||
assert!((ev("acosh(cosh(1.25))", Kind::Unitless).unwrap() - 1.25).abs() < 1e-12);
|
||||
assert!((ev("atanh(tanh(0.5))", Kind::Unitless).unwrap() - 0.5).abs() < 1e-12);
|
||||
assert_eq!(ev("cosh(0) + sinh(0) + tanh(0) + asinh(0) + acosh(1) + atanh(0)", Kind::Unitless).unwrap(), 1.0);
|
||||
assert_eq!(ev("floor(2.7) + ceil(0.2) + round(1.5) + abs(-1)", Kind::Unitless).unwrap(), 6.0);
|
||||
assert!((ev("1 rad", Kind::Angle).unwrap() - 1.0).abs() < 1e-12);
|
||||
assert!((ev("acos(0)", Kind::Angle).unwrap() - std::f64::consts::FRAC_PI_2).abs() < 1e-12);
|
||||
@@ -1337,6 +1392,22 @@ mod tests {
|
||||
assert_eq!(ev("width^2 / width", Kind::Length).unwrap(), 40.0);
|
||||
}
|
||||
|
||||
/// The hyperbolic functions (#9332) take a unit-less number, and the inverses refuse what lies
|
||||
/// outside their domain, as `asin` and `ln` do.
|
||||
#[test]
|
||||
fn hyperbolic_functions_refuse_units_and_numbers_outside_their_domain() {
|
||||
for (e, says) in [
|
||||
("cosh(2 mm)", "`cosh` needs a unit-less number"),
|
||||
("tanh(30 deg)", "`tanh` needs a unit-less number"),
|
||||
("acosh(0.5)", "`acosh` needs a number of at least 1"),
|
||||
("atanh(1)", "`atanh` needs a number between -1 and 1"),
|
||||
("atanh(-1.5)", "`atanh` needs a number between -1 and 1"),
|
||||
] {
|
||||
let err = ev(e, Kind::Unitless).map(|v| v.to_string()).unwrap_or_else(|e| e.to_string());
|
||||
assert!(err.contains(says), "{e}: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
/// A count from a length (`(L - 20) / 210`, the 210 read as unit-less) names the fix (#5901).
|
||||
#[test]
|
||||
fn a_length_where_a_count_is_expected_says_to_divide_by_a_length() {
|
||||
@@ -1449,6 +1520,8 @@ mod tests {
|
||||
assert!(is_literal("20") && is_literal("-1.5 in") && is_literal("3 deg"));
|
||||
assert!(!is_literal("d1") && !is_literal("2 * 3") && !is_literal("2 width"));
|
||||
assert!(is_bare_number(" -1.5 ") && is_bare_number("+2") && !is_bare_number("20 mm") && !is_bare_number("(2)"));
|
||||
// A leading `=` only marks a formula (#4681): `=5` is the bare number `5`, not `==5`.
|
||||
assert!(is_bare_number("=5") && is_bare_number(" = -1.5") && !is_bare_number("=5 mm") && !is_bare_number("==5") && !is_bare_number("=d3"));
|
||||
assert_eq!((length_unit_or_mm(" in "), length_unit_or_mm("deg"), length_unit_or_mm("")), ("in", "mm", "mm"));
|
||||
}
|
||||
|
||||
|
||||
@@ -172,32 +172,7 @@ impl Document {
|
||||
author: u64,
|
||||
author_path: &[u64],
|
||||
) -> Result<FaceAnchor> {
|
||||
// A construction certificate that names this face alone, else its exact history name.
|
||||
let certified = body
|
||||
.body
|
||||
.face_plane(index)
|
||||
.ok_or_else(|| DocError::Invalid("this face has no exact planar source; choose a fixed plane instead".into()))
|
||||
.and_then(|source| {
|
||||
let target = self.strong_face_target(body, index, source.origin)?;
|
||||
if target.certificate.is_none() {
|
||||
return Err(DocError::Invalid("this face has no supported construction certificate; choose a fixed plane instead".into()));
|
||||
}
|
||||
let indices = self.configuration_face_indices(body, &target)?;
|
||||
// A copy a pattern or mirror made on the same body carries its seed's certificate,
|
||||
// which finds the seed's face: the copy is attached by its own history name below.
|
||||
if !indices.contains(&index) {
|
||||
return Err(DocError::Invalid("this face has no supported construction certificate; choose a fixed plane instead".into()));
|
||||
}
|
||||
self.coplanar_pieces(body, &indices, |_| true)?;
|
||||
Ok(target)
|
||||
});
|
||||
let target = match certified {
|
||||
Ok(target) => target,
|
||||
Err(error) => match body.body.planar_face(index).and_then(|plane| self.lineage_face_target(body, index, plane.origin)) {
|
||||
Some(target) => target,
|
||||
None => return Err(error),
|
||||
},
|
||||
};
|
||||
let target = self.flat_face_target(body, index)?;
|
||||
let source = self
|
||||
.anchor_face_plane(body, index, target.certificate.as_ref())
|
||||
.ok_or_else(|| DocError::Invalid("this face has no exact planar source; choose a fixed plane instead".into()))?;
|
||||
@@ -220,6 +195,34 @@ impl Document {
|
||||
Ok(anchor)
|
||||
}
|
||||
|
||||
/// A target for flat face `index` of `body` that a rebuild finds again: a construction
|
||||
/// certificate that names this face alone, else its exact history name. What a sketch on a
|
||||
/// face and a print face hold.
|
||||
pub(crate) fn flat_face_target(&self, body: &ModelBody, index: usize) -> Result<ConfigFaceTarget> {
|
||||
let certified = body
|
||||
.body
|
||||
.face_plane(index)
|
||||
.ok_or_else(|| DocError::Invalid("this face has no exact planar source; choose a fixed plane instead".into()))
|
||||
.and_then(|source| {
|
||||
let target = self.strong_face_target(body, index, source.origin)?;
|
||||
if target.certificate.is_none() {
|
||||
return Err(DocError::Invalid("this face has no supported construction certificate; choose a fixed plane instead".into()));
|
||||
}
|
||||
let indices = self.configuration_face_indices(body, &target)?;
|
||||
// A copy a pattern or mirror made on the same body carries its seed's certificate,
|
||||
// which finds the seed's face: the copy is attached by its own history name below.
|
||||
if !indices.contains(&index) {
|
||||
return Err(DocError::Invalid("this face has no supported construction certificate; choose a fixed plane instead".into()));
|
||||
}
|
||||
self.coplanar_pieces(body, &indices, |_| true)?;
|
||||
Ok(target)
|
||||
});
|
||||
match certified {
|
||||
Ok(target) => Ok(target),
|
||||
Err(error) => body.body.planar_face(index).and_then(|plane| self.lineage_face_target(body, index, plane.origin)).ok_or(error),
|
||||
}
|
||||
}
|
||||
|
||||
/// The faces an attachment stands for, and a note when they were found by fallback.
|
||||
///
|
||||
/// An extrude's cap is certified by the loop of sketch curves around it, so editing that
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
//! Browser groups can nest and be ordered. A group saves its `parent` group (omitted at the top
|
||||
//! level); a group's items and a folder's `browser_order` list may name groups as numbers beside
|
||||
//! item keys as strings; and `browser_order` has a `"<component>/components"` list ordering a
|
||||
//! component's sub-components by placement id. Older designs read unchanged: they have no
|
||||
//! parents and no group entries. The last release refuses the newer format and asks for an
|
||||
//! update instead of dropping the nesting and the order.
|
||||
@@ -0,0 +1,7 @@
|
||||
//! A point cloud keeps the other properties its file gives its points (#8816): named numbers per
|
||||
//! point in `complex` (`columns`, each whole-numbered or not, and the `names` a whole-numbered one's
|
||||
//! values carry), and it can be coloured by one of them (`color_by: "property"`, with
|
||||
//! `color_property`, the ramp over `height_range`) and shown only where `filters` keep it (ranges,
|
||||
//! or sets of named values). Older designs need no transformation: their clouds have no other
|
||||
//! properties and no filters. The last release refuses a design saved with this (the newer format
|
||||
//! number) instead of opening its clouds without their properties, colouring and filters.
|
||||
@@ -0,0 +1,5 @@
|
||||
//! A design saves the print face of its bodies (`print_faces`, by body name): the flat face a
|
||||
//! slicer export lays on the print bed, held by the same face target a sketch on a face holds.
|
||||
//! An older design has none, and every body is exported as it lies, as before. The last
|
||||
//! release refuses a design that has print faces and asks for an update, instead of opening it
|
||||
//! and losing them on the next save.
|
||||
@@ -10,6 +10,7 @@ pub mod appearance_pack;
|
||||
mod assembly;
|
||||
mod body_transfer;
|
||||
mod body_transfer_sheet;
|
||||
mod browser_tree;
|
||||
pub mod pbr_appearance;
|
||||
pub use body_transfer::{valid_transfer_matrix, validate_body_transfer};
|
||||
pub mod canvas;
|
||||
@@ -53,6 +54,8 @@ pub mod physical_material;
|
||||
mod piece_names;
|
||||
pub mod pipe_thread_catalog;
|
||||
pub mod point_cloud;
|
||||
mod point_cloud_complex;
|
||||
pub mod point_cloud_distribution;
|
||||
mod points;
|
||||
mod profile_refs;
|
||||
pub mod progress;
|
||||
@@ -61,6 +64,8 @@ pub use thread_catalog::{TappedThread, TappedThreadFamily, parse_tapped_thread,
|
||||
mod walk;
|
||||
pub use profile_refs::{capture_profile_selection, profile_fallback_notes, profile_selection_indices, profile_selection_indices_lenient};
|
||||
pub mod plastic;
|
||||
mod print_face;
|
||||
pub use print_face::PrintFace;
|
||||
pub mod project;
|
||||
mod project3d;
|
||||
pub mod render_aging;
|
||||
|
||||
@@ -22,7 +22,8 @@ pub struct SavedModel {
|
||||
|
||||
/// Records refer to these at most.
|
||||
const MAX_ITEMS: usize = 1_000_000;
|
||||
/// Bodies, sketches and sheet bodies all the states hold together, at most (a state is a list of clones).
|
||||
/// Bodies, sketches, sheet bodies and sketch keys all the states hold together, at most (a state is
|
||||
/// a list of clones).
|
||||
const MAX_HELD: usize = 50_000_000;
|
||||
/// Longest text a record holds (bytes). A feature's warning can quote every generated name of
|
||||
/// the edges it picked: 122,701 characters in one design, whose saved model was refused, so it
|
||||
@@ -48,6 +49,11 @@ fn unhex(v: &Json) -> Result<u64> {
|
||||
v.as_str().and_then(|s| u64::from_str_radix(s, 16).ok()).ok_or_else(|| bad("saved model: key"))
|
||||
}
|
||||
|
||||
/// A sketch-key set's list `key` (`set` or `drop`).
|
||||
fn keys_list<'a>(set: &'a Json, key: &str) -> Result<&'a Vec<Json>> {
|
||||
set.get(key).and_then(Json::as_array).filter(|l| l.len() <= MAX_ITEMS).ok_or_else(|| bad("saved model: sketch keys"))
|
||||
}
|
||||
|
||||
fn bad(what: &str) -> DocError {
|
||||
DocError::Invalid(what.into())
|
||||
}
|
||||
@@ -128,9 +134,36 @@ struct Writer {
|
||||
sheet_index: HashMap<String, usize>,
|
||||
states: Vec<Json>,
|
||||
state_index: HashMap<usize, usize>,
|
||||
/// The states' sketch keys, each distinct map once, written as the changes from the set
|
||||
/// written before it: every state used to repeat the whole map (one entry per solved sketch),
|
||||
/// 85 % of a 5,484-feature design's records, which then did not fit the cache's manifest.
|
||||
sketch_key_sets: Vec<Json>,
|
||||
sketch_key_index: HashMap<BTreeMap<u64, u64>, usize>,
|
||||
last_sketch_keys: Option<(BTreeMap<u64, u64>, usize)>,
|
||||
}
|
||||
|
||||
impl Writer {
|
||||
/// The place of `keys` among the sketch-key sets, written the first time a state holds them
|
||||
/// (as the changes from the set written last: a later state mostly adds a sketch or two).
|
||||
fn sketch_key_set(&mut self, keys: &BTreeMap<u64, u64>) -> usize {
|
||||
if let Some(i) = self.sketch_key_index.get(keys) {
|
||||
return *i;
|
||||
}
|
||||
let record = match &self.last_sketch_keys {
|
||||
Some((base, at)) => {
|
||||
let set: Vec<Json> = keys.iter().filter(|(f, k)| base.get(f) != Some(k)).map(|(f, k)| json!([f, hex(*k)])).collect();
|
||||
let drop: Vec<u64> = base.keys().filter(|f| !keys.contains_key(f)).copied().collect();
|
||||
json!({"base": at, "set": set, "drop": drop})
|
||||
}
|
||||
None => json!({"set": keys.iter().map(|(f, k)| json!([f, hex(*k)])).collect::<Vec<_>>(), "drop": []}),
|
||||
};
|
||||
self.sketch_key_sets.push(record);
|
||||
let i = self.sketch_key_sets.len() - 1;
|
||||
self.sketch_key_index.insert(keys.clone(), i);
|
||||
self.last_sketch_keys = Some((keys.clone(), i));
|
||||
i
|
||||
}
|
||||
|
||||
fn names(&mut self, b: &ModelBody) -> usize {
|
||||
let id = b.names.id();
|
||||
if let Some(i) = self.names_index.get(&id) {
|
||||
@@ -144,9 +177,6 @@ impl Writer {
|
||||
}
|
||||
|
||||
fn model_body(&mut self, b: &ModelBody) -> Option<usize> {
|
||||
if b.body.is_mesh() {
|
||||
return None;
|
||||
}
|
||||
let key = BodyKey::of(b);
|
||||
if let Some(i) = self.model_body_index.get(&key) {
|
||||
return Some(*i);
|
||||
@@ -216,7 +246,7 @@ impl Writer {
|
||||
"reserved_body_names": st.reserved_body_names.iter().collect::<Vec<_>>(),
|
||||
"sheets": sheets, "construct": serde_json::to_value(&st.construct).ok()?,
|
||||
"threads": serde_json::to_value(&st.threads).ok()?,
|
||||
"sketch_keys": st.sketch_keys.iter().map(|(f, k)| json!([f, hex(*k)])).collect::<Vec<_>>(),
|
||||
"sketch_key_set": self.sketch_key_set(&st.sketch_keys),
|
||||
"blend_edges": serde_json::to_value(&st.blend_edges).ok()?, "fitted": serde_json::to_value(&st.fitted).ok()?,
|
||||
"tapered": serde_json::to_value(&st.tapered).ok()?,
|
||||
});
|
||||
@@ -228,7 +258,7 @@ impl Writer {
|
||||
|
||||
impl Model {
|
||||
/// The model as a design file keeps it: `None` unless it is `doc`'s evaluation without an
|
||||
/// error, holds no mesh bodies, and `doc` keeps no names saved before format 14. Works out
|
||||
/// error and `doc` keeps no names saved before format 14. Works out
|
||||
/// the face names not asked for yet (a saved model keeps them as worked out).
|
||||
pub fn saved(&self, doc: &Document) -> Option<SavedModel> {
|
||||
if self.results.is_empty() || self.results.iter().any(|r| r.error.is_some()) || !self.is_current(doc) || has_legacy_names(doc) {
|
||||
@@ -250,7 +280,7 @@ impl Model {
|
||||
}
|
||||
let records = json!({
|
||||
"features": features, "states": w.states, "model_bodies": w.model_bodies, "names": w.names, "sketches": w.sketches,
|
||||
"sheets": w.sheets,
|
||||
"sheets": w.sheets, "sketch_key_sets": w.sketch_key_sets,
|
||||
});
|
||||
Ok(SavedModel { records, bodies: w.bodies })
|
||||
})
|
||||
@@ -331,9 +361,49 @@ impl SavedModel {
|
||||
for s in list("sheets")? {
|
||||
sheets.push(serde_json::from_value::<crate::sheet::SheetBody>(s.clone()).map_err(|_| bad("saved model: sheet"))?);
|
||||
}
|
||||
let mut states = Vec::new();
|
||||
// The sketch-key sets, each the changes from an earlier one (`base`): a short record can
|
||||
// stand for a large set, so what they hold counts towards MAX_HELD as they are built.
|
||||
let mut key_sets: Vec<BTreeMap<u64, u64>> = Vec::new();
|
||||
let mut held = 0usize;
|
||||
for s in list("states")? {
|
||||
for k in list("sketch_key_sets")? {
|
||||
// A set is the changes from one before it.
|
||||
let base = match k.get("base") {
|
||||
Some(b) => Some(key_sets.get(index(Some(b), key_sets.len())?).ok_or_else(|| bad("saved model: sketch keys"))?),
|
||||
None => None,
|
||||
};
|
||||
let (drop, set) = (keys_list(k, "drop")?, keys_list(k, "set")?);
|
||||
// Counted before the base is copied: at most what the set can hold.
|
||||
held = held.saturating_add(base.map_or(0, BTreeMap::len)).saturating_add(set.len());
|
||||
if held > MAX_HELD {
|
||||
return Err(bad("saved model: sketch keys"));
|
||||
}
|
||||
let mut keys = base.cloned().unwrap_or_default();
|
||||
for f in drop {
|
||||
keys.remove(&f.as_u64().ok_or_else(|| bad("saved model: sketch keys"))?);
|
||||
}
|
||||
for e in set {
|
||||
keys.insert(e.get(0).and_then(Json::as_u64).ok_or_else(|| bad("saved model: sketch keys"))?, unhex(e.get(1).unwrap_or(&Json::Null))?);
|
||||
}
|
||||
key_sets.push(keys);
|
||||
}
|
||||
let records = list("states")?;
|
||||
// What every state holds, counted from its runs and its sketch-key set before any is
|
||||
// built (a hostile file cannot make the states clone more than MAX_HELD items).
|
||||
for s in records {
|
||||
for key in ["bodies", "sketches"] {
|
||||
for run in s.get(key).and_then(Json::as_array).ok_or_else(|| bad("saved model: state"))? {
|
||||
held = held.saturating_add(run.get(1).and_then(Json::as_u64).and_then(|n| usize::try_from(n).ok()).unwrap_or(usize::MAX));
|
||||
}
|
||||
}
|
||||
held = held.saturating_add(s.get("sheets").and_then(Json::as_array).map_or(0, Vec::len));
|
||||
held = held.saturating_add(key_sets.get(index(s.get("sketch_key_set"), key_sets.len())?).map_or(0, BTreeMap::len));
|
||||
if held > MAX_HELD {
|
||||
return Err(bad("saved model: states"));
|
||||
}
|
||||
}
|
||||
// Each state is independent of the others once the records they share are read: built
|
||||
// on threads (a 5,484-feature design spent 3.5 s building them one after another).
|
||||
let built = kernel::map_on_threads(records, 16, |s: &Json| -> Result<ModelState> {
|
||||
// Runs of consecutive indices, each within the list it indexes.
|
||||
let ids = |key: &str, len: usize| -> Result<Vec<usize>> {
|
||||
let mut out = Vec::new();
|
||||
@@ -349,12 +419,8 @@ impl SavedModel {
|
||||
};
|
||||
let field = |key: &str| parse(s.get(key));
|
||||
let invalid = |_| bad("saved model: state");
|
||||
let mut sketch_keys = BTreeMap::new();
|
||||
for k in s.get("sketch_keys").and_then(Json::as_array).ok_or_else(|| bad("saved model: state"))? {
|
||||
sketch_keys
|
||||
.insert(k.get(0).and_then(Json::as_u64).ok_or_else(|| bad("saved model: state"))?, unhex(k.get(1).unwrap_or(&Json::Null))?);
|
||||
}
|
||||
let st = ModelState {
|
||||
let sketch_keys = key_sets.get(index(s.get("sketch_key_set"), key_sets.len())?).cloned().ok_or_else(|| bad("saved model: state"))?;
|
||||
Ok(ModelState {
|
||||
frame_bodies: Default::default(),
|
||||
bodies: ids("bodies", model_bodies.len())?.into_iter().filter_map(|i| model_bodies.get(i).cloned()).collect(),
|
||||
sketches: ids("sketches", sketches.len())?.into_iter().filter_map(|i| sketches.get(i).cloned()).collect(),
|
||||
@@ -377,13 +443,9 @@ impl SavedModel {
|
||||
blend_edges: serde_json::from_value(field("blend_edges")?).map_err(invalid)?,
|
||||
fitted: serde_json::from_value(field("fitted")?).map_err(invalid)?,
|
||||
tapered: serde_json::from_value(field("tapered")?).map_err(invalid)?,
|
||||
};
|
||||
held = held.saturating_add(st.bodies.len()).saturating_add(st.sketches.len()).saturating_add(st.sheets.len());
|
||||
if held > MAX_HELD {
|
||||
return Err(bad("saved model: states"));
|
||||
}
|
||||
states.push(Arc::new(st));
|
||||
}
|
||||
})
|
||||
});
|
||||
let states = built.into_iter().map(|r| r.unwrap_or_else(|| Err(bad("saved model: state"))).map(Arc::new)).collect::<Result<Vec<_>>>()?;
|
||||
Ok(states)
|
||||
}
|
||||
|
||||
@@ -457,7 +519,8 @@ impl SavedModel {
|
||||
let mut stored = std::collections::HashSet::new();
|
||||
// The sheet bodies, each written once and named by place in the states: all kept.
|
||||
let sheets = list("sheets").cloned().unwrap_or_default();
|
||||
let mut used = bytes(sheets.as_slice()).iter().fold(0usize, |t, b| t.saturating_add(*b));
|
||||
let key_sets = list("sketch_key_sets").cloned().unwrap_or_default();
|
||||
let mut used = bytes(sheets.as_slice()).iter().chain(bytes(key_sets.as_slice()).iter()).fold(0usize, |t, b| t.saturating_add(*b));
|
||||
for s in std::iter::once(last).chain(order) {
|
||||
let (mut adds, mut new_names, mut new_sketches) = (HashMap::new(), std::collections::HashSet::new(), std::collections::HashSet::new());
|
||||
let mut records = *state_bytes.get(s)?;
|
||||
@@ -549,7 +612,7 @@ impl SavedModel {
|
||||
}
|
||||
let records = json!({
|
||||
"features": out_features, "states": out_states, "model_bodies": out_bodies, "names": kept_of(names.as_slice(), &names_used),
|
||||
"sketches": kept_of(sketches.as_slice(), &sketches_used), "sheets": sheets,
|
||||
"sketches": kept_of(sketches.as_slice(), &sketches_used), "sheets": sheets, "sketch_key_sets": key_sets,
|
||||
});
|
||||
let kept: Vec<usize> = (0..self.bodies.len()).filter(|i| kernel_used.get(*i) == Some(&true)).collect();
|
||||
let bodies = kept.iter().filter_map(|i| self.bodies.get(*i).cloned()).collect();
|
||||
|
||||
@@ -1150,6 +1150,19 @@ mod design_length_tests {
|
||||
assert_eq!(written("", 5.0), w("mm", "5 mm"));
|
||||
}
|
||||
|
||||
/// A bare number typed as a formula (`=5`) means the design's units like `5` does.
|
||||
#[test]
|
||||
fn a_formula_that_is_a_bare_number_gets_the_design_unit() {
|
||||
let mut doc = Document::new("Units");
|
||||
doc.units = "in".into();
|
||||
let length = crate::expr::Kind::Length;
|
||||
assert_eq!(doc.with_design_unit("5", length), "5 in");
|
||||
assert_eq!(doc.with_design_unit("=5", length), "=5 in");
|
||||
assert_eq!(doc.with_design_unit("=5 mm", length), "=5 mm");
|
||||
let inches = doc.eval(&doc.with_design_unit("=5", length), length).unwrap();
|
||||
assert!((inches - 127.0).abs() < 1e-9, "{inches}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_written_value_reads_back_within_a_micrometre() {
|
||||
for units in ["mm", "cm", "m", "in", "ft"] {
|
||||
|
||||
@@ -152,6 +152,27 @@ pub enum NameError {
|
||||
},
|
||||
}
|
||||
|
||||
/// Whether `text` can be written into a name whole.
|
||||
///
|
||||
/// A curve's id goes into the name of the faces the curve makes (`F2:side:<id>`), and a
|
||||
/// name reads some characters as its own: `|` separates the faces of an edge, `,` the faces
|
||||
/// around a piece, `~` numbers the edges between two faces, `#` numbers a piece and `]`
|
||||
/// closes one. An id holding one of them splits every name it lands in: the id `c|1` gave
|
||||
/// the edges of an extrusion the names `F2:side:c|1#1|F2:side:c|1#2~1`, three separators
|
||||
/// where an edge names two faces and separates them once (#5858).
|
||||
///
|
||||
/// The test is the name's own round trip rather than a list of characters: the text is
|
||||
/// written into a name and read back, and it must read back as itself. A list would have
|
||||
/// to follow every change of the grammar (#5861), and would miss an id that reads back as
|
||||
/// something else (`s1.l4` reads as profile 1 of curve `l4`).
|
||||
pub fn text_fits_name(text: &str) -> bool {
|
||||
let name = format!("F0:side:{text}");
|
||||
match FaceName::parse(&name) {
|
||||
Ok(f) => f.to_string() == name && matches!(&f.base, Base::Made { role: Role::Side { profile: None, curve }, .. } if curve == text),
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
impl FaceName {
|
||||
/// The feature that made the face: its base's, a copy's when it is one.
|
||||
pub fn feature(&self) -> Option<FeatureId> {
|
||||
|
||||
@@ -236,3 +236,15 @@ fn hostile_names_never_panic() {
|
||||
// A fillet on an open boundary's edge.
|
||||
assert!(matches!(face("F1:blend:F10:start").base, Base::Blend { rounds: Rounds::Edge(ref e), .. } if e.faces.len() == 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_text_that_cannot_be_written_into_a_name_whole_is_told() {
|
||||
// A curve's id goes into the name of the faces the curve makes, so it has to come
|
||||
// back out of the name as itself (#5858).
|
||||
for good in ["c1", "l4", "s01.l4", "c-1", "c_1", "a.b", "x"] {
|
||||
assert!(text_fits_name(good), "`{good}` fits a name");
|
||||
}
|
||||
for bad in ["c|1", "c,1", "c~1", "c]1", "c#1", "", "s1.l4"] {
|
||||
assert!(!text_fits_name(bad), "`{bad}` does not fit a name");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +127,7 @@ fn same_geometry_inputs(a: &Document, b: &Document) -> bool {
|
||||
text_params,
|
||||
next_component_color_index,
|
||||
materials,
|
||||
print_faces,
|
||||
user_materials,
|
||||
appearances,
|
||||
body_components,
|
||||
@@ -162,6 +163,7 @@ fn same_geometry_inputs(a: &Document, b: &Document) -> bool {
|
||||
&& *text_params == b.text_params
|
||||
&& *next_component_color_index == b.next_component_color_index
|
||||
&& *materials == b.materials
|
||||
&& *print_faces == b.print_faces
|
||||
&& *user_materials == b.user_materials
|
||||
&& *appearances == b.appearances
|
||||
&& *body_components == b.body_components
|
||||
|
||||
@@ -11,6 +11,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use solidcraft_geom::Vec3;
|
||||
|
||||
use crate::canvas::{base64_decode, base64_encode};
|
||||
pub use crate::point_cloud_complex::{Column, Complex, Filter, Floats, Keep, MAX_COLUMNS, MAX_NAMES, Names};
|
||||
use crate::{DocError, IDENTITY, Mat, Result};
|
||||
|
||||
/// Most points one cloud may hold.
|
||||
@@ -19,6 +20,10 @@ pub const MAX_POINTS: usize = 20_000_000;
|
||||
pub const MAX_COORD: f64 = 1e9;
|
||||
/// Point sizes on screen (pixels).
|
||||
pub const POINT_SIZE: std::ops::RangeInclusive<f64> = 0.5..=16.0;
|
||||
/// Most filters on one cloud.
|
||||
pub const MAX_FILTERS: usize = 8;
|
||||
/// Properties every cloud has besides its file's: the coordinates in the design.
|
||||
pub const COORDINATES: [&str; 3] = ["x", "y", "z"];
|
||||
/// Length units a point cloud file may be in, and millimetres per unit.
|
||||
pub const UNITS: [(&str, f64); 5] = [("mm", 1.0), ("cm", 10.0), ("m", 1000.0), ("in", 25.4), ("ft", 304.8)];
|
||||
|
||||
@@ -65,6 +70,24 @@ pub struct PointCloud {
|
||||
pub height_range: Option<[f64; 2]>,
|
||||
#[serde(default = "yes")]
|
||||
pub visible: bool,
|
||||
/// The file's other properties of the points.
|
||||
#[serde(default, skip_serializing_if = "Complex::is_empty")]
|
||||
pub complex: Complex,
|
||||
/// The property `color_by: property` colours by.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub color_property: Option<String>,
|
||||
/// Only the points every filter keeps are shown.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub filters: Vec<Filter>,
|
||||
}
|
||||
|
||||
/// Where a property's values come from.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Source<'a> {
|
||||
/// A coordinate of each point in the design.
|
||||
Coordinate(usize),
|
||||
/// A column of the file: each point's.
|
||||
Column(&'a [f32]),
|
||||
}
|
||||
|
||||
/// How a cloud's points are coloured.
|
||||
@@ -78,6 +101,8 @@ pub enum ColorBy {
|
||||
Single,
|
||||
/// By height in the design: blue low, through cyan, green and yellow, to red high.
|
||||
Height,
|
||||
/// By `color_property`: the height ramp over its range or, for whole numbers, a palette.
|
||||
Property,
|
||||
}
|
||||
|
||||
impl ColorBy {
|
||||
@@ -90,6 +115,7 @@ impl ColorBy {
|
||||
"file" => Some(ColorBy::File),
|
||||
"single" => Some(ColorBy::Single),
|
||||
"height" => Some(ColorBy::Height),
|
||||
"property" => Some(ColorBy::Property),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -98,6 +124,7 @@ impl ColorBy {
|
||||
ColorBy::File => "file",
|
||||
ColorBy::Single => "single",
|
||||
ColorBy::Height => "height",
|
||||
ColorBy::Property => "property",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,6 +147,27 @@ pub fn height_ramp(t: f64) -> [u8; 3] {
|
||||
std::array::from_fn(|c| (a[c] * (1.0 - f) + b[c] * f).round().clamp(0.0, 255.0) as u8)
|
||||
}
|
||||
|
||||
/// Colours of whole-number labels, the label modulo their count; negative labels are grey.
|
||||
pub const PALETTE: [[u8; 3]; 10] = [
|
||||
[240, 140, 40],
|
||||
[245, 205, 65],
|
||||
[230, 65, 65],
|
||||
[165, 75, 190],
|
||||
[65, 190, 115],
|
||||
[75, 150, 220],
|
||||
[240, 120, 180],
|
||||
[140, 105, 75],
|
||||
[120, 215, 215],
|
||||
[190, 190, 80],
|
||||
];
|
||||
|
||||
pub fn label_color(v: f64) -> [u8; 3] {
|
||||
if !(v.is_finite() && v >= 0.0) {
|
||||
return PLAIN.map(byte);
|
||||
}
|
||||
PALETTE[(v.round() as usize) % PALETTE.len()]
|
||||
}
|
||||
|
||||
/// A colour channel (0..1) as a byte.
|
||||
pub fn byte(v: f32) -> u8 {
|
||||
(v.clamp(0.0, 1.0) * 255.0).round() as u8
|
||||
@@ -182,15 +230,57 @@ impl PointCloud {
|
||||
let (lo, hi) = (at(HEIGHT_TAIL)?, at(1.0 - HEIGHT_TAIL)?);
|
||||
Some(if hi - lo > 1e-9 { [lo, hi] } else { [lo - 0.5, lo + 0.5] })
|
||||
}
|
||||
/// The property colours come from: a column, or a coordinate (`z` for the height ramp).
|
||||
pub fn colored_property(&self) -> Option<&str> {
|
||||
match self.color_by {
|
||||
ColorBy::Height => Some("z"),
|
||||
ColorBy::Property => self.color_property.as_deref(),
|
||||
ColorBy::File | ColorBy::Single => None,
|
||||
}
|
||||
}
|
||||
/// Where the values of `name` are: a coordinate of each point or a column of the file.
|
||||
pub fn source(&self, name: &str) -> Option<Source<'_>> {
|
||||
if let Some(k) = COORDINATES.iter().position(|n| *n == name) {
|
||||
return Some(Source::Coordinate(k));
|
||||
}
|
||||
self.complex.column(name).map(|col| Source::Column(&col.values.0))
|
||||
}
|
||||
/// Every property the cloud can be coloured and filtered by: its coordinates, then its columns.
|
||||
pub fn property_names(&self) -> impl Iterator<Item = &str> {
|
||||
COORDINATES.into_iter().chain(self.complex.columns.iter().map(|c| c.name.as_str()))
|
||||
}
|
||||
/// The smallest and largest value of a property, the coordinates in the design.
|
||||
pub fn span(&self, property: &str) -> Option<[f64; 2]> {
|
||||
if let Some(span) = self.complex.span(property) {
|
||||
return Some(span);
|
||||
}
|
||||
let k = COORDINATES.iter().position(|n| *n == property)?;
|
||||
let v = self.world_corners()?.map(|p| [p.x, p.y, p.z][k]);
|
||||
Some([v.iter().copied().fold(f64::INFINITY, f64::min), v.iter().copied().fold(f64::NEG_INFINITY, f64::max)])
|
||||
}
|
||||
/// The ramp's range as far as it can be told without the elements: the one set, or else the
|
||||
/// coloured property's span within every range filtering it. The automatic range colouring
|
||||
/// by a property is exactly what the filters keep
|
||||
/// ([`crate::point_cloud_distribution::color_range`]), which this bounds.
|
||||
pub fn color_range(&self) -> Option<[f64; 2]> {
|
||||
if self.height_range.is_some() {
|
||||
return self.height_range;
|
||||
}
|
||||
let property = self.colored_property()?;
|
||||
let span = if self.color_by == ColorBy::Height { self.auto_height_range() } else { self.span(property) }?;
|
||||
let shown =
|
||||
self.filters.iter().filter(|f| f.property == property).filter_map(Filter::range).fold(span, |[lo, hi], [a, b]| [lo.max(a), hi.min(b)]);
|
||||
Some(if shown[0] <= shown[1] { crate::point_cloud_distribution::ramp_range(shown) } else { span })
|
||||
}
|
||||
/// The heights the height ramp runs between now: the cloud's own, else the automatic ones.
|
||||
pub fn height_range_now(&self) -> Option<[f64; 2]> {
|
||||
self.height_range.or_else(|| self.auto_height_range())
|
||||
}
|
||||
/// The colour of each point (by index) as the cloud is shown.
|
||||
pub fn shade(&self) -> impl Fn(usize) -> [u8; 3] + '_ {
|
||||
/// The colour of each point (by index) as the cloud is shown, the ramp over `range`.
|
||||
pub fn shade(&self, range: Option<[f64; 2]>) -> impl Fn(usize) -> [u8; 3] + '_ {
|
||||
let single = self.color.unwrap_or(PLAIN).map(byte);
|
||||
let plain = PLAIN.map(byte);
|
||||
let range = if self.color_by == ColorBy::Height { self.height_range_now() } else { None };
|
||||
let column = self.color_property.as_deref().and_then(|n| self.complex.column(n));
|
||||
move |i| match self.color_by {
|
||||
ColorBy::File => self.colors.get(i).unwrap_or(plain),
|
||||
ColorBy::Single => single,
|
||||
@@ -198,6 +288,14 @@ impl PointCloud {
|
||||
(Some([lo, hi]), Some(p)) => height_ramp((self.world_z(p) - lo) / (hi - lo)),
|
||||
_ => plain,
|
||||
},
|
||||
ColorBy::Property => match (column, column.and_then(|c| c.values.0.get(i))) {
|
||||
(Some(c), Some(v)) if c.integer => label_color(f64::from(*v)),
|
||||
(Some(_), Some(v)) => match range {
|
||||
Some([lo, hi]) if hi > lo => height_ramp((f64::from(*v) - lo) / (hi - lo)),
|
||||
_ => plain,
|
||||
},
|
||||
_ => plain,
|
||||
},
|
||||
}
|
||||
}
|
||||
/// The corners of the box around the points, placed in the design (mm).
|
||||
@@ -240,6 +338,18 @@ impl PointCloud {
|
||||
if !rigid(&self.transform) {
|
||||
return bad("its placement is not a rigid move");
|
||||
}
|
||||
self.complex.check(self.points.len())?;
|
||||
if self.filters.len() > MAX_FILTERS
|
||||
|| self.filters.iter().any(|f| match &f.keep {
|
||||
Keep::Range { range: [lo, hi] } => !(lo.is_finite() && hi.is_finite() && lo <= hi),
|
||||
Keep::Values { values } => values.len() > MAX_NAMES,
|
||||
})
|
||||
{
|
||||
return bad("its filters are not ranges, lowest first");
|
||||
}
|
||||
if self.color_by == ColorBy::Property && self.color_property.is_none() {
|
||||
return bad("it is coloured by a property without naming it");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -422,6 +532,9 @@ mod tests {
|
||||
color: None,
|
||||
height_range: None,
|
||||
visible: true,
|
||||
complex: Complex::default(),
|
||||
color_property: None,
|
||||
filters: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,6 +556,20 @@ mod tests {
|
||||
assert_eq!((unit_scale("M"), unit_scale("yd")), (Some(1000.0), None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_ramp_spans_what_is_shown_unless_set() {
|
||||
let mut c = cloud(vec![0.0; 9], Vec::new());
|
||||
c.complex.columns.push(Column { name: "t".into(), integer: false, values: Floats(vec![0.0, 10.0, 20.0].into()) });
|
||||
(c.color_by, c.color_property) = (ColorBy::Property, Some("t".into()));
|
||||
assert_eq!(c.color_range(), Some([0.0, 20.0]), "the whole span");
|
||||
c.filters = vec![Filter { property: "t".into(), keep: Keep::Range { range: [5.0, 50.0] } }];
|
||||
assert_eq!(c.color_range(), Some([5.0, 20.0]), "the span within the filter");
|
||||
c.filters.push(Filter { property: "layer".into(), keep: Keep::Range { range: [0.0, 1.0] } });
|
||||
assert_eq!(c.color_range(), Some([5.0, 20.0]), "another property's filter changes nothing");
|
||||
c.height_range = Some([1.0, 2.0]);
|
||||
assert_eq!(c.color_range(), Some([1.0, 2.0]), "a range set is kept");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn damaged_clouds_are_refused() {
|
||||
// Not whole points, not finite, not base64.
|
||||
@@ -474,26 +601,26 @@ mod tests {
|
||||
let colors: Vec<u8> = (0..102).flat_map(|i| [i as u8, 7, 9]).collect();
|
||||
let mut c = cloud(values, colors);
|
||||
c.origin = Vec3::ZERO;
|
||||
assert_eq!(c.shade()(3), [3, 7, 9], "the file's colours");
|
||||
assert_eq!(c.shade(c.color_range())(3), [3, 7, 9], "the file's colours");
|
||||
c.colors = Colors::default();
|
||||
assert_eq!(c.shade()(3), PLAIN.map(byte), "none: plain");
|
||||
assert_eq!(c.shade(c.color_range())(3), PLAIN.map(byte), "none: plain");
|
||||
c.color_by = ColorBy::Single;
|
||||
c.color = Some([1.0, 0.5, 0.0]);
|
||||
assert_eq!(c.shade()(50), [255, 128, 0]);
|
||||
assert_eq!(c.shade(c.color_range())(50), [255, 128, 0]);
|
||||
// The stray point doesn't stretch the automatic range.
|
||||
c.color_by = ColorBy::Height;
|
||||
// 102 points: the 2nd and the 99th (of 0…101) once sorted.
|
||||
assert_eq!(c.auto_height_range(), Some([2.0, 99.0]));
|
||||
{
|
||||
let shade = c.shade();
|
||||
let shade = c.shade(c.color_range());
|
||||
assert_eq!((shade(0), shade(100), shade(101)), (height_ramp(0.0), height_ramp(1.0), height_ramp(1.0)));
|
||||
assert_eq!(shade(50), height_ramp(48.0 / 97.0));
|
||||
}
|
||||
// A range of its own; moved up 100 mm, everything is above it.
|
||||
c.height_range = Some([0.0, 50.0]);
|
||||
assert_eq!(c.shade()(25), height_ramp(0.5));
|
||||
assert_eq!(c.shade(c.color_range())(25), height_ramp(0.5));
|
||||
c.transform[3][2] = 100.0;
|
||||
assert_eq!(c.shade()(0), height_ramp(1.0));
|
||||
assert_eq!(c.shade(c.color_range())(0), height_ramp(1.0));
|
||||
assert_eq!((height_ramp(0.0), height_ramp(1.0), height_ramp(f64::NAN)), ([40, 70, 200], [230, 70, 50], [40, 70, 200]));
|
||||
let back: PointCloud = serde_json::from_str(&serde_json::to_string(&c).unwrap()).unwrap();
|
||||
assert_eq!(back, c);
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
//! What a point cloud holds besides its points' places and colours: named numbers on its points
|
||||
//! (a PLY file's other vertex properties), to colour the cloud by and to filter it with. Whole
|
||||
//! numbers may have names (`role: 0 outer wall, 1 inner wall` in a PLY file's comments): they
|
||||
//! are labels, filtered by a set of values rather than a range.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use crate::canvas::{base64_decode, base64_encode};
|
||||
use crate::point_cloud::MAX_POINTS;
|
||||
use crate::{DocError, Result};
|
||||
|
||||
/// Most named numbers per point.
|
||||
pub const MAX_COLUMNS: usize = 64;
|
||||
|
||||
/// One named number on every point.
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Column {
|
||||
pub name: String,
|
||||
/// Whole numbers (labels, counts): coloured by a palette rather than a ramp.
|
||||
#[serde(default)]
|
||||
pub integer: bool,
|
||||
pub values: Floats,
|
||||
}
|
||||
|
||||
impl Column {
|
||||
/// The smallest and largest finite value.
|
||||
pub fn span(&self) -> Option<[f64; 2]> {
|
||||
let mut it = self.values.0.iter().filter(|v| v.is_finite()).map(|v| f64::from(*v));
|
||||
let first = it.next()?;
|
||||
Some(it.fold([first, first], |[lo, hi], v| [lo.min(v), hi.max(v)]))
|
||||
}
|
||||
}
|
||||
|
||||
/// The complex a cloud's points form: named numbers on the points, and the names of
|
||||
/// whole-numbered ones. Points alone are a complex of dimension 0.
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Complex {
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub columns: Vec<Column>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub names: Vec<Names>,
|
||||
}
|
||||
|
||||
/// Most named values of one label.
|
||||
pub const MAX_NAMES: usize = 256;
|
||||
|
||||
/// The names of a label's values.
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Names {
|
||||
pub property: String,
|
||||
pub values: Vec<(i32, String)>,
|
||||
}
|
||||
|
||||
impl Names {
|
||||
/// `property: 0 name, 1 another name, -1 none`, or `None` for any other text.
|
||||
pub fn parse(text: &str) -> Option<Names> {
|
||||
let (property, rest) = text.split_once(':')?;
|
||||
let property = property.trim();
|
||||
if property.is_empty() || property.contains(char::is_whitespace) {
|
||||
return None;
|
||||
}
|
||||
let values = rest
|
||||
.split(',')
|
||||
.map(|entry| {
|
||||
let (value, name) = entry.trim().split_once(' ')?;
|
||||
let name = name.trim();
|
||||
(!name.is_empty()).then(|| Some((value.parse::<i32>().ok()?, name.to_string()))).flatten()
|
||||
})
|
||||
.collect::<Option<Vec<_>>>()?;
|
||||
(!values.is_empty() && values.len() <= MAX_NAMES).then(|| Names { property: property.to_string(), values })
|
||||
}
|
||||
pub fn name(&self, value: i32) -> Option<&str> {
|
||||
self.values.iter().find(|(v, _)| *v == value).map(|(_, n)| n.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl Complex {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.columns.is_empty() && self.names.is_empty()
|
||||
}
|
||||
/// The column named `name`.
|
||||
pub fn column(&self, name: &str) -> Option<&Column> {
|
||||
self.columns.iter().find(|c| c.name == name)
|
||||
}
|
||||
/// The smallest and largest value named `name`.
|
||||
pub fn span(&self, name: &str) -> Option<[f64; 2]> {
|
||||
self.column(name).and_then(Column::span)
|
||||
}
|
||||
/// The names of `property`'s values, when it is a label.
|
||||
pub fn names(&self, property: &str) -> Option<&Names> {
|
||||
self.names.iter().find(|n| n.property == property).filter(|_| self.integer(property))
|
||||
}
|
||||
/// Whether the values named `name` are whole numbers.
|
||||
pub fn integer(&self, name: &str) -> bool {
|
||||
self.column(name).is_some_and(|c| c.integer)
|
||||
}
|
||||
/// Every column has one number per point, the cloud having `points` points.
|
||||
pub fn check(&self, points: usize) -> Result<()> {
|
||||
let bad = |what: String| Err(DocError::Invalid(format!("point cloud properties: {what}")));
|
||||
if self.columns.len() > MAX_COLUMNS {
|
||||
return bad("too many properties".into());
|
||||
}
|
||||
for c in &self.columns {
|
||||
if c.values.0.len() != points {
|
||||
return bad(format!("property `{}` does not have one number per point", c.name));
|
||||
}
|
||||
}
|
||||
if self.names.len() > MAX_COLUMNS || self.names.iter().any(|n| n.values.is_empty() || n.values.len() > MAX_NAMES) {
|
||||
return bad("too many named values".into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Which points are shown: those whose property lies in a range (both ends included), or, for a
|
||||
/// label, is one of a set of values.
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Filter {
|
||||
pub property: String,
|
||||
#[serde(flatten)]
|
||||
pub keep: Keep,
|
||||
}
|
||||
|
||||
/// The values a filter keeps.
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Keep {
|
||||
Range { range: [f64; 2] },
|
||||
Values { values: Vec<i32> },
|
||||
}
|
||||
|
||||
impl Filter {
|
||||
pub fn range(&self) -> Option<[f64; 2]> {
|
||||
match &self.keep {
|
||||
Keep::Range { range } => Some(*range),
|
||||
Keep::Values { .. } => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Numbers, shared between copies of the design.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Floats(pub Arc<[f32]>);
|
||||
|
||||
impl PartialEq for Floats {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
Arc::ptr_eq(&self.0, &other.0) || self.0.iter().map(|v| v.to_bits()).eq(other.0.iter().map(|v| v.to_bits()))
|
||||
}
|
||||
}
|
||||
|
||||
/// The largest base64 text of `n` four-byte words.
|
||||
fn base64_len(n: usize) -> usize {
|
||||
(n * 4).div_ceil(3) * 4 + 16
|
||||
}
|
||||
|
||||
fn words<'de, D: Deserializer<'de>>(d: D, max: usize) -> std::result::Result<Vec<[u8; 4]>, D::Error> {
|
||||
let s = String::deserialize(d)?;
|
||||
if s.len() > base64_len(max) {
|
||||
return Err(serde::de::Error::custom("too many numbers"));
|
||||
}
|
||||
let b = base64_decode(&s).ok_or_else(|| serde::de::Error::custom("numbers are not base64"))?;
|
||||
if !b.len().is_multiple_of(4) {
|
||||
return Err(serde::de::Error::custom("numbers are not whole four-byte words"));
|
||||
}
|
||||
Ok(b.as_chunks::<4>().0.to_vec())
|
||||
}
|
||||
|
||||
impl Serialize for Floats {
|
||||
fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
|
||||
s.serialize_str(&base64_encode(&self.0.iter().flat_map(|v| v.to_le_bytes()).collect::<Vec<u8>>()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Floats {
|
||||
fn deserialize<D: Deserializer<'de>>(d: D) -> std::result::Result<Self, D::Error> {
|
||||
Ok(Floats(words(d, MAX_POINTS)?.into_iter().map(f32::from_le_bytes).collect()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn properties_round_trip_and_are_checked_against_the_points() {
|
||||
let complex = Complex {
|
||||
columns: vec![
|
||||
Column { name: "t".into(), integer: false, values: Floats(vec![0.0, 0.5, 1.0].into()) },
|
||||
Column { name: "role".into(), integer: true, values: Floats(vec![0.0, 4.0, 4.0].into()) },
|
||||
],
|
||||
names: Vec::new(),
|
||||
};
|
||||
let back: Complex = serde_json::from_str(&serde_json::to_string(&complex).unwrap()).unwrap();
|
||||
assert_eq!(back, complex);
|
||||
complex.check(3).unwrap();
|
||||
assert!(complex.check(2).is_err(), "three numbers for two points");
|
||||
assert_eq!(complex.span("role"), Some([0.0, 4.0]));
|
||||
assert!(complex.integer("role") && !complex.integer("t") && !complex.integer("nope"));
|
||||
// Named values make a label, and a filter keeps a range or a set of them.
|
||||
let names = Names::parse("role: 0 outer wall, 4 bridge, -1 none").unwrap();
|
||||
assert_eq!((names.name(4), names.name(-1), names.name(2)), (Some("bridge"), Some("none"), None));
|
||||
for text in ["t: seconds since the start", "role 0 wall", ": 0 a", "role: x wall", "role: 0 "] {
|
||||
assert!(Names::parse(text).is_none(), "{text}");
|
||||
}
|
||||
let mut named = complex.clone();
|
||||
named.names = vec![names, Names::parse("t: 0 zero").unwrap()];
|
||||
assert!(named.names("role").is_some() && named.names("t").is_none(), "only whole numbers are labels");
|
||||
let set: Filter = serde_json::from_str(r#"{"property": "role", "values": [0, 4]}"#).unwrap();
|
||||
let range: Filter = serde_json::from_str(r#"{"property": "t", "range": [1, 3]}"#).unwrap();
|
||||
assert_eq!((set.range(), range.range()), (None, Some([1.0, 3.0])));
|
||||
assert_eq!(serde_json::to_value(&set).unwrap(), serde_json::json!({"property": "role", "values": [0, 4]}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
//! How much of a point cloud lies at each value of one of its properties, over what its filters
|
||||
//! keep: the histograms the Edit Point Cloud card draws in its range tracks, split into the
|
||||
//! colours the view draws, and the values where a label changes along a property, which the
|
||||
//! tracks snap to.
|
||||
//!
|
||||
//! Each point weighs the same, the cloud weighing 1. A range filter keeps the points between its
|
||||
//! ends, as the view does; a filter by values keeps those whose rounded value it holds; the
|
||||
//! colour sorts the points kept into the colours they are drawn in. The values the points kept
|
||||
//! take, from the least to the greatest, are the ramp's automatic range: the colours span exactly
|
||||
//! what is shown.
|
||||
|
||||
use crate::point_cloud::{ColorBy, Filter, Keep, PALETTE, PointCloud, Source};
|
||||
|
||||
/// Most bins of one histogram.
|
||||
pub const MAX_BINS: usize = 8192;
|
||||
|
||||
/// What the histograms need of a cloud's geometry, worked out once for its points.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Geometry {
|
||||
/// Each point in the design.
|
||||
world: Vec<[f64; 3]>,
|
||||
}
|
||||
|
||||
impl Geometry {
|
||||
pub fn of(c: &PointCloud) -> Geometry {
|
||||
Geometry { world: (0..c.len()).map(|i| c.world_point(i).map_or([0.0; 3], |p| [p.x, p.y, p.z])).collect() }
|
||||
}
|
||||
}
|
||||
|
||||
/// How a histogram is split by colour, into slots; the last slot holds what has no value to be
|
||||
/// coloured by.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Colour<'a> {
|
||||
/// One colour.
|
||||
One,
|
||||
/// The ramp over `range` of `property`, in `bands` equal bands; below and above the range
|
||||
/// count with the first and the last.
|
||||
Ramp { property: &'a str, range: [f64; 2], bands: usize },
|
||||
/// The palette of a whole-numbered `property`: a slot per colour of [`PALETTE`], then one
|
||||
/// for negative values.
|
||||
Palette { property: &'a str },
|
||||
}
|
||||
|
||||
impl Colour<'_> {
|
||||
/// How many slots the histogram has.
|
||||
pub fn slots(&self) -> usize {
|
||||
match self {
|
||||
Colour::One => 1,
|
||||
Colour::Ramp { bands, .. } => bands + 1,
|
||||
Colour::Palette { .. } => PALETTE.len() + 2,
|
||||
}
|
||||
}
|
||||
fn property(&self) -> Option<&str> {
|
||||
match self {
|
||||
Colour::One => None,
|
||||
Colour::Ramp { property, .. } | Colour::Palette { property } => Some(property),
|
||||
}
|
||||
}
|
||||
/// How a property's values are cut into bands, and the slot of each band.
|
||||
fn cut(&self) -> Option<Cut> {
|
||||
match *self {
|
||||
Colour::One => None,
|
||||
Colour::Ramp { range: [lo, hi], bands, .. } => Some(Cut::Grid { start: lo, step: (hi - lo) / bands as f64, count: bands + 1 }),
|
||||
Colour::Palette { .. } => Some(INTEGERS),
|
||||
}
|
||||
}
|
||||
fn slot(&self, band: usize) -> usize {
|
||||
match *self {
|
||||
Colour::One => 0,
|
||||
Colour::Ramp { bands, .. } => band.saturating_sub(1).min(bands.saturating_sub(1)),
|
||||
Colour::Palette { .. } => match integer(band) {
|
||||
Some(v) if v >= 0 => v as usize % PALETTE.len(),
|
||||
_ => PALETTE.len(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// How much lies in each bin, by colour slot.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Histogram {
|
||||
pub bins: usize,
|
||||
/// `slots[s][b]`: the measure of slot `s` in bin `b`.
|
||||
pub slots: Vec<Vec<f64>>,
|
||||
/// The least and the greatest value the points kept take, within the axis or not.
|
||||
pub extent: Option<[f64; 2]>,
|
||||
}
|
||||
|
||||
/// Where values are cut into bands, by increasing values.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum Cut {
|
||||
/// Below (band 0), within (1, both ends included) and above (2) a range.
|
||||
Range([f64; 2]),
|
||||
/// `count` values `start + k·step`: band 0 lies below the first, band `k` from the `k`th on.
|
||||
Grid { start: f64, step: f64, count: usize },
|
||||
}
|
||||
|
||||
/// The bands of whole numbers: band `k` holds the values rounding to `k - 2³¹`.
|
||||
const INTEGERS: Cut = Cut::Grid { start: 0.5 - 2_147_483_648.0, step: 1.0, count: u32::MAX as usize };
|
||||
|
||||
/// The whole number a band of [`INTEGERS`] holds.
|
||||
fn integer(band: usize) -> Option<i64> {
|
||||
i64::try_from(band).ok().map(|k| k - 2_147_483_648)
|
||||
}
|
||||
|
||||
impl Cut {
|
||||
fn band(&self, v: f64) -> usize {
|
||||
match *self {
|
||||
Cut::Range([a, b]) => usize::from(v >= a) + usize::from(v > b),
|
||||
Cut::Grid { start, step, count } => {
|
||||
if !(v >= start) {
|
||||
return 0;
|
||||
}
|
||||
(((v - start) / step).floor() as usize).saturating_add(1).min(count)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A filter as a cut and the bands it keeps.
|
||||
struct Kept<'a> {
|
||||
cut: Cut,
|
||||
values: Option<&'a [i32]>,
|
||||
}
|
||||
|
||||
impl Kept<'_> {
|
||||
fn of(f: &Filter) -> Kept<'_> {
|
||||
match &f.keep {
|
||||
Keep::Range { range } => Kept { cut: Cut::Range(*range), values: None },
|
||||
Keep::Values { values } => Kept { cut: INTEGERS, values: Some(values) },
|
||||
}
|
||||
}
|
||||
fn keeps(&self, v: f64) -> bool {
|
||||
let band = self.cut.band(v);
|
||||
match self.values {
|
||||
None => band == 1,
|
||||
Some(values) => integer(band).is_some_and(|v| values.iter().any(|k| i64::from(*k) == v)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The value of `s` at point `i`.
|
||||
fn value(s: Source, i: usize, world: &[[f64; 3]]) -> f64 {
|
||||
match s {
|
||||
Source::Coordinate(k) => world.get(i).and_then(|p| p.get(k)).copied().unwrap_or(f64::NAN),
|
||||
Source::Column(v) => v.get(i).map_or(f64::NAN, |x| f64::from(*x)),
|
||||
}
|
||||
}
|
||||
|
||||
/// How much of the cloud lies in each of `bins` bins over `axis` of `property`, by `colour`,
|
||||
/// within every one of `filters`; `None` for an axis or a number of bins it can't have.
|
||||
pub fn histogram(
|
||||
c: &PointCloud,
|
||||
g: &Geometry,
|
||||
property: &str,
|
||||
axis: [f64; 2],
|
||||
bins: usize,
|
||||
colour: &Colour,
|
||||
filters: &[Filter],
|
||||
) -> Option<Histogram> {
|
||||
if !(axis[0].is_finite() && axis[1].is_finite() && axis[1] > axis[0]) || bins == 0 || bins > MAX_BINS {
|
||||
return None;
|
||||
}
|
||||
if let Colour::Ramp { range: [lo, hi], bands, .. } = *colour
|
||||
&& !(hi > lo && bands > 0 && bands <= MAX_BINS)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let slots = colour.slots();
|
||||
let mut out = vec![vec![0.0; bins]; slots];
|
||||
let mut extent = None;
|
||||
let Some(p) = c.source(property) else { return Some(Histogram { bins, slots: out, extent }) };
|
||||
// A point is not filtered by a property the cloud doesn't have, as in the view.
|
||||
let kept: Vec<(Kept, Source)> = filters.iter().filter_map(|f| Some((Kept::of(f), c.source(&f.property)?))).collect();
|
||||
let coloured = colour.property().and_then(|n| c.source(n));
|
||||
let (colour_cut, plain) = (colour.cut(), slots - 1);
|
||||
let m = 1.0 / g.world.len().max(1) as f64;
|
||||
let width = (axis[1] - axis[0]) / bins as f64;
|
||||
for i in 0..g.world.len() {
|
||||
if !kept.iter().all(|(k, s)| k.keeps(value(*s, i, &g.world))) {
|
||||
continue;
|
||||
}
|
||||
let v = value(p, i, &g.world);
|
||||
if !v.is_finite() {
|
||||
continue;
|
||||
}
|
||||
extent = Some(extent.map_or([v, v], |[lo, hi]: [f64; 2]| [lo.min(v), hi.max(v)]));
|
||||
if !(v >= axis[0] && v <= axis[1]) {
|
||||
continue;
|
||||
}
|
||||
let slot = match (colour_cut, coloured) {
|
||||
(Some(cut), Some(s)) => colour.slot(cut.band(value(s, i, &g.world))),
|
||||
(Some(_), None) => plain,
|
||||
(None, _) => 0,
|
||||
};
|
||||
// The last bin holds the axis' upper end too.
|
||||
let bin = (((v - axis[0]) / width).floor() as usize).min(bins - 1);
|
||||
if let Some(b) = out.get_mut(slot).and_then(|s| s.get_mut(bin)) {
|
||||
*b += m;
|
||||
}
|
||||
}
|
||||
Some(Histogram { bins, slots: out, extent })
|
||||
}
|
||||
|
||||
/// A range for a ramp from the values it spans: a single value gets one unit around it.
|
||||
pub fn ramp_range([lo, hi]: [f64; 2]) -> [f64; 2] {
|
||||
if hi > lo { [lo, hi] } else { [lo - 0.5, lo + 0.5] }
|
||||
}
|
||||
|
||||
/// The ramp's range: the one set; else, coloured by a property, exactly the values it takes over
|
||||
/// what `filters` keep (the cloud's own bound, [`PointCloud::color_range`], when nothing is
|
||||
/// kept); coloured by height, the cloud's own (its points but the highest and lowest few).
|
||||
pub fn color_range(c: &PointCloud, g: &Geometry, filters: &[Filter]) -> Option<[f64; 2]> {
|
||||
color_range_by(c, |property, axis| histogram(c, g, property, axis, 1, &Colour::One, filters).and_then(|h| h.extent))
|
||||
}
|
||||
|
||||
/// [`color_range`], with the values the coloured property takes over what is kept given by
|
||||
/// `extent` (of the property and an axis over its span): worked out elsewhere, or not yet.
|
||||
pub fn color_range_by(c: &PointCloud, extent: impl FnOnce(&str, [f64; 2]) -> Option<[f64; 2]>) -> Option<[f64; 2]> {
|
||||
let property = c.colored_property()?;
|
||||
if c.height_range.is_some() || c.color_by != ColorBy::Property {
|
||||
return c.color_range();
|
||||
}
|
||||
let axis = ramp_range(c.span(property)?);
|
||||
extent(property, axis).map(ramp_range).or_else(|| c.color_range())
|
||||
}
|
||||
|
||||
/// The values of `property` where the whole-numbered `label` changes, point after point in order
|
||||
/// of `property`, lowest first.
|
||||
pub fn label_changes(c: &PointCloud, g: &Geometry, label: &str, property: &str) -> Vec<f64> {
|
||||
let (Some(p), Some(l)) = (c.source(property), c.source(label)) else { return Vec::new() };
|
||||
let mut at: Vec<(f64, i64)> = (0..g.world.len())
|
||||
.filter_map(|i| {
|
||||
let (v, l) = (value(p, i, &g.world), value(l, i, &g.world));
|
||||
v.is_finite().then(|| (v, if l.is_finite() { l.round() as i64 } else { i64::MIN }))
|
||||
})
|
||||
.collect();
|
||||
at.sort_by(|a, b| a.0.total_cmp(&b.0));
|
||||
let mut out: Vec<f64> = at.windows(2).filter(|w| w[0].1 != w[1].1).map(|w| w[1].0).collect();
|
||||
out.dedup();
|
||||
out
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "point_cloud_distribution_tests.rs"]
|
||||
mod tests;
|
||||
@@ -0,0 +1,125 @@
|
||||
use solidcraft_geom::Vec3;
|
||||
|
||||
use super::*;
|
||||
use crate::IDENTITY;
|
||||
use crate::point_cloud::{ColorBy, Colors, Column, Complex, Floats, Points};
|
||||
|
||||
/// A cloud of `points` with `columns` on them.
|
||||
fn cloud(points: &[[f32; 3]], columns: Vec<Column>) -> PointCloud {
|
||||
PointCloud {
|
||||
id: 1,
|
||||
name: "c".into(),
|
||||
file: String::new(),
|
||||
origin: Vec3::new(0.0, 0.0, 0.0),
|
||||
points: Points::new(points.iter().flatten().copied().collect::<Vec<f32>>().into()),
|
||||
colors: Colors::default(),
|
||||
source_points: points.len() as u64,
|
||||
units: "mm".into(),
|
||||
transform: IDENTITY,
|
||||
point_size: 2.0,
|
||||
color_by: ColorBy::File,
|
||||
color: None,
|
||||
height_range: None,
|
||||
visible: true,
|
||||
complex: Complex { columns, names: Vec::new() },
|
||||
color_property: None,
|
||||
filters: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn column(name: &str, integer: bool, values: &[f32]) -> Column {
|
||||
Column { name: name.into(), integer, values: Floats(values.to_vec().into()) }
|
||||
}
|
||||
|
||||
fn range(property: &str, range: [f64; 2]) -> Filter {
|
||||
Filter { property: property.into(), keep: Keep::Range { range } }
|
||||
}
|
||||
|
||||
fn close(a: &[f64], b: &[f64]) -> bool {
|
||||
a.len() == b.len() && a.iter().zip(b).all(|(x, y)| (x - y).abs() < 1e-9)
|
||||
}
|
||||
|
||||
/// Ten points along x, `t` the same as x.
|
||||
fn row() -> PointCloud {
|
||||
let points: Vec<[f32; 3]> = (0..10).map(|i| [i as f32 + 0.5, 0.0, 0.0]).collect();
|
||||
let t: Vec<f32> = points.iter().map(|p| p[0]).collect();
|
||||
cloud(&points, vec![column("t", false, &t)])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn each_point_weighs_alike_and_a_range_keeps_those_within_it() {
|
||||
let c = row();
|
||||
let g = Geometry::of(&c);
|
||||
let all = histogram(&c, &g, "t", [0.0, 10.0], 5, &Colour::One, &[]).unwrap();
|
||||
assert!(close(&all.slots[0], &[0.2; 5]));
|
||||
// Kept from t = 2 to 5: three points, ends included.
|
||||
let kept = histogram(&c, &g, "t", [0.0, 10.0], 5, &Colour::One, &[range("t", [2.0, 5.0])]).unwrap();
|
||||
assert!(close(&kept.slots[0], &[0.0, 0.2, 0.1, 0.0, 0.0]));
|
||||
// Filtered by another property: x from 0 to 4 keeps t from 0 to 4.
|
||||
let by_x = histogram(&c, &g, "t", [0.0, 10.0], 5, &Colour::One, &[range("x", [0.0, 4.0])]).unwrap();
|
||||
assert!(close(&by_x.slots[0], &[0.2, 0.2, 0.0, 0.0, 0.0]));
|
||||
// A filter on a property the cloud doesn't have keeps everything, as in the view.
|
||||
assert_eq!(histogram(&c, &g, "t", [0.0, 10.0], 5, &Colour::One, &[range("nope", [0.0, 1.0])]), Some(all));
|
||||
// An axis or a number of bins it can't have.
|
||||
for (axis, bins) in [([1.0, 1.0], 4), ([0.0, f64::NAN], 4), ([0.0, 1.0], 0), ([0.0, 1.0], MAX_BINS + 1)] {
|
||||
assert!(histogram(&c, &g, "t", axis, bins, &Colour::One, &[]).is_none());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn colours_split_what_is_kept() {
|
||||
// Four points, roles 0, 3, 3 and -1, t from 0 to 3.
|
||||
let c = cloud(
|
||||
&[[0.0; 3], [1.0, 0.0, 0.0], [2.0, 0.0, 0.0], [3.0, 0.0, 0.0]],
|
||||
vec![column("t", false, &[0.0, 1.0, 2.0, 3.0]), column("role", true, &[0.0, 3.0, 3.0, -1.0])],
|
||||
);
|
||||
let g = Geometry::of(&c);
|
||||
let colour = Colour::Palette { property: "role" };
|
||||
let h = histogram(&c, &g, "t", [0.0, 4.0], 4, &colour, &[]).unwrap();
|
||||
assert_eq!(h.slots.len(), colour.slots());
|
||||
assert!(close(&h.slots[0], &[0.25, 0.0, 0.0, 0.0]));
|
||||
assert!(close(&h.slots[3], &[0.0, 0.25, 0.25, 0.0]));
|
||||
assert!(close(&h.slots[PALETTE.len()], &[0.0, 0.0, 0.0, 0.25]), "a negative label");
|
||||
// A filter by values keeps role 3 only.
|
||||
let three = Filter { property: "role".into(), keep: Keep::Values { values: vec![3] } };
|
||||
let kept = histogram(&c, &g, "t", [0.0, 4.0], 4, &colour, &[three]).unwrap();
|
||||
let total: f64 = kept.slots.iter().flatten().sum();
|
||||
assert!((total - 0.5).abs() < 1e-9 && close(&kept.slots[3], &h.slots[3]));
|
||||
// A ramp over t in two bands, and coloured by a property the cloud doesn't have: plain.
|
||||
let ramp = Colour::Ramp { property: "t", range: [0.0, 4.0], bands: 2 };
|
||||
let r = histogram(&c, &g, "t", [0.0, 4.0], 4, &ramp, &[]).unwrap();
|
||||
assert!(close(&r.slots[0], &[0.25, 0.25, 0.0, 0.0]) && close(&r.slots[1], &[0.0, 0.0, 0.25, 0.25]));
|
||||
let none = histogram(&c, &g, "t", [0.0, 4.0], 4, &Colour::Palette { property: "nope" }, &[]).unwrap();
|
||||
assert!(close(&none.slots[PALETTE.len() + 1], &[0.25; 4]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn labels_change_where_the_next_point_has_another() {
|
||||
// Six points in order of t, layers 0 0 1 1 2 2.
|
||||
let points: Vec<[f32; 3]> = (0..6).map(|i| [i as f32, 0.0, 0.0]).collect();
|
||||
let c = cloud(&points, vec![column("t", false, &[0.0, 1.0, 2.0, 3.0, 4.0, 5.0]), column("layer", true, &[0.0, 0.0, 1.0, 1.0, 2.0, 2.0])]);
|
||||
let g = Geometry::of(&c);
|
||||
assert_eq!(label_changes(&c, &g, "layer", "t"), vec![2.0, 4.0]);
|
||||
assert!(label_changes(&c, &g, "nope", "t").is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_automatic_range_is_what_every_filter_keeps() {
|
||||
// Filtered by x from 2 to 4, coloured by t: the range is what is kept of t, though no filter
|
||||
// is on t (its span within filters on t would be all of it).
|
||||
let mut c = cloud(&[[0.0, 0.0, 0.0], [2.0, 0.0, 0.0], [4.0, 0.0, 0.0], [20.0, 0.0, 0.0]], vec![column("t", false, &[0.0, 3.0, 5.0, 7.0])]);
|
||||
c.color_by = ColorBy::Property;
|
||||
c.color_property = Some("t".into());
|
||||
let g = Geometry::of(&c);
|
||||
let x = [range("x", [2.0, 4.0])];
|
||||
assert_eq!(c.color_range(), Some([0.0, 7.0]));
|
||||
assert_eq!(color_range(&c, &g, &x), Some([3.0, 5.0]));
|
||||
// The point at x = 20 alone: one value, one unit wide.
|
||||
assert_eq!(color_range(&c, &g, &[range("x", [19.0, 21.0])]), Some([6.5, 7.5]));
|
||||
// Nothing kept: the cloud's own bound; a range set is kept as it is.
|
||||
assert_eq!(color_range(&c, &g, &[range("x", [50.0, 60.0])]), Some([0.0, 7.0]));
|
||||
c.height_range = Some([1.0, 2.0]);
|
||||
assert_eq!(color_range(&c, &g, &x), Some([1.0, 2.0]));
|
||||
let h = histogram(&c, &g, "t", [0.0, 1.0], 4, &Colour::One, &x).unwrap();
|
||||
assert_eq!(h.extent, Some([3.0, 5.0]), "the extent reaches past the axis");
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
//! The print face: the flat face of a body that a slicer export lays on the print bed (#8429).
|
||||
//! A body has at most one (`Document::print_faces`, by body name). It is held by the target a
|
||||
//! sketch on a face holds ([`Document::flat_face_target`]), so a rebuild that adds, removes or
|
||||
//! reorders faces finds the same face again, or says it is gone.
|
||||
|
||||
use solidcraft_geom::Vec3;
|
||||
|
||||
use crate::config::ConfigFaceTarget;
|
||||
use crate::{DocError, Document, ModelBody, ModelState, Result};
|
||||
|
||||
/// Where a print face is: a point on it and its outward unit normal, in the coordinates of the
|
||||
/// body it was found on.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct PrintFace {
|
||||
/// The face's index on the body (its first piece, when a change split it).
|
||||
pub index: usize,
|
||||
pub point: Vec3,
|
||||
pub normal: Vec3,
|
||||
}
|
||||
|
||||
impl Document {
|
||||
/// The target that marks flat face `index` of `body` as its print face.
|
||||
pub fn print_face_target(&self, body: &ModelBody, index: usize) -> Result<ConfigFaceTarget> {
|
||||
if body.body.is_mesh() {
|
||||
return Err(DocError::Invalid("not supported yet: a print face on a mesh body".into()));
|
||||
}
|
||||
if body.body.planar_face(index).is_none() {
|
||||
return Err(DocError::Invalid("only a flat face can be the print face".into()));
|
||||
}
|
||||
// The sketch-on-a-face wording ("choose a fixed plane") offers what a print face has not.
|
||||
self.flat_face_target(body, index).map_err(|_| {
|
||||
DocError::Invalid(
|
||||
"this face can't be the print face: the body's history no longer tells it apart from its other faces (as after a shell of a shell body); pick a face of another body, or edit the feature that reshaped it".into(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Where `body`'s print face is now. `None`: it has none. An error: the face can't be found
|
||||
/// any more, or is no longer flat.
|
||||
pub fn print_face(&self, state: &ModelState, body: &ModelBody) -> Option<Result<PrintFace>> {
|
||||
let target = self.print_faces.get(body.definition_name())?;
|
||||
Some(self.locate_print_face(state, body, target))
|
||||
}
|
||||
|
||||
/// The print face a copy of `source` gets (Move/Copy, Mirror): the face of `copy` named after
|
||||
/// `source`'s print face (a copy's faces carry their source's names, followed by the copy's
|
||||
/// instance, `F1:+x@F2.1`). `None`:
|
||||
/// `source` has no print face.
|
||||
pub fn copied_print_face(&self, state: &ModelState, source: &ModelBody, copy: &ModelBody) -> Option<Result<ConfigFaceTarget>> {
|
||||
let target = self.print_faces.get(source.definition_name())?;
|
||||
let found = || {
|
||||
let (indices, _) = self.anchored_face_indices(state, source, target)?;
|
||||
let first = indices.first().copied().ok_or_else(|| DocError::Unknown("the print face no longer exists".into()))?;
|
||||
let names = crate::naming::face_names(source);
|
||||
let name =
|
||||
names.get(first).map(|n| crate::naming::strip_piece(n)).ok_or_else(|| DocError::Unknown("the print face has no name".into()))?;
|
||||
let index = crate::naming::face_names(copy)
|
||||
.iter()
|
||||
.position(|n| crate::naming::strip_piece(without_last_instance(n)) == name)
|
||||
.ok_or_else(|| DocError::Unknown(format!("the copy has no face `{name}`")))?;
|
||||
self.print_face_target(copy, index)
|
||||
};
|
||||
Some(found())
|
||||
}
|
||||
|
||||
fn locate_print_face(&self, state: &ModelState, body: &ModelBody, target: &ConfigFaceTarget) -> Result<PrintFace> {
|
||||
if body.body.is_mesh() {
|
||||
return Err(DocError::Invalid("not supported yet: a print face on a mesh body".into()));
|
||||
}
|
||||
let (indices, _) = self.anchored_face_indices(state, body, target)?;
|
||||
let first = indices.first().copied().ok_or_else(|| DocError::Unknown("the print face no longer exists".into()))?;
|
||||
let plane = body.body.planar_face(first).ok_or_else(|| DocError::Invalid("the print face is no longer flat".into()))?;
|
||||
let scale = 1. + body.body.size();
|
||||
for index in indices.iter().skip(1) {
|
||||
let other = body.body.planar_face(*index).ok_or_else(|| DocError::Invalid("the print face is no longer flat".into()))?;
|
||||
if plane.normal().dot(other.normal()).abs() < 1. - 1e-8 || (other.origin - plane.origin).dot(plane.normal()).abs() > 1e-7 * scale {
|
||||
return Err(DocError::Invalid("the print face's pieces no longer lie on one plane".into()));
|
||||
}
|
||||
}
|
||||
// The surface's own orientation is how the kernel built it, so the side the material is
|
||||
// on is taken from the face's triangles, which point out of the body.
|
||||
let mesh = body.mesh();
|
||||
let mut area = Vec3::ZERO;
|
||||
for (t, f) in mesh.triangles.iter().zip(&mesh.tri_face) {
|
||||
if let (true, Some([a, b, c])) = (indices.contains(&(*f as usize)), mesh.tri(t)) {
|
||||
area += (b - a).cross(c - a);
|
||||
}
|
||||
}
|
||||
let normal = plane.normal().normalized().ok_or_else(|| DocError::Invalid("the print face's plane is degenerate".into()))?;
|
||||
let outward = area.dot(normal);
|
||||
if !(outward.abs() > 0.) {
|
||||
return Err(DocError::Invalid("the print face has no surface to lie on".into()));
|
||||
}
|
||||
Ok(PrintFace { index: first, point: plane.origin, normal: if outward < 0. { -normal } else { normal } })
|
||||
}
|
||||
}
|
||||
|
||||
/// A face name without the copy instance at its end (`F1:+x@F2.1` is `F1:+x`; a copy of a copy,
|
||||
/// `F1:+x@F2.1@F5.1`, is `F1:+x@F2.1`).
|
||||
fn without_last_instance(n: &str) -> &str {
|
||||
let Some(at) = n.rfind('@') else { return n };
|
||||
let instance = n.get(at + 1..).and_then(|t| t.strip_prefix('F')).and_then(|t| t.split_once('.'));
|
||||
let digits = |t: &str| !t.is_empty() && t.bytes().all(|b| b.is_ascii_digit());
|
||||
if instance.is_some_and(|(feature, k)| digits(feature) && digits(k)) { n.get(..at).unwrap_or(n) } else { n }
|
||||
}
|
||||
@@ -238,13 +238,14 @@ fn resolve_uncached(
|
||||
}
|
||||
LinkSource::Origin => vec![LinkGeom::Point(plane.to_local(Vec3::ZERO))],
|
||||
LinkSource::Axis { name } => {
|
||||
let d = match name.to_ascii_uppercase().as_str() {
|
||||
"X" => Vec3::X,
|
||||
"Y" => Vec3::Y,
|
||||
"Z" => Vec3::Z,
|
||||
_ => return Err(DocError::Unknown(format!("axis `{name}`"))),
|
||||
let (o, d) = match name.to_ascii_uppercase().as_str() {
|
||||
"X" => (Vec3::ZERO, Vec3::X),
|
||||
"Y" => (Vec3::ZERO, Vec3::Y),
|
||||
"Z" => (Vec3::ZERO, Vec3::Z),
|
||||
// A construction axis, by name (#9324), where the timeline so far puts it.
|
||||
_ => crate::eval::construction_axis_in(Some(doc), st, name, 0)?,
|
||||
};
|
||||
let o = plane.to_local(Vec3::ZERO);
|
||||
let o = plane.to_local(o);
|
||||
let d2 = Vec2::new(d.dot(plane.x), d.dot(plane.y));
|
||||
match d2.normalized() {
|
||||
Some(u) if d2.len() > 1e-9 => vec![LinkGeom::Line(o - u * AXIS_HALF, o + u * AXIS_HALF)],
|
||||
|
||||
@@ -39,6 +39,12 @@ pub struct RenderSetup {
|
||||
pub stage: solidcraft_render::stage::Stage,
|
||||
}
|
||||
|
||||
/// The CPU request a size check belongs to, so a refusal can name the profile that admits it.
|
||||
#[derive(Clone, Copy)]
|
||||
struct CpuSize {
|
||||
progressive: Option<solidcraft_render::job::CpuProgressiveOptions>,
|
||||
}
|
||||
|
||||
// Reuse Camera's representation and defaults, but reject misspelled saved fields. A remote
|
||||
// derive constructs Camera itself, so adding a Camera field requires updating this adapter.
|
||||
// Navigation/named-view deserialization keeps its existing compatibility policy.
|
||||
@@ -80,27 +86,29 @@ impl Default for RenderSetup {
|
||||
|
||||
impl RenderSetup {
|
||||
pub fn validate(&self) -> Result<()> {
|
||||
let limits = match &self.job {
|
||||
Some(RenderJob::CpuRaytrace { options }) => options.budget.image_limits(options.progressive.is_some()),
|
||||
Some(RenderJob::GpuPath { .. }) => solidcraft_render::job::GPU_PATH_LIMITS,
|
||||
_ => RenderLimits::default(),
|
||||
let (limits, cpu) = match &self.job {
|
||||
Some(RenderJob::CpuRaytrace { options }) => {
|
||||
(options.budget.image_limits(options.progressive.is_some()), Some(CpuSize { progressive: options.progressive }))
|
||||
}
|
||||
Some(RenderJob::GpuPath { .. }) => (solidcraft_render::job::GPU_PATH_LIMITS, None),
|
||||
_ => (RenderLimits::default(), None),
|
||||
};
|
||||
self.validate_with_limits(limits)
|
||||
self.validate_with_limits(limits, cpu)
|
||||
}
|
||||
|
||||
/// Runtime CPU lowering has already moved typed options out of this setup.
|
||||
/// Saving still uses validate(), so a legacy setup cannot smuggle a large profile.
|
||||
pub fn validate_for_cpu(&self, budget: solidcraft_render::job::CpuBudget) -> Result<()> {
|
||||
budget.validate_image(self.width, self.height, None).map_err(|e| DocError::Invalid(e.to_string()))?;
|
||||
self.validate_with_limits(budget.image_limits(false))
|
||||
self.validate_with_limits(budget.image_limits(false), Some(CpuSize { progressive: None }))
|
||||
}
|
||||
|
||||
/// Typed GPU options have already been lowered; retain their dimension envelope.
|
||||
pub fn validate_for_gpu(&self) -> Result<()> {
|
||||
self.validate_with_limits(solidcraft_render::job::GPU_PATH_LIMITS)
|
||||
self.validate_with_limits(solidcraft_render::job::GPU_PATH_LIMITS, None)
|
||||
}
|
||||
|
||||
fn validate_with_limits(&self, limits: RenderLimits) -> Result<()> {
|
||||
fn validate_with_limits(&self, limits: RenderLimits, cpu: Option<CpuSize>) -> Result<()> {
|
||||
self.surfaces.validate()?;
|
||||
self.optics.validate()?;
|
||||
if !self.optics.is_empty() && !matches!(self.job, Some(RenderJob::GpuPath { .. })) {
|
||||
@@ -123,9 +131,14 @@ impl RenderSetup {
|
||||
if let Some((w, h)) = limits.fit(width, height, None)
|
||||
&& (w, h) != (width, height)
|
||||
{
|
||||
// A CPU refusal names the profile that can render the size (#8941). It is never
|
||||
// selected for the person: resolution and profile change only when they choose.
|
||||
let advice = cpu.map_or_else(String::new, |cpu| {
|
||||
format!("; {}", solidcraft_render::job::CpuBudget::size_advice(self.width, self.height, cpu.progressive))
|
||||
});
|
||||
return Err(DocError::Invalid(format!(
|
||||
"render output {width} × {height} exceeds the image limits ({} pixels per side, {} pixels in all); \
|
||||
the largest image at this aspect is {w} × {h}",
|
||||
the largest image at this aspect is {w} × {h}{advice}",
|
||||
limits.max_dimension, limits.max_pixels
|
||||
)));
|
||||
}
|
||||
@@ -538,9 +551,58 @@ mod tests {
|
||||
let setup = RenderSetup { width: 7680, height: 4320, ..Default::default() };
|
||||
let message = setup.validate().unwrap_err().to_string();
|
||||
assert!(message.contains("the largest image at this aspect is 27"), "{message}");
|
||||
assert!(!message.contains("CPU resource profile"), "a raster refusal names no CPU profile: {message}");
|
||||
assert!(RenderSetup { width: 2048, height: 2048, ..Default::default() }.validate().is_ok());
|
||||
}
|
||||
|
||||
/// #8941: 4K UHD under the default Compact profile names Studio, the profile that renders it;
|
||||
/// a 16K-wide image under Studio names Workstation; past Workstation no profile is named.
|
||||
/// The saved profile is never changed by the check, and GPU path tracing keeps its own limits.
|
||||
#[test]
|
||||
fn a_cpu_size_refusal_names_the_profile_that_can_render_it() {
|
||||
use solidcraft_render::job::CpuBudget;
|
||||
let cpu = |budget, width, height| RenderSetup {
|
||||
width,
|
||||
height,
|
||||
job: Some(RenderJob::CpuRaytrace { options: CpuJob { budget, ..Default::default() } }),
|
||||
..Default::default()
|
||||
};
|
||||
let compact = cpu(CpuBudget::Compact, 3840, 2160);
|
||||
let message = compact.validate().unwrap_err().to_string();
|
||||
assert!(message.contains("3840 × 2160 exceeds the image limits (4096 pixels per side, 4194304 pixels in all)"), "{message}");
|
||||
assert!(message.contains("the largest image at this aspect is 2730 × 1536"), "{message}");
|
||||
assert!(message.contains("choose the Studio CPU resource profile, or a smaller size"), "{message}");
|
||||
assert!(matches!(&compact.job, Some(RenderJob::CpuRaytrace { options }) if options.budget == CpuBudget::Compact));
|
||||
let message = compact.validate_for_cpu(CpuBudget::Compact).unwrap_err().to_string();
|
||||
assert!(message.contains("choose the Studio CPU resource profile"), "{message}");
|
||||
|
||||
cpu(CpuBudget::Studio, 3840, 2160).validate().unwrap();
|
||||
RenderSetup { width: 3840, height: 2160, ..Default::default() }.validate_for_cpu(CpuBudget::Studio).unwrap();
|
||||
|
||||
let message = cpu(CpuBudget::Studio, 15360, 4320).validate().unwrap_err().to_string();
|
||||
assert!(message.contains("choose the Workstation CPU resource profile"), "{message}");
|
||||
cpu(CpuBudget::Workstation, 15360, 4320).validate().unwrap();
|
||||
|
||||
let message = cpu(CpuBudget::Workstation, 16384, 16384).validate().unwrap_err().to_string();
|
||||
assert!(message.contains("no CPU resource profile accepts this size; choose a smaller size"), "{message}");
|
||||
for profile in ["Compact", "Studio", "Workstation"] {
|
||||
assert!(!message.contains(&format!("the {profile} CPU")), "{message}");
|
||||
}
|
||||
|
||||
// GPU path tracing: 4K renders, an over-limit size names no CPU profile.
|
||||
let gpu = |width, height| RenderSetup {
|
||||
width,
|
||||
height,
|
||||
job: Some(RenderJob::GpuPath { transparent_background: false, options: Default::default() }),
|
||||
..Default::default()
|
||||
};
|
||||
gpu(3840, 2160).validate().unwrap();
|
||||
gpu(3840, 2160).validate_for_gpu().unwrap();
|
||||
let message = gpu(8193, 8193).validate().unwrap_err().to_string();
|
||||
assert!(message.contains("(8192 pixels per side, 16777216 pixels in all)"), "{message}");
|
||||
assert!(!message.contains("CPU resource profile"), "{message}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn portable_jobs_preserve_complete_numerical_intent_and_legacy_omission() {
|
||||
use serde_json::json;
|
||||
|
||||
@@ -486,6 +486,8 @@ fn hostile_expressions_never_panic() {
|
||||
"deg",
|
||||
"x",
|
||||
"sqrt",
|
||||
"cosh",
|
||||
"atanh",
|
||||
"pow",
|
||||
"min",
|
||||
"max",
|
||||
@@ -715,3 +717,156 @@ fn timeline_group_legacy_json_omits_empty_joint_membership() {
|
||||
assert!(value["timeline_groups"][0].get("joints").is_none());
|
||||
assert_eq!(Document::from_json(&saved).unwrap().to_json(), saved);
|
||||
}
|
||||
|
||||
/// A sketch whose touching curves are cut into more pieces than the profile search looks at
|
||||
/// says that some profiles may be missing, instead of leaving them out silently (#6896); an
|
||||
/// ordinary sketch says nothing.
|
||||
#[test]
|
||||
fn a_sketch_too_large_to_search_whole_says_profiles_may_be_missing() {
|
||||
let mut doc = Document::new("large");
|
||||
let mut sk = Sketch::new();
|
||||
// Concentric ellipses 100 m across, each sampled into thousands of pieces, all one island.
|
||||
let c = sk.add_point(Vec2::ZERO, None).unwrap();
|
||||
for k in 0..5 {
|
||||
let m = sk.add_point(Vec2::new(1e5 - k as f64 * 1e3, 0.0), None).unwrap();
|
||||
sk.add_curve(solidcraft_sketch::CurveKind::Ellipse { c, m, r: 6e4 - k as f64 * 1e3 }, None).unwrap();
|
||||
}
|
||||
let large = doc.add_feature(FeatureKind::Sketch { plane: PlaneRef::Origin { name: "XY".into() }, sketch: sk }, None).unwrap();
|
||||
let (plate, plate_sketch) = plate_doc();
|
||||
let mut m = Model::new();
|
||||
m.evaluate(&doc);
|
||||
let warning = m.result(large).and_then(|r| r.warning.clone()).unwrap_or_default();
|
||||
assert!(warning.contains("some profiles may be missing"), "{warning}");
|
||||
let mut m = Model::new();
|
||||
m.evaluate(&plate);
|
||||
assert!(m.result(plate_sketch).and_then(|r| r.warning.as_deref()).is_none());
|
||||
}
|
||||
|
||||
fn browser_group(id: u64, component: u64, folder: &str, parent: Option<u64>, items: &[BrowserEntry]) -> BrowserGroup {
|
||||
BrowserGroup { id, name: format!("G{id}"), component, folder: folder.into(), parent, items: items.to_vec() }
|
||||
}
|
||||
|
||||
fn item(key: &str) -> BrowserEntry {
|
||||
BrowserEntry::item(key)
|
||||
}
|
||||
|
||||
fn group(id: u64) -> BrowserEntry {
|
||||
BrowserEntry::Group(id)
|
||||
}
|
||||
|
||||
/// The design with what `browser_pruned` drops gone (the design itself when nothing is).
|
||||
fn pruned(doc: &Document) -> Document {
|
||||
let mut out = doc.clone();
|
||||
if let Some((groups, order)) = doc.browser_pruned() {
|
||||
(out.browser_groups, out.browser_order) = (groups, order);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Pruning keeps every group but cuts a parent that loops, is the group itself, is past the
|
||||
/// nesting limit, or is in another component or folder, to the top level; and it drops entries
|
||||
/// for deleted sketches and placements, repeats, and the lists of a missing component. Body names
|
||||
/// are never judged.
|
||||
#[test]
|
||||
fn browser_pruning_cuts_bad_parents_and_drops_stale_and_repeated_entries() {
|
||||
let mut doc = Document::new("prune");
|
||||
let sketch = doc.add_feature(FeatureKind::Sketch { plane: PlaneRef::Origin { name: "XY".into() }, sketch: Sketch::new() }, None).unwrap();
|
||||
let key = sketch.to_string();
|
||||
doc.components.push(Component { id: 7, name: "Part".into(), ..Default::default() });
|
||||
doc.occurrences.push(Occurrence { id: 20, component: 7, parent: 0, name: "Part".into(), transform: IDENTITY, grounded: false });
|
||||
let deep = MAX_GROUP_NESTING as u64 + 2;
|
||||
doc.browser_groups = vec![
|
||||
browser_group(1, 0, "bodies", Some(2), &[]),
|
||||
browser_group(2, 0, "bodies", Some(1), &[]),
|
||||
browser_group(3, 0, "bodies", Some(3), &[]),
|
||||
browser_group(4, 7, "bodies", Some(5), &[]),
|
||||
browser_group(5, 0, "bodies", None, &[item("a"), group(7), group(7), item("a")]),
|
||||
browser_group(6, 0, "sketches", Some(5), &[item(&key), item("999")]),
|
||||
browser_group(7, 0, "bodies", Some(5), &[]),
|
||||
browser_group(8, 0, "bodies", None, &[item("a"), item("b")]),
|
||||
];
|
||||
doc.browser_groups.extend((0..deep).map(|i| browser_group(100 + i, 0, "construction", i.checked_sub(1).map(|p| 100 + p), &[])));
|
||||
doc.browser_order.insert("0/bodies".into(), vec![item("Lid"), item("Ghost"), item("Lid"), group(5), group(5)]);
|
||||
doc.browser_order.insert("0/components".into(), vec![item("21"), item("20")]);
|
||||
doc.browser_order.insert("99/bodies".into(), vec![item("x")]);
|
||||
let after = pruned(&doc);
|
||||
let parent = |id| after.browser_group(id).unwrap().parent;
|
||||
assert_eq!(after.browser_groups.len(), doc.browser_groups.len(), "no group is dropped");
|
||||
assert_eq!([1, 2, 3, 4, 6].map(parent), [None; 5], "a loop, itself, another component, another folder");
|
||||
assert_eq!(parent(7), Some(5));
|
||||
for i in 0..deep {
|
||||
assert_eq!(parent(100 + i), (1..MAX_GROUP_NESTING as u64).contains(&i).then(|| 99 + i), "level {} of a chain {deep} deep", i + 1);
|
||||
}
|
||||
assert_eq!(after.browser_group(5).unwrap().items, [item("a"), group(7)]);
|
||||
assert_eq!(after.browser_group(8).unwrap().items, [item("b")], "an item stays in the first group that has it");
|
||||
assert_eq!(after.browser_group(6).unwrap().items, [item(&key)], "the deleted sketch goes");
|
||||
assert_eq!(after.browser_order["0/bodies"], [item("Lid"), item("Ghost"), group(5)], "body names stay; repeats go");
|
||||
assert_eq!(after.browser_order["0/components"], [item("20")], "the deleted placement goes");
|
||||
assert!(!after.browser_order.contains_key("99/bodies"));
|
||||
assert!(Document::new("clean").browser_pruned().is_none(), "nothing to prune is not a change");
|
||||
}
|
||||
|
||||
/// Levels and ancestry follow the parents. Groups a list does not name go after the last one it
|
||||
/// names, in creation order, or first when it names none: above the items, as a design saved
|
||||
/// before groups could be ordered has always shown them.
|
||||
#[test]
|
||||
fn group_levels_and_the_order_of_groups_a_list_does_not_name() {
|
||||
let mut doc = Document::new("order");
|
||||
doc.browser_groups = vec![
|
||||
browser_group(1, 0, "bodies", None, &[item("x")]),
|
||||
browser_group(2, 0, "bodies", None, &[]),
|
||||
browser_group(3, 0, "bodies", None, &[]),
|
||||
browser_group(4, 0, "bodies", Some(1), &[]),
|
||||
browser_group(5, 0, "bodies", Some(4), &[]),
|
||||
];
|
||||
assert_eq!([1, 2, 4, 5].map(|id| doc.group_level(id)), [1, 1, 2, 3]);
|
||||
assert!(doc.group_within(5, 1) && doc.group_within(5, 5) && !doc.group_within(1, 5) && !doc.group_within(2, 5));
|
||||
assert_eq!(doc.group_height(1), 3);
|
||||
let top = |doc: &Document| doc.ordered_entries(0, "bodies", None);
|
||||
assert_eq!(top(&doc), [group(1), group(2), group(3)], "no list: creation order");
|
||||
doc.browser_order.insert("0/bodies".into(), vec![item("a")]);
|
||||
assert_eq!(top(&doc), [group(1), group(2), group(3), item("a")], "a list of items only: groups above them");
|
||||
doc.browser_order.insert("0/bodies".into(), vec![item("a"), group(2), item("b"), group(2), group(9)]);
|
||||
assert_eq!(top(&doc), [item("a"), group(2), group(1), group(3), item("b")], "after the last listed; repeats and unknown ignored");
|
||||
assert_eq!(doc.ordered_entries(0, "bodies", Some(1)), [group(4), item("x")], "inside a group too");
|
||||
}
|
||||
|
||||
/// Nested groups and a `components` order survive a save and a load, saved as numbers (groups) and
|
||||
/// strings (items). A design from before groups could nest reads with the browser it always had:
|
||||
/// no parents, groups in creation order above the items in their saved order, saved back as read.
|
||||
#[test]
|
||||
fn browser_data_survives_a_save_and_old_designs_read_unchanged() {
|
||||
let mut doc = Document::new("nested");
|
||||
doc.components.push(Component { id: 7, name: "Part".into(), ..Default::default() });
|
||||
for id in [20, 21] {
|
||||
doc.occurrences.push(Occurrence { id, component: 7, parent: 0, name: format!("P{id}"), transform: IDENTITY, grounded: false });
|
||||
}
|
||||
doc.browser_groups = vec![browser_group(2, 0, "bodies", None, &[item("Lid"), group(3)]), browser_group(3, 0, "bodies", Some(2), &[item("Base")])];
|
||||
doc.browser_order.insert("0/bodies".into(), vec![item("Cap"), group(2)]);
|
||||
doc.browser_order.insert("0/components".into(), vec![item("21"), item("20")]);
|
||||
let saved = doc.to_json();
|
||||
let value: serde_json::Value = serde_json::from_str(&saved).unwrap();
|
||||
assert_eq!(value["browser_order"]["0/bodies"], serde_json::json!(["Cap", 2]));
|
||||
assert_eq!((&value["browser_groups"][1]["parent"], value["browser_groups"][0].get("parent")), (&serde_json::json!(2), None));
|
||||
let loaded = Document::from_json(&saved).unwrap();
|
||||
assert_eq!((&loaded.browser_groups, &loaded.browser_order), (&doc.browser_groups, &doc.browser_order));
|
||||
let resaved: serde_json::Value = serde_json::from_str(&loaded.to_json()).unwrap();
|
||||
assert_eq!((&resaved["browser_groups"], &resaved["browser_order"]), (&value["browser_groups"], &value["browser_order"]));
|
||||
|
||||
let groups = serde_json::json!([
|
||||
{"id": 3, "name": "Fasteners", "component": 0, "folder": "bodies", "items": ["Bolt", "Nut"]},
|
||||
{"id": 4, "name": "Plates", "component": 0, "folder": "bodies", "items": ["Plate"]},
|
||||
]);
|
||||
for (order, top) in [
|
||||
(serde_json::json!({}), vec![group(3), group(4)]),
|
||||
(serde_json::json!({"0/bodies": ["Washer", "Bolt", "Cap"]}), vec![group(3), group(4), item("Washer"), item("Bolt"), item("Cap")]),
|
||||
] {
|
||||
let mut old: serde_json::Value = serde_json::from_str(&Document::new("old").to_json()).unwrap();
|
||||
(old["browser_groups"], old["browser_order"]) = (groups.clone(), order.clone());
|
||||
let loaded = Document::from_json(&old.to_string()).unwrap();
|
||||
assert!(loaded.browser_groups.iter().all(|g| g.parent.is_none()), "{order}");
|
||||
assert_eq!(loaded.ordered_entries(0, "bodies", None), top, "{order}");
|
||||
let resaved: serde_json::Value = serde_json::from_str(&loaded.to_json()).unwrap();
|
||||
assert_eq!(resaved["browser_groups"], groups, "{order}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,17 @@
|
||||
//! kept apart from the number parameters: a text has no unit and no number expression reads it.
|
||||
//! A parameter edit or a configuration row that sets one relabels every text built from it, and
|
||||
//! the features that use those texts follow in the same rebuild.
|
||||
//!
|
||||
//! The words can show a number parameter's value too (#9281): `'Width: ' + Width` reads
|
||||
//! `Width: 85 mm`, a length in the design's units, an angle in degrees (`30°`), up to three
|
||||
//! decimals without trailing zeros. `number(Width)` is the number alone (`85`) and
|
||||
//! `number(Width, 2)` gives it two decimals (`85.00`).
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::expr::Value;
|
||||
use crate::{DocError, Document, FeatureKind, MAX_PARAMS, Result};
|
||||
|
||||
/// The unit that marks a text parameter where parameters are listed, added or imported.
|
||||
@@ -49,8 +57,29 @@ pub fn unquote(s: &str) -> String {
|
||||
enum Term {
|
||||
Text(String),
|
||||
Name(String),
|
||||
/// `number(name)` or `number(name, decimals)`, with the name's byte range.
|
||||
Number {
|
||||
name: String,
|
||||
at: (usize, usize),
|
||||
decimals: Option<u8>,
|
||||
},
|
||||
}
|
||||
|
||||
/// How the words show a number parameter.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Show {
|
||||
/// As named: its value and unit, up to three decimals.
|
||||
Named,
|
||||
/// In `number(...)`: the number alone, with these decimals or up to three.
|
||||
Number(Option<u8>),
|
||||
}
|
||||
|
||||
/// The function that shows a number parameter's value without its unit.
|
||||
const NUMBER: &str = "number";
|
||||
|
||||
/// Most decimals `number(name, decimals)` takes.
|
||||
const MAX_DECIMALS: u8 = 8;
|
||||
|
||||
/// The terms of a words expression with their byte ranges: quoted texts (single or double
|
||||
/// quotes, a quote doubled inside stands for one) and parameter names, joined by `+`.
|
||||
fn terms(s: &str) -> Result<Vec<(Term, usize, usize)>> {
|
||||
@@ -96,10 +125,16 @@ fn terms(s: &str) -> Result<Vec<(Term, usize, usize)>> {
|
||||
want_term = false;
|
||||
} else if c.is_alphabetic() || c == '_' {
|
||||
let start = i;
|
||||
while cs.get(i).is_some_and(|x| x.1.is_alphanumeric() || x.1 == '_') {
|
||||
i += 1;
|
||||
i = word_end(&cs, i);
|
||||
let word = s.get(at(start)..at(i)).unwrap_or_default().to_string();
|
||||
let open = skip_blanks(&cs, i);
|
||||
if word == NUMBER && cs.get(open).is_some_and(|x| x.1 == '(') {
|
||||
let (term, end) = number_call(s, &cs, open + 1)?;
|
||||
i = end;
|
||||
out.push((term, at(start), at(i)));
|
||||
} else {
|
||||
out.push((Term::Name(word), at(start), at(i)));
|
||||
}
|
||||
out.push((Term::Name(s.get(at(start)..at(i)).unwrap_or_default().to_string()), at(start), at(i)));
|
||||
want_term = false;
|
||||
} else {
|
||||
return Err(DocError::Expr(format!("unexpected `{c}`: a text expression joins quoted texts and text parameters with +")));
|
||||
@@ -112,11 +147,88 @@ fn terms(s: &str) -> Result<Vec<(Term, usize, usize)>> {
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
fn word_end(cs: &[(usize, char)], mut i: usize) -> usize {
|
||||
while cs.get(i).is_some_and(|x| x.1.is_alphanumeric() || x.1 == '_') {
|
||||
i += 1;
|
||||
}
|
||||
i
|
||||
}
|
||||
|
||||
fn skip_blanks(cs: &[(usize, char)], mut i: usize) -> usize {
|
||||
while cs.get(i).is_some_and(|x| x.1.is_whitespace()) {
|
||||
i += 1;
|
||||
}
|
||||
i
|
||||
}
|
||||
|
||||
/// `number(` has been read up to `i`: the name, optional decimals and `)`; the term and where
|
||||
/// it ends.
|
||||
fn number_call(s: &str, cs: &[(usize, char)], i: usize) -> Result<(Term, usize)> {
|
||||
let at = |i: usize| cs.get(i).map_or(s.len(), |x| x.0);
|
||||
let usage = || DocError::Expr("number() takes a number parameter and, if wanted, its decimals: number(Width, 2)".into());
|
||||
let start = skip_blanks(cs, i);
|
||||
if !cs.get(start).is_some_and(|x| x.1.is_alphabetic() || x.1 == '_') {
|
||||
return Err(usage());
|
||||
}
|
||||
let end = word_end(cs, start);
|
||||
let name = s.get(at(start)..at(end)).unwrap_or_default().to_string();
|
||||
let mut i = skip_blanks(cs, end);
|
||||
let mut decimals = None;
|
||||
if cs.get(i).is_some_and(|x| x.1 == ',') {
|
||||
let from = skip_blanks(cs, i + 1);
|
||||
let mut to = from;
|
||||
while cs.get(to).is_some_and(|x| x.1.is_ascii_digit()) {
|
||||
to += 1;
|
||||
}
|
||||
let digits = s.get(at(from)..at(to)).unwrap_or_default();
|
||||
let n: u8 = digits.parse().map_err(|_| usage())?;
|
||||
if n > MAX_DECIMALS {
|
||||
return Err(DocError::Expr(format!("number() shows at most {MAX_DECIMALS} decimals")));
|
||||
}
|
||||
decimals = Some(n);
|
||||
i = skip_blanks(cs, to);
|
||||
}
|
||||
if !cs.get(i).is_some_and(|x| x.1 == ')') {
|
||||
return Err(usage());
|
||||
}
|
||||
Ok((Term::Number { name, at: (at(start), at(end)), decimals }, i + 1))
|
||||
}
|
||||
|
||||
/// A number parameter's value as the words show it: `v` in millimetres, radians or no unit
|
||||
/// (`len`, `ang`: its dimension), lengths in `units`.
|
||||
pub fn show_number(v: f64, len: i8, ang: i8, units: &str, show: Show) -> Result<String> {
|
||||
let (value, unit) = match (len, ang) {
|
||||
(0, 0) => (v, ""),
|
||||
(1, 0) => {
|
||||
let (scale, name) = crate::display_units::unit_scale(units);
|
||||
(v * scale, name)
|
||||
}
|
||||
(0, 1) => (v.to_degrees(), "°"),
|
||||
_ => return Err(DocError::Expr("a text shows lengths, angles and plain numbers only".into())),
|
||||
};
|
||||
if !value.is_finite() || value.abs() > 1e12 {
|
||||
return Err(DocError::Expr("the number is out of range for a text".into()));
|
||||
}
|
||||
let digits = match show {
|
||||
Show::Number(Some(d)) => format!("{value:.*}", usize::from(d)),
|
||||
Show::Named | Show::Number(None) => {
|
||||
let t = format!("{value:.3}");
|
||||
let t = t.trim_end_matches('0').trim_end_matches('.');
|
||||
if t == "-0" { "0".to_string() } else { t.to_string() }
|
||||
}
|
||||
};
|
||||
Ok(match (show, unit) {
|
||||
(Show::Number(_), _) | (_, "") => digits,
|
||||
(Show::Named, "°") => format!("{digits}°"),
|
||||
(Show::Named, unit) => format!("{digits} {unit}"),
|
||||
})
|
||||
}
|
||||
|
||||
/// The parameters a words expression names (none when it does not parse).
|
||||
pub fn references(s: &str) -> Vec<String> {
|
||||
let mut out: Vec<String> = Vec::new();
|
||||
for (t, _, _) in terms(s).unwrap_or_default() {
|
||||
if let Term::Name(n) = t
|
||||
if let Term::Name(n) | Term::Number { name: n, .. } = t
|
||||
&& !out.contains(&n)
|
||||
{
|
||||
out.push(n);
|
||||
@@ -131,23 +243,27 @@ pub fn rename_reference(s: &str, old: &str, new: &str) -> String {
|
||||
let mut out = String::with_capacity(s.len());
|
||||
let mut last = 0;
|
||||
for (t, a, b) in terms {
|
||||
if matches!(&t, Term::Name(n) if n == old) {
|
||||
out.push_str(s.get(last..a).unwrap_or_default());
|
||||
out.push_str(new);
|
||||
last = b;
|
||||
}
|
||||
let at = match &t {
|
||||
Term::Name(n) if n == old => (a, b),
|
||||
Term::Number { name, at, .. } if name == old => *at,
|
||||
_ => continue,
|
||||
};
|
||||
out.push_str(s.get(last..at.0).unwrap_or_default());
|
||||
out.push_str(new);
|
||||
last = at.1;
|
||||
}
|
||||
out.push_str(s.get(last..).unwrap_or_default());
|
||||
out
|
||||
}
|
||||
|
||||
/// The words an expression comes to, each parameter's text from `lookup`.
|
||||
pub fn eval(s: &str, lookup: &dyn Fn(&str) -> Result<String>) -> Result<String> {
|
||||
pub fn eval(s: &str, lookup: &dyn Fn(&str, Show) -> Result<String>) -> Result<String> {
|
||||
let mut out = String::new();
|
||||
for (t, _, _) in terms(s)? {
|
||||
match t {
|
||||
Term::Text(text) => out.push_str(&text),
|
||||
Term::Name(n) => out.push_str(&lookup(&n)?),
|
||||
Term::Name(n) => out.push_str(&lookup(&n, Show::Named)?),
|
||||
Term::Number { name, decimals, .. } => out.push_str(&lookup(&name, Show::Number(decimals))?),
|
||||
}
|
||||
if out.len() > MAX_LEN {
|
||||
return Err(DocError::Expr("the text is too long".into()));
|
||||
@@ -161,12 +277,14 @@ impl Document {
|
||||
self.text_params.iter().find(|p| p.name == name)
|
||||
}
|
||||
|
||||
/// The words a words expression comes to in this design.
|
||||
pub fn eval_words(&self, e: &str) -> Result<String> {
|
||||
eval(e, &|n| match self.text_param(n) {
|
||||
Some(p) => Ok(p.text.clone()),
|
||||
None if self.name_taken(n) => Err(DocError::Expr(format!("`{n}` is a number parameter: a text's words take text parameters"))),
|
||||
None => Err(DocError::Expr(format!("unknown text parameter `{n}`"))),
|
||||
/// The words a words expression comes to in this design, number parameters from `vals`.
|
||||
pub fn eval_words(&self, e: &str, vals: &BTreeMap<String, Value>) -> Result<String> {
|
||||
eval(e, &|n, show| match (self.text_param(n), vals.get(n)) {
|
||||
(Some(_), _) if show != Show::Named => Err(DocError::Expr(format!("`{n}` is a text parameter: number() takes a number parameter"))),
|
||||
(Some(p), _) => Ok(p.text.clone()),
|
||||
(None, Some(v)) => show_number(v.v, v.len, v.ang, &self.units, show).map_err(|error| DocError::Expr(format!("`{n}`: {error}"))),
|
||||
(None, None) if self.name_taken(n) => Err(DocError::Expr(format!("`{n}` has no value"))),
|
||||
(None, None) => Err(DocError::Expr(format!("unknown parameter `{n}`"))),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -230,7 +348,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn words_expressions_join_quoted_texts_and_parameters() {
|
||||
let look = |n: &str| match n {
|
||||
let look = |n: &str, _: Show| match n {
|
||||
"Label" => Ok("TAG-1".to_string()),
|
||||
_ => Err(DocError::Expr(format!("unknown text parameter `{n}`"))),
|
||||
};
|
||||
@@ -249,4 +367,30 @@ mod tests {
|
||||
assert_eq!(unquote("'a' + 'b'"), "'a' + 'b'");
|
||||
assert_eq!(unquote("TAG-1"), "TAG-1");
|
||||
}
|
||||
|
||||
/// #9281: a number parameter in the words shows its value; `number()` drops the unit and
|
||||
/// can fix the decimals; a rename reaches inside `number()`.
|
||||
#[test]
|
||||
fn number_parameters_show_their_value_in_the_words() {
|
||||
let look = |n: &str, show: Show| match n {
|
||||
"Width" => show_number(85.0, 1, 0, "mm", show),
|
||||
"Gap" => show_number(12.7, 1, 0, "in", show),
|
||||
"Tilt" => show_number(30f64.to_radians(), 0, 1, "mm", show),
|
||||
"Count" => show_number(4.0, 0, 0, "mm", show),
|
||||
"Area" => show_number(1.0, 2, 0, "mm", show),
|
||||
_ => Err(DocError::Expr(format!("unknown parameter `{n}`"))),
|
||||
};
|
||||
assert_eq!(eval("'Width: ' + Width", &look).unwrap(), "Width: 85 mm");
|
||||
assert_eq!(eval("Gap", &look).unwrap(), "0.5 in");
|
||||
assert_eq!(eval("Tilt + ' / ' + Count", &look).unwrap(), "30° / 4");
|
||||
assert_eq!(eval("number(Width) + 'x' + number( Width , 2 )", &look).unwrap(), "85x85.00");
|
||||
assert_eq!(eval("number(Tilt, 1)", &look).unwrap(), "30.0");
|
||||
for bad in ["number(Width", "number()", "number(Width, x)", "number(Width, 9)", "number(Width 2)", "Area", "number('a')"] {
|
||||
assert!(eval(bad, &look).is_err(), "{bad}");
|
||||
}
|
||||
assert_eq!(references("number(Width, 1) + Count"), ["Width", "Count"]);
|
||||
assert_eq!(rename_reference("number(Width, 1) + Width + 'Width'", "Width", "W"), "number(W, 1) + W + 'Width'");
|
||||
assert_eq!(show_number(-0.0001, 1, 0, "mm", Show::Named).unwrap(), "0 mm");
|
||||
assert!(show_number(f64::NAN, 0, 0, "mm", Show::Named).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use crate::schema::ParamCheck;
|
||||
use crate::{CommandSpec, EngineError, find_command};
|
||||
use crate::{EngineError, find_command};
|
||||
|
||||
/// The first step a batch would refuse at its turn.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
@@ -57,11 +57,11 @@ pub fn check(steps: &[Value], level: ParamCheck, host: &[&str]) -> Option<Refusa
|
||||
if host.contains(&id) {
|
||||
continue;
|
||||
}
|
||||
let Some(spec) = find_command(id) else {
|
||||
if find_command(id).is_none() {
|
||||
return Some(Refusal { index, command: Some(id.to_string()), error: EngineError::UnknownCommand(id.to_string()).to_string() });
|
||||
};
|
||||
}
|
||||
if level == ParamCheck::Strict
|
||||
&& let Some(refusal) = contract_refusal(index, id, spec, step)
|
||||
&& let Some(refusal) = contract_refusal(index, id, step)
|
||||
{
|
||||
return Some(refusal);
|
||||
}
|
||||
@@ -76,13 +76,13 @@ pub fn check(steps: &[Value], level: ParamCheck, host: &[&str]) -> Option<Refusa
|
||||
pub fn contracts(steps: &[Value]) -> Option<Refusal> {
|
||||
steps.iter().enumerate().find_map(|(index, step)| {
|
||||
let id = step.get("command").or_else(|| step.get("id")).and_then(Value::as_str)?;
|
||||
contract_refusal(index, id, find_command(id)?, step)
|
||||
contract_refusal(index, id, step)
|
||||
})
|
||||
}
|
||||
|
||||
/// Step `index`'s refusal when its parameters break `spec`'s typed contract.
|
||||
fn contract_refusal(index: usize, id: &str, spec: &CommandSpec, step: &Value) -> Option<Refusal> {
|
||||
let first = spec.contract?.check(step.get("params").unwrap_or(&Value::Null)).into_iter().next()?;
|
||||
/// Step `index`'s refusal when its parameters break the typed contract of its command `id`.
|
||||
fn contract_refusal(index: usize, id: &str, step: &Value) -> Option<Refusal> {
|
||||
let first = crate::schema::issues(id, step.get("params").unwrap_or(&Value::Null)).into_iter().next()?;
|
||||
Some(Refusal { index, command: Some(id.to_string()), error: crate::params::bad(id, first.message).to_string() })
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ use std::collections::BTreeMap;
|
||||
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use solidcraft_doc::FeatureKind;
|
||||
use solidcraft_doc::{BrowserEntry, FeatureKind, MAX_GROUP_NESTING};
|
||||
|
||||
use super::CommandSpec;
|
||||
use crate::params::{bad, bool_, str_};
|
||||
@@ -24,11 +24,11 @@ pub static COMMANDS: &[CommandSpec] = &[
|
||||
.params("sketch: id or name; plane: as sketch.create; frame?: as sketch.create; author_path?: exact owner occurrence ids; explicitly rebinds or freezes the plane while preserving geometry and dimension names"),
|
||||
CommandSpec::new("browser.group", "New Group", group)
|
||||
.icon("folder")
|
||||
.params("folder: bodies|sketches|construction; component?: id (default 0); name?; items?: [keys] (they leave other groups)"),
|
||||
CommandSpec::new("browser.ungroup", "Ungroup", ungroup).icon("folder").params("group: id (its items go back to the folder)"),
|
||||
.params("folder?: bodies|sketches|construction; component?: id (default 0); parent?: group id (the new group nests in it, taking its component and folder; at most 8 levels); name?; items?: [keys] (they leave other groups)"),
|
||||
CommandSpec::new("browser.ungroup", "Ungroup", ungroup).icon("folder").params("group: id (its items and child groups go up to the group that holds it, or the folder)"),
|
||||
CommandSpec::new("browser.rename_group", "Rename Group", rename_group).icon("rename").params("group: id, name"),
|
||||
CommandSpec::new("browser.move", "Move Browser Items", move_items).icon("reorder")
|
||||
.params("folder, component?, items: [keys]; group?: id (into it; omit to take them out of groups); before?: key (position in the group)"),
|
||||
.params("folder, component?, items: [keys (strings) and group ids (numbers)]; group?: id (into it; omit for the folder's top level); before?: key or group id (position in it; default the end). A group cannot go into itself or past 8 levels"),
|
||||
CommandSpec::new("document.units", "Change Design Units", document_units)
|
||||
.icon("settings")
|
||||
.params("units: mm|cm|m|in|ft (display and unit-less parameters; values keep their meaning)"),
|
||||
@@ -41,7 +41,7 @@ pub static COMMANDS: &[CommandSpec] = &[
|
||||
CommandSpec::new("view.apply", "Restore Named View", view_apply).icon("camera").noundo().params(
|
||||
"view?: front|back|top|bottom|left|right|iso|home (frames the model) | a named view's name; or camera?: {target: [x,y,z], yaw, pitch, distance, fov?, roll?} (radians, mm, about the up axis; yaw: heading of the line of sight, 0 along +Y (Front), π/2 along +X (Left), π (Back), −π/2 (Right); pitch: the eye's elevation, within ±π/2, π/2 = Top; roll defaults to 0, turns the picture clockwise, must be finite and is clamped to ±π; the View angles readout shows these in degrees; up is refused: the up axis comes from the design settings) → camera. The viewport (and the MCP screenshot) moves there. View state: not saved, not an undo step",
|
||||
),
|
||||
CommandSpec::new("browser.order", "Order Browser Items", order).icon("reorder").params("folder, component?, order: [keys] (the folder's items in display order)"),
|
||||
CommandSpec::new("browser.order", "Order Browser Items", order).icon("reorder").params("folder: bodies|sketches|construction|components, component?, order: [keys (strings) and top-level group ids (numbers)] (display order; for `components`, the ids of the components placed in it)"),
|
||||
CommandSpec::new("browser.visibility", "Show/Hide", visibility).icon("eye").params(
|
||||
"visible: bool; items?: [sketch id or name | body name | construction name | joint name | O|X|Y|Z|XY|XZ|YZ]; \
|
||||
or joints?: [joint id or name] (their glyphs; they still constrain and move parts); \
|
||||
@@ -98,11 +98,102 @@ fn group_id(s: &Session, p: &Value, cmd: &str) -> Result<u64> {
|
||||
if s.doc.browser_groups.iter().any(|g| g.id == id) { Ok(id) } else { Err(bad(cmd, format!("no group {id}"))) }
|
||||
}
|
||||
|
||||
/// A list of groups (numbers) and items (strings): a number is always a group.
|
||||
fn entries(p: &Value, k: &str, cmd: &str) -> Result<Vec<BrowserEntry>> {
|
||||
let list = match p.get(k) {
|
||||
Some(Value::Array(a)) => a
|
||||
.iter()
|
||||
.map(|x| match x {
|
||||
Value::String(s) => Some(BrowserEntry::Item(s.clone())),
|
||||
_ => x.as_u64().map(BrowserEntry::Group),
|
||||
})
|
||||
.collect::<Option<Vec<_>>>(),
|
||||
None => Some(Vec::new()),
|
||||
_ => None,
|
||||
};
|
||||
let list = list.ok_or_else(|| bad(cmd, format!("`{k}` must be a list of item keys (strings) and group ids (numbers)")))?;
|
||||
if list.len() > 10_000 || list.iter().any(|e| e.as_item().is_some_and(|k| k.is_empty() || k.len() > 256)) {
|
||||
return Err(bad(cmd, format!("`{k}` has bad keys")));
|
||||
}
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
/// A number in a list of items or an order names a group; say so when there is none, as a sketch
|
||||
/// or plane id passed as a number is the likely mistake (item keys are strings).
|
||||
fn no_group(cmd: &str, id: u64) -> crate::EngineError {
|
||||
bad(cmd, format!("no group {id} (a number is a group id; pass item keys as strings)"))
|
||||
}
|
||||
|
||||
/// The group a command names under `key`, if it names one (`null` or absent: none).
|
||||
fn optional_group(s: &Session, p: &Value, key: &str, cmd: &str) -> Result<Option<u64>> {
|
||||
match p.get(key) {
|
||||
None | Some(Value::Null) => Ok(None),
|
||||
Some(v) => {
|
||||
let id = v.as_u64().ok_or_else(|| bad(cmd, format!("`{key}` must be a group id")))?;
|
||||
if s.doc.browser_group(id).is_some() { Ok(Some(id)) } else { Err(no_group(cmd, id)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The list a group's contents (or the folder's top level, `None`) are ordered by.
|
||||
fn holder_list<'a>(d: &'a mut solidcraft_doc::Document, component: u64, folder: &str, holder: Option<u64>) -> Option<&'a mut Vec<BrowserEntry>> {
|
||||
match holder {
|
||||
Some(g) => d.browser_groups.iter_mut().find(|x| x.id == g).map(|g| &mut g.items),
|
||||
None => Some(d.browser_order.entry(format!("{component}/{folder}")).or_default()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Name every group under `holder` in its list, where they are drawn, so a position among them
|
||||
/// can be found.
|
||||
fn settle(d: &mut solidcraft_doc::Document, component: u64, folder: &str, holder: Option<u64>) {
|
||||
let all = d.ordered_entries(component, folder, holder);
|
||||
if let Some(list) = holder_list(d, component, folder, holder) {
|
||||
*list = all;
|
||||
}
|
||||
}
|
||||
|
||||
/// Take entries out of every list of a component's folder: its groups' and the top level's.
|
||||
fn take_out(d: &mut solidcraft_doc::Document, component: u64, folder: &str, gone: &[BrowserEntry]) {
|
||||
for g in d.browser_groups.iter_mut().filter(|g| g.component == component && g.folder == folder) {
|
||||
g.items.retain(|e| !gone.contains(e));
|
||||
}
|
||||
if let Some(list) = d.browser_order.get_mut(&format!("{component}/{folder}")) {
|
||||
list.retain(|e| !gone.contains(e));
|
||||
if list.is_empty() {
|
||||
d.browser_order.remove(&format!("{component}/{folder}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn group(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let cmd = "browser.group";
|
||||
let folder = folder_of(p, cmd)?;
|
||||
let component = component_of(s, p, cmd)?;
|
||||
let items = keys(p, "items", cmd)?;
|
||||
let parent = optional_group(s, p, "parent", cmd)?;
|
||||
let (mut component, mut folder) = (None, None);
|
||||
if let Some(g) = parent.and_then(|g| s.doc.browser_group(g)) {
|
||||
// Omitted, they come from the parent; given, they must agree with it.
|
||||
if p.get("component").is_some() && component_of(s, p, cmd)? != g.component {
|
||||
return Err(bad(cmd, "`parent` belongs to another component"));
|
||||
}
|
||||
if p.get("folder").is_some() && folder_of(p, cmd)? != g.folder {
|
||||
return Err(bad(cmd, "`parent` belongs to another folder"));
|
||||
}
|
||||
if s.doc.group_level(g.id) >= MAX_GROUP_NESTING {
|
||||
return Err(bad(cmd, format!("groups nest at most {MAX_GROUP_NESTING} levels")));
|
||||
}
|
||||
(component, folder) = (Some(g.component), Some(g.folder.clone()));
|
||||
}
|
||||
let folder = match folder {
|
||||
Some(f) => f,
|
||||
None => folder_of(p, cmd)?,
|
||||
};
|
||||
let component = match component {
|
||||
Some(c) => c,
|
||||
None => component_of(s, p, cmd)?,
|
||||
};
|
||||
let items = match entries(p, "items", cmd)?.into_iter().map(|e| e.as_item().map(str::to_string)).collect::<Option<Vec<_>>>() {
|
||||
Some(items) => items,
|
||||
None => return Err(bad(cmd, "`items` are item keys: move groups with browser.move")),
|
||||
};
|
||||
let d = s.doc_mut();
|
||||
let id = d.allocate_id()?;
|
||||
let name = match str_(p, "name").map(str::trim).filter(|n| !n.is_empty()) {
|
||||
@@ -110,16 +201,32 @@ fn group(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
Some(_) => return Err(bad(cmd, "`name` is too long")),
|
||||
None => format!("Group{}", d.browser_groups.iter().filter(|g| g.folder == folder).count() + 1),
|
||||
};
|
||||
for g in d.browser_groups.iter_mut().filter(|g| g.component == component && g.folder == folder) {
|
||||
g.items.retain(|k| !items.contains(k));
|
||||
}
|
||||
d.browser_groups.push(solidcraft_doc::BrowserGroup { id, name: name.clone(), component, folder, items });
|
||||
Ok(json!({ "group": id, "name": name }))
|
||||
let gone: Vec<BrowserEntry> = items.iter().map(|k| BrowserEntry::item(k)).collect();
|
||||
take_out(d, component, &folder, &gone);
|
||||
let items = gone;
|
||||
d.browser_groups.push(solidcraft_doc::BrowserGroup { id, name: name.clone(), component, folder, parent, items });
|
||||
Ok(json!({ "group": id, "name": name, "parent": parent }))
|
||||
}
|
||||
|
||||
/// A group's contents go up to the group that holds it (the folder's top level for a top-level
|
||||
/// group), in the place the group had; so do its child groups. Its own entry goes.
|
||||
fn ungroup(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let id = group_id(s, p, "browser.ungroup")?;
|
||||
s.doc_mut().browser_groups.retain(|g| g.id != id);
|
||||
let d = s.doc_mut();
|
||||
let Some(g) = d.browser_group(id).cloned() else { return Ok(json!({ "group": id })) };
|
||||
settle(d, g.component, &g.folder, Some(id));
|
||||
settle(d, g.component, &g.folder, g.parent);
|
||||
let inside = d.browser_group(id).map(|g| g.items.clone()).unwrap_or_default();
|
||||
for child in d.browser_groups.iter_mut().filter(|c| c.parent == Some(id)) {
|
||||
child.parent = g.parent;
|
||||
}
|
||||
if let Some(list) = holder_list(d, g.component, &g.folder, g.parent)
|
||||
&& let Some(at) = list.iter().position(|e| *e == BrowserEntry::Group(id))
|
||||
{
|
||||
list.splice(at..=at, inside);
|
||||
}
|
||||
take_out(d, g.component, &g.folder, &[BrowserEntry::Group(id)]);
|
||||
d.browser_groups.retain(|x| x.id != id);
|
||||
Ok(json!({ "group": id }))
|
||||
}
|
||||
|
||||
@@ -138,43 +245,91 @@ fn move_items(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let cmd = "browser.move";
|
||||
let folder = folder_of(p, cmd)?;
|
||||
let component = component_of(s, p, cmd)?;
|
||||
let items = keys(p, "items", cmd)?;
|
||||
let items = entries(p, "items", cmd)?;
|
||||
if items.is_empty() {
|
||||
return Err(bad(cmd, "`items` must list item keys"));
|
||||
return Err(bad(cmd, "`items` must list item keys or group ids"));
|
||||
}
|
||||
let to = match p.get("group") {
|
||||
Some(Value::Null) | None => None,
|
||||
Some(_) => Some(group_id(s, p, cmd)?),
|
||||
};
|
||||
let to = optional_group(s, p, "group", cmd)?;
|
||||
if let Some(g) = to
|
||||
&& !s.doc.browser_groups.iter().any(|x| x.id == g && x.component == component && x.folder == folder)
|
||||
{
|
||||
return Err(bad(cmd, "that group is in another folder"));
|
||||
}
|
||||
let before = str_(p, "before").map(str::to_string);
|
||||
let d = s.doc_mut();
|
||||
for g in d.browser_groups.iter_mut().filter(|g| g.component == component && g.folder == folder) {
|
||||
g.items.retain(|k| !items.contains(k));
|
||||
if Some(g.id) == to {
|
||||
let at = before.as_ref().and_then(|b| g.items.iter().position(|k| k == b)).unwrap_or(g.items.len());
|
||||
for (i, k) in items.iter().enumerate() {
|
||||
g.items.insert(at + i, k.clone());
|
||||
}
|
||||
let to_level = to.map_or(0, |g| s.doc.group_level(g));
|
||||
for e in &items {
|
||||
let BrowserEntry::Group(id) = e else { continue };
|
||||
let Some(g) = s.doc.browser_group(*id) else { return Err(no_group(cmd, *id)) };
|
||||
if g.component != component || g.folder != folder {
|
||||
return Err(bad(cmd, format!("group {id} is in another folder")));
|
||||
}
|
||||
if to.is_some_and(|t| s.doc.group_within(t, *id)) {
|
||||
return Err(bad(cmd, format!("group {id} cannot go inside itself")));
|
||||
}
|
||||
if to_level + s.doc.group_height(*id) > MAX_GROUP_NESTING {
|
||||
return Err(bad(cmd, format!("groups nest at most {MAX_GROUP_NESTING} levels")));
|
||||
}
|
||||
}
|
||||
let before = match p.get("before") {
|
||||
None | Some(Value::Null) => None,
|
||||
Some(Value::String(k)) => Some(BrowserEntry::Item(k.clone())),
|
||||
Some(v) => {
|
||||
let id = v.as_u64().ok_or_else(|| bad(cmd, "`before` must be an item key or a group id"))?;
|
||||
if s.doc.browser_group(id).is_none() {
|
||||
return Err(no_group(cmd, id));
|
||||
}
|
||||
Some(BrowserEntry::Group(id))
|
||||
}
|
||||
};
|
||||
let d = s.doc_mut();
|
||||
// Where a group list is silent about its child groups, the ones it names come first: name
|
||||
// them all, so `before` can be one of them.
|
||||
settle(d, component, &folder, to);
|
||||
take_out(d, component, &folder, &items);
|
||||
for e in &items {
|
||||
if let BrowserEntry::Group(id) = e
|
||||
&& let Some(g) = d.browser_groups.iter_mut().find(|g| g.id == *id)
|
||||
{
|
||||
g.parent = to;
|
||||
}
|
||||
}
|
||||
if let Some(list) = holder_list(d, component, &folder, to) {
|
||||
let at = before.as_ref().and_then(|b| list.iter().position(|e| e == b)).unwrap_or(list.len());
|
||||
list.splice(at..at, items.iter().cloned());
|
||||
}
|
||||
Ok(json!({ "items": items, "group": to }))
|
||||
}
|
||||
|
||||
fn order(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let cmd = "browser.order";
|
||||
let folder = folder_of(p, cmd)?;
|
||||
let component = component_of(s, p, cmd)?;
|
||||
let order = keys(p, "order", cmd)?;
|
||||
let components = str_(p, "folder") == Some("components");
|
||||
let folder = if components { "components".to_string() } else { folder_of(p, cmd)? };
|
||||
let order = entries(p, "order", cmd)?;
|
||||
for e in &order {
|
||||
match e {
|
||||
// Sub-components, by placement id, all of them placed in this component.
|
||||
BrowserEntry::Item(k) if components => {
|
||||
let placed = k.parse::<u64>().ok().is_some_and(|o| s.doc.occurrences.iter().any(|x| x.id == o && x.parent == component));
|
||||
if !placed {
|
||||
return Err(bad(cmd, "the `components` folder is ordered by the ids of the components placed in it"));
|
||||
}
|
||||
}
|
||||
BrowserEntry::Item(_) => {}
|
||||
BrowserEntry::Group(id) => match s.doc.browser_group(*id) {
|
||||
None => return Err(no_group(cmd, *id)),
|
||||
Some(g) if components || g.component != component || g.folder != folder || g.parent.is_some() => {
|
||||
return Err(bad(cmd, format!("group {id} is not a top-level group of this folder")));
|
||||
}
|
||||
Some(_) => {}
|
||||
},
|
||||
}
|
||||
}
|
||||
let key = format!("{component}/{folder}");
|
||||
let d = s.doc_mut();
|
||||
if order.is_empty() {
|
||||
d.browser_order.remove(&key);
|
||||
} else {
|
||||
// An item that sits in a group belongs to the group's list only.
|
||||
d.browser_order.insert(key, order);
|
||||
}
|
||||
Ok(json!({}))
|
||||
@@ -1731,6 +1886,43 @@ mod tests {
|
||||
assert!(crate::view::sketch_shown(&s, wall) && !crate::view::sketch_shown(&s, base));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_hole_at_sketch_points_hides_its_sketch_as_an_extrude_does() {
|
||||
// #9082: a Hole's points sketch stayed shown, and its points were drawn in the holes. A
|
||||
// Single hole snapped to one point (#2110) leaves its sketch shown: it was placed on a face,
|
||||
// and a hidden sketch offers no points for the next one to snap to.
|
||||
let mut s = Session::default();
|
||||
let plate = run(&mut s, "sketch.create", json!({"plane": "XY", "name": "PlateSketch"}))["sketch"].as_u64().unwrap();
|
||||
run(&mut s, "sketch.rectangle.two_point", json!({"p0": [0, 0], "p1": [40, 30]}));
|
||||
run(&mut s, "sketch.finish", json!({}));
|
||||
run(&mut s, "solid.extrude", json!({"sketch": plate, "distance": 6}));
|
||||
assert!(!crate::view::sketch_shown(&s, plate), "the Extrude used its sketch");
|
||||
let pts = run(&mut s, "sketch.create", json!({"plane": "XY", "name": "HolePts", "offset": 6}))["sketch"].as_u64().unwrap();
|
||||
run(&mut s, "sketch.point", json!({"point": [10, 10]}));
|
||||
run(&mut s, "sketch.point", json!({"point": [30, 20]}));
|
||||
run(&mut s, "sketch.finish", json!({}));
|
||||
let spot = run(&mut s, "sketch.create", json!({"plane": "XY", "name": "Spot", "offset": 6}))["sketch"].as_u64().unwrap();
|
||||
run(&mut s, "sketch.point", json!({"point": [20, 15], "id": "c"}));
|
||||
run(&mut s, "sketch.finish", json!({}));
|
||||
assert!(crate::view::sketch_shown(&s, pts) && crate::view::sketch_shown(&s, spot), "unused sketches are shown");
|
||||
run(&mut s, "solid.hole", json!({"sketch": "HolePts", "diameter": 3, "name": "Holes"}));
|
||||
assert!(!crate::view::sketch_shown(&s, pts), "the Hole used its points sketch");
|
||||
run(&mut s, "solid.hole", json!({"sketch": "Spot", "points": ["c"], "single": true, "diameter": 3, "depth": 3, "name": "One"}));
|
||||
assert!(crate::view::sketch_shown(&s, spot), "a Single hole leaves the sketch it snapped to shown");
|
||||
let d = run(&mut s, "document.inspect", json!({}));
|
||||
let visible = |id: u64| d["sketches"].as_array().unwrap().iter().find(|x| x["id"] == id).unwrap()["visible"].clone();
|
||||
assert_eq!((visible(pts), visible(spot)), (json!(false), json!(true)), "{d}");
|
||||
// While the Hole is edited it is drawn, then hidden again; shown on its own, it stays drawn.
|
||||
s.feature_edit_sketch = Some(pts);
|
||||
assert!(crate::view::sketch_shown(&s, pts));
|
||||
s.feature_edit_sketch = None;
|
||||
assert!(!crate::view::sketch_shown(&s, pts));
|
||||
run(&mut s, "browser.visibility", json!({"items": [pts], "visible": true}));
|
||||
assert!(crate::view::sketch_shown(&s, pts) && !crate::view::sketch_shown(&s, plate));
|
||||
run(&mut s, "browser.visibility", json!({"items": [pts], "visible": false}));
|
||||
assert!(!crate::view::sketch_shown(&s, pts));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn helper_sketches_hide_and_show() {
|
||||
let mut s = Session::default();
|
||||
@@ -1985,17 +2177,17 @@ mod tests {
|
||||
let g = run(&mut s, "browser.group", json!({"folder": "bodies", "items": [a.clone()]}))["group"].as_u64().unwrap();
|
||||
assert_eq!(s.doc.browser_groups[0].name, "Group1");
|
||||
run(&mut s, "browser.move", json!({"folder": "bodies", "items": [b.clone()], "group": g, "before": a.clone()}));
|
||||
assert_eq!(s.doc.browser_groups[0].items, vec![b.clone(), a.clone()]);
|
||||
assert_eq!(s.doc.browser_groups[0].items, vec![b.clone().into(), a.clone().into()]);
|
||||
run(&mut s, "browser.rename_group", json!({"group": g, "name": "Fasteners"}));
|
||||
run(&mut s, "body.rename", json!({"body": a, "name": "Bolt"}));
|
||||
assert_eq!(s.doc.browser_groups[0].items, vec![b.clone(), "Bolt".to_string()]);
|
||||
assert_eq!(s.doc.browser_groups[0].items, vec![b.clone().into(), "Bolt".into()]);
|
||||
// Out of the group, then a folder order; saved with the design.
|
||||
run(&mut s, "browser.move", json!({"folder": "bodies", "items": [b.clone()]}));
|
||||
assert_eq!(s.doc.browser_groups[0].items, vec!["Bolt".to_string()]);
|
||||
assert_eq!(s.doc.browser_groups[0].items, vec!["Bolt".into()]);
|
||||
run(&mut s, "browser.order", json!({"folder": "bodies", "order": [b.clone(), "Bolt"]}));
|
||||
let saved: solidcraft_doc::Document = serde_json::from_str(&serde_json::to_string(&*s.doc).unwrap()).unwrap();
|
||||
assert_eq!(saved.browser_groups, s.doc.browser_groups);
|
||||
assert_eq!(saved.browser_order["0/bodies"], vec![b, "Bolt".to_string()]);
|
||||
assert_eq!(saved.browser_order["0/bodies"], vec![b.into(), "Bolt".into()]);
|
||||
run(&mut s, "browser.ungroup", json!({"group": g}));
|
||||
assert!(s.doc.browser_groups.is_empty());
|
||||
// Hostile input is refused.
|
||||
@@ -2178,4 +2370,107 @@ mod tests {
|
||||
let e = s.execute("browser.visibility", &json!({"visible": false, "features": ["Sphere1"], "path": [first]})).expect_err("other component");
|
||||
assert!(e.to_string().contains("component"), "{e}");
|
||||
}
|
||||
|
||||
use solidcraft_doc::{BrowserEntry, MAX_GROUP_NESTING};
|
||||
|
||||
fn three_bodies(s: &mut Session) {
|
||||
for (i, name) in ["A", "B", "C"].into_iter().enumerate() {
|
||||
run(s, "solid.box", json!({"length": 10, "width": 10, "height": 10, "corner": [i as f64 * 20.0, 0, 0], "body_name": name}));
|
||||
}
|
||||
}
|
||||
|
||||
fn new_group(s: &mut Session, mut p: Value) -> u64 {
|
||||
p["folder"] = json!("bodies");
|
||||
run(s, "browser.group", p)["group"].as_u64().unwrap()
|
||||
}
|
||||
|
||||
fn state(s: &Session) -> (Vec<solidcraft_doc::BrowserGroup>, std::collections::BTreeMap<String, Vec<BrowserEntry>>) {
|
||||
(s.doc.browser_groups.clone(), s.doc.browser_order.clone())
|
||||
}
|
||||
|
||||
fn refused(s: &mut Session, id: &str, p: Value) {
|
||||
let before = s.doc.to_json();
|
||||
let r = s.execute(id, &p);
|
||||
assert!(matches!(r, Err(crate::EngineError::BadParams { .. })), "{id} {p}: {r:?}");
|
||||
assert_eq!(s.doc.to_json(), before, "{id} {p}: a refused command changes nothing");
|
||||
}
|
||||
|
||||
/// Nest a group, order groups and components, ungroup; each step undoes and redoes, and none of
|
||||
/// it evaluates anything.
|
||||
#[test]
|
||||
fn groups_nest_reorder_and_ungroup_with_undo_redo_and_no_evaluation() {
|
||||
let mut s = Session::default();
|
||||
three_bodies(&mut s);
|
||||
let mut placed = Vec::new();
|
||||
for name in ["P", "Q"] {
|
||||
placed.push(run(&mut s, "component.create", json!({"name": name}))["occurrence"].as_u64().unwrap());
|
||||
run(&mut s, "component.activate", json!({"component": 0}));
|
||||
}
|
||||
let mut states = vec![state(&s)];
|
||||
s.evaluations = 0;
|
||||
let outer = new_group(&mut s, json!({"items": ["A"]}));
|
||||
states.push(state(&s));
|
||||
// The inner group takes its component and folder from the parent.
|
||||
let inner = run(&mut s, "browser.group", json!({"parent": outer, "items": ["B"]}))["group"].as_u64().unwrap();
|
||||
states.push(state(&s));
|
||||
let g = s.doc.browser_group(inner).unwrap();
|
||||
assert_eq!((g.parent, g.component, g.folder.as_str()), (Some(outer), 0, "bodies"));
|
||||
run(&mut s, "browser.order", json!({"folder": "components", "order": [placed[1].to_string(), placed[0].to_string()]}));
|
||||
states.push(state(&s));
|
||||
let mut shown = placed.clone();
|
||||
s.doc.sort_placements(0, &mut shown);
|
||||
assert_eq!(shown, [placed[1], placed[0]]);
|
||||
run(&mut s, "browser.order", json!({"folder": "bodies", "order": ["C", outer]}));
|
||||
states.push(state(&s));
|
||||
run(&mut s, "browser.move", json!({"folder": "bodies", "items": [inner], "before": "C"}));
|
||||
states.push(state(&s));
|
||||
assert_eq!(s.doc.ordered_entries(0, "bodies", None), [BrowserEntry::Group(inner), "C".into(), BrowserEntry::Group(outer)]);
|
||||
run(&mut s, "browser.ungroup", json!({"group": outer}));
|
||||
states.push(state(&s));
|
||||
// The group's items go where it was.
|
||||
assert_eq!(s.doc.browser_order["0/bodies"], [BrowserEntry::Group(inner), "C".into(), "A".into()]);
|
||||
for i in (0..states.len() - 1).rev() {
|
||||
run(&mut s, "edit.undo", json!({}));
|
||||
assert_eq!(state(&s), states[i], "undo to step {i}");
|
||||
}
|
||||
for (i, expected) in states.iter().enumerate().skip(1) {
|
||||
run(&mut s, "edit.redo", json!({}));
|
||||
assert_eq!(state(&s), *expected, "redo to step {i}");
|
||||
}
|
||||
assert_eq!(s.evaluations, 0, "changing only the browser evaluates nothing");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nesting_and_ordering_that_cannot_stand_are_refused() {
|
||||
let mut s = Session::default();
|
||||
three_bodies(&mut s);
|
||||
let part = run(&mut s, "component.create", json!({"name": "Part"}));
|
||||
run(&mut s, "component.activate", json!({"component": 0}));
|
||||
let outer = new_group(&mut s, json!({"items": ["A"]}));
|
||||
let inner = new_group(&mut s, json!({"parent": outer}));
|
||||
// A group inside itself or its own child.
|
||||
refused(&mut s, "browser.move", json!({"folder": "bodies", "items": [outer], "group": outer}));
|
||||
refused(&mut s, "browser.move", json!({"folder": "bodies", "items": [outer], "group": inner}));
|
||||
// Past eight levels, by a new group or by moving one with a child of its own.
|
||||
let mut chain = vec![new_group(&mut s, json!({}))];
|
||||
while chain.len() < MAX_GROUP_NESTING {
|
||||
chain.push(new_group(&mut s, json!({"parent": chain[chain.len() - 1]})));
|
||||
}
|
||||
refused(&mut s, "browser.group", json!({"folder": "bodies", "parent": chain[MAX_GROUP_NESTING - 1]}));
|
||||
let heavy = new_group(&mut s, json!({}));
|
||||
new_group(&mut s, json!({"parent": heavy}));
|
||||
refused(&mut s, "browser.move", json!({"folder": "bodies", "items": [heavy], "group": chain[MAX_GROUP_NESTING - 2]}));
|
||||
// A parent that is missing, or in another folder or component.
|
||||
refused(&mut s, "browser.group", json!({"folder": "bodies", "parent": 424242}));
|
||||
refused(&mut s, "browser.group", json!({"folder": "sketches", "parent": outer}));
|
||||
refused(&mut s, "browser.group", json!({"parent": outer, "component": part["component"]}));
|
||||
// Orders and moves naming a group that cannot be there; a number is a group, not an item key.
|
||||
refused(&mut s, "browser.order", json!({"folder": "bodies", "order": [inner]}));
|
||||
refused(&mut s, "browser.order", json!({"folder": "bodies", "order": [424242]}));
|
||||
refused(&mut s, "browser.move", json!({"folder": "bodies", "items": ["B"], "before": 424242}));
|
||||
refused(&mut s, "browser.group", json!({"folder": "bodies", "items": [12]}));
|
||||
// The components folder takes placements of that component, and nothing else.
|
||||
refused(&mut s, "browser.order", json!({"folder": "components", "order": ["424242"]}));
|
||||
refused(&mut s, "browser.order", json!({"folder": "components", "order": [outer]}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@ pub static COMMANDS: &[CommandSpec] = &[
|
||||
CommandSpec::new("timeline.paste", "Paste Features", paste).icon("paste").params(
|
||||
"translate?: [x,y,z] (moves the copies' geometry); component?: where the copies go (default: the active one), \
|
||||
component_path?: its placement (default: the active one, else its only one); the copies go at the marker, each where \
|
||||
its original is in the world (copies into their own component keep its coordinates); body transfers are left out",
|
||||
its original is in the world (copies into their own component keep its coordinates); body transfers are left out; \
|
||||
from_design?: true to paste sketch features from the host's cross-design clipboard snapshot",
|
||||
),
|
||||
];
|
||||
|
||||
@@ -35,6 +36,7 @@ fn copy(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let copied: Vec<solidcraft_doc::Feature> = s.doc.features.iter().filter(|f| ids.contains(&f.id)).cloned().collect();
|
||||
let names: Vec<String> = copied.iter().map(|f| f.name.clone()).collect();
|
||||
s.clipboard = copied;
|
||||
s.foreign_copy = Some(std::sync::Arc::new(crate::ForeignCopy::of_features(s)));
|
||||
Ok(json!({"copied": names}))
|
||||
}
|
||||
|
||||
@@ -55,6 +57,11 @@ pub(in crate::cmd) fn explicit_planes(p: &mut PlaneRef) {
|
||||
|
||||
fn paste(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let cmd = "timeline.paste";
|
||||
match p.get("from_design") {
|
||||
Some(Value::Bool(true)) => return super::clipboard_foreign::paste(s, p),
|
||||
None | Some(Value::Bool(false)) => {}
|
||||
_ => return Err(bad(cmd, "`from_design` must be true or false")),
|
||||
}
|
||||
if s.clipboard.is_empty() {
|
||||
return Err(bad(cmd, "nothing copied (timeline.copy first)"));
|
||||
}
|
||||
@@ -249,6 +256,212 @@ mod tests {
|
||||
run(s, "inspect.measure", json!({}))["total"]["volume_mm3"].as_f64().unwrap_or(f64::NAN)
|
||||
}
|
||||
|
||||
fn dimensioned_sketch() -> Session {
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "parameters.change", json!({"name": "base", "expression": "5 mm"}));
|
||||
run(&mut s, "parameters.change", json!({"name": "width", "expression": "base * 4"}));
|
||||
run(&mut s, "sketch.create", json!({"plane": "XY", "name": "S"}));
|
||||
run(&mut s, "sketch.rectangle.two_point", json!({"p0": [0, 0], "p1": [20, 10]}));
|
||||
run(&mut s, "sketch.constraint.coincident", json!({"a": "p1", "b": "origin"}));
|
||||
let width = run(&mut s, "sketch.dimension", json!({"entities": ["l1"], "value": "width"}))["param"].as_str().unwrap().to_string();
|
||||
run(&mut s, "sketch.dimension", json!({"entities": ["l2"], "value": format!("{width} / 2")}));
|
||||
run(&mut s, "sketch.finish", json!({}));
|
||||
s
|
||||
}
|
||||
|
||||
fn sketch_area(s: &Session, id: u64) -> f64 {
|
||||
s.model.state().sketch(id).unwrap().profiles.iter().map(|p| p.area).sum()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn foreign_sketch_paste_keeps_parameter_graph_snapshot_and_undo() {
|
||||
let mut source = dimensioned_sketch();
|
||||
run(&mut source, "timeline.copy", json!({"features": ["S"]}));
|
||||
let mut target = dimensioned_sketch();
|
||||
run(&mut target, "document.units", json!({"units": "in"}));
|
||||
run(&mut target, "parameters.change", json!({"name": "base", "expression": "7 mm"}));
|
||||
target.foreign_copy = source.foreign_copy.clone();
|
||||
run(&mut source, "parameters.change", json!({"name": "base", "expression": "30 mm"}));
|
||||
drop(source);
|
||||
let before = target.doc.to_json();
|
||||
let undo = target.undo.len();
|
||||
let result = run(&mut target, "timeline.paste", json!({"from_design": true}));
|
||||
let id = result["pasted"][0]["id"].as_u64().unwrap();
|
||||
assert_eq!(result["pasted"][0]["name"], "S (2)");
|
||||
assert!((sketch_area(&target, id) - 200.).abs() < 1e-6);
|
||||
assert_eq!(target.doc.param("base").unwrap().expr, "7 mm");
|
||||
assert_eq!(target.doc.param("base_2").unwrap().expr, "5 mm");
|
||||
assert_eq!(target.doc.param("width_2").unwrap().expr, "base_2 * 4");
|
||||
assert_eq!(target.undo.len(), undo + 1);
|
||||
let after = target.doc.to_json();
|
||||
run(&mut target, "edit.undo", json!({}));
|
||||
assert_eq!(target.doc.to_json(), before);
|
||||
run(&mut target, "edit.redo", json!({}));
|
||||
assert_eq!(target.doc.to_json(), after);
|
||||
let second = run(&mut target, "timeline.paste", json!({"from_design": true}))["pasted"][0]["id"].as_u64().unwrap();
|
||||
assert!((sketch_area(&target, second) - 200.).abs() < 1e-6);
|
||||
run(&mut target, "parameters.change", json!({"name": "base_2", "expression": "10 mm"}));
|
||||
assert!((sketch_area(&target, id) - 800.).abs() < 1e-6);
|
||||
assert!((sketch_area(&target, second) - 200.).abs() < 1e-6, "each paste owns its parameters");
|
||||
assert!(solidcraft_doc::Document::from_json(&target.doc.to_json()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn foreign_sketch_paste_preserves_world_position_in_a_placed_component() {
|
||||
use solidcraft_geom::Vec3;
|
||||
|
||||
let mut source = dimensioned_sketch();
|
||||
let arc = [Vec3::new(1., 0., 0.), Vec3::new(0., 0., 1.), Vec3::new(-1., 0., 0.)];
|
||||
for space in [solidcraft_sketch::WireSpace::World, solidcraft_sketch::WireSpace::Component] {
|
||||
source.doc_mut().sketch_mut(1).unwrap().add_drawn_arc_in(&arc, space).unwrap();
|
||||
}
|
||||
source.refresh();
|
||||
run(&mut source, "timeline.copy", json!({"features": ["S"]}));
|
||||
let mut target = Session::default();
|
||||
let made = run(&mut target, "component.create", json!({"name": "Target", "activate": false}));
|
||||
let component = made["component"].as_u64().unwrap();
|
||||
let occurrence = made["occurrence"].as_u64().unwrap();
|
||||
run(&mut target, "occurrence.ground", json!({"occurrence": occurrence, "grounded": false}));
|
||||
run(&mut target, "occurrence.move", json!({"occurrence": occurrence, "translate": [100, 20, 30], "axis": [0, 0, 1], "angle": "90 deg"}));
|
||||
target.foreign_copy = source.foreign_copy.clone();
|
||||
let id = run(&mut target, "timeline.paste", json!({"from_design": true, "component": component, "component_path": [occurrence]}))["pasted"]
|
||||
[0]["id"]
|
||||
.as_u64()
|
||||
.unwrap();
|
||||
let original = source.world_state().sketch(1).unwrap().plane;
|
||||
let pasted = target.world_state().sketch(id).unwrap().plane;
|
||||
assert!(pasted.origin.dist(original.origin) < 1e-6, "{original:?} vs {pasted:?}");
|
||||
assert!(pasted.x.dist(original.x) < 1e-6);
|
||||
assert!(pasted.y.dist(original.y) < 1e-6);
|
||||
assert!((sketch_area(&target, id) - 200.).abs() < 1e-6);
|
||||
assert_eq!(target.doc.feature(id).unwrap().component, component);
|
||||
for (original, pasted) in
|
||||
source.world_state().sketch(1).unwrap().sketch.wires.iter().zip(&target.world_state().sketch(id).unwrap().sketch.wires)
|
||||
{
|
||||
for (a, b) in original.pts.iter().chain(&original.fit).zip(pasted.pts.iter().chain(&pasted.fit)) {
|
||||
assert!(a.dist(*b) < 1e-6, "wire {:?}: {a:?} vs {b:?}", original.space);
|
||||
}
|
||||
let (a, b) = (original.arc.unwrap(), pasted.arc.unwrap());
|
||||
assert!(a.center.dist(b.center) < 1e-6);
|
||||
assert!(a.normal.dist(b.normal) < 1e-6);
|
||||
assert!((a.radius - b.radius).abs() < 1e-6);
|
||||
}
|
||||
let moved = run(
|
||||
&mut target,
|
||||
"timeline.paste",
|
||||
json!({"from_design": true, "component": component, "component_path": [occurrence], "translate": [2, 3, 4]}),
|
||||
)["pasted"][0]["id"]
|
||||
.as_u64()
|
||||
.unwrap();
|
||||
let frame = target.placed_document().path_transform(component, &[occurrence]).unwrap();
|
||||
let delta = solidcraft_doc::apply_vector(&frame, Vec3::new(2., 3., 4.));
|
||||
for (original, pasted) in
|
||||
source.world_state().sketch(1).unwrap().sketch.wires.iter().zip(&target.world_state().sketch(moved).unwrap().sketch.wires)
|
||||
{
|
||||
for (a, b) in original.pts.iter().chain(&original.fit).zip(pasted.pts.iter().chain(&pasted.fit)) {
|
||||
assert!((*a + delta).dist(*b) < 1e-6, "translated wire {:?}: {a:?} vs {b:?}", original.space);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn foreign_sketch_paste_refuses_external_references_and_bad_parameters_atomically() {
|
||||
let mut source = dimensioned_sketch();
|
||||
let mut target = dimensioned_sketch();
|
||||
// The same numeric ID in the target must never become the sketch's missing plane.
|
||||
if let solidcraft_doc::FeatureKind::Sketch { plane, .. } = &mut source.doc_mut().feature_mut(1).unwrap().kind {
|
||||
*plane = solidcraft_doc::PlaneRef::Construction { feature: 1.into() };
|
||||
}
|
||||
run(&mut source, "timeline.copy", json!({"features": ["S"]}));
|
||||
target.foreign_copy = source.foreign_copy.clone();
|
||||
let before = target.doc.to_json();
|
||||
let undo = target.undo.len();
|
||||
let error = target.execute("timeline.paste", &json!({"from_design": true})).unwrap_err().to_string();
|
||||
assert!(error.contains("fixed plane"), "{error}");
|
||||
assert_eq!(target.doc.to_json(), before);
|
||||
assert_eq!(target.undo.len(), undo);
|
||||
for params in
|
||||
[json!({"from_design":"yes"}), json!({"from_design":true,"translate":"bad"}), json!({"from_design":true,"component_path":["bad"]})]
|
||||
{
|
||||
assert!(target.execute("timeline.paste", ¶ms).is_err());
|
||||
assert_eq!(target.doc.to_json(), before);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn foreign_sketch_paste_remaps_links_and_rejects_missing_source_sketches() {
|
||||
let mut source = dimensioned_sketch();
|
||||
run(&mut source, "sketch.create", json!({"plane":"XY","offset":5,"name":"Projected"}));
|
||||
run(&mut source, "sketch.project", json!({"refs":[{"sketch":1,"curve":"l1"}]}));
|
||||
run(&mut source, "sketch.finish", json!({}));
|
||||
let mut target = dimensioned_sketch();
|
||||
run(&mut source, "timeline.copy", json!({"features":["Projected"]}));
|
||||
target.foreign_copy = source.foreign_copy.clone();
|
||||
let before = target.doc.to_json();
|
||||
let err = target.execute("timeline.paste", &json!({"from_design":true})).unwrap_err().to_string();
|
||||
assert!(err.contains("external projected geometry"), "{err}");
|
||||
assert_eq!(before, target.doc.to_json());
|
||||
run(&mut source, "timeline.copy", json!({"features":["S","Projected"]}));
|
||||
target.foreign_copy = source.foreign_copy.clone();
|
||||
let result = run(&mut target, "timeline.paste", json!({"from_design":true}));
|
||||
let first = result["pasted"][0]["id"].as_u64().unwrap();
|
||||
let second = result["pasted"][1]["id"].as_u64().unwrap();
|
||||
let copied = target.doc.sketch(second).unwrap();
|
||||
assert!(matches!(copied.links[0].source, solidcraft_sketch::LinkSource::SketchCurve { sketch, .. } if sketch == first));
|
||||
assert!(target.model.result(second).unwrap().error.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn foreign_sketch_paste_keeps_plane_input_units_and_text_parameters() {
|
||||
let mut source = Session::default();
|
||||
run(&mut source, "document.units", json!({"units":"in"}));
|
||||
run(&mut source, "parameters.add", json!({"name":"height","expression":"2","unit":""}));
|
||||
run(&mut source, "parameters.add", json!({"name":"Label","expression":"ABC","unit":"text"}));
|
||||
run(&mut source, "sketch.create", json!({"plane":"XY","offset":"height","name":"Text"}));
|
||||
run(&mut source, "sketch.text", json!({"text_expression":"Label + '-X'","at":[0,0],"height":"5 mm"}));
|
||||
run(&mut source, "sketch.finish", json!({}));
|
||||
run(&mut source, "timeline.copy", json!({"features":["Text"]}));
|
||||
let mut target = Session::default();
|
||||
run(&mut target, "parameters.add", json!({"name":"Label","expression":"other","unit":"text"}));
|
||||
run(&mut target, "parameters.add", json!({"name":"d1","expression":"reserved","unit":"text"}));
|
||||
target.foreign_copy = source.foreign_copy.clone();
|
||||
let id = run(&mut target, "timeline.paste", json!({"from_design":true}))["pasted"][0]["id"].as_u64().unwrap();
|
||||
assert!((target.model.state().sketch(id).unwrap().plane.origin.z - 50.8).abs() < 1e-9);
|
||||
assert_eq!(target.doc.text_param("Label").unwrap().text, "other");
|
||||
assert_eq!(target.doc.text_param("Label_2").unwrap().text, "ABC");
|
||||
assert_eq!(target.doc.text_param("d1").unwrap().text, "reserved");
|
||||
assert!(target.doc.param("d1").is_none());
|
||||
let state = target.model.state();
|
||||
let text = &state.sketch(id).unwrap().sketch.links[0].source;
|
||||
assert!(matches!(text, solidcraft_sketch::LinkSource::Text { text, .. } if text == "ABC-X"));
|
||||
assert!(solidcraft_doc::Document::from_json(&target.doc.to_json()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn foreign_sketch_paste_copies_equation_parameters_but_not_the_local_variable() {
|
||||
let mut source = Session::default();
|
||||
run(&mut source, "parameters.add", json!({"name":"height","expression":"3 mm"}));
|
||||
run(&mut source, "sketch.create", json!({"plane":"XY","name":"Curve"}));
|
||||
run(&mut source, "sketch.equation_curve", json!({"x":"t","y":"height + t * t","start":-2,"end":2}));
|
||||
run(&mut source, "sketch.finish", json!({}));
|
||||
run(&mut source, "timeline.copy", json!({"features":["Curve"]}));
|
||||
let mut target = Session::default();
|
||||
run(&mut target, "parameters.add", json!({"name":"height","expression":"99 mm"}));
|
||||
target.foreign_copy = source.foreign_copy.clone();
|
||||
let id = run(&mut target, "timeline.paste", json!({"from_design":true}))["pasted"][0]["id"].as_u64().unwrap();
|
||||
let state = target.model.state();
|
||||
let sketch = &state.sketch(id).unwrap().sketch;
|
||||
let equation = &sketch.equations[0];
|
||||
assert_eq!(equation.x.as_deref(), Some("t"));
|
||||
assert_eq!(equation.y, "height_2 + t * t");
|
||||
let curve = sketch.curves.iter().find(|c| c.id == equation.curve).unwrap();
|
||||
for point in curve.kind.point_ids() {
|
||||
let p = sketch.points[point].pos;
|
||||
assert!((p.y - 3. - p.x * p.x).abs() < 1e-8, "{p:?}");
|
||||
}
|
||||
assert!(target.doc.param("t").is_none());
|
||||
}
|
||||
|
||||
/// Features pasted into a component placed elsewhere (offset and turned) are carried into its
|
||||
/// frame: the pasted body sits where the original is in the world, in that component, and the
|
||||
/// pasted sketch is named after the original.
|
||||
|
||||
@@ -0,0 +1,310 @@
|
||||
//! Independent sketch features pasted from the immutable clipboard of another design.
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use solidcraft_doc::{Document, Feature, FeatureKind, Mat, PlaneRef};
|
||||
|
||||
use crate::params::{bad, vec3};
|
||||
use crate::{Result, Sel, Session};
|
||||
|
||||
const CMD: &str = "timeline.paste";
|
||||
|
||||
/// References to geometry outside the copied sketches must never resolve against coincident
|
||||
/// IDs or names in the destination. Fixed and expression-placed planes are self-contained.
|
||||
fn independent_plane(plane: &PlaneRef, depth: usize) -> Result<()> {
|
||||
if depth > 64 {
|
||||
return Err(bad(CMD, "copied plane nesting exceeds 64"));
|
||||
}
|
||||
match plane {
|
||||
PlaneRef::Origin { .. } | PlaneRef::Custom { .. } | PlaneRef::Placed { .. } => Ok(()),
|
||||
PlaneRef::Offset { base, .. } | PlaneRef::Flipped { base, .. } | PlaneRef::AtAngle { base, line: None, .. } => {
|
||||
independent_plane(base, depth + 1)
|
||||
}
|
||||
_ => Err(bad(CMD, "the copied sketch's plane depends on model geometry; convert it to a fixed plane before pasting into another design")),
|
||||
}
|
||||
}
|
||||
|
||||
fn independent_links(sketch: &solidcraft_sketch::Sketch, features: &BTreeSet<u64>, moved: bool) -> Result<()> {
|
||||
use solidcraft_sketch::LinkSource as L;
|
||||
for link in &sketch.links {
|
||||
let supported = match &link.source {
|
||||
L::Text { .. } | L::SketchAxis { .. } => true,
|
||||
L::SketchCurve { sketch, .. } | L::SketchPoint { sketch, .. } => features.contains(sketch),
|
||||
L::Origin | L::Axis { .. } => !moved,
|
||||
L::Plane { name } => !moved && solidcraft_geom::Plane::named(name).is_some(),
|
||||
_ => false,
|
||||
};
|
||||
if !supported {
|
||||
return Err(bad(
|
||||
CMD,
|
||||
"the copied sketch has external projected geometry; unlink it or copy its source sketches before pasting into another design",
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn free_parameter_name(base: &str, taken: &mut BTreeSet<String>) -> Result<String> {
|
||||
for index in 1..=solidcraft_doc::MAX_PARAMS + 1 {
|
||||
let name = if index == 1 { base.to_string() } else { format!("{base}_{index}") };
|
||||
if Document::param_name_problem(&name).is_none() && taken.insert(name.clone()) {
|
||||
return Ok(name);
|
||||
}
|
||||
}
|
||||
Err(bad(CMD, format!("no free name for copied parameter {base}")))
|
||||
}
|
||||
|
||||
fn model_parameter_name(taken: &mut BTreeSet<String>) -> Result<String> {
|
||||
for index in 1..=taken.len() + 1 {
|
||||
let name = format!("d{index}");
|
||||
if taken.insert(name.clone()) {
|
||||
return Ok(name);
|
||||
}
|
||||
}
|
||||
Err(bad(CMD, "no free model parameter name"))
|
||||
}
|
||||
|
||||
/// Bring the complete expression dependency graph, including dimensions driven by another
|
||||
/// dimension. Use the source definitions' units, never a destination parameter with the same name.
|
||||
fn parameters(target: &mut Document, source: &Document, features: &[Feature], mapped: &mut BTreeMap<String, String>) -> Result<()> {
|
||||
let definitions: BTreeMap<_, _> = source.param_defs().into_iter().map(|p| (p.name.clone(), p)).collect();
|
||||
let mut pending: Vec<String> = features.iter().flat_map(|f| f.kind.expressions()).flat_map(solidcraft_doc::expr::references).collect();
|
||||
let mut texts = BTreeSet::new();
|
||||
for feature in features {
|
||||
if let FeatureKind::Sketch { sketch, .. } = &feature.kind {
|
||||
pending.extend(sketch.constraints.iter().filter_map(|c| c.param.clone()));
|
||||
let text_and_points = sketch
|
||||
.links
|
||||
.iter()
|
||||
.filter_map(|l| match &l.source {
|
||||
solidcraft_sketch::LinkSource::Text { exprs, .. } => Some(exprs.list()),
|
||||
_ => None,
|
||||
})
|
||||
.flatten()
|
||||
.chain(sketch.points.iter().filter_map(|p| p.exprs.as_deref()).flat_map(solidcraft_sketch::PointExprs::list));
|
||||
pending.extend(text_and_points.flat_map(|(_, e)| solidcraft_doc::expr::references(e)));
|
||||
for equation in &sketch.equations {
|
||||
pending
|
||||
.extend(equation.list().into_iter().flat_map(|(_, e)| solidcraft_doc::expr::references(e)).filter(|n| n != equation.variable()));
|
||||
}
|
||||
texts.extend(sketch.text_word_exprs().into_iter().flat_map(|e| solidcraft_doc::text_params::references(e)));
|
||||
}
|
||||
}
|
||||
let mut used = BTreeSet::new();
|
||||
let mut work = 0;
|
||||
while let Some(name) = pending.pop() {
|
||||
work += 1;
|
||||
if work > 100_000 {
|
||||
return Err(bad(CMD, "copied parameter references exceed work limit"));
|
||||
}
|
||||
if used.insert(name.clone()) {
|
||||
let p = definitions.get(&name).ok_or_else(|| bad(CMD, format!("copied sketch uses missing parameter {name}")))?;
|
||||
pending.extend(solidcraft_doc::expr::references(&p.expr));
|
||||
}
|
||||
}
|
||||
if target.params.len() + target.text_params.len() + used.len() + texts.len() > solidcraft_doc::MAX_PARAMS {
|
||||
return Err(bad(CMD, "too many copied parameters"));
|
||||
}
|
||||
let mut taken: BTreeSet<_> = target.param_defs().into_iter().map(|p| p.name).chain(target.text_params.iter().map(|p| p.name.clone())).collect();
|
||||
for name in &used {
|
||||
if mapped.contains_key(name) {
|
||||
continue;
|
||||
}
|
||||
let definition = definitions.get(name).ok_or_else(|| bad(CMD, "missing copied parameter definition"))?;
|
||||
let model = source.param(name).is_none_or(|p| p.model);
|
||||
let new = if model {
|
||||
let new = model_parameter_name(&mut taken)?;
|
||||
target.params.push(solidcraft_doc::Param {
|
||||
name: new.clone(),
|
||||
expr: definition.expr.clone(),
|
||||
unit: definition.unit.clone(),
|
||||
comment: String::new(),
|
||||
model: true,
|
||||
});
|
||||
new
|
||||
} else {
|
||||
let new = free_parameter_name(name, &mut taken)?;
|
||||
let mut p = source.param(name).cloned().ok_or_else(|| bad(CMD, "missing copied user parameter"))?;
|
||||
p.name = new.clone();
|
||||
target.params.push(p);
|
||||
new
|
||||
};
|
||||
mapped.insert(name.clone(), new);
|
||||
}
|
||||
for name in texts {
|
||||
let mut p = source.text_param(&name).cloned().ok_or_else(|| bad(CMD, format!("copied sketch uses missing text parameter {name}")))?;
|
||||
p.name = free_parameter_name(&name, &mut taken)?;
|
||||
mapped.insert(name, p.name.clone());
|
||||
target.text_params.push(p);
|
||||
}
|
||||
for (old, new) in mapped.iter() {
|
||||
if let Some(definition) = definitions.get(old)
|
||||
&& let Some(p) = target.params.iter_mut().find(|p| p.name == *new)
|
||||
{
|
||||
p.expr = solidcraft_doc::clone_expression(&definition.expr, mapped);
|
||||
p.comment = source.param(old).map(|p| p.comment.clone()).or_else(|| source.param_comments.get(old).cloned()).unwrap_or_default();
|
||||
}
|
||||
if let Some(comment) = source.param_comments.get(old) {
|
||||
target.param_comments.insert(new.clone(), comment.clone());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn text_expression(source: &str, mapped: &BTreeMap<String, String>) -> String {
|
||||
let mut result = source.to_string();
|
||||
let mut temporary = Vec::new();
|
||||
for (i, (old, new)) in mapped.iter().enumerate() {
|
||||
let mut token = format!("__paste_text_{i}");
|
||||
while source.contains(&token) || mapped.keys().chain(mapped.values()).any(|name| name == &token) {
|
||||
token.push('_');
|
||||
}
|
||||
result = solidcraft_doc::text_params::rename_reference(&result, old, &token);
|
||||
temporary.push((token, new));
|
||||
}
|
||||
for (token, new) in temporary {
|
||||
result = solidcraft_doc::text_params::rename_reference(&result, &token, new);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub(super) fn paste(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let copy = s.foreign_copy.clone().ok_or_else(|| bad(CMD, "nothing was copied in another design"))?;
|
||||
if copy.features.is_empty() || copy.features.len() > 1000 {
|
||||
return Err(bad(CMD, "copy 1…1000 sketches before pasting into another design"));
|
||||
}
|
||||
let component = match p.get("component") {
|
||||
None => s.active_component,
|
||||
Some(v) => {
|
||||
let key = match v {
|
||||
Value::String(name) => name.clone(),
|
||||
Value::Number(n) => n.to_string(),
|
||||
_ => return Err(bad(CMD, "`component` must be a component id or name")),
|
||||
};
|
||||
s.doc.find_component(&key).ok_or_else(|| bad(CMD, format!("no component `{key}`")))?
|
||||
}
|
||||
};
|
||||
let path = match p.get("component_path") {
|
||||
Some(v) => v
|
||||
.as_array()
|
||||
.filter(|a| a.len() <= 64)
|
||||
.and_then(|a| a.iter().map(Value::as_u64).collect::<Option<Vec<_>>>())
|
||||
.ok_or_else(|| bad(CMD, "`component_path` must list 0..64 occurrence ids"))?,
|
||||
None if component == s.active_component => s.active_occurrence_path.clone().unwrap_or_else(|| s.doc.first_path(component)),
|
||||
None => super::component::placing::placement_of(s, &s.placed_document(), component, None, "component_path", CMD)?.0,
|
||||
};
|
||||
let placed = s.placed_document();
|
||||
let frame = placed.path_transform(component, &path).ok_or_else(|| bad(CMD, "that path is not a placement of the target"))?;
|
||||
let into_target = solidcraft_doc::mat_inverse(&frame).ok_or_else(|| bad(CMD, "the target's frame is not invertible"))?;
|
||||
drop(placed);
|
||||
let mut shift = solidcraft_doc::IDENTITY;
|
||||
if let Some(t) = p.get("translate") {
|
||||
let t = vec3(t).ok_or_else(|| bad(CMD, "`translate` must be [x, y, z]"))?;
|
||||
shift[3] = [t.x, t.y, t.z, 1.];
|
||||
}
|
||||
let world_shift = solidcraft_doc::mat_mul(&frame, &solidcraft_doc::mat_mul(&shift, &into_target));
|
||||
let mut placed_source = (*copy.doc).clone();
|
||||
for occurrence in &mut placed_source.occurrences {
|
||||
if let Some(m) = copy.pending_moves.get(&occurrence.id) {
|
||||
occurrence.transform = *m;
|
||||
}
|
||||
}
|
||||
let selected: BTreeSet<_> = copy.features.iter().map(|f| f.id).collect();
|
||||
let mut transforms: BTreeMap<u64, Mat> = BTreeMap::new();
|
||||
for feature in ©.features {
|
||||
let FeatureKind::Sketch { plane, sketch } = &feature.kind else {
|
||||
return Err(bad(CMD, "only sketch features can be pasted between designs; copy a component to include other feature history"));
|
||||
};
|
||||
independent_plane(plane, 0)?;
|
||||
let source_frame = placed_source
|
||||
.path_transform(feature.component, &placed_source.first_path(feature.component))
|
||||
.ok_or_else(|| bad(CMD, "the copied sketch's component placement is missing"))?;
|
||||
let transform = solidcraft_doc::mat_mul(&shift, &solidcraft_doc::mat_mul(&into_target, &source_frame));
|
||||
independent_links(sketch, &selected, !solidcraft_doc::is_identity(&transform))?;
|
||||
transforms.insert(feature.id, transform);
|
||||
}
|
||||
let mut scratch = s.scratch();
|
||||
let mut mapped = BTreeMap::new();
|
||||
let mut ids = BTreeMap::new();
|
||||
let mut names = BTreeMap::new();
|
||||
let mut made = Vec::new();
|
||||
for feature in ©.features {
|
||||
let taken = |name: &str| scratch.doc.features.iter().any(|f| f.name == name);
|
||||
let name = if taken(&feature.name) { solidcraft_doc::copy_name(&feature.name, taken) } else { feature.name.clone() };
|
||||
let id = scratch.doc_mut().add_feature(feature.kind.clone(), Some(&name))?;
|
||||
let mut taken: BTreeSet<_> =
|
||||
scratch.doc.param_defs().into_iter().map(|p| p.name).chain(scratch.doc.text_params.iter().map(|p| p.name.clone())).collect();
|
||||
let text_names: BTreeSet<_> = scratch.doc.text_params.iter().map(|p| p.name.clone()).collect();
|
||||
if let Some(added) = scratch.doc_mut().feature_mut(id) {
|
||||
for name in &mut added.param_names {
|
||||
if text_names.contains(name) {
|
||||
*name = model_parameter_name(&mut taken)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(added) = scratch.doc.feature(id) {
|
||||
mapped.extend(feature.param_names.iter().cloned().zip(added.param_names.iter().cloned()));
|
||||
}
|
||||
ids.insert(feature.id, id);
|
||||
names.insert(feature.name.clone(), name.clone());
|
||||
made.push(json!({"id": id, "name": name, "from": feature.name}));
|
||||
}
|
||||
parameters(scratch.doc_mut(), ©.doc, ©.features, &mut mapped)?;
|
||||
let source_values = copy.doc.param_values().0;
|
||||
for original in ©.features {
|
||||
let mut feature = original.clone();
|
||||
let transform = transforms.get(&original.id).ok_or_else(|| bad(CMD, "missing copied sketch transform"))?;
|
||||
// Nonliteral unitless length inputs are interpreted in their design's display units.
|
||||
// Make that unit explicit before moving the expressions to a design with other units.
|
||||
if copy.doc.units != s.doc.units {
|
||||
for (_, e, kind) in feature.kind.inputs_mut() {
|
||||
if kind == solidcraft_doc::expr::Kind::Length
|
||||
&& !solidcraft_doc::expr::is_literal(e)
|
||||
&& let Some((solidcraft_doc::expr::Kind::Length, scale)) = solidcraft_doc::expr::unit_info(copy.doc.units.trim())
|
||||
&& solidcraft_doc::expr::eval_with(e, &|name| {
|
||||
source_values.get(name).copied().ok_or_else(|| solidcraft_doc::DocError::Expr(format!("unknown parameter {name}")))
|
||||
})?
|
||||
.unitless()
|
||||
{
|
||||
*e = format!("({e}) * {scale} mm").into();
|
||||
}
|
||||
}
|
||||
}
|
||||
feature.remap_clone(&ids, &names, &BTreeMap::new(), &mapped)?;
|
||||
if let FeatureKind::Sketch { plane, sketch } = &mut feature.kind {
|
||||
// 3D wires carry their own coordinate space, independently of the sketch plane.
|
||||
for wire in &mut sketch.wires {
|
||||
let matrix = if wire.space == solidcraft_sketch::WireSpace::Component { transform } else { &world_shift };
|
||||
solidcraft_doc::map_wire(wire, matrix);
|
||||
}
|
||||
// Patterns and text words have expressions outside FeatureKind's numeric inputs.
|
||||
for pattern in &mut sketch.patterns {
|
||||
for e in pattern.count_expression.iter_mut().chain(pattern.expressions.values_mut()) {
|
||||
*e = solidcraft_doc::clone_expression(e, &mapped);
|
||||
}
|
||||
}
|
||||
for e in sketch.text_word_exprs_mut() {
|
||||
*e = text_expression(e, &mapped);
|
||||
}
|
||||
if !solidcraft_doc::is_identity(transform) {
|
||||
super::clipboard::explicit_planes(plane);
|
||||
}
|
||||
}
|
||||
feature.kind.transform_geometry(transform);
|
||||
let id = *ids.get(&original.id).ok_or_else(|| bad(CMD, "missing copied sketch id"))?;
|
||||
let added = scratch.doc_mut().feature_mut(id).ok_or_else(|| bad(CMD, "missing pasted sketch"))?;
|
||||
added.kind = feature.kind;
|
||||
added.component = component;
|
||||
added.suppressed = original.suppressed;
|
||||
}
|
||||
scratch.refresh();
|
||||
for id in ids.values() {
|
||||
if let Some(error) = scratch.model.result(*id).and_then(|r| r.error.as_ref()) {
|
||||
return Err(bad(CMD, format!("the pasted sketch did not rebuild: {error}")));
|
||||
}
|
||||
}
|
||||
scratch.selection = ids.values().map(|id| Sel::Feature { id: *id }).collect();
|
||||
s.accept_computed(scratch);
|
||||
Ok(json!({"pasted": made, "errors": [], "notices": []}))
|
||||
}
|
||||
@@ -294,6 +294,22 @@ fn row(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
if rename.as_ref().is_some_and(|n| n != &name && s.doc.configs.rows.iter().any(|r| &r.name == n)) {
|
||||
return Err(bad(cmd, "configuration name already exists"));
|
||||
}
|
||||
let changes: Vec<_> = s
|
||||
.doc
|
||||
.configs
|
||||
.columns
|
||||
.iter()
|
||||
.zip(&cells)
|
||||
.filter_map(|(column, value)| match column {
|
||||
Column::Param(name) => Some((name.clone(), value.clone())),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
if !changes.is_empty() {
|
||||
// Check the final parameter graph without activating the row or rebuilding geometry.
|
||||
let mut trial = s.doc.as_ref().clone();
|
||||
trial.change_params_atomic(&changes).map_err(|e| bad(cmd, e.to_string()))?;
|
||||
}
|
||||
let t = &mut s.doc_mut().configs;
|
||||
let validated_name = rename.clone().unwrap_or_else(|| name.clone());
|
||||
match idx {
|
||||
@@ -664,6 +680,52 @@ mod tests {
|
||||
assert!((volume(&mut s) - 2000.0).abs() < 1e-6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn configuration_rows_reject_invalid_parameter_expressions_without_changes() {
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "parameters.add", json!({"name":"L", "expression":"10 mm"}));
|
||||
run(&mut s, "config.configure", json!({"params":["L"]}));
|
||||
let before = s.doc.clone();
|
||||
let undo_len = s.undo.len();
|
||||
for expression in ["abc", "1/", "1/0"] {
|
||||
for params in [
|
||||
json!({"name":"Bad", "values":{"param:L":expression}}),
|
||||
json!({"name":"Copy", "from":"Default", "values":{"L":expression}}),
|
||||
json!({"name":"Default", "rename":"Renamed", "values":{"L":expression}}),
|
||||
] {
|
||||
let error = s.execute("config.row", ¶ms).expect_err("invalid expressions must be rejected when saving a row");
|
||||
assert!(error.to_string().contains("parameter `L`"), "{params}: {error}");
|
||||
assert_eq!(s.doc, before, "{params}");
|
||||
assert_eq!(s.undo.len(), undo_len, "{params}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn configuration_rows_validate_the_final_parameter_graph_without_activating_it() {
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "parameters.add", json!({"name":"L", "expression":"10 mm"}));
|
||||
run(&mut s, "parameters.add", json!({"name":"W", "expression":"L + 2 mm"}));
|
||||
run(&mut s, "config.configure", json!({"params":["L", "W"]}));
|
||||
let before = s.doc.clone();
|
||||
// Changing L first would make a cycle with W's current expression. The final row is valid.
|
||||
run(&mut s, "config.row", json!({"name":"Reordered", "values":{"L":"W + 2 mm", "W":"20 mm"}}));
|
||||
assert_eq!(s.doc.params, before.params);
|
||||
assert_eq!(s.doc.configs.active, before.configs.active);
|
||||
let saved = s.doc.clone();
|
||||
let undo_len = s.undo.len();
|
||||
assert!(s.execute("config.row", &json!({"name":"Cycle", "from":"Reordered", "values":{"W":"L"}})).is_err());
|
||||
assert_eq!(s.doc, saved);
|
||||
assert_eq!(s.undo.len(), undo_len);
|
||||
run(&mut s, "config.activate", json!({"row":"Reordered"}));
|
||||
let (values, errors) = s.doc.param_values();
|
||||
assert!(errors.is_empty(), "{errors:?}");
|
||||
assert_eq!(values["L"].v, 22.0);
|
||||
assert_eq!(values["W"].v, 20.0);
|
||||
run(&mut s, "edit.undo", json!({}));
|
||||
assert_eq!(s.doc, saved);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn configured_feature_inputs_and_suppression_match_typed_cells() {
|
||||
let mut s = Session::default();
|
||||
@@ -746,9 +808,7 @@ mod tests {
|
||||
// Bad input.
|
||||
assert!(s.execute("config.row", &json!({"name": "X", "values": {"nope": 1}})).is_err());
|
||||
assert!(s.execute("config.activate", &json!({"row": "Missing"})).is_err());
|
||||
run(&mut s, "config.row", json!({"name": "Broken", "values": {"length": "1/"}}));
|
||||
assert!(s.execute("config.activate", &json!({"row": "Broken"})).is_err());
|
||||
run(&mut s, "config.row", json!({"name": "Broken", "delete": true}));
|
||||
assert!(s.execute("config.row", &json!({"name": "Broken", "values": {"length": "1/"}})).is_err());
|
||||
run(&mut s, "config.column", json!({"feature": fillet, "delete": true}));
|
||||
assert_eq!(run(&mut s, "config.table", json!({}))["columns"].as_array().map(Vec::len), Some(1));
|
||||
}
|
||||
|
||||
@@ -14,135 +14,267 @@ use solidcraft_geom::Vec3;
|
||||
use super::CommandSpec;
|
||||
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,
|
||||
};
|
||||
use crate::schema::features::FeatureResult;
|
||||
use crate::schema::{TypedContract, output_schema_for};
|
||||
use crate::{Result, Session};
|
||||
|
||||
const REF: &str =
|
||||
pub(crate) const REF: &str =
|
||||
"{face|edge|vertex|point: [x,y,z]} | {plane: XY|name|{origin, normal}} | {axis: X|Y|Z|name} | {construct_point: name} | {sketch, point|curve}";
|
||||
|
||||
const AXIS_THROUGH_CYLINDER_CONTRACT: TypedContract<AxisThroughCylinderParams> = TypedContract::new();
|
||||
const AXIS_NORMAL_TO_FACE_CONTRACT: TypedContract<AxisNormalToFaceParams> = TypedContract::new();
|
||||
const AXIS_TWO_PLANES_CONTRACT: TypedContract<AxisTwoPlanesParams> = TypedContract::new();
|
||||
const AXIS_TWO_POINTS_CONTRACT: TypedContract<AxisTwoPointsParams> = TypedContract::new();
|
||||
const AXIS_EDGE_CONTRACT: TypedContract<AxisEdgeParams> = TypedContract::new();
|
||||
const POINT_VERTEX_CONTRACT: TypedContract<PointVertexParams> = TypedContract::new();
|
||||
const POINT_TWO_EDGES_CONTRACT: TypedContract<PointTwoEdgesParams> = TypedContract::new();
|
||||
const POINT_THREE_PLANES_CONTRACT: TypedContract<PointThreePlanesParams> = TypedContract::new();
|
||||
const POINT_CENTER_CONTRACT: TypedContract<PointCenterParams> = TypedContract::new();
|
||||
const POINT_EDGE_PLANE_CONTRACT: TypedContract<PointEdgePlaneParams> = TypedContract::new();
|
||||
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_THREE_POINTS_CONTRACT: TypedContract<PlaneThreePointsParams> = TypedContract::new();
|
||||
const PLANE_TANGENT_CONTRACT: TypedContract<PlaneTangentParams> = TypedContract::new();
|
||||
const PLANE_ALONG_PATH_CONTRACT: TypedContract<PlaneAlongPathParams> = TypedContract::new();
|
||||
const PLANE_PERPENDICULAR_CONTRACT: TypedContract<PlanePerpendicularParams> = TypedContract::new();
|
||||
|
||||
pub static COMMANDS: &[CommandSpec] = &[
|
||||
CommandSpec::new("construct.axis.through_cylinder", "Axis Through Cylinder/Cone/Torus", axis_face)
|
||||
CommandSpec::new("construct.axis.through_cylinder", "Axis Through Cylinder/Cone/Torus", crate::checked!("construct.axis.through_cylinder", axis_face))
|
||||
.at("SOLID", "CONSTRUCT")
|
||||
.icon("axis_through_cylinder")
|
||||
.contract(&AXIS_THROUGH_CYLINDER_CONTRACT)
|
||||
.output(output_schema_for::<FeatureResult>)
|
||||
.params("face: ref (a cylinder, cone or torus face, or a circular edge); name?"),
|
||||
CommandSpec::new("construct.axis.normal_to_face", "Axis Perpendicular To Face", axis_normal)
|
||||
CommandSpec::new("construct.axis.normal_to_face", "Axis Perpendicular To Face", crate::checked!("construct.axis.normal_to_face", axis_normal))
|
||||
.at("SOLID", "CONSTRUCT")
|
||||
.icon("axis_normal_to_face")
|
||||
.contract(&AXIS_NORMAL_TO_FACE_CONTRACT)
|
||||
.output(output_schema_for::<FeatureResult>)
|
||||
.params("face: ref; point: ref (the axis goes through it, square to the face); name?"),
|
||||
CommandSpec::new("construct.axis.two_planes", "Axis Through Two Planes", axis_planes)
|
||||
CommandSpec::new("construct.axis.two_planes", "Axis Through Two Planes", crate::checked!("construct.axis.two_planes", axis_planes))
|
||||
.at("SOLID", "CONSTRUCT")
|
||||
.icon("axis_two_planes")
|
||||
.contract(&AXIS_TWO_PLANES_CONTRACT)
|
||||
.output(output_schema_for::<FeatureResult>)
|
||||
.params("a, b: plane or planar face refs; name?"),
|
||||
CommandSpec::new("construct.axis.two_points", "Axis Through Two Points", axis_points)
|
||||
CommandSpec::new("construct.axis.two_points", "Axis Through Two Points", crate::checked!("construct.axis.two_points", axis_points))
|
||||
.at("SOLID", "CONSTRUCT")
|
||||
.icon("axis_two_points")
|
||||
.contract(&AXIS_TWO_POINTS_CONTRACT)
|
||||
.output(output_schema_for::<FeatureResult>)
|
||||
.params("a, b: point refs (vertices, sketch points, points); name?"),
|
||||
CommandSpec::new("construct.axis.edge", "Axis Through Edge", axis_edge)
|
||||
CommandSpec::new("construct.axis.edge", "Axis Through Edge", crate::checked!("construct.axis.edge", axis_edge))
|
||||
.at("SOLID", "CONSTRUCT")
|
||||
.icon("axis_edge")
|
||||
.contract(&AXIS_EDGE_CONTRACT)
|
||||
.output(output_schema_for::<FeatureResult>)
|
||||
.params("edge: ref (a straight edge or sketch line); name?"),
|
||||
CommandSpec::new("construct.point.vertex", "Point At Vertex", point_at)
|
||||
CommandSpec::new("construct.point.vertex", "Point At Vertex", crate::checked!("construct.point.vertex", point_at))
|
||||
.at("SOLID", "CONSTRUCT")
|
||||
.icon("point_vertex")
|
||||
.contract(&POINT_VERTEX_CONTRACT)
|
||||
.output(output_schema_for::<FeatureResult>)
|
||||
.params("point: ref (vertex, sketch point, point); name?"),
|
||||
CommandSpec::new("construct.point.two_edges", "Point Through Two Edges", point_edges)
|
||||
CommandSpec::new("construct.point.two_edges", "Point Through Two Edges", crate::checked!("construct.point.two_edges", point_edges))
|
||||
.at("SOLID", "CONSTRUCT")
|
||||
.icon("point_two_edges")
|
||||
.contract(&POINT_TWO_EDGES_CONTRACT)
|
||||
.output(output_schema_for::<FeatureResult>)
|
||||
.params("a, b: edge or line refs (where they meet, or come closest); name?"),
|
||||
CommandSpec::new("construct.point.three_planes", "Point Through Three Planes", point_planes)
|
||||
CommandSpec::new("construct.point.three_planes", "Point Through Three Planes", crate::checked!("construct.point.three_planes", point_planes))
|
||||
.at("SOLID", "CONSTRUCT")
|
||||
.icon("point_three_planes")
|
||||
.contract(&POINT_THREE_PLANES_CONTRACT)
|
||||
.output(output_schema_for::<FeatureResult>)
|
||||
.params("a, b, c: plane or planar face refs; name?"),
|
||||
CommandSpec::new("construct.point.center", "Point At Center Of Circle/Sphere/Torus", point_center)
|
||||
CommandSpec::new("construct.point.center", "Point At Center Of Circle/Sphere/Torus", crate::checked!("construct.point.center", point_center))
|
||||
.at("SOLID", "CONSTRUCT")
|
||||
.icon("point_center")
|
||||
.contract(&POINT_CENTER_CONTRACT)
|
||||
.output(output_schema_for::<FeatureResult>)
|
||||
.params("of: ref (circular edge, sketch circle or arc, sphere or torus face); name?"),
|
||||
CommandSpec::new("construct.point.edge_plane", "Point At Edge And Plane", point_edge_plane)
|
||||
CommandSpec::new("construct.point.edge_plane", "Point At Edge And Plane", crate::checked!("construct.point.edge_plane", point_edge_plane))
|
||||
.at("SOLID", "CONSTRUCT")
|
||||
.icon("point_edge_plane")
|
||||
.contract(&POINT_EDGE_PLANE_CONTRACT)
|
||||
.output(output_schema_for::<FeatureResult>)
|
||||
.params("edge: edge or line ref; plane: plane or planar face ref; name?"),
|
||||
CommandSpec::new("construct.point.along_path", "Point Along Path", point_along)
|
||||
CommandSpec::new("construct.point.along_path", "Point Along Path", crate::checked!("construct.point.along_path", point_along))
|
||||
.at("SOLID", "CONSTRUCT")
|
||||
.icon("point_along_path")
|
||||
.contract(&POINT_ALONG_PATH_CONTRACT)
|
||||
.output(output_schema_for::<FeatureResult>)
|
||||
.params("path: edge or sketch curve ref; t: expr, 0…1 along it (default 0.5); name?"),
|
||||
CommandSpec::new("construct.plane.midplane", "Midplane", plane_mid)
|
||||
CommandSpec::new("construct.plane.midplane", "Midplane", crate::checked!("construct.plane.midplane", plane_mid))
|
||||
.at("SOLID", "CONSTRUCT")
|
||||
.icon("plane_midplane")
|
||||
.contract(&PLANE_MIDPLANE_CONTRACT)
|
||||
.output(output_schema_for::<FeatureResult>)
|
||||
.params("a, b: plane or planar face refs (halfway between; bisecting when they meet at an angle); name?"),
|
||||
CommandSpec::new("construct.plane.two_edges", "Plane Through Two Edges", plane_edges)
|
||||
CommandSpec::new("construct.plane.two_edges", "Plane Through Two Edges", crate::checked!("construct.plane.two_edges", plane_edges))
|
||||
.at("SOLID", "CONSTRUCT")
|
||||
.icon("plane_two_edges")
|
||||
.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.three_points", "Plane Through Three Points", plane_points)
|
||||
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")
|
||||
.contract(&PLANE_THREE_POINTS_CONTRACT)
|
||||
.output(output_schema_for::<FeatureResult>)
|
||||
.params("a, b, c: point refs; name?"),
|
||||
CommandSpec::new("construct.plane.tangent", "Tangent Plane", plane_tangent)
|
||||
CommandSpec::new("construct.plane.tangent", "Tangent Plane", crate::checked!("construct.plane.tangent", plane_tangent))
|
||||
.at("SOLID", "CONSTRUCT")
|
||||
.icon("plane_tangent")
|
||||
.contract(&PLANE_TANGENT_CONTRACT)
|
||||
.output(output_schema_for::<FeatureResult>)
|
||||
.params("face: ref (cylinder, cone, sphere or torus); at?: [x,y,z] where it touches (default: the face pick); name?"),
|
||||
CommandSpec::new("construct.plane.along_path", "Plane Along Path", plane_along)
|
||||
CommandSpec::new("construct.plane.along_path", "Plane Along Path", crate::checked!("construct.plane.along_path", plane_along))
|
||||
.at("SOLID", "CONSTRUCT")
|
||||
.icon("plane_along_path")
|
||||
.contract(&PLANE_ALONG_PATH_CONTRACT)
|
||||
.output(output_schema_for::<FeatureResult>)
|
||||
.params("path: edge or sketch curve ref; t: expr, 0…1 along it (default 0.5); name? The plane is square to the path there"),
|
||||
CommandSpec::new("construct.plane.perpendicular", "Perpendicular Plane", plane_perpendicular).at("SOLID", "CONSTRUCT").icon("plane_perpendicular").params(
|
||||
"plane: plane or planar face ref; line: straight edge, sketch line or axis ref; name? The plane holds the line and is square to the plane",
|
||||
),
|
||||
CommandSpec::new("construct.plane.perpendicular", "Perpendicular Plane", crate::checked!("construct.plane.perpendicular", plane_perpendicular))
|
||||
.at("SOLID", "CONSTRUCT")
|
||||
.icon("plane_perpendicular")
|
||||
.contract(&PLANE_PERPENDICULAR_CONTRACT)
|
||||
.output(output_schema_for::<FeatureResult>)
|
||||
.params(
|
||||
"plane: plane or planar face ref; line: straight edge, sketch line or axis ref; name? The plane holds the line and is square to the plane",
|
||||
),
|
||||
];
|
||||
|
||||
/// Whether [`model_axis_param`] reads `v` as an edge or face of the model: `{edge: …}`,
|
||||
/// `{face: …}`, or as saved (`{type: edge|face, at, name?}`), as an edit dialog gives back one it
|
||||
/// kept.
|
||||
pub(crate) fn is_model_axis(v: &Value) -> bool {
|
||||
v.get("edge").is_some() || v.get("face").is_some() || matches!(v.get("type").and_then(Value::as_str), Some("edge" | "face"))
|
||||
}
|
||||
|
||||
/// An axis on an edge or round face of the model (`{edge: [x,y,z]}` or `{face: [x,y,z]}`, the
|
||||
/// point on it), with the line it gives now (`solidcraft_doc::construct::model_axis`); `None`
|
||||
/// for any other form of `axis`.
|
||||
pub(super) fn model_axis_param(s: &Session, v: Option<&Value>, cmd: &str) -> Result<Option<(GeoRef, Vec3, Vec3)>> {
|
||||
// Also as saved (`{type: edge|face, at, name?}`), as an edit dialog gives back one it kept.
|
||||
let saved = |v: &Value| matches!(v.get("type").and_then(Value::as_str), Some("edge" | "face"));
|
||||
let Some(v) = v.filter(|v| v.get("edge").is_some() || v.get("face").is_some() || saved(v)) else { return Ok(None) };
|
||||
let Some(v) = v.filter(|v| is_model_axis(v)) else { return Ok(None) };
|
||||
let g = geo(s, Some(v), cmd, "axis")?;
|
||||
let (o, d) = solidcraft_doc::construct::model_axis(&s.world_state(), &g).map_err(|e| bad(cmd, format!("`axis`: {e}")))?;
|
||||
Ok(Some((g, o, d)))
|
||||
}
|
||||
|
||||
/// A reference as [`geo`] reads it before looking anything up in the design: which form it has.
|
||||
pub(crate) enum GeoForm<'a> {
|
||||
/// As saved (`{type, …}`), read whole.
|
||||
Saved(GeoRef),
|
||||
Face(Vec3),
|
||||
Edge(Vec3),
|
||||
Vertex(Vec3),
|
||||
/// `{sketch, point|curve}`: the sketch as given, and the point or curve, when one is given.
|
||||
Sketch {
|
||||
sketch: &'a Value,
|
||||
part: Option<SketchPart<'a>>,
|
||||
},
|
||||
Point(Vec3),
|
||||
/// `{plane: …}`'s value, or a bare string: a plane, as `plane_param` reads it.
|
||||
Plane(&'a Value),
|
||||
/// `{axis: X|Y|Z|name}`.
|
||||
Axis(&'a str),
|
||||
/// `{construct_point: name}`.
|
||||
ConstructPoint(&'a str),
|
||||
}
|
||||
|
||||
/// A sketch reference's point or curve id.
|
||||
pub(crate) enum SketchPart<'a> {
|
||||
Point(&'a str),
|
||||
Curve(&'a str),
|
||||
}
|
||||
|
||||
/// Why [`geo_form`] refuses a reference.
|
||||
pub(crate) enum GeoFormError {
|
||||
/// A saved form serde refuses, with its message.
|
||||
Saved(String),
|
||||
/// None of the forms.
|
||||
Unknown,
|
||||
}
|
||||
|
||||
/// What [`geo`] says when a sketch reference has neither `point` nor `curve`.
|
||||
pub(crate) const SKETCH_PART: &str = "a sketch reference needs `point` or `curve`";
|
||||
|
||||
/// The form [`geo`] reads `v` as. The first that fits wins, in this order: `type` (read whole), a
|
||||
/// point at `face`, `edge` or `vertex`, `sketch` (whatever else it has), a point at `point`,
|
||||
/// `plane` (whatever it holds), `axis` and `construct_point` as text, and a bare string.
|
||||
pub(crate) fn geo_form(v: &Value) -> std::result::Result<GeoForm<'_>, GeoFormError> {
|
||||
let p3 = |k: &str| v.get(k).and_then(vec3);
|
||||
Ok(if v.get("type").is_some() {
|
||||
GeoForm::Saved(serde_json::from_value::<GeoRef>(v.clone()).map_err(|e| GeoFormError::Saved(e.to_string()))?)
|
||||
} else if let Some(at) = p3("face") {
|
||||
GeoForm::Face(at)
|
||||
} else if let Some(at) = p3("edge") {
|
||||
GeoForm::Edge(at)
|
||||
} else if let Some(at) = p3("vertex") {
|
||||
GeoForm::Vertex(at)
|
||||
} else if let Some(sketch) = v.get("sketch") {
|
||||
let part = match (str_(v, "point"), str_(v, "curve")) {
|
||||
(Some(point), _) => Some(SketchPart::Point(point)),
|
||||
(None, Some(curve)) => Some(SketchPart::Curve(curve)),
|
||||
_ => None,
|
||||
};
|
||||
GeoForm::Sketch { sketch, part }
|
||||
} else if let Some(p) = p3("point") {
|
||||
GeoForm::Point(p)
|
||||
} else if let Some(plane) = v.get("plane") {
|
||||
GeoForm::Plane(plane)
|
||||
} else if let Some(a) = str_(v, "axis") {
|
||||
GeoForm::Axis(a)
|
||||
} else if let Some(n) = str_(v, "construct_point") {
|
||||
GeoForm::ConstructPoint(n)
|
||||
} else if v.is_string() {
|
||||
GeoForm::Plane(v)
|
||||
} else {
|
||||
return Err(GeoFormError::Unknown);
|
||||
})
|
||||
}
|
||||
|
||||
/// A reference from its JSON form.
|
||||
pub(super) fn geo(s: &Session, v: Option<&Value>, cmd: &str, what: &str) -> Result<GeoRef> {
|
||||
let v = v.ok_or_else(|| bad(cmd, format!("`{what}` is required: {REF}")))?;
|
||||
let p3 = |k: &str| v.get(k).and_then(vec3);
|
||||
let mut g = if v.get("type").is_some() {
|
||||
serde_json::from_value::<GeoRef>(v.clone()).map_err(|e| bad(cmd, format!("`{what}`: {e}")))?
|
||||
} else if let Some(at) = p3("face") {
|
||||
GeoRef::Face { at, name: None }
|
||||
} else if let Some(at) = p3("edge") {
|
||||
GeoRef::Edge { at, name: None }
|
||||
} else if let Some(at) = p3("vertex") {
|
||||
GeoRef::Vertex { at, edges: None }
|
||||
} else if let Some(sk) = v.get("sketch") {
|
||||
let sketch = sketch_id(s, Some(sk), cmd, what)?;
|
||||
match (str_(v, "point"), str_(v, "curve")) {
|
||||
(Some(point), _) => GeoRef::SketchPoint { sketch, point: point.to_string() },
|
||||
(None, Some(curve)) => GeoRef::SketchCurve { sketch, curve: curve.to_string() },
|
||||
_ => return Err(bad(cmd, format!("`{what}`: a sketch reference needs `point` or `curve`"))),
|
||||
}
|
||||
} else if let Some(p) = p3("point") {
|
||||
GeoRef::Point { p }
|
||||
} else if v.get("plane").is_some() {
|
||||
GeoRef::Plane { plane: plane_param(s, v.get("plane"), cmd)? }
|
||||
} else if let Some(a) = str_(v, "axis") {
|
||||
if ["x", "y", "z"].contains(&a.to_ascii_lowercase().as_str()) {
|
||||
GeoRef::World { axis: a.to_ascii_uppercase() }
|
||||
} else {
|
||||
match s.doc.find_feature(a).map(|f| &f.kind) {
|
||||
Some(FeatureKind::ConstructionAxis { .. }) => GeoRef::Axis { name: a.into() },
|
||||
_ => return Err(bad(cmd, format!("no axis `{a}`"))),
|
||||
let mut g = match geo_form(v) {
|
||||
Ok(GeoForm::Saved(g)) => g,
|
||||
Ok(GeoForm::Face(at)) => GeoRef::Face { at, name: None },
|
||||
Ok(GeoForm::Edge(at)) => GeoRef::Edge { at, name: None },
|
||||
Ok(GeoForm::Vertex(at)) => GeoRef::Vertex { at, edges: None },
|
||||
Ok(GeoForm::Sketch { sketch, part }) => {
|
||||
let sketch = sketch_id(s, Some(sketch), cmd, what)?;
|
||||
match part {
|
||||
Some(SketchPart::Point(point)) => GeoRef::SketchPoint { sketch, point: point.to_string() },
|
||||
Some(SketchPart::Curve(curve)) => GeoRef::SketchCurve { sketch, curve: curve.to_string() },
|
||||
None => return Err(bad(cmd, format!("`{what}`: {SKETCH_PART}"))),
|
||||
}
|
||||
}
|
||||
} else if let Some(n) = str_(v, "construct_point") {
|
||||
match s.doc.find_feature(n).map(|f| &f.kind) {
|
||||
Ok(GeoForm::Point(p)) => GeoRef::Point { p },
|
||||
Ok(GeoForm::Plane(plane)) => GeoRef::Plane { plane: plane_param(s, Some(plane), cmd)? },
|
||||
Ok(GeoForm::Axis(a)) => {
|
||||
if ["x", "y", "z"].contains(&a.to_ascii_lowercase().as_str()) {
|
||||
GeoRef::World { axis: a.to_ascii_uppercase() }
|
||||
} else {
|
||||
match s.doc.find_feature(a).map(|f| &f.kind) {
|
||||
Some(FeatureKind::ConstructionAxis { .. }) => GeoRef::Axis { name: a.into() },
|
||||
_ => return Err(bad(cmd, format!("no axis `{a}`"))),
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(GeoForm::ConstructPoint(n)) => match s.doc.find_feature(n).map(|f| &f.kind) {
|
||||
Some(FeatureKind::ConstructionPoint { .. }) => GeoRef::ConstructPoint { name: n.into() },
|
||||
_ => return Err(bad(cmd, format!("no construction point `{n}`"))),
|
||||
}
|
||||
} else if v.as_str().is_some() {
|
||||
// A bare string: a plane.
|
||||
GeoRef::Plane { plane: plane_param(s, Some(v), cmd)? }
|
||||
} else {
|
||||
return Err(bad(cmd, format!("`{what}` must be {REF}")));
|
||||
},
|
||||
Err(GeoFormError::Saved(e)) => return Err(bad(cmd, format!("`{what}`: {e}"))),
|
||||
Err(GeoFormError::Unknown) => return Err(bad(cmd, format!("`{what}` must be {REF}"))),
|
||||
};
|
||||
g.name_picks(&s.model.state());
|
||||
Ok(g)
|
||||
|
||||
@@ -12,7 +12,8 @@ use solidcraft_geom::Vec3;
|
||||
use super::{CommandSpec, not_in_sketch};
|
||||
use crate::params::{UNKNOWN_COMMAND, bad, bool_, expr, expr_problem, req_expr, str_, string_list, string_list_or_default, vec2, vec3};
|
||||
use crate::schema::features::{
|
||||
BoundingSolidParams, BoxParams, ConeParams, CylinderParams, ExtrudeParams, FeatureResult, PipeParams, ScaleParams, SphereParams, TorusParams,
|
||||
BoundingSolidParams, BoxParams, ConeParams, CylinderParams, ExtrudeParams, FeatureResult, OffsetPlaneParams, PipeParams, PlaneAtAngleParams,
|
||||
ScaleParams, SphereParams, TorusParams,
|
||||
};
|
||||
use crate::schema::{TypedContract, output_schema_for};
|
||||
use crate::{EngineError, Result, Session};
|
||||
@@ -26,6 +27,8 @@ const CONE_CONTRACT: TypedContract<ConeParams> = TypedContract::new();
|
||||
const PIPE_CONTRACT: TypedContract<PipeParams> = TypedContract::new();
|
||||
const BOUNDING_SOLID_CONTRACT: TypedContract<BoundingSolidParams> = TypedContract::new();
|
||||
const SCALE_CONTRACT: TypedContract<ScaleParams> = TypedContract::new();
|
||||
const OFFSET_PLANE_CONTRACT: TypedContract<OffsetPlaneParams> = TypedContract::new();
|
||||
const PLANE_AT_ANGLE_CONTRACT: TypedContract<PlaneAtAngleParams> = TypedContract::new();
|
||||
|
||||
pub static COMMANDS: &[CommandSpec] = &[
|
||||
CommandSpec::new("solid.extrude", "Extrude", crate::checked!("solid.extrude", extrude))
|
||||
@@ -137,13 +140,17 @@ pub static COMMANDS: &[CommandSpec] = &[
|
||||
.at("SOLID", "MODIFY")
|
||||
.icon("draft")
|
||||
.params("faces: [[x,y,z] points, or face names from model.faces]; angle: expr; neutral: XY|XZ|YZ|plane name|{origin, normal}; pull?: [x,y,z] (default: neutral plane normal); body?"),
|
||||
CommandSpec::new("construct.plane.offset", "Offset Plane", plane_offset)
|
||||
CommandSpec::new("construct.plane.offset", "Offset Plane", crate::checked!("construct.plane.offset", plane_offset))
|
||||
.at("SOLID", "CONSTRUCT")
|
||||
.icon("plane")
|
||||
.contract(&OFFSET_PLANE_CONTRACT)
|
||||
.output(output_schema_for::<FeatureResult>)
|
||||
.params("base: XY|XZ|YZ|plane name, or a planar face {face: [x,y,z], body?, index?, path?}; offset: expr, or through: a point ref (vertex, sketch point, construction point) the plane goes through, parallel to base; name?"),
|
||||
CommandSpec::new("construct.plane.at_angle", "Plane at Angle", plane_angle)
|
||||
CommandSpec::new("construct.plane.at_angle", "Plane at Angle", crate::checked!("construct.plane.at_angle", plane_angle))
|
||||
.at("SOLID", "CONSTRUCT")
|
||||
.icon("plane_angle")
|
||||
.contract(&PLANE_AT_ANGLE_CONTRACT)
|
||||
.output(output_schema_for::<FeatureResult>)
|
||||
.params("base: XY|XZ|YZ|plane name, or a planar face {face: [x,y,z], body?, index?, path?}; axis: X|Y|Z|construction axis name (where it is now)|{origin, dir} | {edge|face: [x,y,z]} (a straight or circular edge, or a cylinder, cone or torus face: about its axis, followed as the model changes); angle: expr; name?"),
|
||||
CommandSpec::new("solid.split_face", "Split Face", split_face)
|
||||
.at("SOLID", "MODIFY")
|
||||
@@ -1885,6 +1892,7 @@ fn move_body_feature(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
)?;
|
||||
if copy {
|
||||
copy_keeps_appearance(s, &reply, &bodies, cmd)?;
|
||||
super::print_face::copies_keep_print_faces(s, &reply, &bodies);
|
||||
}
|
||||
Ok(reply)
|
||||
}
|
||||
@@ -1946,19 +1954,24 @@ fn named_axis_line(s: &Session, p: &Value, cmd: &str) -> Result<(Vec3, Vec3, Opt
|
||||
_ => Err(bad(cmd, "axis must be X, Y, Z, a construction axis, {origin, dir} or {edge|face: [x,y,z]}")),
|
||||
},
|
||||
Some(o @ Value::Object(_)) => {
|
||||
let origin = match o.get("origin") {
|
||||
None => Vec3::ZERO,
|
||||
Some(v) => vec3(v).ok_or_else(|| {
|
||||
bad(cmd, "axis.origin must contain three finite numeric coordinates in millimetres; expressions are not supported")
|
||||
})?,
|
||||
};
|
||||
let dir = o.get("dir").and_then(vec3).and_then(|d| d.normalized()).ok_or_else(|| bad(cmd, "axis needs a non-zero `dir`"))?;
|
||||
let (origin, dir) = axis_line_object(o).map_err(|why| bad(cmd, why))?;
|
||||
Ok((origin, dir, None))
|
||||
}
|
||||
_ => Err(bad(cmd, "`axis` is required")),
|
||||
}
|
||||
}
|
||||
|
||||
/// An axis line `{origin?, dir}` as [`named_axis_line`] reads it: the origin (default: the
|
||||
/// origin) and the unit direction.
|
||||
pub(crate) fn axis_line_object(o: &Value) -> std::result::Result<(Vec3, Vec3), &'static str> {
|
||||
let origin = match o.get("origin") {
|
||||
None => Vec3::ZERO,
|
||||
Some(v) => vec3(v).ok_or("axis.origin must contain three finite numeric coordinates in millimetres; expressions are not supported")?,
|
||||
};
|
||||
let dir = o.get("dir").and_then(vec3).and_then(|d| d.normalized()).ok_or("axis needs a non-zero `dir`")?;
|
||||
Ok((origin, dir))
|
||||
}
|
||||
|
||||
/// A pattern feature, or for `components`, new occurrences of them at each placement.
|
||||
fn finish_pattern(
|
||||
s: &mut Session,
|
||||
@@ -2086,7 +2099,12 @@ fn mirror(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let plane = plane_param(s, p.get("plane"), cmd)?;
|
||||
let combine = bool_(p, "combine").unwrap_or(false);
|
||||
let (bodies, placements) = placed_bodies(s, bodies, cmd)?;
|
||||
add_feature(s, p, FeatureKind::Mirror { features, plane, bodies: bodies.into_iter().map(BodyName).collect(), combine, placements })
|
||||
let reply =
|
||||
add_feature(s, p, FeatureKind::Mirror { features, plane, bodies: bodies.iter().cloned().map(BodyName).collect(), combine, placements })?;
|
||||
if !combine {
|
||||
super::print_face::copies_keep_print_faces(s, &reply, &bodies);
|
||||
}
|
||||
Ok(reply)
|
||||
}
|
||||
|
||||
/// Bodies picked where the viewport shows them: each world name ("Body1", or "Body1 (Occ:2)"
|
||||
@@ -2129,13 +2147,31 @@ pub(super) fn origin_plane_alias(name: &str) -> Option<&'static str> {
|
||||
/// A construction plane may not take an origin plane's name (`XY`, `Top`...): a plane
|
||||
/// parameter could then mean either.
|
||||
pub(super) fn check_plane_name(name: &str, cmd: &str) -> Result<()> {
|
||||
if solidcraft_geom::Plane::named(name.trim()).is_some() {
|
||||
return Err(bad(
|
||||
cmd,
|
||||
format!("`{}` is reserved for an origin plane (XY, XZ, YZ, Top, Front, Right); give the construction plane another name", name.trim()),
|
||||
));
|
||||
match plane_name_problem(name) {
|
||||
Some(why) => Err(bad(cmd, why)),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// What [`check_plane_name`] says about `name`, or `None` when a construction plane may take it.
|
||||
pub(crate) fn plane_name_problem(name: &str) -> Option<String> {
|
||||
solidcraft_geom::Plane::named(name.trim()).map(|_| {
|
||||
format!("`{}` is reserved for an origin plane (XY, XZ, YZ, Top, Front, Right); give the construction plane another name", name.trim())
|
||||
})
|
||||
}
|
||||
|
||||
/// The forms [`plane_param`] takes, for its messages.
|
||||
pub(crate) const PLANE_FORMS: &str =
|
||||
"XY, XZ, YZ, a construction plane name or id, or {origin, normal} (origin coordinates may be length expressions)";
|
||||
|
||||
/// The plane an explicit plane object gives through `origin`, as [`plane_param`] reads it: from
|
||||
/// `x_dir` and `y_dir` when both are points, else from `normal`; `None` when they don't make one.
|
||||
pub(crate) fn explicit_plane(o: &Value, origin: Vec3) -> Option<solidcraft_geom::Plane> {
|
||||
match (o.get("x_dir").and_then(vec3), o.get("y_dir").and_then(vec3), o.get("normal").and_then(vec3)) {
|
||||
(Some(x), Some(y), _) => solidcraft_geom::Plane::new(origin, x, y),
|
||||
(_, _, Some(n)) => solidcraft_geom::Plane::from_normal(origin, n),
|
||||
_ => None,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A plane reference from a parameter: an origin plane, a construction plane (by name or
|
||||
@@ -2144,7 +2180,7 @@ pub(super) fn check_plane_name(name: &str, cmd: &str) -> Result<()> {
|
||||
/// plane reads it here.
|
||||
pub(super) fn plane_param(s: &Session, v: Option<&Value>, cmd: &str) -> Result<solidcraft_doc::PlaneRef> {
|
||||
use solidcraft_doc::PlaneRef;
|
||||
const WANT: &str = "XY, XZ, YZ, a construction plane name or id, or {origin, normal} (origin coordinates may be length expressions)";
|
||||
const WANT: &str = PLANE_FORMS;
|
||||
let construction = |r: &str| match s.doc.find_feature(r) {
|
||||
Some(f) if matches!(f.kind, FeatureKind::ConstructionPlane { .. }) => Ok(PlaneRef::Construction { feature: f.id.into() }),
|
||||
Some(f) => Err(bad(cmd, format!("`{}` is not a construction plane (a plane is {WANT})", f.name))),
|
||||
@@ -2181,12 +2217,7 @@ pub(super) fn plane_param(s: &Session, v: Option<&Value>, cmd: &str) -> Result<s
|
||||
};
|
||||
let at = |e: &str| s.doc.eval(e, Kind::Length).unwrap_or(0.0);
|
||||
let here = Vec3::new(at(&origin[0]), at(&origin[1]), at(&origin[2]));
|
||||
let pl = match (o.get("x_dir").and_then(vec3), o.get("y_dir").and_then(vec3), o.get("normal").and_then(vec3)) {
|
||||
(Some(x), Some(y), _) => solidcraft_geom::Plane::new(here, x, y),
|
||||
(_, _, Some(n)) => solidcraft_geom::Plane::from_normal(here, n),
|
||||
_ => None,
|
||||
};
|
||||
let plane = pl.ok_or_else(|| bad(cmd, "plane needs x_dir and y_dir, or normal"))?;
|
||||
let plane = explicit_plane(o, here).ok_or_else(|| bad(cmd, "plane needs x_dir and y_dir, or normal"))?;
|
||||
if origin.iter().all(|e| solidcraft_doc::expr::is_literal(e)) {
|
||||
return Ok(PlaneRef::Custom { plane });
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ use solidcraft_doc::Document;
|
||||
use solidcraft_io::Format;
|
||||
|
||||
use super::CommandSpec;
|
||||
use super::print_face;
|
||||
use crate::params::{bad, bool_, num, str_, string_list};
|
||||
use crate::{EngineError, Result, Session};
|
||||
|
||||
@@ -694,6 +695,26 @@ fn export_to(
|
||||
Ok(json!({"path": path, "bytes": bytes.len(), "format": format!("{format:?}")}))
|
||||
}
|
||||
|
||||
/// [`export_to`] for a mesh file a slicer is sent (STL or 3MF): each body laid on its print face.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn export_laid_to(
|
||||
s: &Session,
|
||||
path: &str,
|
||||
format: Format,
|
||||
bodies: &[String],
|
||||
parts: &[(String, Vec<String>)],
|
||||
quality: solidcraft_io::MeshQuality,
|
||||
) -> Result<Value> {
|
||||
let name = std::path::Path::new(path).file_stem().map(|x| x.to_string_lossy().to_string()).unwrap_or_else(|| s.doc.name.clone());
|
||||
let mut state = s.doc.painted(&s.world_state());
|
||||
let laid = print_face::lay_on_print_faces(s, &mut state, bodies, parts);
|
||||
let bytes = solidcraft_io::export_with_parts(&state, bodies, parts, format, &name, &s.doc.metadata, quality)?;
|
||||
solidcraft_io::vfs::write(path, &bytes).map_err(|e| EngineError::Other(format!("{path}: {e}")))?;
|
||||
let mut out = json!({"path": path, "bytes": bytes.len(), "format": format!("{format:?}")});
|
||||
laid.report(&mut out);
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
fn export(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let cmd = "file.export";
|
||||
let path = path_arg(p, cmd)?;
|
||||
@@ -1456,6 +1477,51 @@ mod tests {
|
||||
std::fs::remove_file(path).unwrap();
|
||||
}
|
||||
|
||||
/// #9170: a design holding a mesh body (an inserted STL) keeps its saved model and opens from
|
||||
/// it. One mesh body left the cache out for the whole model, so every feature, the boxes
|
||||
/// too, was evaluated again on opening.
|
||||
#[test]
|
||||
fn a_design_with_a_mesh_body_keeps_its_saved_model() {
|
||||
let dir = std::env::temp_dir().join(format!("solidcraft-mesh-cache-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let stl = dir.join("tetra.stl");
|
||||
let facets = [
|
||||
[[0, 0, 0], [0, 10, 0], [10, 0, 0]],
|
||||
[[0, 0, 0], [10, 0, 0], [0, 0, 10]],
|
||||
[[0, 0, 0], [0, 0, 10], [0, 10, 0]],
|
||||
[[10, 0, 0], [0, 10, 0], [0, 0, 10]],
|
||||
];
|
||||
let mut text = String::from("solid tetra\n");
|
||||
for f in facets {
|
||||
text += "facet normal 0 0 0\nouter loop\n";
|
||||
for [x, y, z] in f {
|
||||
text += &format!("vertex {x} {y} {z}\n");
|
||||
}
|
||||
text += "endloop\nendfacet\n";
|
||||
}
|
||||
std::fs::write(&stl, text + "endsolid tetra\n").unwrap();
|
||||
let mut session = Session::new(Document::new("Boxes and a mesh"));
|
||||
for x in [0, 40, 80] {
|
||||
session.execute("solid.box", &json!({"length": 20, "width": 20, "height": 20, "corner": [x, 0, 0], "operation": "new_body"})).unwrap();
|
||||
}
|
||||
session.execute("file.insert_mesh", &json!({"path": stl})).unwrap();
|
||||
let triangles = |s: &Session| s.model.state().bodies.iter().filter_map(|b| b.body.triangle_mesh().map(|(_, t)| t.len())).collect::<Vec<_>>();
|
||||
assert_eq!(triangles(&session), [4], "one mesh body");
|
||||
let bytes = session.design_container(solidcraft_io::DesignCompression::Fast, false, true).unwrap();
|
||||
assert!(solidcraft_io::read_design_geometry(&bytes).is_some(), "the model is saved with the mesh body");
|
||||
let path = dir.join("boxes-mesh.solidcraft");
|
||||
std::fs::write(&path, &bytes).unwrap();
|
||||
let mut reopened = Session::default();
|
||||
reopened.execute("doc.open", &json!({"path": path})).unwrap();
|
||||
std::fs::remove_dir_all(&dir).unwrap();
|
||||
assert_eq!(reopened.model.results.len(), session.model.results.len());
|
||||
assert!(reopened.model.results.iter().zip(&session.model.results).all(|(a, b)| a.ms == b.ms), "taken from the file, not evaluated");
|
||||
assert_eq!(triangles(&reopened), [4]);
|
||||
let volume = |s: &mut Session| s.execute("inspect.measure", &json!({})).unwrap()["total"]["volume_mm3"].as_f64().unwrap();
|
||||
let (saved, opened) = (volume(&mut session), volume(&mut reopened));
|
||||
assert!((opened - saved).abs() < 1e-9 && (saved - (3. * 8000. + 1000. / 6.)).abs() < 1e-6, "{opened} vs {saved}");
|
||||
}
|
||||
|
||||
/// A sheet-metal design keeps each sheet body once in its saved model, not again with every
|
||||
/// later feature's state: a design of many features after its sheet metal wrote the sheets
|
||||
/// over and over, and past the cache's size limit kept only some of its states.
|
||||
@@ -1499,6 +1565,54 @@ mod tests {
|
||||
std::fs::remove_file(path).unwrap();
|
||||
}
|
||||
|
||||
/// A design's saved model keeps each set of sketch keys once, not again with every state:
|
||||
/// every state wrote the key of each sketch before it, so a design of many sketches and many
|
||||
/// features after them (5,484 features) passed the cache's size limit and kept only some of
|
||||
/// its states. A set is written as the changes from the one before it; the model opens from
|
||||
/// the file with every state (the keys read back: `eval_cost_tests`).
|
||||
#[test]
|
||||
fn a_saved_model_keeps_each_set_of_sketch_keys_once() {
|
||||
let mut s = Session::new(Document::new("Sketches"));
|
||||
let run = |s: &mut Session, id: &str, p: Value| s.execute(id, &p).unwrap();
|
||||
for i in 0..6 {
|
||||
run(&mut s, "sketch.create", json!({"plane":"XY","name":format!("Pad{i}")}));
|
||||
run(&mut s, "sketch.rectangle.two_point", json!({"p0":[20 * i,0],"p1":[20 * i + 10,10]}));
|
||||
run(&mut s, "solid.extrude", json!({"distance":5,"operation":"new"}));
|
||||
}
|
||||
for i in 0..10 {
|
||||
run(&mut s, "solid.box", json!({"length":5,"width":5,"height":5,"corner":[20 * i,40,0],"operation":"new"}));
|
||||
}
|
||||
let saved = saved_geometry(&s, &s.doc).unwrap();
|
||||
let features = saved.records["features"].as_array().unwrap();
|
||||
assert_eq!(features.len(), s.doc.features.len());
|
||||
assert!(features.iter().all(|f| f.get("state").is_some()), "every state kept");
|
||||
let states = saved.records["states"].as_array().unwrap();
|
||||
let sets = saved.records["sketch_key_sets"].as_array().unwrap();
|
||||
// One set per sketch: each later one adds that sketch's key to the one before it.
|
||||
assert_eq!(sets.len(), 6, "{sets:?}");
|
||||
assert_eq!(sets[0]["set"].as_array().unwrap().len(), 1);
|
||||
for (i, set) in sets.iter().enumerate().skip(1) {
|
||||
assert_eq!(
|
||||
(set["base"].as_u64(), set["set"].as_array().unwrap().len(), set["drop"].as_array().unwrap().len()),
|
||||
(Some(i as u64 - 1), 1, 0)
|
||||
);
|
||||
}
|
||||
let last = sets.len() - 1;
|
||||
assert_eq!(
|
||||
states.iter().filter(|st| st["sketch_key_set"] == json!(last)).count(),
|
||||
2 + 10,
|
||||
"the last sketch, its extrude and every box share one set"
|
||||
);
|
||||
let path = std::env::temp_dir().join(format!("solidcraft-sketch-key-cache-{}.solidcraft", std::process::id()));
|
||||
let bytes = solidcraft_io::write_design_container_with_cache(&s.doc, solidcraft_io::DesignCompression::Fast, None, Some(&saved)).unwrap();
|
||||
std::fs::write(&path, bytes).unwrap();
|
||||
let mut reopened = Session::default();
|
||||
reopened.execute("doc.open", &json!({"path": path})).unwrap();
|
||||
assert_eq!(reopened.model.last_recomputed, 0, "taken from the file, not evaluated");
|
||||
assert!(reopened.model.results.iter().zip(&s.model.results).all(|(a, b)| a.ms == b.ms));
|
||||
std::fs::remove_file(path).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "manual native cache save benchmark; generate issue303-step.json first"]
|
||||
fn benchmark_native_cache_save() {
|
||||
|
||||
@@ -19,10 +19,12 @@ pub(super) struct Exported {
|
||||
pub fell_back: bool,
|
||||
/// The bodies that went in: the ones named, else the selected ones.
|
||||
pub bodies: Vec<String>,
|
||||
/// The bodies laid on their print face, and the ones that couldn't be.
|
||||
pub laid: super::print_face::Laid,
|
||||
}
|
||||
|
||||
/// Export the bodies (the selected ones when none are named). A 3MF that can't be written is
|
||||
/// written as an STL, and `fell_back` says so.
|
||||
/// Export the bodies (the selected ones when none are named), each laid on its print face. A
|
||||
/// 3MF that can't be written is written as an STL, and `fell_back` says so.
|
||||
pub(super) fn export_bodies(s: &Session, cmd: &str, named: &[String], format: Format) -> Result<Exported> {
|
||||
let mut bodies = named.to_vec();
|
||||
if bodies.is_empty() {
|
||||
@@ -31,7 +33,8 @@ pub(super) fn export_bodies(s: &Session, cmd: &str, named: &[String], format: Fo
|
||||
if bodies.is_empty() {
|
||||
return Err(bad(cmd, "select the bodies to send, or give `bodies`"));
|
||||
}
|
||||
let doc = s.doc.painted(&s.world_state());
|
||||
let mut doc = s.doc.painted(&s.world_state());
|
||||
let laid = super::print_face::lay_on_print_faces(s, &mut doc, &bodies, &[]);
|
||||
let write = |format| solidcraft_io::export_with_metadata(&doc, &bodies, format, &s.doc.name, &s.doc.metadata);
|
||||
let mut fell_back = false;
|
||||
let (bytes, ext) = match (format, write(format)) {
|
||||
@@ -42,7 +45,7 @@ pub(super) fn export_bodies(s: &Session, cmd: &str, named: &[String], format: Fo
|
||||
}
|
||||
(_, Err(e)) => return Err(e.into()),
|
||||
};
|
||||
Ok(Exported { bytes, ext, fell_back, bodies })
|
||||
Ok(Exported { bytes, ext, fell_back, bodies, laid })
|
||||
}
|
||||
|
||||
pub(super) fn open_in_easyprint(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
@@ -56,7 +59,7 @@ pub(super) fn open_in_easyprint(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
if !bool_(p, "consent").unwrap_or(false) {
|
||||
return Err(bad(cmd, "this uploads the model to Printables' servers: ask the user, then pass `consent: true`"));
|
||||
}
|
||||
let Exported { bytes, ext, fell_back, .. } = export_bodies(s, cmd, &string_list(cmd, p, "bodies")?, format)?;
|
||||
let Exported { bytes, ext, fell_back, laid, .. } = export_bodies(s, cmd, &string_list(cmd, p, "bodies")?, format)?;
|
||||
if bytes.len() > MAX_BYTES {
|
||||
let mb = bytes.len().div_ceil(1_000_000);
|
||||
return Err(bad(cmd, format!("the model is {mb} MB and EasyPrint takes {} MB at most: send fewer bodies", MAX_BYTES / 1_000_000)));
|
||||
@@ -64,5 +67,7 @@ pub(super) fn open_in_easyprint(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let name = easyprint::file_name(&s.doc.name, ext);
|
||||
let size = bytes.len();
|
||||
upload(&name, bytes).map_err(|e| bad(cmd, e))?;
|
||||
Ok(json!({"file_name": name, "bytes": size, "format": ext, "stl_fallback": fell_back, "uploading": true}))
|
||||
let mut out = json!({"file_name": name, "bytes": size, "format": ext, "stl_fallback": fell_back, "uploading": true});
|
||||
laid.report(&mut out);
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use serde_json::{Value, json};
|
||||
use solidcraft_io::Format;
|
||||
|
||||
use super::easyprint::{Exported, export_bodies};
|
||||
use super::export_to;
|
||||
use super::export_laid_to;
|
||||
use crate::params::bad;
|
||||
use crate::slicers::{self, Slicer, TargetKind};
|
||||
use crate::{EngineError, Result, Session};
|
||||
@@ -81,7 +81,7 @@ pub(super) fn send(s: &mut Session, r: Send<'_>) -> Result<Value> {
|
||||
return Err(bad(r.cmd, format!("{} does not take {}", target.name, format_name(r.format))));
|
||||
}
|
||||
let mut out = match r.dest {
|
||||
Dest::Path(path) => export_to(s, path, r.format, &r.bodies, &r.parts, r.quality)?,
|
||||
Dest::Path(path) => export_laid_to(s, path, r.format, &r.bodies, &r.parts, r.quality)?,
|
||||
Dest::Folder => write_in_folder(s, &r)?,
|
||||
};
|
||||
let path = out.get("path").and_then(Value::as_str).unwrap_or_default().to_string();
|
||||
@@ -92,11 +92,13 @@ pub(super) fn send(s: &mut Session, r: Send<'_>) -> Result<Value> {
|
||||
/// Write the bodies into the slicer folder.
|
||||
fn write_in_folder(s: &mut Session, r: &Send<'_>) -> Result<Value> {
|
||||
use crate::slicer_files;
|
||||
let Exported { bytes, ext, fell_back, bodies } = export_bodies(s, r.cmd, &r.bodies, r.format)?;
|
||||
let Exported { bytes, ext, fell_back, bodies, laid } = export_bodies(s, r.cmd, &r.bodies, r.format)?;
|
||||
let dir = slicer_files::dir().ok_or_else(|| bad(r.cmd, "no per-user folder on this system"))?;
|
||||
let stem = slicer_files::stem(&s.doc.name, s.path.as_deref(), s.uid, &bodies);
|
||||
let (file, numbered) = slicer_files::write(&dir, &stem, ext, &bytes).map_err(|e| EngineError::Other(format!("{}: {e}", dir.display())))?;
|
||||
Ok(json!({"path": file.to_string_lossy(), "bytes": bytes.len(), "format": ext, "numbered": numbered, "stl_fallback": fell_back}))
|
||||
let mut out = json!({"path": file.to_string_lossy(), "bytes": bytes.len(), "format": ext, "numbered": numbered, "stl_fallback": fell_back});
|
||||
laid.report(&mut out);
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// Hand the file at `path` to `target`, starting it first if it has to be, and say what happened in
|
||||
|
||||
@@ -34,7 +34,7 @@ pub static COMMANDS: &[CommandSpec] = &[
|
||||
revision (moves whenever the design, what is hidden or a component's uncaptured position changes, \
|
||||
never on a selection or a view; from one counter for the whole run, so it never repeats). Read-only",
|
||||
),
|
||||
CommandSpec::new("sketch.inspect", "Inspect Sketch", sketch_inspect).icon("inspect").noundo().params("sketch?: id|name (default active)"),
|
||||
CommandSpec::new("sketch.inspect", "Inspect Sketch", sketch_inspect).at("SKETCH", "INSPECT").icon("inspect").noundo().params("sketch?: id|name (default active)"),
|
||||
CommandSpec::new("model.edges", "List Edges", model_edges).icon("inspect").noundo().params("body: name"),
|
||||
CommandSpec::new("model.faces", "List Faces", model_faces).icon("inspect").noundo().params(
|
||||
"body: name → per face: index, name (what face.delete, sheet.convert `face`, solid.shell … take in place of a point), exact, area, centroid (not always on the face), \
|
||||
@@ -56,7 +56,7 @@ pub static COMMANDS: &[CommandSpec] = &[
|
||||
.params("target: appearance.chain target. Copies its resolved look to a transient session clipboard. No document change or dialogs."),
|
||||
CommandSpec::new("appearance.paste", "Paste Look", appearance_gestures::paste).icon("paste").key("Ctrl+Alt+V")
|
||||
.params("bodies/faces/components: appearance.assign targets. Applies the copied look atomically in one undo step; no dialogs."),
|
||||
CommandSpec::new("appearance.favorite", "Favourite Look", appearance_gestures::favorite).icon("star").noundo().doc_only()
|
||||
CommandSpec::new("appearance.favorite", "Favorite Look", appearance_gestures::favorite).icon("star").noundo().doc_only()
|
||||
.params("look: appearance.assign look; favorite: bool → validated look and preference request; UI library preference only, no document change or dialogs."),
|
||||
CommandSpec::new("appearance.chain", "Appearance Chain", appearance_chain::chain).icon("inspect").noundo().params(
|
||||
"target: {body: name} | {body: name, face: zero-based model.faces index} | {component: name or id}. Returns steps [{step: face|body|component|material, look: {name,color,opacity,pbr?} or null}] in precedence order and active (step or null). Component material is the common default look of its directly owned bodies, null if mixed/empty. Imported paint is a display fallback outside the four steps. Read-only; no dialogs",
|
||||
@@ -240,7 +240,12 @@ fn sketch_json(s: &Session, id: u64) -> Option<Value> {
|
||||
.profiles
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, p)| json!({"index": i, "area_mm2": p.area, "centroid_sketch": p.centroid, "curves": p.outer_curves, "holes": p.hole_curves}))
|
||||
// A loop records one entry per piece it walks, and a conic or spline is walked as many
|
||||
// short pieces: each loop names a curve once, in loop order.
|
||||
.map(|(i, p)| {
|
||||
let holes: Vec<Vec<String>> = p.hole_curves.iter().map(|h| solidcraft_sketch::distinct_curves(h)).collect();
|
||||
json!({"index": i, "area_mm2": p.area, "centroid_sketch": p.centroid, "curves": solidcraft_sketch::distinct_curves(&p.outer_curves), "holes": holes})
|
||||
})
|
||||
.collect();
|
||||
Some(json!({
|
||||
"id": id,
|
||||
|
||||