mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 02:25:55 +00:00
Compare commits
118 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 33fdcdb3a0 | |||
| 5899e5b6c5 | |||
| e23a5bf243 | |||
| 4b23bb3a03 | |||
| 920398ac53 | |||
| 688124937d | |||
| b49557c294 | |||
| dfd58293a6 | |||
| ba4f7864ff | |||
| b289dc2421 | |||
| 2daf22923c | |||
| 2fdc105d87 | |||
| 95b23f28d6 | |||
| 14b27f323a | |||
| 4ab3a85323 | |||
| c54b711345 | |||
| 84ec512d7a | |||
| 17ece2edc0 | |||
| 63f210b49f | |||
| 9acff62422 | |||
| 915ee8a178 | |||
| ac1768e08b | |||
| e1daf0a8b6 | |||
| 57c3e94df6 | |||
| 5e05d925e0 | |||
| 50313d147f | |||
| c07ab5f8f9 | |||
| 728f753b80 | |||
| f22a026c27 | |||
| 4b37c046e4 | |||
| 54072b7014 | |||
| cb3cabeda1 | |||
| 775e5155bb | |||
| e570c0b4eb | |||
| d9059d8da5 | |||
| 463cea0615 | |||
| 0225e4d2a5 | |||
| fea32f8aac | |||
| 034aa60d47 | |||
| 971b223246 | |||
| 8c861f9f46 | |||
| e04a016e7c | |||
| 93a2922c5e | |||
| ff8b09c225 | |||
| 975a08c992 | |||
| 55587f4ac4 | |||
| e94900933d | |||
| 4927214976 | |||
| 57bcc40838 | |||
| 9974413a5b | |||
| 818af2b856 | |||
| 80238a7cfb | |||
| bd65a561c8 | |||
| 776541b081 | |||
| 79a843c963 | |||
| 1bcc3602b4 | |||
| 93684a4728 | |||
| 9db39f3d7d | |||
| 29369a0bb5 | |||
| 5a9e771cc9 | |||
| 63cb48a4ee | |||
| 36c732ddb7 | |||
| fdec948abc | |||
| 7803b3968f | |||
| cbac4eab3f | |||
| 08eff4f9dd | |||
| 9b80163421 | |||
| 164554edba | |||
| 453c6bcb14 | |||
| f44ff18ca4 | |||
| 98cd67f029 | |||
| d9d6af51c6 | |||
| 38c710e72e | |||
| 5b5b051e02 | |||
| 686576e368 | |||
| 68e4186080 | |||
| b66ad0a9c9 | |||
| 2eacbc37f4 | |||
| f2f9c6fe27 | |||
| d21e2b1afd | |||
| bdd2fb1a5c | |||
| e64f1e0427 | |||
| ac362945cc | |||
| 382680d8e8 | |||
| 316b49cd84 | |||
| b0932a487e | |||
| edad8078ba | |||
| 26c0c19ef7 | |||
| 2f43ad5c5b | |||
| 6a8604c55d | |||
| b621819c0c | |||
| ec552a5300 | |||
| e0cc740cc3 | |||
| 5de48e916d | |||
| 80af7bbe3e | |||
| 191e983423 | |||
| 2b9445b049 | |||
| 7ddf48ca16 | |||
| d2f6848377 | |||
| 0b6d6fecd8 | |||
| fa691707c2 | |||
| b9a55c74e2 | |||
| 2bb9c81069 | |||
| cedb51b8ea | |||
| 66d13059dd | |||
| 800ea40c0d | |||
| 20442eecd3 | |||
| 22d1cd7032 | |||
| 6a0dc18655 | |||
| e006dbf1af | |||
| 8241a750fa | |||
| 41779cf7bb | |||
| b01b9689c8 | |||
| 4426da6f5d | |||
| 5ead82e627 | |||
| c29b0d2f13 | |||
| 480fd8d517 | |||
| 4c09fa5355 |
@@ -280,6 +280,10 @@ jobs:
|
||||
run: cargo xtask licences --check
|
||||
- name: Dependency layers
|
||||
run: cargo xtask layers
|
||||
- name: Universal UI containers
|
||||
run: |
|
||||
cargo xtask ui-containers
|
||||
cargo test -p xtask ui_containers --locked
|
||||
- name: Sorted lists
|
||||
run: cargo xtask lists
|
||||
- name: Design format pending changes
|
||||
@@ -290,8 +294,8 @@ jobs:
|
||||
run: python3 -I scripts/ci/test_affected.py
|
||||
- name: Merge-of-main detection self-test
|
||||
run: python3 -I scripts/ci/test_main_merge.py
|
||||
- name: Claim and triage bot tests
|
||||
run: node --test scripts/claim/*.test.js scripts/triage/*.test.js
|
||||
- name: Claim, triage and kernel-paused bot tests
|
||||
run: node --test scripts/claim/*.test.js scripts/triage/*.test.js scripts/kernel-paused/*.test.js
|
||||
|
||||
# Fast feedback for a pull request that has not been approved for the full CI yet.
|
||||
quick-compile:
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
# While a new geometry kernel is evaluated, new issues, pull requests and discussions about kernel
|
||||
# work are labelled `kernel-paused` for a maintainer to answer (CONTRIBUTING.md, "Kernel work is
|
||||
# paused"). The logic is in scripts/kernel-paused/ (tested in ci.yml).
|
||||
name: Kernel paused
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
# pull_request_target so PRs from forks can be labelled. It runs the base branch's
|
||||
# scripts/kernel-paused and only lists the PR's changed paths; the PR's code is never checked out.
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
discussion:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
contents: read # to check out scripts/kernel-paused
|
||||
issues: write
|
||||
pull-requests: read # to list a PR's changed paths
|
||||
discussions: write
|
||||
|
||||
concurrency:
|
||||
group: kernel-paused-${{ github.event.issue.number || github.event.pull_request.number || github.event.discussion.number }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
paused:
|
||||
name: Kernel paused bot
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
sparse-checkout: scripts/kernel-paused
|
||||
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const paused = require('./scripts/kernel-paused/kernel-paused.js');
|
||||
const done = context.eventName === 'discussion'
|
||||
? await paused.handleDiscussion({ github, context })
|
||||
: await paused.handleOpened({ github, context });
|
||||
core.info(done);
|
||||
@@ -88,6 +88,14 @@ People trust SolidCraft with their designs; a crash loses their work. **This out
|
||||
egui/eframe/winit/wgpu/rfd. **The UI crate is swappable.** Only `crates/kernel` may use `truck`.
|
||||
- **The UI is thin**: panels read engine state and act through `app.run(id, params)` /
|
||||
`app.start(id)`. Colours come from `theme::Tokens`.
|
||||
- **One outer UI container**: all panels, floating sheets, menus, overlays and platform UI
|
||||
windows use `ui_kit::Container` (directly or through `Panel`, `Window`, `Confirm`, `Menu`).
|
||||
Declare the semantic `Surface`; workspace panels use its shared layout policy and chrome.
|
||||
System/report windows use `ui_kit::Window`; its low-level host builder is kit-private.
|
||||
Do not construct raw egui hosts, outer popup/window frames or shadows in feature modules.
|
||||
`cargo xtask ui-containers` recursively checks source/examples in all current and future crates
|
||||
and apps. It runs in the fast repository CI job and `cargo xtask ci`, with no caller allowances.
|
||||
See `docs/ui-container-audit.md` for the inventory and legitimate content/native exceptions.
|
||||
- **One set of camera controls**: every page or canvas with a camera (viewport, Render
|
||||
preview, future flat pattern or drawing pages) calls `navigation::handle` with its `NavCaps`
|
||||
(orbit off for flat 2D pages). New canvases must use `navigation::handle`; never hand-roll
|
||||
|
||||
+18
-1
@@ -94,7 +94,6 @@ Editable drawing code, native projects and shipped exports are collected under `
|
||||
| tests/oracle/61-shaft-keyway/reference.stl | SolidCraft contributors | triangulation of our own recipe (tests/oracle/61-shaft-keyway/recipe.json) built with FreeCAD as an external tool by tools/freecad-oracle | MIT OR Apache-2.0 |
|
||||
| tests/oracle/63-hinge-knuckle/reference.stl | SolidCraft contributors | triangulation of our own recipe (tests/oracle/63-hinge-knuckle/recipe.json) built with FreeCAD as an external tool by tools/freecad-oracle | MIT OR Apache-2.0 |
|
||||
| tests/oracle/65-sketch-gear-tooth/reference.stl | SolidCraft contributors | triangulation of our own recipe (tests/oracle/65-sketch-gear-tooth/recipe.json) built with FreeCAD as an external tool by tools/freecad-oracle | MIT OR Apache-2.0 |
|
||||
|
||||
| assets/appearances/library.rs | SolidCraft contributors | Original SolidCraft resource data or drawing code, collected without redesign | 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/ibm-plex-mono/OFL.txt | IBM Corp. | Unmodified accompanying font licence | SIL OFL 1.1 |
|
||||
@@ -119,6 +118,7 @@ Editable drawing code, native projects and shipped exports are collected under `
|
||||
| assets/locales/cs.json | potuzai | Czech translation of the English catalog, contributed upstream | MIT OR Apache-2.0 |
|
||||
| assets/locales/da.json | SolidCraft contributors | Original Danish translation of SolidCraft's English catalog; no commercial CAD text used | MIT OR Apache-2.0 |
|
||||
| assets/locales/de.json | SolidCraft contributors | Original SolidCraft resource data or drawing code, collected without redesign | MIT OR Apache-2.0 |
|
||||
| assets/locales/en-GB.json | SolidCraft contributors | English (UK): the English catalog's messages whose spelling differs, in UK spelling | MIT OR Apache-2.0 |
|
||||
| assets/locales/en.json | SolidCraft contributors | Original SolidCraft resource data or drawing code, collected without redesign | MIT OR Apache-2.0 |
|
||||
| assets/locales/es.json | jawler | Spanish (Spain) translation of the English catalog, contributed upstream | MIT OR Apache-2.0 |
|
||||
| assets/locales/fr.json | rlartot | French translation of the English catalog, contributed upstream | MIT OR Apache-2.0 |
|
||||
@@ -156,6 +156,20 @@ Editable drawing code, native projects and shipped exports are collected under `
|
||||
| assets/styles/ui/themes/gruvbox-light.json | SolidCraft contributors | original SolidCraft theme file mapping SolidCraft's UI colours to the Gruvbox palette's colour values (Gruvbox by Pavel Pertsev, https://github.com/morhetz/gruvbox, MIT) | MIT OR Apache-2.0 |
|
||||
| assets/styles/ui/themes/gruvbox-light-hard.json | SolidCraft contributors | original SolidCraft theme file mapping SolidCraft's UI colours to the Gruvbox palette's colour values (Gruvbox by Pavel Pertsev, https://github.com/morhetz/gruvbox, MIT) | MIT OR Apache-2.0 |
|
||||
| assets/styles/ui/themes/gruvbox-light-soft.json | SolidCraft contributors | original SolidCraft theme file mapping SolidCraft's UI colours to the Gruvbox palette's colour values (Gruvbox by Pavel Pertsev, https://github.com/morhetz/gruvbox, MIT) | MIT OR Apache-2.0 |
|
||||
| assets/styles/ui/themes/solidcraft-95.json | SolidCraft contributors | original SolidCraft theme file | MIT OR Apache-2.0 |
|
||||
| assets/styles/ui/themes/platinum-9.json | SolidCraft contributors | original SolidCraft theme file | MIT OR Apache-2.0 |
|
||||
| assets/styles/ui/themes/cupertino.json | SolidCraft contributors | original SolidCraft theme file | MIT OR Apache-2.0 |
|
||||
| assets/styles/ui/themes/green-phosphor.json | SolidCraft contributors | original SolidCraft theme file | MIT OR Apache-2.0 |
|
||||
| assets/styles/ui/themes/amber-phosphor.json | SolidCraft contributors | original SolidCraft theme file | MIT OR Apache-2.0 |
|
||||
| assets/styles/ui/themes/blueprint.json | SolidCraft contributors | original SolidCraft theme file | MIT OR Apache-2.0 |
|
||||
| assets/styles/ui/themes/one-dark.json | SolidCraft contributors | original SolidCraft theme file with colours adapted from the One Dark palette (Atom One Dark by GitHub Atom, https://github.com/atom/atom, MIT) | MIT OR Apache-2.0 |
|
||||
| assets/styles/ui/themes/tokyo-night.json | SolidCraft contributors | original SolidCraft theme file adapting the colours of a published palette (Tokyo Night by enkia, https://github.com/enkia/tokyo-night-vscode-theme, MIT) | MIT OR Apache-2.0 |
|
||||
| assets/styles/ui/themes/solarized-light.json | SolidCraft contributors | original SolidCraft theme file adapting the colours of a published palette (Solarized by Ethan Schoonover, https://github.com/altercation/solarized, MIT) | MIT OR Apache-2.0 |
|
||||
| assets/styles/ui/themes/nord.json | SolidCraft contributors | original SolidCraft theme file adapting the colours of a published palette (Nord by Sven Greb and the Nord contributors, https://github.com/nordtheme/nord, MIT) | MIT OR Apache-2.0 |
|
||||
| assets/styles/ui/themes/high-contrast.json | SolidCraft contributors | original SolidCraft theme file | MIT OR Apache-2.0 |
|
||||
| assets/styles/ui/themes/graphite.json | SolidCraft contributors | original SolidCraft theme file | MIT OR Apache-2.0 |
|
||||
| assets/styles/ui/themes/nosferatu.json | SolidCraft contributors | original SolidCraft theme file adapting the colours of a published palette (Dracula by Zeno Rocha and the Dracula Theme contributors, https://github.com/dracula/dracula-theme, MIT) | MIT OR Apache-2.0 |
|
||||
| assets/styles/ui/themes/catppuccin-mocha.json | SolidCraft contributors | original SolidCraft theme file adapting the colours of a published palette (Catppuccin by the Catppuccin organisation, https://github.com/catppuccin/catppuccin, MIT) | MIT OR Apache-2.0 |
|
||||
| assets/styles/web/loading-logo.svg | SolidCraft contributors | Animation of the SolidCraft mark, built from the paths and gradients of assets/icons/app/macos/source/SolidCraft.icon/Assets/03-solid-blue-cutout.svg; original animation | MIT OR Apache-2.0 |
|
||||
| assets/styles/web/loading.css | SolidCraft contributors | Original SolidCraft resource data or drawing code, collected without redesign | MIT OR Apache-2.0 |
|
||||
| crates/io/src/native_cad/fixtures/block.FCStd | SolidCraft contributors | original 10 × 20 × 30 mm test box saved by FreeCAD 1.1.4 run as an external program (`make_block_fcstd.py` beside it); no third-party geometry | MIT OR Apache-2.0 |
|
||||
@@ -319,6 +333,9 @@ Editable drawing code, native projects and shipped exports are collected under `
|
||||
| assets/icons/ui/design/svg/model/offset_symmetric.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/flat/import_tray.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/flat/export_tray.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/flat/send.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/flat/stop.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/flat/shield.svg | SolidCraft contributors | original icon master (64 grid) | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/model/placeholder.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/model/plane.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
|
||||
| assets/icons/ui/design/svg/model/plane_angle.svg | SolidCraft contributors | two-tone modelling icon master (64 grid), original SolidCraft artwork | MIT OR Apache-2.0 |
|
||||
|
||||
@@ -15,6 +15,15 @@ Before choosing a task, check the [command inventory](docs/features.md) and
|
||||
|
||||
Questions, ideas or want to say what you're working on? Join the [Discord](https://discord.gg/FtU4NQNVDH).
|
||||
|
||||
## Kernel work is paused
|
||||
|
||||
We're evaluating a new geometry kernel. Until that's decided, please don't open issues, pull
|
||||
requests or discussions about kernel work: booleans, fillets, chamfers, shell, offsets, sweeps and
|
||||
lofts, threads and holes, STEP/IGES import and export, tessellation, or anything else that changes
|
||||
`crates/kernel/`. The pinned issue on the tracker explains it. New ones get the `kernel-paused`
|
||||
label (`.github/workflows/kernel-paused.yml`) and a maintainer answers them. Crashes and data loss
|
||||
are still taken as issues, and everything outside the kernel carries on as normal.
|
||||
|
||||
## Get it running (about 10 minutes on a fast machine)
|
||||
|
||||
You need stable Rust 1.95 or newer (required by egui/eframe 0.36). For the web
|
||||
|
||||
Generated
+20
-17
@@ -4162,7 +4162,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft"
|
||||
version = "0.18.1"
|
||||
version = "0.19.0"
|
||||
dependencies = [
|
||||
"block2 0.6.2",
|
||||
"eframe",
|
||||
@@ -4193,13 +4193,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-3dx-client"
|
||||
version = "0.18.1"
|
||||
version = "0.19.0"
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-cli"
|
||||
version = "0.18.1"
|
||||
version = "0.19.0"
|
||||
dependencies = [
|
||||
"image",
|
||||
"log",
|
||||
"serde_json",
|
||||
"solidcraft-engine",
|
||||
"solidcraft-gpu",
|
||||
@@ -4208,7 +4209,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-derive"
|
||||
version = "0.18.1"
|
||||
version = "0.19.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4217,7 +4218,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-doc"
|
||||
version = "0.18.1"
|
||||
version = "0.19.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
@@ -4234,7 +4235,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-engine"
|
||||
version = "0.18.1"
|
||||
version = "0.19.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"schemars",
|
||||
@@ -4253,7 +4254,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-geom"
|
||||
version = "0.18.1"
|
||||
version = "0.19.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4262,7 +4263,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-gpu"
|
||||
version = "0.18.1"
|
||||
version = "0.19.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"naga",
|
||||
@@ -4279,7 +4280,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-io"
|
||||
version = "0.18.1"
|
||||
version = "0.19.0"
|
||||
dependencies = [
|
||||
"cadmpeg-core",
|
||||
"cadmpeg-ir",
|
||||
@@ -4302,7 +4303,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-kernel"
|
||||
version = "0.18.1"
|
||||
version = "0.19.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"proptest",
|
||||
@@ -4322,7 +4323,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-mcp"
|
||||
version = "0.18.1"
|
||||
version = "0.19.0"
|
||||
dependencies = [
|
||||
"serde_json",
|
||||
"solidcraft-engine",
|
||||
@@ -4331,7 +4332,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-render"
|
||||
version = "0.18.1"
|
||||
version = "0.19.0"
|
||||
dependencies = [
|
||||
"image",
|
||||
"moxcms",
|
||||
@@ -4345,7 +4346,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-sketch"
|
||||
version = "0.18.1"
|
||||
version = "0.19.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4356,14 +4357,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-space-mouse"
|
||||
version = "0.18.1"
|
||||
version = "0.19.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-ui-egui"
|
||||
version = "0.18.1"
|
||||
version = "0.19.0"
|
||||
dependencies = [
|
||||
"arboard",
|
||||
"egui",
|
||||
@@ -4390,7 +4391,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "solidcraft-web"
|
||||
version = "0.18.1"
|
||||
version = "0.19.0"
|
||||
dependencies = [
|
||||
"eframe",
|
||||
"egui",
|
||||
@@ -5920,10 +5921,12 @@ checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7"
|
||||
|
||||
[[package]]
|
||||
name = "xtask"
|
||||
version = "0.18.1"
|
||||
version = "0.19.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"serde_json",
|
||||
"sha2 0.10.9",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ members = ["crates/*", "apps/*", "xtask"]
|
||||
exclude = ["vendor"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.18.1"
|
||||
version = "0.19.0"
|
||||
edition = "2024"
|
||||
license = "MIT OR Apache-2.0"
|
||||
rust-version = "1.95"
|
||||
|
||||
+19
-2
@@ -55,7 +55,7 @@ PCB / Package, Render, Animation, Simulation, Manufacture/CAM, Drawings.
|
||||
| M3 | General fillets and chamfers (curved edges, chains, vertex blends); saddle rolling-ball surfaces now use continuous seam sections and arc-length fitting with shared contact boundaries and bounded accuracy checks; tiny rim edges remain selected instead of nearby wall seams when filleting (#2498) | in progress | 30 |
|
||||
| M4 | Files: STEP import (done — [docs/step-import.md](docs/step-import.md)), FreeCAD/Rhino solids through cadmpeg (done, desktop and CLI), 3MF export and 3MF/STL import as mesh bodies (done), 3MF with B-rep face tags ([docs/formats/3mf-face-tags.md](docs/formats/3mf-face-tags.md), done), constant-section parametric mesh reconstruction, DXF sketches, OBJ import | in progress | 6 |
|
||||
| M5 | Persistent naming of faces and edges (docs/book/src/dev/naming.md): faces named from their exact kernel-reported history (sketch curve sides, caps, revolve sections, primitive sides, blends, shell inner walls, pattern/mirror copies; split pieces told apart by the faces around them, lumps of a cut body named after their faces), edges by their faces; fillets/chamfers, sketches on faces, shell/draft/offset/replace faces, hole placements, joint origins and face appearances resolve by name first and warn when the entity split or vanished: done. sheet metal edges by base sketch line and flange side, shell inner faces, sweep and loft roles: done. kernel-reported face history: done (merged faces answer to every name; operations without history yet fall back to geometric names, flagged unverified; format 14 keeps older designs' geometric names). Left: history through sheet metal, plastic, surface and emboss features; FaceSource certificates through history | in progress | 20 |
|
||||
| M6 | UI depth: selection feedback and origin (done), timeline editing (done: hover and selection highlight modifier faces, including fillets and chamfers; edit feature, history marker drag, dependency-checked single and multi-item reorder, suppress, rename, delete with dependents, re-resolved references; redefining selections refreshes persistent edge/face references while retaining feature identity and dimension names; optional compact named left feature history with rollback bar, multi-selection and persistent folders above the parts browser), right-docked dialogs (done), live previews for every feature dialog (done), keyboard-first commands with on-canvas value boxes and sketch inline dimensions (done), drag manipulators with automatic join/cut (done: extrude incl. two sides, fillet, shell, hole depth, move, pattern spacing, revolve rotator, extrude taper), dark theme (done), right-click menu with Repeat (done), measure tool (done), section analysis (done), selection filter (done), drag-solve of sketch points (done); appearance controls now preserve body/face/component targeting, named transparent looks, reuse/search and override removal; preferences | in progress | 6 |
|
||||
| M6 | UI depth: selection feedback and origin (done), timeline editing (done: hover and selection highlight modifier faces, including fillets and chamfers; edit feature, history marker drag, dependency-checked single and multi-item reorder, suppress, rename, delete with dependents, re-resolved references; redefining selections refreshes persistent edge/face references while retaining feature identity and dimension names; responsive feature history with width-driven icons/names/title, snapping width resize, bottom/left/browser-stack docking, shared-column collapse, rollback bar, multi-selection and persistent folders), right-docked dialogs (done), live previews for every feature dialog (done), keyboard-first commands with on-canvas value boxes and sketch inline dimensions (done), drag manipulators with automatic join/cut (done: extrude incl. two sides, fillet, shell, hole depth, move, pattern spacing, revolve rotator, extrude taper), dark theme (done), right-click menu with Repeat (done), measure tool (done), section analysis (done), selection filter (done), drag-solve of sketch points (done); appearance controls now preserve body/face/component targeting, named transparent looks, reuse/search and override removal; preferences | in progress | 6 |
|
||||
| M7 | Thin extrude (walls along open or closed curves: centre, inside, outside or two sides; mitred corners, crossing walls joined; #721), sweep, loft (incl. to a point), rib and web (to next / depth), emboss (planar faces and exact radial emboss/deboss of bounded orthogonal profiles on complete exterior cylinders/sleeves), thread (cosmetic, and modelled M3–M20 outside and inside: the ISO profile screwed in as a faceted cut), coil (smooth B-spline helical walls), pipe, pattern on path, patterns of bodies and components, replace face (parallel), align, remove, feature copy/paste: done. Unique eligible cylindrical targets are inferred for new Emboss features. Left: general curved-face emboss and exact (unfaceted) modelled threads | in progress | 12 |
|
||||
| M8 | Surface workspace (#772). Done: Patch (sketch regions; edge loops filled G0, plus `crown`), Stitch, Thicken (planes and cylinders), Extend (straight edges of planar surfaces), Trim (by a plane, a face or sketch curves swept onto the surface, keeping a side or the picked regions; mutual trim of two surfaces, extended to meet and stitched, #767), Delete Face (healing), Boundary Fill, zebra/draft/curvature-map analysis, Surface Extrude/Revolve/Sweep/Loft from open and closed curves, Offset, Unstitch and Reverse Normal; the continuity-driven core (`kernel/src/bridge.rs`, G0–G3 Hermite rows) with **Blend** (`surface.blend`, #764: edge chains along one or several faces, closed rims paired seam to seam or from picked seams, a dialog), **Match** (`surface.match`), **Boundary** (`surface.boundary`, #763 — two sketch curves exact with G0–G3, three or four as the flat patch of their loop, and curve networks: Coons and Gordon surfaces through sketch, 3D-sketch or edge curves, a point for a three-sided side, G1/G2 against the faces along one direction's edges, joined into their body on request; G1/G2 on two sides that meet at a corner waits for the Gregory-type corner), **Fair** (`surface.fair`, CV fairing), **continuity analysis** (`inspect.continuity`, #770; a toolbar tool with edge markers and a legend, and curvature combs along face isocurves) and a multi-section **loft** with per-end continuity and crown, and guide rails on solid and surface lofts (one or two, scaling or stretching the sections between; #201). Also in the Class-A pass: G1…G4 blend curves in the sketch, chord and variable-radius fillets on curved edges (with honest refusals where the fit cannot hold), and a G2 corner patch where filleted edges meet (kernel; the command refuses until the patch's sections can be sewn to the blends' contacts). Left: bias/form-factor fillet sections (design recorded), a Gregory-type corner, surface Fillet between separate surface bodies (#769), Ruled (#762), Untrim (#761), curved-face Extend (#760), and every remaining #757–#772 item | deferred | — |
|
||||
| M9 | Components and assemblies: New Component / Create Components from Bodies, active component, per-component origin/bodies/sketches/timeline entries, occurrences with transforms, Copy/Paste vs Paste New, nested Browser tree with visibility and isolate; Ground, Move/Copy of occurrences, joints (rigid, revolute, slider, cylindrical, pin-slot, planar, ball) by joint origins with limits and Drive Joints, closed kinematic loops (four-bar, slider-crank: the loop's other joints follow the driven one on its branch, and driving stops where a loop can no longer close), cams (`joint.cam`: a revolute cam drives a slider or revolute follower along a closed sketch profile or the cam's edges, knife, roller or flat followers, worked out again when the profile changes; drives, studies, closed loops, contact sets), As-built Joint, Rigid Group, Contact Sets (driving and motion studies stop where bodies in a set would pass through each other), Motion Study (joint values keyed along a timeline, playback, export of occurrence positions as JSON/CSV), motion analysis (joint values and a point's path charted over a study as value, velocity and acceleration; the parts that collide along a study, with the steps where they do), Exploded views (automatic or by hand, shown without changing placements); interference, per-component physical properties; STEP with assembly structure; URDF export for robot simulators (links with meshes, mass and inertia; joint chains whose values match Drive Joints; docs/urdf.md). Physical properties now measure material-aware component/occurrence placements, descendants and repeated instances, including pending moves and body reassignment offsets; contact geometry and automatic exploded centres respect those offsets. Groundwork done: component tree, occurrences with transforms (instances, Paste New), world placement, active component as authoring frame, ground, Capture/Revert Position, moving bodies and sketches. UI: ASSEMBLE panel dialogs for New Component, Joint (snaps on faces, edges, circles and vertices; animated motion preview; offsets, flip, limits), As-built Joint, Joint Origin, Rigid Group, Drive Joints (live slider), Motion Link, Cam and Interference (overlaps shown red); joints in the timeline and the Browser | in progress | 32 |
|
||||
@@ -70,7 +70,7 @@ pointer movement orbits without a mouse button, and vertical scrolling zooms. Ot
|
||||
retain their saved two-finger scroll preference; camera motion is available through
|
||||
`view.navigate` and modifier-aware `ui.move` automation.
|
||||
|
||||
M11 precursor: File → Export Current View as DXF exports shown placed bodies from the current
|
||||
M11 precursor: File → Export Current View as DXF exports shown placed bodies and sketches from the current
|
||||
camera as a 2D millimetre drawing, with hidden edges omitted or dashed. Vector occlusion splits
|
||||
partly hidden edges and includes curved silhouettes. Repeated part meshes and triangle
|
||||
hierarchies are shared; bounded desktop preparation and occlusion use up to eight workers.
|
||||
@@ -329,8 +329,25 @@ completed drawings publish atomically. The browser still exports synchronously.
|
||||
- Saved projections from a removed strip retain their geometry and report the lost
|
||||
reference. Explicitly reselect the wall to repair that projection.
|
||||
|
||||
## Workspace layouts (#2029)
|
||||
|
||||
- General preferences save Floating or Compact using the universal UI containers.
|
||||
Compact joins the workspace selector and ribbon, docks the content-hugging Browser
|
||||
flush beneath them with a square join, and opens tool sheets below the compass.
|
||||
Sheets remain draggable, rounding all corners away from their right dock; footer
|
||||
backgrounds follow the same corners. A separate preference hides ribbon icon labels
|
||||
for a shorter ribbon in either layout.
|
||||
Assistant keeps the outermost edge and History keeps its chosen dock. Switching
|
||||
retains the active tool, camera, selection and design; system windows and menus
|
||||
keep their existing behavior.
|
||||
|
||||
## View navigation
|
||||
|
||||
- Navigation has an opt-in Free orbit preference for mouse and 3D-mouse rotation through
|
||||
both poles with roll. It defaults off, preserving existing preset snapping and pole limits.
|
||||
The SolidWorks face menu offers Normal to Selection (`view.normal_to`), shared with Look At
|
||||
and Ctrl+8, including repeat-to-flip, preserving scale and projection.
|
||||
|
||||
- Desktop Ctrl/Cmd +/- and 0 change or reset the saved UI scale. The scale slider keeps
|
||||
its layout stable during a drag and applies the selected scale on release.
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ path = "src/alias.rs"
|
||||
solidcraft-engine = { workspace = true }
|
||||
solidcraft-gpu = { workspace = true }
|
||||
solidcraft-mcp = { workspace = true }
|
||||
log = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
//! What the engine logs (`log::info!`, `log::debug!`: why a body has no display plan, why a save
|
||||
//! left its model cache out), written to stderr. Off unless asked for, so the CLI's output and
|
||||
//! the scripts reading it stay as they are: `--debug` shows SolidCraft's own debug messages (and
|
||||
//! other crates' warnings, whose debug output is large), `RUST_LOG=error|warn|info|debug|trace`
|
||||
//! sets one level for every crate.
|
||||
|
||||
use log::{LevelFilter, Log, Metadata, Record};
|
||||
|
||||
struct Stderr {
|
||||
/// The level for SolidCraft's crates.
|
||||
ours: LevelFilter,
|
||||
/// The level for every other crate.
|
||||
others: LevelFilter,
|
||||
}
|
||||
|
||||
impl Log for Stderr {
|
||||
fn enabled(&self, m: &Metadata) -> bool {
|
||||
m.level() <= if m.target().starts_with("solidcraft") { self.ours } else { self.others }
|
||||
}
|
||||
|
||||
fn log(&self, r: &Record) {
|
||||
if self.enabled(r.metadata()) {
|
||||
use std::io::Write as _;
|
||||
// A closed stderr is no reason to stop.
|
||||
let _ = writeln!(std::io::stderr().lock(), "[{} {}] {}", r.level().as_str().to_ascii_lowercase(), r.target(), r.args());
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
/// Removes `--debug` from `args`, wherever it is (every subcommand takes it), and installs the
|
||||
/// logger when it or `RUST_LOG` asks for one.
|
||||
pub fn init(args: &mut Vec<String>) {
|
||||
let before = args.len();
|
||||
args.retain(|a| a != "--debug");
|
||||
let debug = args.len() != before;
|
||||
let env = std::env::var("RUST_LOG").ok().and_then(|v| v.trim().parse::<LevelFilter>().ok());
|
||||
let (ours, others) = match (debug, env) {
|
||||
(false, Some(all)) => (all, all),
|
||||
(true, Some(all)) => (all.max(LevelFilter::Debug), all),
|
||||
(true, None) => (LevelFilter::Debug, LevelFilter::Warn),
|
||||
(false, None) => return,
|
||||
};
|
||||
// Installed once, for the life of the process.
|
||||
if log::set_logger(Box::leak(Box::new(Stderr { ours, others }))).is_ok() {
|
||||
log::set_max_level(ours.max(others));
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@
|
||||
//! MCP server on stdio (docs/mcp.md)
|
||||
//! solidcraft-cli licences SolidCraft's licence and the third-party licences
|
||||
//! solidcraft-cli icon --out F.png|F.ico [--size N] the app icon (packaging; drawn, not a file)
|
||||
//!
|
||||
//! --debug (with any command) SolidCraft's debug log on stderr (or RUST_LOG=LEVEL)
|
||||
//! ```
|
||||
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
|
||||
#![forbid(unsafe_code)]
|
||||
@@ -24,6 +26,7 @@
|
||||
mod bench_agent;
|
||||
mod crashes;
|
||||
mod fuzz;
|
||||
mod logger;
|
||||
mod oracle;
|
||||
mod preview;
|
||||
mod recipe;
|
||||
@@ -82,10 +85,13 @@ const USAGE: &str = "usage:
|
||||
lands on: gpu, gpu-software, or the CPU rasterizer (auto with no adapter, or cpu); the policy defaults
|
||||
to SOLIDCRAFT_GPU, else auto; exits with an error when hardware or software finds no such adapter)
|
||||
solidcraft-cli icon --out icon.png [--size 256] | --out icon.ico
|
||||
--debug, with any command: what SolidCraft logs (why a body has no display plan, why a save left its
|
||||
model cache out), on stderr; RUST_LOG=error|warn|info|debug|trace sets one level for every crate
|
||||
";
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let args: Vec<String> = std::env::args().skip(1).collect();
|
||||
let mut args: Vec<String> = std::env::args().skip(1).collect();
|
||||
logger::init(&mut args);
|
||||
let r = match args.first().map(String::as_str) {
|
||||
Some("run") => cmd_run(&args[1..]),
|
||||
Some("eval" | "inspect") => cmd_eval(&args[1..]),
|
||||
@@ -134,6 +140,12 @@ fn main() -> ExitCode {
|
||||
println!("solidcraft-cli {}", env!("CARGO_PKG_VERSION"));
|
||||
Ok(())
|
||||
}
|
||||
// Asked-for help is not an error: usage on stdout, exit 0.
|
||||
Some("--help" | "-h" | "help") => {
|
||||
use std::io::Write as _;
|
||||
let _ = std::io::stdout().lock().write_all(USAGE.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(USAGE.to_string()),
|
||||
};
|
||||
match r {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
//! An explicit help request prints usage to stdout and succeeds; an unknown command still fails.
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
#[test]
|
||||
fn help_prints_usage_and_succeeds() {
|
||||
for arg in ["--help", "-h", "help"] {
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_solidcraft-cli")).arg(arg).output().unwrap();
|
||||
assert!(output.status.success(), "{arg}: {}", String::from_utf8_lossy(&output.stderr));
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
assert!(stdout.starts_with("usage:"), "{arg}: {stdout}");
|
||||
assert!(stdout.contains("solidcraft-cli run"), "{arg}: {stdout}");
|
||||
assert!(output.stderr.is_empty(), "{arg}: {}", String::from_utf8_lossy(&output.stderr));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_command_still_fails_with_usage() {
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_solidcraft-cli")).arg("--not-a-command").output().unwrap();
|
||||
assert!(!output.status.success());
|
||||
assert!(output.stdout.is_empty());
|
||||
assert!(String::from_utf8_lossy(&output.stderr).starts_with("error: usage:"));
|
||||
}
|
||||
@@ -23,7 +23,7 @@ mod web {
|
||||
|
||||
use solidcraft_engine::Session;
|
||||
use solidcraft_engine::io::vfs;
|
||||
use solidcraft_ui_egui::{Services, SolveApp};
|
||||
use solidcraft_ui_egui::{Services, SolveApp, ui_kit};
|
||||
use wasm_bindgen::JsCast as _;
|
||||
|
||||
use crate::api::Api;
|
||||
@@ -229,24 +229,21 @@ mod web {
|
||||
if !self.files.panel.get() {
|
||||
return;
|
||||
}
|
||||
let mut open = true;
|
||||
let mut act: Option<(&'static str, String)> = None;
|
||||
egui::Window::new("Files in this browser")
|
||||
.open(&mut open)
|
||||
.collapsible(false)
|
||||
.resizable(true)
|
||||
.default_width(520.0)
|
||||
.pivot(egui::Align2::CENTER_CENTER)
|
||||
.default_pos(ctx.content_rect().center())
|
||||
.show(ctx, |ui| {
|
||||
let closed = solidcraft_ui_egui::ui_kit::Window::new(egui::Id::new("sc_browser_files"), "folder", "Files in this browser")
|
||||
.cards(3.0)
|
||||
.show_body(ctx, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("Open from computer…").clicked() {
|
||||
if ui_kit::button(ui, "Open from computer…", ui_kit::Button::Normal).clicked() {
|
||||
files::pick_from_disk(&self.files, Intent::Open);
|
||||
}
|
||||
if ui.button("Insert from computer…").on_hover_text("STEP, 3MF or STL into the open design").clicked() {
|
||||
if ui_kit::button(ui, "Insert from computer…", ui_kit::Button::Normal)
|
||||
.on_hover_text("STEP, 3MF or STL into the open design")
|
||||
.clicked()
|
||||
{
|
||||
files::pick_from_disk(&self.files, Intent::Insert);
|
||||
}
|
||||
if ui.button("Download open design").clicked() {
|
||||
if ui_kit::button(ui, "Download open design", ui_kit::Button::Normal).clicked() {
|
||||
act = Some(("download_open", String::new()));
|
||||
}
|
||||
});
|
||||
@@ -270,7 +267,7 @@ mod web {
|
||||
for e in &entries {
|
||||
match &mut self.renaming {
|
||||
Some((from, to)) if *from == e.name => {
|
||||
let r = ui.add(egui::TextEdit::singleline(to).desired_width(200.0));
|
||||
let r = ui_kit::cell_editor(ui, ui.make_persistent_id(("stored_file_name", from.as_str())), to, "", false);
|
||||
if std::mem::take(&mut self.rename_focus) {
|
||||
r.request_focus();
|
||||
}
|
||||
@@ -288,28 +285,31 @@ mod web {
|
||||
}
|
||||
ui.label(format!("{:.1} KB", e.size as f64 / 1024.0));
|
||||
ui.horizontal(|ui| {
|
||||
if ui.small_button("Open").clicked() {
|
||||
if ui_kit::button(ui, "Open", ui_kit::Button::Small).clicked() {
|
||||
act = Some(("open", e.name.clone()));
|
||||
}
|
||||
let design = solidcraft_engine::io::is_design_path(&e.name);
|
||||
if !design && ui.small_button("Insert").on_hover_text("Into the open design (STEP, 3MF, STL)").clicked() {
|
||||
if !design
|
||||
&& ui_kit::button(ui, "Insert", ui_kit::Button::Small)
|
||||
.on_hover_text("Into the open design (STEP, 3MF, STL)")
|
||||
.clicked()
|
||||
{
|
||||
act = Some(("insert", e.name.clone()));
|
||||
}
|
||||
if ui.small_button("Rename").clicked() {
|
||||
if ui_kit::button(ui, "Rename", ui_kit::Button::Small).clicked() {
|
||||
self.renaming = Some((e.name.clone(), e.name.clone()));
|
||||
self.rename_focus = true;
|
||||
}
|
||||
if ui
|
||||
.small_button("Save to computer…")
|
||||
if ui_kit::button(ui, "Save to computer…", ui_kit::Button::Small)
|
||||
.on_hover_text("A copy in a file you choose; the stored design stays here")
|
||||
.clicked()
|
||||
{
|
||||
act = Some(("save_copy", e.name.clone()));
|
||||
}
|
||||
if ui.small_button("Download").clicked() {
|
||||
if ui_kit::button(ui, "Download", ui_kit::Button::Small).clicked() {
|
||||
act = Some(("download", e.name.clone()));
|
||||
}
|
||||
if ui.small_button("Delete").clicked() {
|
||||
if ui_kit::button(ui, "Delete", ui_kit::Button::Small).clicked() {
|
||||
act = Some(("delete", e.name.clone()));
|
||||
}
|
||||
});
|
||||
@@ -318,7 +318,7 @@ mod web {
|
||||
});
|
||||
});
|
||||
});
|
||||
if !open {
|
||||
if closed {
|
||||
self.files.panel.set(false);
|
||||
self.renaming = None;
|
||||
}
|
||||
@@ -386,20 +386,19 @@ mod web {
|
||||
fn recovery_prompt(&mut self, ctx: &egui::Context) {
|
||||
let Some((name, path)) = self.files.recovery.borrow().clone() else { return };
|
||||
let mut answer = None;
|
||||
egui::Window::new("Recover unsaved work?").collapsible(false).resizable(false).anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0]).show(
|
||||
ctx,
|
||||
|ui| {
|
||||
let _ = solidcraft_ui_egui::ui_kit::Window::new(egui::Id::new("sc_browser_recovery"), "restore", "Recover unsaved work?")
|
||||
.required()
|
||||
.show_body(ctx, |ui| {
|
||||
ui.label(format!("“{name}” had unsaved changes when this page last closed."));
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("Recover").clicked() {
|
||||
if ui_kit::button(ui, "Recover", ui_kit::Button::Normal).clicked() {
|
||||
answer = Some(true);
|
||||
}
|
||||
if ui.button("Discard").clicked() {
|
||||
if ui_kit::button(ui, "Discard", ui_kit::Button::Normal).clicked() {
|
||||
answer = Some(false);
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
let Some(recover) = answer else { return };
|
||||
*self.files.recovery.borrow_mut() = None;
|
||||
if recover {
|
||||
|
||||
@@ -52,6 +52,7 @@ raw-window-handle = "0.6"
|
||||
# The window's resize border and DPI size (src/native_frame.rs): the window procedure, hit test, DPI.
|
||||
windows-sys = { version = "0.61", features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Graphics_Gdi",
|
||||
"Win32_Security",
|
||||
"Win32_Storage_FileSystem",
|
||||
"Win32_System_Diagnostics_Debug",
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
|
||||
use std::io::{BufRead, BufReader, Write};
|
||||
use std::net::{TcpListener, TcpStream};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
|
||||
use std::sync::mpsc::{Receiver, Sender, TryRecvError, channel};
|
||||
use std::time::Duration;
|
||||
use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use solidcraft_engine::MAX_LINE;
|
||||
@@ -178,11 +179,137 @@ struct Pending {
|
||||
reply: Receiver<Value>,
|
||||
}
|
||||
|
||||
fn serve(stream: TcpStream, token: &str, tx: Sender<ControlRequest>, ctx: egui::Context, patient: bool) {
|
||||
serve_with(stream, token, tx, ctx, patient, WAITS);
|
||||
/// Every request the app has been sent and not answered yet, from every connection, for
|
||||
/// `control.running` (#3654). Each connection's own thread answers that, so it never waits behind
|
||||
/// a running request.
|
||||
struct Running {
|
||||
list: Mutex<Vec<InFlight>>,
|
||||
connections: AtomicU64,
|
||||
keys: AtomicU64,
|
||||
}
|
||||
|
||||
fn serve_with(stream: TcpStream, token: &str, tx: Sender<ControlRequest>, ctx: egui::Context, patient: bool, waits: Waits) {
|
||||
/// What `control.running` says about a request: never its parameters beyond the command id.
|
||||
struct InFlight {
|
||||
key: u64,
|
||||
connection: u64,
|
||||
id: Value,
|
||||
method: String,
|
||||
command: Option<&'static str>,
|
||||
steps: Option<usize>,
|
||||
since: Instant,
|
||||
started: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
/// The app's requests, from both of its listeners.
|
||||
static RUNNING: Running = Running::new();
|
||||
|
||||
impl Running {
|
||||
const fn new() -> Self {
|
||||
Running { list: Mutex::new(Vec::new()), connections: AtomicU64::new(0), keys: AtomicU64::new(0) }
|
||||
}
|
||||
|
||||
fn lock(&self) -> MutexGuard<'_, Vec<InFlight>> {
|
||||
self.list.lock().unwrap_or_else(PoisonError::into_inner)
|
||||
}
|
||||
|
||||
/// List `req`, sent on `connection`, until the returned ticket is dropped.
|
||||
fn add(&'static self, connection: u64, id: &Value, req: &ControlRequest) -> Ticket {
|
||||
let key = self.keys.fetch_add(1, Ordering::Relaxed);
|
||||
// Ids and methods come from the client: only short ones are repeated to the others.
|
||||
let id = match id {
|
||||
Value::Number(_) => id.clone(),
|
||||
Value::String(s) if s.len() <= 64 => id.clone(),
|
||||
_ => Value::Null,
|
||||
};
|
||||
let p = &req.params;
|
||||
let command = match req.method.as_str() {
|
||||
"engine.execute" | "command" => p.get("command").and_then(Value::as_str).and_then(solidcraft_engine::find_command).map(|c| c.id),
|
||||
_ => None,
|
||||
};
|
||||
let steps = match req.method.as_str() {
|
||||
"engine.batch" | "engine.script" => p.get("commands").and_then(Value::as_array).map(Vec::len),
|
||||
_ => None,
|
||||
};
|
||||
let method = req.method.chars().take(64).collect();
|
||||
let started = req.started.clone();
|
||||
self.lock().push(InFlight { key, connection, id, method, command, steps, since: Instant::now(), started });
|
||||
Ticket { running: self, key }
|
||||
}
|
||||
|
||||
/// The `control.running` result, as `asker`'s connection sees it.
|
||||
fn result(&self, asker: u64) -> Value {
|
||||
let rows: Vec<Value> = self
|
||||
.lock()
|
||||
.iter()
|
||||
.map(|e| {
|
||||
let mut r = json!({
|
||||
"connection": e.connection,
|
||||
"id": e.id,
|
||||
"method": e.method,
|
||||
"running_s": (e.since.elapsed().as_secs_f64() * 10.0).round() / 10.0,
|
||||
"queued": !e.started.load(Ordering::Relaxed),
|
||||
});
|
||||
if let Some(c) = e.command {
|
||||
r["command"] = json!(c);
|
||||
}
|
||||
if let Some(n) = e.steps {
|
||||
r["steps"] = json!(n);
|
||||
}
|
||||
if e.connection == asker {
|
||||
r["self"] = json!(true);
|
||||
}
|
||||
r
|
||||
})
|
||||
.collect();
|
||||
json!({"running": rows})
|
||||
}
|
||||
}
|
||||
|
||||
/// A listed request; dropped when its reply arrives.
|
||||
struct Ticket {
|
||||
running: &'static Running,
|
||||
key: u64,
|
||||
}
|
||||
|
||||
impl Drop for Ticket {
|
||||
fn drop(&mut self) {
|
||||
let key = self.key;
|
||||
self.running.lock().retain(|e| e.key != key);
|
||||
}
|
||||
}
|
||||
|
||||
/// The reply to a timed-out request, received on a thread of its own so the request stays listed
|
||||
/// until the app answers, also once its connection has closed (#3654).
|
||||
fn watch(reply: Receiver<Value>, ticket: Ticket) -> Receiver<Value> {
|
||||
let (late_tx, late_rx) = channel();
|
||||
// A small thread per timed-out request (each connection has at most one). If none can be
|
||||
// made, the reply is lost and `control.pending` says the app ended the request without one.
|
||||
// The reply is passed on before the request leaves the list: once `control.running` no longer
|
||||
// shows it, its connection's `control.pending` has it.
|
||||
let _ = std::thread::Builder::new().stack_size(64 * 1024).spawn(move || {
|
||||
if let Ok(v) = reply.recv() {
|
||||
let _ = late_tx.send(v);
|
||||
}
|
||||
drop(late_tx);
|
||||
drop(ticket);
|
||||
});
|
||||
late_rx
|
||||
}
|
||||
|
||||
fn serve(stream: TcpStream, token: &str, tx: Sender<ControlRequest>, ctx: egui::Context, patient: bool) {
|
||||
serve_with(stream, token, tx, ctx, patient, WAITS, &RUNNING);
|
||||
}
|
||||
|
||||
fn serve_with(
|
||||
stream: TcpStream,
|
||||
token: &str,
|
||||
tx: Sender<ControlRequest>,
|
||||
ctx: egui::Context,
|
||||
patient: bool,
|
||||
waits: Waits,
|
||||
running: &'static Running,
|
||||
) {
|
||||
let connection = running.connections.fetch_add(1, Ordering::Relaxed) + 1;
|
||||
let Ok(read) = stream.try_clone() else { return };
|
||||
let mut out = stream;
|
||||
let mut reader = BufReader::new(read);
|
||||
@@ -216,13 +343,16 @@ fn serve_with(stream: TcpStream, token: &str, tx: Sender<ControlRequest>, ctx: e
|
||||
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).
|
||||
let late = pending.as_ref().and_then(|p| match p.reply.try_recv() {
|
||||
// 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() {
|
||||
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.pending" {
|
||||
let mut r = if method == "control.running" {
|
||||
json!({"ok": true, "result": running.result(connection)})
|
||||
} else if method == "control.pending" {
|
||||
match (pending.take(), late) {
|
||||
(None, _) => json!({"ok": true, "result": {"pending": null}}),
|
||||
(Some(p), Some(reply)) => json!({"ok": true, "result": {"pending": p.id, "method": p.method, "done": true, "reply": reply}}),
|
||||
@@ -243,6 +373,7 @@ fn serve_with(stream: TcpStream, token: &str, tx: Sender<ControlRequest>, ctx: e
|
||||
// A batch replaces many short requests: the whole of it may take long (#743).
|
||||
let wait = if patient || method == "engine.batch" { waits.long } else { waits.short };
|
||||
let (req, rrx) = ControlRequest::new(method.clone(), params);
|
||||
let ticket = running.add(connection, &id, &req);
|
||||
if tx.send(req).is_err() {
|
||||
break;
|
||||
}
|
||||
@@ -251,7 +382,7 @@ fn serve_with(stream: TcpStream, token: &str, tx: Sender<ControlRequest>, ctx: e
|
||||
Ok(r) => r,
|
||||
Err(_) => {
|
||||
let r = json!({"ok": false, "running": true, "error": format!("timeout: `{method}` is still running in the app and will still apply; ask `control.pending` for its result")});
|
||||
pending = Some(Pending { id: id.clone(), method, reply: rrx });
|
||||
pending = Some(Pending { id: id.clone(), method, reply: watch(rrx, ticket) });
|
||||
r
|
||||
}
|
||||
}
|
||||
@@ -307,9 +438,10 @@ mod tests {
|
||||
}
|
||||
});
|
||||
let waits = Waits { short: Duration::from_millis(100), long: Duration::from_millis(100) };
|
||||
static RUNNING: Running = Running::new();
|
||||
std::thread::spawn(move || {
|
||||
if let Some(stream) = listener.incoming().flatten().next() {
|
||||
serve_with(stream, token, tx, egui::Context::default(), false, waits);
|
||||
serve_with(stream, token, tx, egui::Context::default(), false, waits, &RUNNING);
|
||||
}
|
||||
});
|
||||
let s = TcpStream::connect(addr).unwrap();
|
||||
@@ -349,6 +481,102 @@ mod tests {
|
||||
assert_eq!(*ran.lock().unwrap(), ["slow", "ui.view"], "the busy request never reached the app");
|
||||
}
|
||||
|
||||
/// #3654: `control.running` answers on every connection, at once, with what any connection
|
||||
/// has the app running or queued (command ids, never parameters); a timed-out request stays
|
||||
/// listed until the app answers it, and an idle app lists nothing.
|
||||
#[test]
|
||||
fn every_connection_sees_what_the_app_is_running() {
|
||||
static RUNNING: Running = Running::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: one request at a time, as `drain_control` takes them; the export is held
|
||||
// until the test lets it go.
|
||||
let (gate_tx, gate_rx) = channel::<()>();
|
||||
std::thread::spawn(move || {
|
||||
for req in rx {
|
||||
req.started.store(true, Ordering::Relaxed);
|
||||
if req.method == "engine.execute" {
|
||||
let _ = gate_rx.recv();
|
||||
}
|
||||
let _ = req.reply.send(json!({"ok": true, "result": req.method}));
|
||||
}
|
||||
});
|
||||
let waits = Waits { short: Duration::from_millis(100), long: Duration::from_secs(60) };
|
||||
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));
|
||||
}
|
||||
});
|
||||
let open = || {
|
||||
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);
|
||||
move |v: Value| -> Value {
|
||||
writeln!(out, "{v}").unwrap();
|
||||
let mut l = String::new();
|
||||
lines.read_line(&mut l).unwrap();
|
||||
serde_json::from_str(&l).unwrap()
|
||||
}
|
||||
};
|
||||
let running = |ask: &mut dyn FnMut(Value) -> Value, until: &dyn Fn(&[Value]) -> bool| {
|
||||
let deadline = Instant::now() + Duration::from_secs(10);
|
||||
loop {
|
||||
let r = ask(json!({"id": 0, "method": "control.running", "token": token}));
|
||||
let rows = r["result"]["running"].as_array().cloned().unwrap_or_default();
|
||||
if until(&rows) || Instant::now() > deadline {
|
||||
return (r, rows);
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
};
|
||||
let mut watcher = open();
|
||||
let (r, rows) = running(&mut watcher, &|_| true);
|
||||
assert!(r["ok"] == true && rows.is_empty(), "idle: {r}");
|
||||
|
||||
let mut export = open();
|
||||
let path = "/private/part.step";
|
||||
let params = json!({"command": "file.export", "params": {"path": path}});
|
||||
let r = export(json!({"id": 7, "method": "engine.execute", "params": params, "token": token}));
|
||||
assert_eq!(r["running"], true, "{r}");
|
||||
// A batch from another connection waits behind the export.
|
||||
let mut batch = open();
|
||||
let queued = std::thread::spawn(move || {
|
||||
let steps = json!([{"command": "solid.box"}, {"command": "solid.box"}]);
|
||||
batch(json!({"id": "b", "method": "engine.batch", "params": {"commands": steps}, "token": token}))
|
||||
});
|
||||
// The window may not have taken the export yet, even though it timed out.
|
||||
let (r, rows) = running(&mut watcher, &|rows| rows.len() == 2 && rows[0]["queued"] == false);
|
||||
assert_eq!(rows.len(), 2, "{r}");
|
||||
let (e, b) = (&rows[0], &rows[1]);
|
||||
assert_eq!(
|
||||
(&e["id"], &e["method"], &e["command"], &e["queued"]),
|
||||
(&json!(7), &json!("engine.execute"), &json!("file.export"), &json!(false)),
|
||||
"{r}"
|
||||
);
|
||||
assert!(e["running_s"].as_f64().is_some_and(|s| s >= 0.1), "{r}");
|
||||
assert_eq!((&b["id"], &b["method"], &b["steps"], &b["queued"]), (&json!("b"), &json!("engine.batch"), &json!(2), &json!(true)), "{r}");
|
||||
assert_ne!(e["connection"], b["connection"], "{r}");
|
||||
assert!(rows.iter().all(|row| row.get("self").is_none()), "{r}");
|
||||
assert!(!r.to_string().contains(path), "parameters stay with their connection: {r}");
|
||||
// The connection whose request timed out may ask too, and sees its own marked.
|
||||
let r = export(json!({"id": 8, "method": "control.running"}));
|
||||
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}");
|
||||
|
||||
gate_tx.send(()).unwrap();
|
||||
assert_eq!(queued.join().unwrap()["result"], "engine.batch");
|
||||
let (r, rows) = running(&mut watcher, &|rows| rows.is_empty());
|
||||
assert!(rows.is_empty(), "done: {r}");
|
||||
// Asking `control.running` did not take the export's reply from its connection.
|
||||
let r = export(json!({"id": 9, "method": "control.pending"}));
|
||||
assert_eq!((&r["result"]["pending"], &r["result"]["done"]), (&json!(7), &json!(true)), "{r}");
|
||||
}
|
||||
|
||||
/// #205: a connection runs nothing until a request carries the launch's token; a wrong or
|
||||
/// missing token, or garbage, is refused and the connection closed. Once shown, the token
|
||||
/// need not be repeated on that connection.
|
||||
|
||||
@@ -28,16 +28,54 @@ mod open_documents;
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
mod space_mouse;
|
||||
mod startup;
|
||||
#[cfg(any(target_os = "windows", test))]
|
||||
mod window_fit;
|
||||
|
||||
use solidcraft_engine::{Session, io};
|
||||
use solidcraft_ui_egui::{Services, SolveApp};
|
||||
|
||||
/// Windows: put the restored window back inside the monitor's work area if it is out of reach
|
||||
/// (a gone monitor, another scale, a taskbar edge). Sizes are in points, the work area in pixels.
|
||||
#[cfg(target_os = "windows")]
|
||||
fn fit_window_to_work_area(ctx: &egui::Context, frame: &eframe::Frame) {
|
||||
use window_fit::Area;
|
||||
let Some((x, y, w, h)) = native_frame::work_area(frame) else { return };
|
||||
let (outer, inner, scale, maximized) = ctx.input(|i| {
|
||||
let v = i.viewport();
|
||||
(v.outer_rect, v.inner_rect, v.native_pixels_per_point, v.maximized.unwrap_or(false) || v.fullscreen.unwrap_or(false))
|
||||
});
|
||||
let (Some(outer), Some(inner), Some(scale)) = (outer, inner, scale.filter(|s| s.is_finite() && *s > 0.0)) else { return };
|
||||
if maximized {
|
||||
return;
|
||||
}
|
||||
let work = Area::new(x as f32 / scale, y as f32 / scale, w as f32 / scale, h as f32 / scale);
|
||||
let now = Area::new(outer.min.x, outer.min.y, outer.width(), outer.height());
|
||||
let fitted = window_fit::fit(now, work, (960.0, 600.0));
|
||||
if fitted != now {
|
||||
// The frame (title bar, borders) keeps its size; only the inner size follows.
|
||||
let inner_size = egui::vec2((inner.width() - (now.w - fitted.w)).max(1.0), (inner.height() - (now.h - fitted.h)).max(1.0));
|
||||
ctx.send_viewport_cmd(egui::ViewportCommand::InnerSize(inner_size));
|
||||
ctx.send_viewport_cmd(egui::ViewportCommand::OuterPosition(egui::pos2(fitted.x, fitted.y)));
|
||||
}
|
||||
}
|
||||
|
||||
/// `SOLIDCRAFT_PROFILE_STARTUP=1` prints the time since launch at each startup step.
|
||||
fn mark(step: &str) {
|
||||
static START: std::sync::OnceLock<(std::time::Instant, bool)> = std::sync::OnceLock::new();
|
||||
let (start, on) = START.get_or_init(|| (std::time::Instant::now(), solidcraft_engine::geom::env::var_os("PROFILE_STARTUP").is_some()));
|
||||
if *on {
|
||||
eprintln!("startup {:>6.0} ms {step}", start.elapsed().as_secs_f64() * 1000.0);
|
||||
}
|
||||
}
|
||||
|
||||
struct App {
|
||||
solve: SolveApp,
|
||||
/// The settings folder whose start marker the first frame clears (`startup::started`).
|
||||
starting: Option<std::path::PathBuf>,
|
||||
/// Frames begun; the second one means the first was drawn (startup is over).
|
||||
frames: u32,
|
||||
/// Started over the control channel: the window doesn't ask for the foreground.
|
||||
take_focus_unwanted: bool,
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
space_mouse: space_mouse::SpaceMouse,
|
||||
}
|
||||
@@ -46,9 +84,23 @@ impl eframe::App for App {
|
||||
fn logic(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
if self.frames < 2 {
|
||||
self.frames += 1;
|
||||
// Before the window is shown: its title bar must be within reach (window_fit.rs).
|
||||
#[cfg(target_os = "windows")]
|
||||
if self.frames == 1 {
|
||||
fit_window_to_work_area(ctx, _frame);
|
||||
}
|
||||
mark(if self.frames == 1 { "first frame begins" } else { "second frame begins (first presented)" });
|
||||
// A second frame right away, which means the first was presented: the app is up.
|
||||
ctx.request_repaint();
|
||||
if self.frames == 2 {
|
||||
// The window is shown only once its first frame is presented, and Windows then
|
||||
// keeps a window of a process started from a shortcut or an installer behind the
|
||||
// others. Ask for the foreground once, now that it is on screen; never again, so
|
||||
// later frames don't take the focus from whatever the user moved to. Automation
|
||||
// over the control channel keeps the focus where it is.
|
||||
if !self.take_focus_unwanted {
|
||||
ctx.send_viewport_cmd(egui::ViewportCommand::Focus);
|
||||
}
|
||||
solidcraft_engine::crash::phase(solidcraft_engine::crash::RUNNING);
|
||||
if let Some(dir) = self.starting.take() {
|
||||
startup::started(&dir);
|
||||
@@ -477,6 +529,7 @@ fn app_viewport() -> Result<egui::ViewportBuilder, eframe::Error> {
|
||||
const APP_ID: &str = solidcraft_engine::io::APP_ID;
|
||||
|
||||
fn main() -> eframe::Result {
|
||||
mark("main");
|
||||
adopt_legacy_settings();
|
||||
let mut safe_mode = startup::env_on(std::env::var(startup::SAFE_MODE_ENV).ok().as_deref());
|
||||
let mut control_port: Option<u16> = solidcraft_engine::geom::env::var("CONTROL_PORT").and_then(|p| p.parse().ok());
|
||||
@@ -587,13 +640,17 @@ fn main() -> eframe::Result {
|
||||
let created = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
|
||||
let created_in_app = created.clone();
|
||||
let starting = settings.clone();
|
||||
mark("run_native");
|
||||
let result = eframe::run_native(
|
||||
"SolidCraft",
|
||||
options,
|
||||
Box::new(move |cc| {
|
||||
created_in_app.store(true, std::sync::atomic::Ordering::SeqCst);
|
||||
solidcraft_engine::crash::phase("app setup");
|
||||
solidcraft_ui_egui::fonts::setup_fonts(&cc.egui_ctx);
|
||||
mark("window and device ready");
|
||||
solidcraft_ui_egui::fonts::setup_bundled_fonts(&cc.egui_ctx);
|
||||
solidcraft_ui_egui::fonts::load_system_fonts_in_background(&cc.egui_ctx);
|
||||
mark("fonts");
|
||||
let mut app = SolveApp::new(Session::default(), services(cc));
|
||||
app.apply_off_thread = Some(solidcraft_ui_egui::dialogs::APPLY_AT_ONCE);
|
||||
app.custom_titlebar = CUSTOM_TITLEBAR;
|
||||
@@ -611,6 +668,7 @@ fn main() -> eframe::Result {
|
||||
if let Some(p) = cc.storage.and_then(|s| solidcraft_ui_egui::prefs::stored(|k| s.get_string(k))).filter(|_| !safe_mode) {
|
||||
app.load_prefs(&p);
|
||||
}
|
||||
mark("app state and prefs");
|
||||
if let Some(rs) = &cc.wgpu_render_state {
|
||||
eprintln!("SolidCraft graphics: {:?}, MSAA {}x (requested {}x)", rs.adapter.get_info().backend, rs.msaa_samples, samples);
|
||||
solidcraft_engine::crash::phase("viewport pipelines (shader compile)");
|
||||
@@ -628,6 +686,7 @@ fn main() -> eframe::Result {
|
||||
info.backend, info.name, info.driver, rs.msaa_samples
|
||||
));
|
||||
}
|
||||
mark("viewport pipelines");
|
||||
if let Some(port) = control_port {
|
||||
let rx = control_server::start(port, cc.egui_ctx.clone());
|
||||
app = app.with_control(rx);
|
||||
@@ -680,11 +739,13 @@ fn main() -> eframe::Result {
|
||||
}
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
let space_mouse = space_mouse::SpaceMouse::start(&cc.egui_ctx);
|
||||
mark("app created");
|
||||
solidcraft_engine::crash::phase("first frame");
|
||||
Ok(Box::new(App {
|
||||
solve: app,
|
||||
starting,
|
||||
frames: 0,
|
||||
take_focus_unwanted: control_port.is_some(),
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
space_mouse,
|
||||
}))
|
||||
|
||||
@@ -26,6 +26,7 @@ use std::sync::atomic::{AtomicIsize, Ordering};
|
||||
use raw_window_handle::{HasWindowHandle, RawWindowHandle};
|
||||
use solidcraft_ui_egui::titlebar;
|
||||
use windows_sys::Win32::Foundation::{HWND, LPARAM, LRESULT, RECT, SIZE, WPARAM};
|
||||
use windows_sys::Win32::Graphics::Gdi::{GetMonitorInfoW, MONITOR_DEFAULTTONEAREST, MONITORINFO, MonitorFromWindow};
|
||||
use windows_sys::Win32::UI::HiDpi::{GetDpiForWindow, GetSystemMetricsForDpi};
|
||||
use windows_sys::Win32::UI::WindowsAndMessaging::{
|
||||
CallWindowProcW, GWL_STYLE, GWLP_WNDPROC, GetClientRect, GetWindowLongPtrW, GetWindowRect, HTBOTTOM, HTBOTTOMLEFT, HTBOTTOMRIGHT, HTLEFT,
|
||||
@@ -33,6 +34,26 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{
|
||||
WM_NCDESTROY, WM_NCHITTEST, WNDPROC, WS_THICKFRAME,
|
||||
};
|
||||
|
||||
/// The work area (the monitor without the taskbar) of the monitor nearest the window, in physical
|
||||
/// pixels: left, top, width, height.
|
||||
pub fn work_area(window: &impl HasWindowHandle) -> Option<(i32, i32, i32, i32)> {
|
||||
let handle = window.window_handle().ok()?;
|
||||
let RawWindowHandle::Win32(w) = handle.as_raw() else { return None };
|
||||
let hwnd = w.hwnd.get() as HWND;
|
||||
// SAFETY: `hwnd` is the live main window; `info` is a MONITORINFO with its size set, as
|
||||
// GetMonitorInfoW requires, and the monitor handle comes from the call before it.
|
||||
let rc = unsafe {
|
||||
let monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
||||
let mut info: MONITORINFO = std::mem::zeroed();
|
||||
info.cbSize = std::mem::size_of::<MONITORINFO>() as u32;
|
||||
if monitor.is_null() || GetMonitorInfoW(monitor, &mut info) == 0 {
|
||||
return None;
|
||||
}
|
||||
info.rcWork
|
||||
};
|
||||
Some((rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top))
|
||||
}
|
||||
|
||||
/// The window procedure ours replaced (winit's), as an address; 0 before [`install`].
|
||||
static PREVIOUS: AtomicIsize = AtomicIsize::new(0);
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ mod hidraw;
|
||||
#[cfg(target_os = "linux")]
|
||||
mod spacenavd;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::sync::atomic::AtomicU8;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration, Instant};
|
||||
@@ -19,9 +21,22 @@ use quick_xml::Reader;
|
||||
use quick_xml::events::Event;
|
||||
use solidcraft_space_mouse::Layout;
|
||||
use solidcraft_ui_egui::SolveApp;
|
||||
#[cfg(target_os = "linux")]
|
||||
use solidcraft_ui_egui::prefs::SpaceMouseDriver;
|
||||
#[cfg(target_os = "linux")]
|
||||
use solidcraft_ui_egui::space_mouse::DriversPresent;
|
||||
use solidcraft_ui_egui::space_mouse::{Axes, AxisRule, Calibration, DriverMapping, WheelGuard, calibrated, calibration, navigate_frame};
|
||||
|
||||
const STALE_AFTER: Duration = Duration::from_millis(150);
|
||||
/// How long a held (spacenavd) reading that is nearly at rest may go without a new record before
|
||||
/// it reads as rest: spacenavd sends no final record when the cap springs back, only the last
|
||||
/// small leftover. Longer than `STALE_AFTER` so a light, steady hold gets a moment before it ends.
|
||||
const HELD_SETTLE_AFTER: Duration = Duration::from_millis(500);
|
||||
/// How far from rest (raw units, full deflection is 350) every axis of a held reading must be for
|
||||
/// it to settle. The dead zone is 20, but the last records of real releases reach 129 on one axis
|
||||
/// (tangency-labs/solidcraft#3523), so this sits well above it. A firmer push is a deliberate
|
||||
/// hold and stays.
|
||||
const HELD_SETTLE_BAND: u16 = 150;
|
||||
/// Whether the 3Dconnexion driver (macOS) has the device: it then reports the cap, and reading
|
||||
/// the HID device would fail or report it twice.
|
||||
static DRIVER_CONNECTED: AtomicBool = AtomicBool::new(false);
|
||||
@@ -199,9 +214,11 @@ struct Reading {
|
||||
/// When each of X, Y, Z, Rx, Ry and Rz last arrived. Each ages on its own: a device may send
|
||||
/// translation and rotation in separate reports, or spread even one group over several.
|
||||
at: [Instant; 6],
|
||||
/// The source says when the cap is back at rest, so a reading stays until the next one.
|
||||
/// A HID device repeats its report while the cap is held; spacenavd sends a record only when
|
||||
/// the values change, and a steady hold can go seconds without one.
|
||||
/// The source sends a record only when the values change, so a reading stays until the next
|
||||
/// one: a steady hold can go seconds without one. It never says when the cap springs back,
|
||||
/// though, so a reading within `HELD_SETTLE_BAND` of rest on every axis settles after
|
||||
/// `HELD_SETTLE_AFTER`. A HID device repeats its report while the cap is held, so it ages out
|
||||
/// per axis instead.
|
||||
held: bool,
|
||||
}
|
||||
|
||||
@@ -214,7 +231,14 @@ impl Reading {
|
||||
/// is calibrated, so a device that rests off zero calibrates to no motion rather than drifting.
|
||||
fn current(self, now: Instant, rest: Axes) -> Axes {
|
||||
let mut axes = self.axes;
|
||||
if !self.held {
|
||||
if self.held {
|
||||
let values = axes.translation.into_iter().chain(axes.rotation);
|
||||
let rests = rest.translation.into_iter().chain(rest.rotation);
|
||||
let near_rest = values.zip(rests).all(|(value, rest)| value.abs_diff(rest) <= HELD_SETTLE_BAND);
|
||||
if near_rest && now.duration_since(self.at[0]) >= HELD_SETTLE_AFTER {
|
||||
return rest;
|
||||
}
|
||||
} else {
|
||||
let rest = rest.translation.into_iter().chain(rest.rotation);
|
||||
for ((value, at), rest) in axes.translation.iter_mut().chain(axes.rotation.iter_mut()).zip(self.at).zip(rest) {
|
||||
if now.duration_since(at) >= STALE_AFTER {
|
||||
@@ -359,39 +383,130 @@ fn device_layout(device: &hidapi::HidDevice, vendor_id: u16) -> Option<Layout> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the 3D mouse through spacenavd when it runs, so what `spnavcfg` sets (swapped and inverted
|
||||
/// axes, sensitivity, dead zones) applies here as in other programs. Without the daemon it reads
|
||||
/// `/dev/hidrawN` with plain blocking reads, which end with an error when the device is unplugged.
|
||||
/// Opening needs permission: most distributions grant it to the logged-in user for devices that
|
||||
/// carry the `uaccess` udev tag, which a rule for the 3D mouse has to add.
|
||||
/// What the user chose for the reader and what the reader found, shared with the egui thread.
|
||||
#[cfg(target_os = "linux")]
|
||||
fn reader(ctx: Context, shared: Arc<Mutex<Reading>>) {
|
||||
use std::io::Read;
|
||||
#[derive(Default)]
|
||||
struct Drivers {
|
||||
/// `SpaceMouseDriver::to_u8` of the preference.
|
||||
choice: AtomicU8,
|
||||
/// `PRESENT_SPACENAVD` and `PRESENT_DIRECT`, as of the reader's last look.
|
||||
found: AtomicU8,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
const PRESENT_SPACENAVD: u8 = 1;
|
||||
#[cfg(target_os = "linux")]
|
||||
const PRESENT_DIRECT: u8 = 2;
|
||||
/// How often a connected reader looks again at what is there, and how long a read of spacenavd
|
||||
/// waits for a record before it checks the choice.
|
||||
#[cfg(target_os = "linux")]
|
||||
const LOOK_EVERY: Duration = Duration::from_secs(1);
|
||||
#[cfg(target_os = "linux")]
|
||||
const SPACENAVD_WAKE: Duration = Duration::from_millis(250);
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
impl Drivers {
|
||||
fn choice(&self) -> SpaceMouseDriver {
|
||||
SpaceMouseDriver::from_u8(self.choice.load(Ordering::Relaxed))
|
||||
}
|
||||
|
||||
fn present(&self) -> DriversPresent {
|
||||
let found = self.found.load(Ordering::Relaxed);
|
||||
DriversPresent { spacenavd: found & PRESENT_SPACENAVD != 0, direct: found & PRESENT_DIRECT != 0 }
|
||||
}
|
||||
|
||||
fn publish(&self, present: DriversPresent) {
|
||||
let found = (u8::from(present.spacenavd) * PRESENT_SPACENAVD) | (u8::from(present.direct) * PRESENT_DIRECT);
|
||||
self.found.store(found, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
/// The reader that was picked.
|
||||
#[cfg(target_os = "linux")]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum Pick {
|
||||
Spacenavd,
|
||||
Direct,
|
||||
}
|
||||
|
||||
/// Which reader to use: the one asked for when it is there, else the other, else none. Automatic
|
||||
/// asks for spacenavd, which is what has always been read first.
|
||||
#[cfg(target_os = "linux")]
|
||||
fn choose(wanted: SpaceMouseDriver, spacenavd: bool, direct: bool) -> Option<Pick> {
|
||||
let first = match wanted {
|
||||
SpaceMouseDriver::Automatic | SpaceMouseDriver::Spacenavd => Pick::Spacenavd,
|
||||
SpaceMouseDriver::Direct => Pick::Direct,
|
||||
};
|
||||
let there = |pick| match pick {
|
||||
Pick::Spacenavd => spacenavd,
|
||||
Pick::Direct => direct,
|
||||
};
|
||||
let other = if first == Pick::Spacenavd { Pick::Direct } else { Pick::Spacenavd };
|
||||
[first, other].into_iter().find(|&pick| there(pick))
|
||||
}
|
||||
|
||||
/// Reads the 3D mouse through spacenavd or `/dev/hidrawN`, as `Drivers::choice` says (Automatic
|
||||
/// is spacenavd when it runs, so what `spnavcfg` sets, swapped and inverted axes, sensitivity,
|
||||
/// dead zones, applies here as in other programs). Both reads wake up when idle to see whether
|
||||
/// the choice changed, which then moves to the other reader without a restart. hidraw is opened
|
||||
/// non-blocking and ends with an error when the device is unplugged. Opening needs permission:
|
||||
/// most distributions grant it to the logged-in user for devices that carry the `uaccess` udev
|
||||
/// tag, which a rule for the 3D mouse has to add.
|
||||
#[cfg(target_os = "linux")]
|
||||
fn reader(ctx: Context, shared: Arc<Mutex<Reading>>, drivers: Arc<Drivers>) {
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
let look = || DriversPresent { spacenavd: spacenavd::exists(), direct: hidraw::find().is_some() };
|
||||
let mut denied = None;
|
||||
let mut told = None;
|
||||
loop {
|
||||
if let Some(daemon) = spacenavd::connect() {
|
||||
log::info!("3D mouse: reading spacenavd, which applies the spnavcfg settings");
|
||||
connected(&ctx, &shared, Source::Spacenavd);
|
||||
spacenavd::read(daemon, |axes| publish_held(&ctx, &shared, axes));
|
||||
reset(&ctx, &shared);
|
||||
} else if let Some(device) = hidraw::find() {
|
||||
match std::fs::File::open(&device.path) {
|
||||
Ok(mut file) => {
|
||||
let device = hidraw::find();
|
||||
let present = DriversPresent { spacenavd: spacenavd::exists(), direct: device.is_some() };
|
||||
drivers.publish(present);
|
||||
let wanted = drivers.choice();
|
||||
let mut pick = choose(wanted, present.spacenavd, present.direct);
|
||||
// A socket file with no daemon behind it is no spacenavd.
|
||||
let daemon = if pick == Some(Pick::Spacenavd) { spacenavd::connect() } else { None };
|
||||
if pick == Some(Pick::Spacenavd) && daemon.is_none() {
|
||||
pick = choose(wanted, false, present.direct);
|
||||
}
|
||||
let asked_for = match wanted {
|
||||
SpaceMouseDriver::Automatic => None,
|
||||
SpaceMouseDriver::Spacenavd => Some(Pick::Spacenavd),
|
||||
SpaceMouseDriver::Direct => Some(Pick::Direct),
|
||||
};
|
||||
if let (Some(asked), Some(pick)) = (asked_for, pick)
|
||||
&& asked != pick
|
||||
&& told != Some((wanted, pick))
|
||||
{
|
||||
log::info!("3D mouse: {asked:?} is not available, reading {pick:?} instead");
|
||||
told = Some((wanted, pick));
|
||||
}
|
||||
// Asked at every idle moment of a read: has the choice changed, and what is there now.
|
||||
let mut looked = Instant::now();
|
||||
let mut stop = || {
|
||||
if looked.elapsed() >= LOOK_EVERY {
|
||||
looked = Instant::now();
|
||||
drivers.publish(look());
|
||||
}
|
||||
drivers.choice() != wanted
|
||||
};
|
||||
match (pick, daemon, device) {
|
||||
(Some(Pick::Spacenavd), Some(daemon), _) => {
|
||||
log::info!("3D mouse: reading spacenavd, which applies the spnavcfg settings");
|
||||
if let Err(error) = daemon.set_read_timeout(Some(SPACENAVD_WAKE)) {
|
||||
log::warn!("3D mouse: spacenavd reads cannot time out ({error}); a change of driver waits for the next event");
|
||||
}
|
||||
connected(&ctx, &shared, Source::Spacenavd);
|
||||
spacenavd::read(daemon, |axes| publish_held(&ctx, &shared, axes), &mut stop);
|
||||
reset(&ctx, &shared);
|
||||
}
|
||||
(Some(Pick::Direct), _, Some(device)) => match std::fs::OpenOptions::new().read(true).custom_flags(hidraw::O_NONBLOCK).open(&device.path)
|
||||
{
|
||||
Ok(file) => {
|
||||
denied = None;
|
||||
log::info!("3D mouse: reading {}", device.path.display());
|
||||
connected(&ctx, &shared, Source::Hid(device.ids));
|
||||
let mut report = [0_u8; REPORT_BUFFER_BYTES];
|
||||
loop {
|
||||
match file.read(&mut report) {
|
||||
Ok(0) => break,
|
||||
Ok(size) => publish(&ctx, &shared, device.layout, report.get(..size).unwrap_or_default()),
|
||||
Err(error) if error.kind() == std::io::ErrorKind::Interrupted => {}
|
||||
Err(error) => {
|
||||
log::info!("3D mouse disconnected: {error}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
hidraw::read_reports(file, |report| publish(&ctx, &shared, device.layout, report), &mut stop);
|
||||
reset(&ctx, &shared);
|
||||
}
|
||||
// Say so once per device node, not every retry.
|
||||
@@ -400,9 +515,13 @@ fn reader(ctx: Context, shared: Arc<Mutex<Reading>>) {
|
||||
denied = Some(device.path);
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
// A new choice is taken up at once; anything else waits to try again.
|
||||
if drivers.choice() == wanted {
|
||||
std::thread::sleep(RETRY_AFTER);
|
||||
}
|
||||
std::thread::sleep(RETRY_AFTER);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,6 +564,8 @@ fn start_driver_client(ctx: &Context, shared: &Arc<Mutex<Reading>>) {
|
||||
|
||||
pub struct SpaceMouse {
|
||||
shared: Arc<Mutex<Reading>>,
|
||||
#[cfg(target_os = "linux")]
|
||||
drivers: Arc<Drivers>,
|
||||
last_frame: Instant,
|
||||
driver_mapping: DriverMapping,
|
||||
mapping_checked_at: Instant,
|
||||
@@ -462,13 +583,25 @@ impl SpaceMouse {
|
||||
let shared = Arc::new(Mutex::new(Reading::new(now)));
|
||||
let worker_state = Arc::clone(&shared);
|
||||
let worker_ctx = ctx.clone();
|
||||
if let Err(error) = std::thread::Builder::new().name("solidcraft-3d-mouse".into()).spawn(move || reader(worker_ctx, worker_state)) {
|
||||
#[cfg(target_os = "linux")]
|
||||
let drivers = Arc::new(Drivers::default());
|
||||
#[cfg(target_os = "linux")]
|
||||
let worker_drivers = Arc::clone(&drivers);
|
||||
let spawned = std::thread::Builder::new().name("solidcraft-3d-mouse".into()).spawn(move || {
|
||||
#[cfg(target_os = "linux")]
|
||||
reader(worker_ctx, worker_state, worker_drivers);
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
reader(worker_ctx, worker_state);
|
||||
});
|
||||
if let Err(error) = spawned {
|
||||
log::warn!("3D mouse: reader could not start: {error}");
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
start_driver_client(ctx, &shared);
|
||||
Self {
|
||||
shared,
|
||||
#[cfg(target_os = "linux")]
|
||||
drivers,
|
||||
last_frame: now,
|
||||
driver_mapping: load_driver_mapping().unwrap_or_else(without_profile),
|
||||
mapping_checked_at: now,
|
||||
@@ -516,6 +649,11 @@ impl SpaceMouse {
|
||||
}
|
||||
self.mapping_checked_at = now;
|
||||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
self.drivers.choice.store(app.preferences.space_mouse_driver.to_u8(), Ordering::Relaxed);
|
||||
app.space_mouse.drivers = Some(self.drivers.present());
|
||||
}
|
||||
let Ok(reading) = self.shared.lock().map(|reading| *reading) else { return };
|
||||
app.space_mouse.device = reading.device.map(Source::key);
|
||||
let calibration = calibration(app);
|
||||
@@ -529,11 +667,108 @@ impl SpaceMouse {
|
||||
}
|
||||
}
|
||||
|
||||
/// A stream that answers reads from a script, for the timeout paths.
|
||||
#[cfg(all(test, target_os = "linux"))]
|
||||
mod fake {
|
||||
use std::collections::VecDeque;
|
||||
use std::io::{Error, ErrorKind, Read, Result};
|
||||
|
||||
enum Step {
|
||||
Data(Vec<u8>),
|
||||
Fail(ErrorKind),
|
||||
End,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Script(VecDeque<Step>);
|
||||
|
||||
impl Script {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn data(&mut self, bytes: &[u8]) -> &mut Self {
|
||||
self.0.push_back(Step::Data(bytes.to_vec()));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn timeout(&mut self, kind: ErrorKind) -> &mut Self {
|
||||
self.fail(kind)
|
||||
}
|
||||
|
||||
pub fn fail(&mut self, kind: ErrorKind) -> &mut Self {
|
||||
self.0.push_back(Step::Fail(kind));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn end(&mut self) -> &mut Self {
|
||||
self.0.push_back(Step::End);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Past the script's last step the stream has ended.
|
||||
impl Read for Script {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
match self.0.pop_front() {
|
||||
Some(Step::Data(bytes)) => {
|
||||
let size = bytes.len().min(buf.len());
|
||||
buf[..size].copy_from_slice(&bytes[..size]);
|
||||
Ok(size)
|
||||
}
|
||||
Some(Step::Fail(kind)) => Err(Error::from(kind)),
|
||||
Some(Step::End) | None => Ok(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use solidcraft_ui_egui::space_mouse::{DeviceMotion, Motion};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn the_reader_asked_for_is_used_when_it_is_there_and_the_other_when_it_is_not() {
|
||||
use Pick::{Direct, Spacenavd};
|
||||
use SpaceMouseDriver::{Automatic, Direct as AskDirect, Spacenavd as AskSpacenavd};
|
||||
// (asked, spacenavd there, device there) and what is read.
|
||||
let cases = [
|
||||
(Automatic, true, true, Some(Spacenavd)),
|
||||
(Automatic, true, false, Some(Spacenavd)),
|
||||
(Automatic, false, true, Some(Direct)),
|
||||
(Automatic, false, false, None),
|
||||
(AskSpacenavd, true, true, Some(Spacenavd)),
|
||||
(AskSpacenavd, true, false, Some(Spacenavd)),
|
||||
(AskSpacenavd, false, true, Some(Direct)),
|
||||
(AskSpacenavd, false, false, None),
|
||||
(AskDirect, true, true, Some(Direct)),
|
||||
(AskDirect, true, false, Some(Spacenavd)),
|
||||
(AskDirect, false, true, Some(Direct)),
|
||||
(AskDirect, false, false, None),
|
||||
];
|
||||
for (asked, spacenavd, direct, expected) in cases {
|
||||
assert_eq!(choose(asked, spacenavd, direct), expected, "{asked:?}, spacenavd {spacenavd}, device {direct}");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn what_the_reader_found_and_the_choice_pass_between_threads() {
|
||||
let drivers = Drivers::default();
|
||||
assert_eq!(drivers.present(), DriversPresent::default());
|
||||
for (spacenavd, direct) in [(true, false), (false, true), (true, true), (false, false)] {
|
||||
drivers.publish(DriversPresent { spacenavd, direct });
|
||||
assert_eq!(drivers.present(), DriversPresent { spacenavd, direct });
|
||||
}
|
||||
assert_eq!(drivers.choice(), SpaceMouseDriver::Automatic);
|
||||
for choice in SpaceMouseDriver::ALL {
|
||||
drivers.choice.store(choice.to_u8(), Ordering::Relaxed);
|
||||
assert_eq!(drivers.choice(), choice);
|
||||
}
|
||||
}
|
||||
|
||||
/// The logical axes a reading reaches. Logical Rz rolls the view and logical Ry turns it about
|
||||
/// its vertical (`navigation::device_camera` in solidcraft-ui-egui, tested there).
|
||||
fn rotation(mapping: DriverMapping, rotation: [i16; 3]) -> [f64; 3] {
|
||||
@@ -625,6 +860,55 @@ mod tests {
|
||||
assert_eq!(hid.current(later, Axes::default()), Axes::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_held_reading_near_rest_settles_after_the_timeout() {
|
||||
// The last records of seven releases from tangency-labs/solidcraft#3523: spacenavd sends
|
||||
// nothing after them.
|
||||
let leftovers = [
|
||||
[15, -60, 13, 59, 34, 129],
|
||||
[-12, -69, 4, -26, 7, -6],
|
||||
[0, -45, -3, 16, 8, -60],
|
||||
[-31, 27, 22, -5, 6, -10],
|
||||
[0, -30, -3, 3, -6, 0],
|
||||
[5, 32, -2, 43, -8, 12],
|
||||
[3, 2, -22, 3, 6, 5],
|
||||
];
|
||||
let now = Instant::now();
|
||||
for [x, y, z, rx, ry, rz] in leftovers {
|
||||
let axes = Axes { translation: [x, y, z], rotation: [rx, ry, rz] };
|
||||
let held = Reading { axes, held: true, ..Reading::new(now) };
|
||||
assert_eq!(held.current(now + HELD_SETTLE_AFTER - Duration::from_millis(1), Axes::default()), axes);
|
||||
assert_eq!(held.current(now + HELD_SETTLE_AFTER, Axes::default()), Axes::default());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_settled_held_reading_reads_as_the_devices_own_rest() {
|
||||
let now = Instant::now();
|
||||
let rest = Axes { translation: [10, -8, 0], rotation: [0, 5, 0] };
|
||||
// 40 from zero on X, but only 30 from this device's rest.
|
||||
let held = Reading { axes: Axes { translation: [40, -8, 0], rotation: [0, 5, 0] }, held: true, ..Reading::new(now) };
|
||||
assert_eq!(held.current(now + HELD_SETTLE_AFTER, rest), rest);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_firm_held_push_stays_however_long_it_lasts() {
|
||||
let now = Instant::now();
|
||||
let later = now + Duration::from_secs(10);
|
||||
let push = Axes { translation: [0, 350, 0], rotation: [0; 3] };
|
||||
let held = Reading { axes: push, held: true, ..Reading::new(now) };
|
||||
assert_eq!(held.current(later, Axes::default()), push);
|
||||
// One firm axis keeps the small ones with it.
|
||||
let mixed = Axes { translation: [5, 300, -10], rotation: [3, 0, 20] };
|
||||
let held = Reading { axes: mixed, held: true, ..Reading::new(now) };
|
||||
assert_eq!(held.current(later, Axes::default()), mixed);
|
||||
// The band is per reading, edge included: at it is a leftover, one past it is a hold.
|
||||
let band = HELD_SETTLE_BAND as i16;
|
||||
let twist = |value| Reading { axes: Axes { translation: [0; 3], rotation: [0, 0, value] }, held: true, ..Reading::new(now) };
|
||||
assert_eq!(twist(band).current(later, Axes::default()), Axes::default());
|
||||
assert_eq!(twist(band + 1).current(later, Axes::default()).rotation, [0, 0, band + 1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn applies_3dxware_axis_swaps_and_reversals() {
|
||||
let profile = br#"
|
||||
@@ -689,6 +973,8 @@ mod tests {
|
||||
reading.axes.rotation = [0, 350, 0];
|
||||
let mut mouse = SpaceMouse {
|
||||
shared: Arc::new(Mutex::new(reading)),
|
||||
#[cfg(target_os = "linux")]
|
||||
drivers: Arc::default(),
|
||||
last_frame: now - Duration::from_millis(20),
|
||||
driver_mapping: DriverMapping::object_mode(),
|
||||
mapping_checked_at: now,
|
||||
@@ -717,6 +1003,8 @@ mod tests {
|
||||
fn mouse(reading: Reading, now: Instant) -> SpaceMouse {
|
||||
SpaceMouse {
|
||||
shared: Arc::new(Mutex::new(reading)),
|
||||
#[cfg(target_os = "linux")]
|
||||
drivers: Arc::default(),
|
||||
last_frame: now,
|
||||
driver_mapping: DriverMapping::object_mode(),
|
||||
mapping_checked_at: now,
|
||||
|
||||
@@ -2,13 +2,23 @@
|
||||
//! its report descriptor and ids under `/sys/class/hidraw/hidrawN/device/`. Reading them is plain
|
||||
//! file I/O, so no udev or hidapi library is needed.
|
||||
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
|
||||
use solidcraft_space_mouse::{Descriptor, Layout};
|
||||
|
||||
const SYSFS: &str = "/sys/class/hidraw";
|
||||
const DEV: &str = "/dev";
|
||||
|
||||
/// `O_NONBLOCK` on Linux's common architectures (x86, ARM, RISC-V). The device is opened with it so
|
||||
/// a read with nothing to read returns at once and the reader can see whether it should stop.
|
||||
pub const O_NONBLOCK: i32 = 0o4000;
|
||||
|
||||
/// How long the reader waits when the device has nothing to say, which is also the most a report
|
||||
/// can be delayed.
|
||||
const IDLE_POLL: Duration = Duration::from_millis(5);
|
||||
|
||||
pub struct Device {
|
||||
pub path: PathBuf,
|
||||
pub layout: Layout,
|
||||
@@ -23,6 +33,30 @@ pub fn find() -> Option<Device> {
|
||||
names.into_iter().find_map(|name| probe(&Path::new(SYSFS).join(&name), Path::new(DEV).join(&name)))
|
||||
}
|
||||
|
||||
/// Hands every report to `publish` until the device is gone (end of file or an error) or `stop`
|
||||
/// says so. `source` is non-blocking: when it has nothing, `stop` is asked and the reader waits
|
||||
/// [`IDLE_POLL`]. Reports are read and handed over exactly as they were with blocking reads.
|
||||
pub fn read_reports(mut source: impl Read, mut publish: impl FnMut(&[u8]), mut stop: impl FnMut() -> bool) {
|
||||
let mut report = [0_u8; super::REPORT_BUFFER_BYTES];
|
||||
loop {
|
||||
match source.read(&mut report) {
|
||||
Ok(0) => return,
|
||||
Ok(size) => publish(report.get(..size).unwrap_or_default()),
|
||||
Err(error) if error.kind() == std::io::ErrorKind::Interrupted => {}
|
||||
Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => {
|
||||
if stop() {
|
||||
return;
|
||||
}
|
||||
std::thread::sleep(IDLE_POLL);
|
||||
}
|
||||
Err(error) => {
|
||||
log::info!("3D mouse disconnected: {error}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn probe(sysfs: &Path, path: PathBuf) -> Option<Device> {
|
||||
let descriptor = Descriptor::parse(&std::fs::read(sysfs.join("device/report_descriptor")).ok()?);
|
||||
// A controller that declares no motion axis (buttons only, say) isn't read.
|
||||
@@ -42,6 +76,35 @@ fn ids(uevent: &str) -> Option<(u16, u16)> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn reports_come_through_idle_gaps_until_the_device_ends_or_a_stop() {
|
||||
use crate::space_mouse::fake::Script;
|
||||
let mut script = Script::new();
|
||||
script.timeout(std::io::ErrorKind::WouldBlock).data(&[1, 2, 3]).timeout(std::io::ErrorKind::WouldBlock).data(&[4]).end();
|
||||
let (mut seen, mut asked) = (Vec::new(), 0);
|
||||
read_reports(
|
||||
script,
|
||||
|report| seen.push(report.to_vec()),
|
||||
|| {
|
||||
asked += 1;
|
||||
false
|
||||
},
|
||||
);
|
||||
assert_eq!(seen, [vec![1, 2, 3], vec![4]]);
|
||||
assert_eq!(asked, 2);
|
||||
let mut script = Script::new();
|
||||
script.data(&[1]).timeout(std::io::ErrorKind::WouldBlock).data(&[2]);
|
||||
let mut seen = Vec::new();
|
||||
read_reports(script, |report| seen.push(report.to_vec()), || true);
|
||||
assert_eq!(seen, [vec![1]], "a stop at an idle moment ends the read");
|
||||
// An unplugged device is an error, which ends the read as before.
|
||||
let mut script = Script::new();
|
||||
script.data(&[1]).fail(std::io::ErrorKind::BrokenPipe).data(&[2]);
|
||||
let mut seen = Vec::new();
|
||||
read_reports(script, |report| seen.push(report.to_vec()), || false);
|
||||
assert_eq!(seen, [vec![1]]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ids_come_from_the_uevent() {
|
||||
let wireless = "DRIVER=hid-generic\nHID_ID=0005:0000256F:0000C63A\nHID_NAME=SpaceMouse Wireless BT\n";
|
||||
|
||||
@@ -23,6 +23,11 @@ const RECORD_BYTES: usize = 32;
|
||||
/// sensitivity). Our axes are in the raw HID units of [`FULL_SCALE`].
|
||||
const DAEMON_FULL_SCALE: i64 = 500;
|
||||
|
||||
/// Whether the daemon's socket is there, without connecting to it.
|
||||
pub fn exists() -> bool {
|
||||
SOCKETS.iter().any(|path| Path::new(path).exists())
|
||||
}
|
||||
|
||||
pub fn connect() -> Option<UnixStream> {
|
||||
SOCKETS.iter().find_map(|path| connect_to(Path::new(path)))
|
||||
}
|
||||
@@ -56,18 +61,34 @@ fn unit(value: impl Into<i64>) -> i16 {
|
||||
i16::try_from(scaled.clamp(i64::from(i16::MIN), i64::from(i16::MAX))).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Hands every motion record to `publish` until the daemon closes the connection or fails. A
|
||||
/// partial record at the end is dropped.
|
||||
pub fn read(mut stream: impl Read, mut publish: impl FnMut(Axes)) {
|
||||
/// Hands every motion record to `publish` until the daemon closes the connection or fails, or
|
||||
/// `stop` says so. A stream with a read timeout (`set_read_timeout`) times out when the daemon is
|
||||
/// quiet, which is no record and no disconnect: `stop` is asked, and the read goes on, with what
|
||||
/// of a record had arrived kept. A partial record at the end is dropped.
|
||||
pub fn read(mut stream: impl Read, mut publish: impl FnMut(Axes), mut stop: impl FnMut() -> bool) {
|
||||
let mut record = [0_u8; RECORD_BYTES];
|
||||
let mut filled = 0;
|
||||
loop {
|
||||
match stream.read_exact(&mut record) {
|
||||
Ok(()) => {
|
||||
if let Some(axes) = decode(&record) {
|
||||
publish(axes);
|
||||
match stream.read(record.get_mut(filled..).unwrap_or_default()) {
|
||||
Ok(0) => {
|
||||
log::info!("3D mouse: spacenavd connection ended");
|
||||
return;
|
||||
}
|
||||
Ok(size) => {
|
||||
filled += size;
|
||||
if filled == RECORD_BYTES {
|
||||
filled = 0;
|
||||
if let Some(axes) = decode(&record) {
|
||||
publish(axes);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(error) if error.kind() == std::io::ErrorKind::Interrupted => {}
|
||||
Err(error) if matches!(error.kind(), std::io::ErrorKind::WouldBlock | std::io::ErrorKind::TimedOut) => {
|
||||
if stop() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
log::info!("3D mouse: spacenavd connection ended: {error}");
|
||||
return;
|
||||
@@ -132,10 +153,41 @@ mod tests {
|
||||
daemon.write_all(&bytes).expect("written");
|
||||
drop(daemon);
|
||||
let mut seen = Vec::new();
|
||||
read(client, |axes| seen.push(axes.translation));
|
||||
read(client, |axes| seen.push(axes.translation), || false);
|
||||
assert_eq!(seen, [[70, 0, 0], [0, 0, 0]]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_quiet_daemon_times_out_without_losing_the_record_it_was_sending() {
|
||||
use crate::space_mouse::fake::Script;
|
||||
use std::io::ErrorKind::{TimedOut, WouldBlock};
|
||||
let bytes = record([0, 100, 0, 0, 0, 0, 0, 8]);
|
||||
let mut script = Script::new();
|
||||
script.timeout(WouldBlock).data(&bytes[..10]).timeout(TimedOut).timeout(WouldBlock).data(&bytes[10..]).timeout(WouldBlock).end();
|
||||
let (mut seen, mut asked) = (Vec::new(), 0);
|
||||
read(
|
||||
script,
|
||||
|axes| seen.push(axes.translation),
|
||||
|| {
|
||||
asked += 1;
|
||||
false
|
||||
},
|
||||
);
|
||||
assert_eq!(seen, [[70, 0, 0]]);
|
||||
assert_eq!(asked, 4, "each timeout asks whether to stop");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_stop_ends_the_read_at_the_next_timeout_only() {
|
||||
use crate::space_mouse::fake::Script;
|
||||
let bytes = record([0, 100, 0, 0, 0, 0, 0, 8]);
|
||||
let mut script = Script::new();
|
||||
script.data(&bytes).timeout(std::io::ErrorKind::WouldBlock).data(&bytes);
|
||||
let mut seen = 0;
|
||||
read(script, |_| seen += 1, || true);
|
||||
assert_eq!(seen, 1, "the record before the timeout arrived; the one after never was read");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_daemon_means_no_connection() {
|
||||
let missing = std::env::temp_dir().join(format!("sc-no-spnav-{}.sock", std::process::id()));
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
//! Keeping the restored window reachable.
|
||||
//!
|
||||
//! The window comes back where it was left. After a monitor was unplugged, the scale changed or
|
||||
//! the settings came from a larger screen, that can put its title bar above the top edge or
|
||||
//! under the taskbar, out of reach of the mouse. [`fit`] pulls a window back into the monitor's
|
||||
//! work area (the screen without the taskbar), in whatever unit the caller measures both, as
|
||||
//! long as it is the same one.
|
||||
|
||||
/// A rectangle: left, top, width, height.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Area {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub w: f32,
|
||||
pub h: f32,
|
||||
}
|
||||
|
||||
impl Area {
|
||||
pub const fn new(x: f32, y: f32, w: f32, h: f32) -> Self {
|
||||
Area { x, y, w, h }
|
||||
}
|
||||
fn right(self) -> f32 {
|
||||
self.x + self.w
|
||||
}
|
||||
fn bottom(self) -> f32 {
|
||||
self.y + self.h
|
||||
}
|
||||
fn sane(self) -> bool {
|
||||
[self.x, self.y, self.w, self.h].iter().all(|v| v.is_finite()) && self.w > 0.0 && self.h > 0.0
|
||||
}
|
||||
}
|
||||
|
||||
/// How much of the title bar must stay over the work area at least, to grab it.
|
||||
pub const REACHABLE: f32 = 100.0;
|
||||
/// The size a window gets when its saved geometry is unusable, before it is fitted to the screen.
|
||||
pub const DEFAULT_SIZE: (f32, f32) = (1600.0, 1000.0);
|
||||
|
||||
/// `window` moved and shrunk so that it can be reached on `work`: no larger than the work area
|
||||
/// (but not below `min`), the title bar not above the work area's top, the window not below its
|
||||
/// bottom, and at least [`REACHABLE`] of it over the work area horizontally. A window that is
|
||||
/// fine is returned unchanged. Geometry that makes no sense (not finite, empty), or a work
|
||||
/// area that does not, gives the default size centred on the work area.
|
||||
pub fn fit(window: Area, work: Area, min: (f32, f32)) -> Area {
|
||||
if !work.sane() {
|
||||
return window;
|
||||
}
|
||||
let min = (min.0.min(work.w), min.1.min(work.h));
|
||||
if !window.sane() {
|
||||
let w = DEFAULT_SIZE.0.min(work.w * 0.9).max(min.0);
|
||||
let h = DEFAULT_SIZE.1.min(work.h * 0.9).max(min.1);
|
||||
return Area::new(work.x + (work.w - w) / 2.0, work.y + (work.h - h) / 2.0, w, h);
|
||||
}
|
||||
let w = window.w.clamp(min.0, work.w);
|
||||
let h = window.h.clamp(min.1, work.h);
|
||||
let reach = REACHABLE.min(w);
|
||||
// A window that had to shrink goes fully onto the screen; one that fits may keep straddling two.
|
||||
let x = if w < window.w { window.x.max(work.x).min(work.right() - w) } else { window.x.max(work.x + reach - w).min(work.right() - reach) };
|
||||
let y = window.y.min(work.bottom() - h).max(work.y);
|
||||
Area::new(x, y, w, h)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const WORK: Area = Area::new(0.0, 0.0, 2560.0, 1400.0);
|
||||
const MIN: (f32, f32) = (960.0, 600.0);
|
||||
|
||||
#[test]
|
||||
fn a_window_on_screen_is_left_alone() {
|
||||
let w = Area::new(300.0, 120.0, 1600.0, 1000.0);
|
||||
assert_eq!(fit(w, WORK, MIN), w);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_title_bar_above_the_top_or_under_the_taskbar_comes_back() {
|
||||
let fitted = fit(Area::new(300.0, -400.0, 1600.0, 1000.0), WORK, MIN);
|
||||
assert_eq!((fitted.y, fitted.h), (0.0, 1000.0));
|
||||
let fitted = fit(Area::new(300.0, 1300.0, 1600.0, 1000.0), WORK, MIN);
|
||||
assert_eq!(fitted.y + fitted.h, 1400.0);
|
||||
// A taskbar at the top moves the work area down.
|
||||
let below_bar = Area::new(0.0, 48.0, 2560.0, 1352.0);
|
||||
assert_eq!(fit(Area::new(300.0, 0.0, 1600.0, 1000.0), below_bar, MIN).y, 48.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_window_from_a_monitor_that_is_gone_keeps_a_grabbable_part_on_screen() {
|
||||
let right = fit(Area::new(5000.0, 100.0, 1600.0, 1000.0), WORK, MIN);
|
||||
assert_eq!(right.x, 2560.0 - REACHABLE);
|
||||
let left = fit(Area::new(-4000.0, 100.0, 1600.0, 1000.0), WORK, MIN);
|
||||
assert_eq!(left.x + left.w, REACHABLE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_window_larger_than_the_screen_shrinks_to_it() {
|
||||
let small = Area::new(0.0, 0.0, 1280.0, 720.0);
|
||||
let fitted = fit(Area::new(-20.0, -30.0, 3200.0, 2000.0), small, MIN);
|
||||
assert_eq!(fitted, small);
|
||||
// Never below the minimum, unless the screen itself is smaller.
|
||||
assert_eq!(fit(Area::new(0.0, 0.0, 100.0, 100.0), WORK, MIN).w, 960.0);
|
||||
assert_eq!(fit(Area::new(0.0, 0.0, 100.0, 100.0), Area::new(0.0, 0.0, 800.0, 500.0), MIN).h, 500.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unusable_geometry_is_centred_at_a_sensible_size() {
|
||||
for bad in [Area::new(f32::NAN, 0.0, 1600.0, 1000.0), Area::new(0.0, 0.0, 0.0, 0.0), Area::new(0.0, 0.0, f32::INFINITY, 10.0)] {
|
||||
let fitted = fit(bad, WORK, MIN);
|
||||
assert_eq!(fitted, Area::new(480.0, 200.0, 1600.0, 1000.0));
|
||||
}
|
||||
let small = Area::new(100.0, 50.0, 1366.0, 728.0);
|
||||
let fitted = fit(Area::new(f32::NAN, 0.0, 1.0, 1.0), small, MIN);
|
||||
assert!(fitted.w <= small.w && fitted.h <= small.h && fitted.x >= small.x && fitted.y >= small.y);
|
||||
// No work area to go by: nothing to change.
|
||||
let w = Area::new(1.0, 2.0, 3.0, 4.0);
|
||||
assert_eq!(fit(w, Area::new(0.0, 0.0, 0.0, 0.0), MIN), w);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -84,7 +84,7 @@ The CLI still supports arbitrary PNG/ICO exports with `solidcraft-cli icon`.
|
||||
## Code and provenance
|
||||
|
||||
Resources remain compiled by their owning layer through existing APIs:
|
||||
`ui-egui::icons::paint` and `Tokens` at L5, model presets in L2 `doc`, and sketch
|
||||
`ui-egui::icons::paint_icon` and `Tokens` at L5, model presets in L2 `doc`, and sketch
|
||||
fonts at L1. State, projection, interaction, commands and GPU bindings stay in
|
||||
their current modules. Embedded data uses `CARGO_MANIFEST_DIR`; application code
|
||||
needs no runtime resource registry or repository lookup.
|
||||
|
||||
@@ -91,7 +91,7 @@ Palette changes preserve accepted artwork and never update review hashes.
|
||||
| `source/` | Rust viewport glyphs, markers, navigation and pointer; not command icons |
|
||||
|
||||
`crates/ui-egui/build.rs` embeds SVGs, palette and aliases. `icon_svg.rs` parses and
|
||||
rasterises them for `icons::paint`; adding an icon still requires its registry or
|
||||
rasterises them for `icons::paint_icon`; adding an icon still requires its registry or
|
||||
UI assignment and an application rebuild. Review status never controls rendering.
|
||||
|
||||
## Checks and reports
|
||||
|
||||
@@ -141,18 +141,13 @@ new false
|
||||
normal false
|
||||
offset false
|
||||
offset_face false
|
||||
offset_one_side false
|
||||
offset_symmetric false
|
||||
offset_two_sides false
|
||||
open false
|
||||
orbit false
|
||||
origin false
|
||||
pan false
|
||||
params false
|
||||
paste false
|
||||
pattern_center false
|
||||
pattern_circ false
|
||||
pattern_flip false
|
||||
pattern_path false
|
||||
pattern_rect false
|
||||
perforate false
|
||||
@@ -208,9 +203,11 @@ scale false
|
||||
screw false
|
||||
section false
|
||||
select false
|
||||
send false
|
||||
settings false
|
||||
sheet_rules false
|
||||
shell false
|
||||
shield false
|
||||
silhouette false
|
||||
sketch false
|
||||
sketch_break false
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<title>Send message</title>
|
||||
<path d="M32 51L32 13M17 28L32 13L47 28" fill="none" stroke="currentColor" stroke-width="5.6" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 243 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<title>Approval required</title>
|
||||
<path d="M32 9L51 16L51 31C51 43 43 51 32 56C21 51 13 43 13 31L13 16Z" fill="none" stroke="currentColor" stroke-width="5.6" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 278 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<title>Stop response</title>
|
||||
<path d="M20 17L44 17A3 3 0 0 1 47 20L47 44A3 3 0 0 1 44 47L20 47A3 3 0 0 1 17 44L17 20A3 3 0 0 1 20 17Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 229 B |
+45
-2
@@ -167,6 +167,7 @@
|
||||
"HISTORY": "HISTORIE",
|
||||
"Height": "Výška",
|
||||
"Hidden edges": "Skryté hrany",
|
||||
"History layout": "Rozložení historie",
|
||||
"Holes": "Otvory",
|
||||
"Holes (edges on them)": "Otvory (jejich hrany)",
|
||||
"Home view": "Výchozí pohled",
|
||||
@@ -232,7 +233,7 @@
|
||||
"No crash reports.": "Žádná hlášení o pádech.",
|
||||
"No matching appearance presets.": "Žádné odpovídající předvolby vzhledu.",
|
||||
"No matching appearances": "Žádné odpovídající vzhledy",
|
||||
"No visible bodies to export.": "Žádná viditelná tělesa k exportu.",
|
||||
"No visible bodies or sketches to export.": "Žádná viditelná tělesa ani náčrty k exportu.",
|
||||
"None": "Žádné",
|
||||
"Not available yet": "Zatím není k dispozici",
|
||||
"Notch": "Zářez",
|
||||
@@ -831,7 +832,7 @@
|
||||
"dxf.progress.projection": "Promítání geometrie (3/5)",
|
||||
"dxf.progress.write": "Zápis DXF (5/5)",
|
||||
"dxf.tolerance": "Tolerance křivek",
|
||||
"dxf.tolerance_help": "Pouze viditelná tělesa. Křivky jsou nahrazeny úsečkami. Menší tolerance dává jemnější křivky, ale zabere více času a paměti.",
|
||||
"dxf.tolerance_help": "Pouze viditelná tělesa a zobrazené náčrty. Křivky jsou nahrazeny úsečkami. Menší tolerance dává jemnější křivky, ale zabere více času a paměti.",
|
||||
"file_load.built": "Dosud sestaveno: {built} z {total} prvků.",
|
||||
"file_load.converting": "Převod tělesa {done} z {total}",
|
||||
"file_load.inserting": "Vkládání geometrie",
|
||||
@@ -1092,6 +1093,7 @@
|
||||
"ui.agent_chat_add": "Připojit soubory",
|
||||
"ui.agent_chat_add_file": "Vybrat soubor…",
|
||||
"ui.agent_chat_allow": "Povolit",
|
||||
"ui.agent_chat_allow_once": "Povolit jednou",
|
||||
"ui.agent_chat_allowed": "Povoleno",
|
||||
"ui.agent_chat_answered": "Odpověď: {option}",
|
||||
"ui.agent_chat_approval": "Povolit agentovi tuto akci?",
|
||||
@@ -1149,6 +1151,9 @@
|
||||
"ui.agent_chat_left": "Vlevo",
|
||||
"ui.agent_chat_limit": "Agent dosáhl limitu {limit} změn na jednu zprávu. Pošlete další zprávu, aby mohl pokračovat.",
|
||||
"ui.agent_chat_minimize": "Minimalizovat do pruhu",
|
||||
"ui.agent_chat_mode_all": "Vše",
|
||||
"ui.agent_chat_mode_ask": "Ptát se",
|
||||
"ui.agent_chat_mode_edits": "Úpravy",
|
||||
"ui.agent_chat_model_tip": "Model, který agent používá. Vaše volba se pro tohoto agenta zapamatuje.",
|
||||
"ui.agent_chat_new": "Nový chat",
|
||||
"ui.agent_chat_new_confirm": "Začít nový chat? Aktuální bude vymazán.",
|
||||
@@ -1161,6 +1166,7 @@
|
||||
"ui.agent_chat_none_found_short": "Agent nenalezen",
|
||||
"ui.agent_chat_not_installed": "Není nainstalován",
|
||||
"ui.agent_chat_not_signed_in": "nepřihlášen",
|
||||
"ui.agent_chat_open_settings": "Otevřít předvolby",
|
||||
"ui.agent_chat_own_window": "Vlastní okno",
|
||||
"ui.agent_chat_paste_picture": "Vložit obrázek",
|
||||
"ui.agent_chat_permission": "Agent žádá o spuštění: {title}",
|
||||
@@ -1185,6 +1191,8 @@
|
||||
"ui.agent_chat_send": "Odeslat",
|
||||
"ui.agent_chat_send_now": "Odeslat hned",
|
||||
"ui.agent_chat_send_now_tip": "Zastavit aktuální krok agenta a poslat tuto zprávu jako další (Ctrl+Enter)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft komunikuje s programovacím agentem nainstalovaným v tomto počítači. Vyberte ho v části Předvolby › AI.",
|
||||
"ui.agent_chat_setup_title": "Nastavit asistenta",
|
||||
"ui.agent_chat_side": "Strana chatu s agentem",
|
||||
"ui.agent_chat_signed_in": "přihlášen",
|
||||
"ui.agent_chat_state_auth": "Vyžadováno přihlášení",
|
||||
@@ -1201,6 +1209,7 @@
|
||||
"ui.agent_chat_undo_label": "Chat s agentem: {message}",
|
||||
"ui.agent_chat_undone": "(vráceno)",
|
||||
"ui.agent_chat_view_attached": "Pohled přiložen",
|
||||
"ui.agent_chat_waiting": "Čeká na vás",
|
||||
"ui.agent_cursor": "Kurzor agenta",
|
||||
"ui.agent_cursor_speed": "Rychlost kurzoru agenta",
|
||||
"ui.ai_add": "Přidat",
|
||||
@@ -1528,6 +1537,11 @@
|
||||
"ui.hideOrigin": "Skrýt počátek",
|
||||
"ui.hidePlanes": "Skrýt roviny",
|
||||
"ui.hideProfile": "Skrýt profil",
|
||||
"ui.history_back": "O krok zpět",
|
||||
"ui.history_below_browser": "Pod prohlížečem",
|
||||
"ui.history_first": "Přejít na začátek",
|
||||
"ui.history_forward": "O krok vpřed",
|
||||
"ui.history_last": "Přejít na konec",
|
||||
"ui.history_title": "Historie",
|
||||
"ui.hole": "Otvor",
|
||||
"ui.hole_tap_type": "Typ závitu otvoru",
|
||||
@@ -1545,6 +1559,8 @@
|
||||
"ui.joints": "Klouby",
|
||||
"ui.keyboard_shortcuts": "Klávesové zkratky",
|
||||
"ui.language": "Jazyk",
|
||||
"ui.layout_compact": "Kompaktní",
|
||||
"ui.layout_floating": "Plovoucí",
|
||||
"ui.length_decimals": "Desetinná místa délky",
|
||||
"ui.light": "Světlý",
|
||||
"ui.line": "Úsečka",
|
||||
@@ -1682,6 +1698,7 @@
|
||||
"ui.reverse_zoom_direction": "Obrátit směr přiblížení",
|
||||
"ui.revolve": "Rotace",
|
||||
"ui.ribbon_exit": "Ukončit",
|
||||
"ui.ribbon_labels": "Zobrazovat text pod ikonami pásu karet",
|
||||
"ui.ribbon_pin_footer": "Připnuté příkazy se zobrazují na pásu karet. Najetím na řádek jej připnete nebo odepnete.",
|
||||
"ui.ribbon_workspace": "Pracovní prostředí",
|
||||
"ui.right": "Zprava",
|
||||
@@ -1737,6 +1754,7 @@
|
||||
"ui.smooth_wheel_zoom": "Plynulé přiblížení kolečkem",
|
||||
"ui.smooth_wheel_zoom_tip": "Vypnutím se každý pohyb kolečka projeví okamžitě, bez dobíhání",
|
||||
"ui.snap": "Přichytávání",
|
||||
"ui.space_mouse_axes_reassigned": "Osy jsou v Předvolbách přeřazené a to platí i po této kalibraci.",
|
||||
"ui.space_mouse_calibrate": "Kalibrovat…",
|
||||
"ui.space_mouse_calibrate_tip": "Pohybujte hlavicí podle pokynů v okně, aby se SolidCraft naučil, kterým směrem a jak daleko se pohybuje každá osa této 3D myši. Uloží se pro tuto 3D myš.",
|
||||
"ui.space_mouse_calibrate_title": "Kalibrace 3D myši",
|
||||
@@ -1746,12 +1764,26 @@
|
||||
"ui.space_mouse_calibration_no_response": "Jedna osa se skoro nepohnula, takže ji nelze zkalibrovat. Začněte znovu a pokaždé hlavicí pohněte dál.",
|
||||
"ui.space_mouse_dead_zone": "Mrtvá zóna",
|
||||
"ui.space_mouse_dead_zone_tip": "Jak daleko se musí hlavice pohnout, než se pohne pohled, jako podíl jejího plného rozsahu. Zvyšte ji, pokud pohled ujíždí, i když je hlavice v klidu.",
|
||||
"ui.space_mouse_driven_by": "Který kalibrovaný pohyb zařízení ovládá tuto osu. Když vyberete pohyb, který už používá jiná osa, obě se prohodí.",
|
||||
"ui.space_mouse_driven_by_label": "ovládá",
|
||||
"ui.space_mouse_driver": "Ovladač 3D myši",
|
||||
"ui.space_mouse_driver_auto": "Automaticky",
|
||||
"ui.space_mouse_driver_direct": "Přímo (hidraw)",
|
||||
"ui.space_mouse_driver_only_direct": "Zařízení se čte přímo; spacenavd neběží",
|
||||
"ui.space_mouse_driver_only_spacenavd": "Čte se přes spacenavd, jiná možnost není",
|
||||
"ui.space_mouse_forget": "Zapomenout kalibraci",
|
||||
"ui.space_mouse_hint": "Posun a otáčení pohledu současně; náčrty používají jen posun a přiblížení.",
|
||||
"ui.space_mouse_motion_rotation_x": "Rotace X",
|
||||
"ui.space_mouse_motion_rotation_y": "Rotace Y",
|
||||
"ui.space_mouse_motion_rotation_z": "Rotace Z",
|
||||
"ui.space_mouse_motion_translation_x": "Posun X",
|
||||
"ui.space_mouse_motion_translation_y": "Posun Y",
|
||||
"ui.space_mouse_motion_translation_z": "Posun Z",
|
||||
"ui.space_mouse_navigate": "Navigace pomocí SpaceNavigator / SpaceMouse",
|
||||
"ui.space_mouse_none": "3D myš nenalezena",
|
||||
"ui.space_mouse_not_calibrated": "Nezkalibrováno",
|
||||
"ui.space_mouse_reading": "Hodnoty",
|
||||
"ui.space_mouse_reset_axes": "Obnovit přiřazení os",
|
||||
"ui.space_mouse_step_lift": "Zvedněte hlavici nahoru, podržte a pusťte.",
|
||||
"ui.space_mouse_step_press": "Zatlačte hlavici dolů, podržte a pusťte.",
|
||||
"ui.space_mouse_step_pull": "Přitáhněte hlavici k sobě, podržte a pusťte.",
|
||||
@@ -1765,6 +1797,16 @@
|
||||
"ui.space_mouse_step_tilt_right": "Nakloňte hlavici doprava (pravá hrana dolů), podržte a pusťte.",
|
||||
"ui.space_mouse_step_twist_clockwise": "Otočte hlavici po směru hodinových ručiček, podržte a pusťte.",
|
||||
"ui.space_mouse_step_twist_counterclockwise": "Otočte hlavici proti směru hodinových ručiček, podržte a pusťte.",
|
||||
"ui.space_mouse_view_axis": "Osa pohledu",
|
||||
"ui.space_mouse_view_off": "Vypnuto",
|
||||
"ui.space_mouse_view_off_tip": "Profil ovladače 3D myši tuto osu vypíná, takže její pohyb s pohledem nehýbe. Když tu vyberete pohyb jiné osy, prohodí se s ní a s pohledem přestane hýbat.",
|
||||
"ui.space_mouse_view_orbit_sideways": "Orbit vlevo / vpravo",
|
||||
"ui.space_mouse_view_orbit_up_down": "Orbit nahoru / dolů",
|
||||
"ui.space_mouse_view_pan_sideways": "Posun vlevo / vpravo",
|
||||
"ui.space_mouse_view_pan_up_down": "Posun nahoru / dolů",
|
||||
"ui.space_mouse_view_roll": "Natočení",
|
||||
"ui.space_mouse_view_unmapped": "Osa {n}",
|
||||
"ui.space_mouse_view_zoom": "Zoom",
|
||||
"ui.spacing": "Rozteč",
|
||||
"ui.spacing_2": "Rozteč 2",
|
||||
"ui.sphere": "Koule",
|
||||
@@ -1836,6 +1878,7 @@
|
||||
"ui.workspace_design_tabs": "Model · Plocha · Sestava · Vložit · Kontrola · Správa · Zobrazení",
|
||||
"ui.workspace_drawing_description": "2D výkresy s pohledy, kótami a popisovými poli",
|
||||
"ui.workspace_footer": "Všechna pracovní prostředí pracují se stejným návrhem, historií a výběrem; mění se pouze karty pásu karet. Přepínač je zamknutý, dokud se upravuje náčrt nebo prvek.",
|
||||
"ui.workspace_layout": "Rozložení pracovní plochy",
|
||||
"ui.workspace_manufacture_description": "Slicing, dráhy nástroje a nastavení",
|
||||
"ui.workspace_planned": "Plánováno",
|
||||
"ui.workspace_render_description": "Náhled produktu, nastavení scény a renderované obrázky",
|
||||
|
||||
+20
-2
@@ -167,6 +167,7 @@
|
||||
"HISTORY": "HISTORIK",
|
||||
"Height": "Højde",
|
||||
"Hidden edges": "Skjulte kanter",
|
||||
"History layout": "Historiklayout",
|
||||
"Holes": "Huller",
|
||||
"Holes (edges on them)": "Huller (kanterne omkring dem)",
|
||||
"Home view": "Hjemmevisning",
|
||||
@@ -232,7 +233,7 @@
|
||||
"No crash reports.": "Ingen nedbrudsrapporter.",
|
||||
"No matching appearance presets.": "Ingen matchende udseendeforudindstillinger.",
|
||||
"No matching appearances": "Ingen matchende udseender",
|
||||
"No visible bodies to export.": "Der er ingen synlige emner at eksportere.",
|
||||
"No visible bodies or sketches to export.": "Der er ingen synlige emner eller skitser at eksportere.",
|
||||
"None": "Ingen",
|
||||
"Not available yet": "Endnu ikke tilgængelig",
|
||||
"Notch": "Udskæring",
|
||||
@@ -831,7 +832,7 @@
|
||||
"dxf.progress.projection": "Projicerer geometri (3/5)",
|
||||
"dxf.progress.write": "Skriver DXF (5/5)",
|
||||
"dxf.tolerance": "Kurvetolerance",
|
||||
"dxf.tolerance_help": "Kun synlige emner. Kurver består af rette linjestykker. En mindre tolerance giver finere kurver og kræver mere tid og hukommelse.",
|
||||
"dxf.tolerance_help": "Kun synlige emner og viste skitser. Kurver består af rette linjestykker. En mindre tolerance giver finere kurver og kræver mere tid og hukommelse.",
|
||||
"file_load.built": "Bygget indtil videre: {built} af {total} features.",
|
||||
"file_load.converting": "Konverterer solid {done} af {total}",
|
||||
"file_load.inserting": "Indsætter geometri",
|
||||
@@ -1092,6 +1093,7 @@
|
||||
"ui.agent_chat_add": "Vedhæft filer",
|
||||
"ui.agent_chat_add_file": "Vælg en fil…",
|
||||
"ui.agent_chat_allow": "Tillad",
|
||||
"ui.agent_chat_allow_once": "Tillad én gang",
|
||||
"ui.agent_chat_allowed": "Tilladt",
|
||||
"ui.agent_chat_answered": "Svar: {option}",
|
||||
"ui.agent_chat_approval": "Tillad agenten at gøre dette?",
|
||||
@@ -1149,6 +1151,9 @@
|
||||
"ui.agent_chat_left": "Venstre",
|
||||
"ui.agent_chat_limit": "Agenten nåede grænsen på {limit} ændringer for én besked. Send en ny besked for at lade den fortsætte.",
|
||||
"ui.agent_chat_minimize": "Minimér til en stribe",
|
||||
"ui.agent_chat_mode_all": "Alt",
|
||||
"ui.agent_chat_mode_ask": "Spørg",
|
||||
"ui.agent_chat_mode_edits": "Ændringer",
|
||||
"ui.agent_chat_model_tip": "Modellen, som agenten bruger. Dit valg huskes for denne agent.",
|
||||
"ui.agent_chat_new": "Ny chat",
|
||||
"ui.agent_chat_new_confirm": "Start en ny chat? Den aktuelle chat ryddes.",
|
||||
@@ -1161,6 +1166,7 @@
|
||||
"ui.agent_chat_none_found_short": "Ingen agent fundet",
|
||||
"ui.agent_chat_not_installed": "Ikke installeret",
|
||||
"ui.agent_chat_not_signed_in": "ikke logget ind",
|
||||
"ui.agent_chat_open_settings": "Åbn indstillinger",
|
||||
"ui.agent_chat_own_window": "Eget vindue",
|
||||
"ui.agent_chat_paste_picture": "Indsæt et billede",
|
||||
"ui.agent_chat_permission": "Agenten beder om at køre: {title}",
|
||||
@@ -1185,6 +1191,8 @@
|
||||
"ui.agent_chat_send": "Send",
|
||||
"ui.agent_chat_send_now": "Send nu",
|
||||
"ui.agent_chat_send_now_tip": "Stop agentens aktuelle trin, og send denne som den næste (Ctrl+Enter)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft kommunikerer med en kodeagent, der er installeret på denne computer. Vælg en under Indstillinger › AI.",
|
||||
"ui.agent_chat_setup_title": "Opsæt en assistent",
|
||||
"ui.agent_chat_side": "Agentchattens side",
|
||||
"ui.agent_chat_signed_in": "logget ind",
|
||||
"ui.agent_chat_state_auth": "Login kræves",
|
||||
@@ -1201,6 +1209,7 @@
|
||||
"ui.agent_chat_undo_label": "Agentchat: {message}",
|
||||
"ui.agent_chat_undone": "(fortrudt)",
|
||||
"ui.agent_chat_view_attached": "Visning vedhæftet",
|
||||
"ui.agent_chat_waiting": "Venter på dig",
|
||||
"ui.agent_cursor": "Agentmarkør",
|
||||
"ui.agent_cursor_speed": "Agentmarkørens hastighed",
|
||||
"ui.ai_add": "Tilføj",
|
||||
@@ -1528,6 +1537,11 @@
|
||||
"ui.hideOrigin": "Skjul nulpunkt",
|
||||
"ui.hidePlanes": "Skjul planer",
|
||||
"ui.hideProfile": "Skjul profil",
|
||||
"ui.history_back": "Gå ét trin tilbage",
|
||||
"ui.history_below_browser": "Under browseren",
|
||||
"ui.history_first": "Gå til begyndelsen",
|
||||
"ui.history_forward": "Gå ét trin frem",
|
||||
"ui.history_last": "Gå til slutningen",
|
||||
"ui.history_title": "Historik",
|
||||
"ui.hole": "Hul",
|
||||
"ui.hole_tap_type": "Hullets gevindtype",
|
||||
@@ -1545,6 +1559,8 @@
|
||||
"ui.joints": "Led",
|
||||
"ui.keyboard_shortcuts": "Tastaturgenveje",
|
||||
"ui.language": "Sprog",
|
||||
"ui.layout_compact": "Kompakt",
|
||||
"ui.layout_floating": "Flydende",
|
||||
"ui.length_decimals": "Længdedecimaler",
|
||||
"ui.light": "Lys",
|
||||
"ui.line": "Linje",
|
||||
@@ -1682,6 +1698,7 @@
|
||||
"ui.reverse_zoom_direction": "Vend zoomretning",
|
||||
"ui.revolve": "Omdrejning",
|
||||
"ui.ribbon_exit": "Afslut",
|
||||
"ui.ribbon_labels": "Vis tekst under ikonerne på båndet",
|
||||
"ui.ribbon_pin_footer": "Fastgjorte kommandoer vises på båndet. Hold musen over en række for at fastgøre eller frigøre den.",
|
||||
"ui.ribbon_workspace": "Arbejdsområde",
|
||||
"ui.right": "Fra højre",
|
||||
@@ -1836,6 +1853,7 @@
|
||||
"ui.workspace_design_tabs": "Model · Overflade · Saml · Indsæt · Undersøg · Administrér · Visning",
|
||||
"ui.workspace_drawing_description": "2D-tegninger med visninger, mål og tegningshoveder",
|
||||
"ui.workspace_footer": "Alle arbejdsområder arbejder på det samme design, den samme historik og den samme markering; kun båndets faner ændres. Skifteren er låst, mens en skitse eller feature redigeres.",
|
||||
"ui.workspace_layout": "Arbejdsområdets layout",
|
||||
"ui.workspace_manufacture_description": "Slicing, værktøjsbaner og opsætning",
|
||||
"ui.workspace_planned": "Planlagt",
|
||||
"ui.workspace_render_description": "Produktforhåndsvisning, scene-indstillinger og renderede billeder",
|
||||
|
||||
+49
-2
@@ -167,6 +167,7 @@
|
||||
"HISTORY": "VERLAUF",
|
||||
"Height": "Höhe",
|
||||
"Hidden edges": "Verdeckte Kanten",
|
||||
"History layout": "Verlaufslayout",
|
||||
"Holes": "Löcher",
|
||||
"Holes (edges on them)": "Löcher (ihre Kanten)",
|
||||
"Home view": "Startansicht",
|
||||
@@ -232,7 +233,7 @@
|
||||
"No crash reports.": "Keine Absturzberichte.",
|
||||
"No matching appearance presets.": "Keine passenden Erscheinungsbildvorlagen.",
|
||||
"No matching appearances": "Keine passenden Erscheinungsbilder",
|
||||
"No visible bodies to export.": "Keine sichtbaren Körper zum Exportieren.",
|
||||
"No visible bodies or sketches to export.": "Keine sichtbaren Körper oder Skizzen zum Exportieren.",
|
||||
"None": "Keine",
|
||||
"Not available yet": "Noch nicht verfügbar",
|
||||
"Notch": "Ausklinkung",
|
||||
@@ -242,6 +243,7 @@
|
||||
"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-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.",
|
||||
"Operator": "Operator",
|
||||
"Orbit (right drag)": "Drehen (mit rechts ziehen)",
|
||||
"Other side": "Andere Seite",
|
||||
"Other surface (mutual trim)": "Anderer Flächenkörper (gegenseitig trimmen)",
|
||||
"Outside": "Außen",
|
||||
@@ -758,6 +760,7 @@
|
||||
"cmd.view_graphics_preset": "Grafikprofil",
|
||||
"cmd.view_list": "Benannte Ansichten auflisten",
|
||||
"cmd.view_navigate": "Ansicht navigieren",
|
||||
"cmd.view_normal_to": "Senkrecht zur Auswahl",
|
||||
"cmd.view_rename": "Benannte Ansicht umbenennen",
|
||||
"cmd.view_save": "Neue benannte Ansicht",
|
||||
"color.copy": "Klicken, um Farbwerte zu kopieren",
|
||||
@@ -831,7 +834,7 @@
|
||||
"dxf.progress.projection": "Geometrie projizieren (3/5)",
|
||||
"dxf.progress.write": "DXF schreiben (5/5)",
|
||||
"dxf.tolerance": "Kurventoleranz",
|
||||
"dxf.tolerance_help": "Nur sichtbare Körper. Kurven werden durch gerade Segmente angenähert. Eine kleinere Toleranz liefert feinere Kurven und benötigt mehr Zeit und Speicher.",
|
||||
"dxf.tolerance_help": "Nur sichtbare Körper und angezeigte Skizzen. Kurven werden durch gerade Segmente angenähert. Eine kleinere Toleranz liefert feinere Kurven und benötigt mehr Zeit und Speicher.",
|
||||
"file_load.built": "Bisher erstellt: {built} von {total} Merkmalen.",
|
||||
"file_load.converting": "Volumenkörper {done} von {total} wird konvertiert",
|
||||
"file_load.inserting": "Geometrie wird eingefügt",
|
||||
@@ -926,6 +929,8 @@
|
||||
"motion.to_end": "Zum Ende",
|
||||
"motion.to_start": "Zum Anfang",
|
||||
"motion.track_name": "Spur {number}",
|
||||
"nav.free_orbit.help": "Maus und 3D-Maus drehen über beide Pole hinweg und behalten die Rollneigung bei. Das Einrasten mit Umschalt ist deaktiviert.",
|
||||
"nav.free_orbit.label": "Freie Drehung",
|
||||
"nav.touchpad.help": "Umschalt gedrückt halten und den Zeiger zum Verschieben bewegen; mit Alt/Option drehen. Keine Maustaste nötig. Vertikales Scrollen zoomt.",
|
||||
"nav.touchpad.label": "Touchpad",
|
||||
"nav.touchpad.orbit": "Drehen (Alt/Option halten und bewegen; ohne Maustaste)",
|
||||
@@ -1092,6 +1097,7 @@
|
||||
"ui.agent_chat_add": "Dateien anhängen",
|
||||
"ui.agent_chat_add_file": "Datei auswählen…",
|
||||
"ui.agent_chat_allow": "Erlauben",
|
||||
"ui.agent_chat_allow_once": "Einmal erlauben",
|
||||
"ui.agent_chat_allowed": "Erlaubt",
|
||||
"ui.agent_chat_answered": "Antwort: {option}",
|
||||
"ui.agent_chat_approval": "Darf der Agent das tun?",
|
||||
@@ -1149,6 +1155,9 @@
|
||||
"ui.agent_chat_left": "Links",
|
||||
"ui.agent_chat_limit": "Der Agent hat die Grenze von {limit} Änderungen für eine Nachricht erreicht. Senden Sie eine weitere Nachricht, damit er weitermacht.",
|
||||
"ui.agent_chat_minimize": "Zu einer Leiste minimieren",
|
||||
"ui.agent_chat_mode_all": "Alles",
|
||||
"ui.agent_chat_mode_ask": "Fragen",
|
||||
"ui.agent_chat_mode_edits": "Ändern",
|
||||
"ui.agent_chat_model_tip": "Das vom Agenten verwendete Modell. Ihre Auswahl wird für diesen Agenten gespeichert.",
|
||||
"ui.agent_chat_new": "Neuer Chat",
|
||||
"ui.agent_chat_new_confirm": "Neuen Chat starten? Der aktuelle wird geleert.",
|
||||
@@ -1161,6 +1170,7 @@
|
||||
"ui.agent_chat_none_found_short": "Kein Agent gefunden",
|
||||
"ui.agent_chat_not_installed": "Nicht installiert",
|
||||
"ui.agent_chat_not_signed_in": "nicht angemeldet",
|
||||
"ui.agent_chat_open_settings": "Einstellungen öffnen",
|
||||
"ui.agent_chat_own_window": "Eigenes Fenster",
|
||||
"ui.agent_chat_paste_picture": "Bild einfügen",
|
||||
"ui.agent_chat_permission": "Der Agent möchte ausführen: {title}",
|
||||
@@ -1185,6 +1195,8 @@
|
||||
"ui.agent_chat_send": "Senden",
|
||||
"ui.agent_chat_send_now": "Jetzt senden",
|
||||
"ui.agent_chat_send_now_tip": "Den aktuellen Schritt des Agenten stoppen und dies als Nächstes senden (Strg+Eingabe)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft kommuniziert mit einem auf diesem Computer installierten Programmieragenten. Wählen Sie einen unter Einstellungen › KI aus.",
|
||||
"ui.agent_chat_setup_title": "Assistenten einrichten",
|
||||
"ui.agent_chat_side": "Seite des Agenten-Chats",
|
||||
"ui.agent_chat_signed_in": "angemeldet",
|
||||
"ui.agent_chat_state_auth": "Anmeldung nötig",
|
||||
@@ -1201,6 +1213,7 @@
|
||||
"ui.agent_chat_undo_label": "Agenten-Chat: {message}",
|
||||
"ui.agent_chat_undone": "(rückgängig gemacht)",
|
||||
"ui.agent_chat_view_attached": "Ansicht angehängt",
|
||||
"ui.agent_chat_waiting": "Wartet auf Sie",
|
||||
"ui.agent_cursor": "Agent-Cursor",
|
||||
"ui.agent_cursor_speed": "Agent-Cursor-Geschwindigkeit",
|
||||
"ui.ai_add": "Hinzufügen",
|
||||
@@ -1528,6 +1541,11 @@
|
||||
"ui.hideOrigin": "Ursprung ausblenden",
|
||||
"ui.hidePlanes": "Ebenen ausblenden",
|
||||
"ui.hideProfile": "Profil ausblenden",
|
||||
"ui.history_back": "Einen Schritt zurück",
|
||||
"ui.history_below_browser": "Unter dem Browser",
|
||||
"ui.history_first": "Zum Anfang",
|
||||
"ui.history_forward": "Einen Schritt vor",
|
||||
"ui.history_last": "Zum Ende",
|
||||
"ui.history_title": "Verlauf",
|
||||
"ui.hole": "Bohrung",
|
||||
"ui.hole_tap_type": "Gewindetyp",
|
||||
@@ -1545,6 +1563,8 @@
|
||||
"ui.joints": "Gelenke",
|
||||
"ui.keyboard_shortcuts": "Tastenkürzel",
|
||||
"ui.language": "Sprache",
|
||||
"ui.layout_compact": "Kompakt",
|
||||
"ui.layout_floating": "Schwebend",
|
||||
"ui.length_decimals": "Längen-Dezimalstellen",
|
||||
"ui.light": "Hell",
|
||||
"ui.line": "Linie",
|
||||
@@ -1682,6 +1702,7 @@
|
||||
"ui.reverse_zoom_direction": "Zoomrichtung umkehren",
|
||||
"ui.revolve": "Drehen",
|
||||
"ui.ribbon_exit": "Beenden",
|
||||
"ui.ribbon_labels": "Text unter den Symbolen im Menüband anzeigen",
|
||||
"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",
|
||||
@@ -1737,6 +1758,7 @@
|
||||
"ui.smooth_wheel_zoom": "Sanftes Zoomen mit dem Rad",
|
||||
"ui.smooth_wheel_zoom_tip": "Ausschalten, um jedes Radereignis sofort anzuwenden, ohne Nachlauf",
|
||||
"ui.snap": "Einrasten",
|
||||
"ui.space_mouse_axes_reassigned": "Die Achsen sind in den Einstellungen neu zugeordnet, und das gilt auch nach dieser Kalibrierung.",
|
||||
"ui.space_mouse_calibrate": "Kalibrieren…",
|
||||
"ui.space_mouse_calibrate_tip": "Bewegen Sie die Kappe so, wie das Fenster es verlangt. So lernt SolidCraft, in welche Richtung und wie weit jede Achse dieser 3D-Maus geht. Wird für diese 3D-Maus gespeichert.",
|
||||
"ui.space_mouse_calibrate_title": "3D-Maus kalibrieren",
|
||||
@@ -1746,12 +1768,26 @@
|
||||
"ui.space_mouse_calibration_no_response": "Eine Achse hat sich kaum bewegt und lässt sich nicht kalibrieren. Beginnen Sie von vorn und bewegen Sie die Kappe jedes Mal weiter.",
|
||||
"ui.space_mouse_dead_zone": "Totzone",
|
||||
"ui.space_mouse_dead_zone_tip": "Wie weit sich die Kappe bewegen muss, bevor sich die Ansicht bewegt, als Anteil ihres vollen Wegs. Erhöhen Sie sie, wenn die Ansicht wandert, obwohl die Kappe ruht.",
|
||||
"ui.space_mouse_driven_by": "Welche kalibrierte Bewegung des Geräts diese Achse steuert. Wählen Sie eine, die schon eine andere Achse nutzt, werden die beiden getauscht.",
|
||||
"ui.space_mouse_driven_by_label": "gesteuert von",
|
||||
"ui.space_mouse_driver": "3D-Maus-Treiber",
|
||||
"ui.space_mouse_driver_auto": "Automatisch",
|
||||
"ui.space_mouse_driver_direct": "Direkt (hidraw)",
|
||||
"ui.space_mouse_driver_only_direct": "Das Gerät wird direkt gelesen; spacenavd läuft nicht",
|
||||
"ui.space_mouse_driver_only_spacenavd": "Lesen über spacenavd, die einzige verfügbare Möglichkeit",
|
||||
"ui.space_mouse_forget": "Kalibrierung vergessen",
|
||||
"ui.space_mouse_hint": "Ansicht gleichzeitig verschieben und drehen; Skizzen verwenden nur Schwenken und Zoomen.",
|
||||
"ui.space_mouse_motion_rotation_x": "Rotation X",
|
||||
"ui.space_mouse_motion_rotation_y": "Rotation Y",
|
||||
"ui.space_mouse_motion_rotation_z": "Rotation Z",
|
||||
"ui.space_mouse_motion_translation_x": "Translation X",
|
||||
"ui.space_mouse_motion_translation_y": "Translation Y",
|
||||
"ui.space_mouse_motion_translation_z": "Translation Z",
|
||||
"ui.space_mouse_navigate": "Mit SpaceNavigator / SpaceMouse navigieren",
|
||||
"ui.space_mouse_none": "Keine 3D-Maus gefunden",
|
||||
"ui.space_mouse_not_calibrated": "Nicht kalibriert",
|
||||
"ui.space_mouse_reading": "Werte",
|
||||
"ui.space_mouse_reset_axes": "Achsenzuordnung zurücksetzen",
|
||||
"ui.space_mouse_step_lift": "Kappe nach oben ziehen, halten, dann loslassen.",
|
||||
"ui.space_mouse_step_press": "Kappe nach unten drücken, halten, dann loslassen.",
|
||||
"ui.space_mouse_step_pull": "Kappe zu sich heranziehen, halten, dann loslassen.",
|
||||
@@ -1765,6 +1801,16 @@
|
||||
"ui.space_mouse_step_tilt_right": "Kappe nach rechts kippen (rechte Kante runter), halten, dann loslassen.",
|
||||
"ui.space_mouse_step_twist_clockwise": "Kappe im Uhrzeigersinn drehen, halten, dann loslassen.",
|
||||
"ui.space_mouse_step_twist_counterclockwise": "Kappe gegen den Uhrzeigersinn drehen, halten, dann loslassen.",
|
||||
"ui.space_mouse_view_axis": "Ansichtsachse",
|
||||
"ui.space_mouse_view_off": "Aus",
|
||||
"ui.space_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.",
|
||||
"ui.space_mouse_view_orbit_sideways": "Orbit links / rechts",
|
||||
"ui.space_mouse_view_orbit_up_down": "Orbit hoch / runter",
|
||||
"ui.space_mouse_view_pan_sideways": "Schwenken links / rechts",
|
||||
"ui.space_mouse_view_pan_up_down": "Schwenken hoch / runter",
|
||||
"ui.space_mouse_view_roll": "Rollen",
|
||||
"ui.space_mouse_view_unmapped": "Achse {n}",
|
||||
"ui.space_mouse_view_zoom": "Zoom",
|
||||
"ui.spacing": "Abstand",
|
||||
"ui.spacing_2": "Abstand 2",
|
||||
"ui.sphere": "Sphäre",
|
||||
@@ -1836,6 +1882,7 @@
|
||||
"ui.workspace_design_tabs": "Modell · Fläche · Zusammenbau · Einfügen · Prüfen · Verwalten · Ansicht",
|
||||
"ui.workspace_drawing_description": "2D-Zeichnungen mit Ansichten, Bemaßungen und Schriftfeldern",
|
||||
"ui.workspace_footer": "Jeder Arbeitsbereich arbeitet am selben Design, Verlauf und derselben Auswahl; nur die Menüband-Register ändern sich. Der Umschalter ist gesperrt, solange eine Skizze oder ein Merkmal bearbeitet wird.",
|
||||
"ui.workspace_layout": "Arbeitsbereich-Layout",
|
||||
"ui.workspace_manufacture_description": "Slicing, Werkzeugbahnen und Einrichtung",
|
||||
"ui.workspace_planned": "Geplant",
|
||||
"ui.workspace_render_description": "Produktvorschau, Szeneneinstellungen und gerenderte Bilder",
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"Center X": "Centre X",
|
||||
"Center Y": "Centre Y",
|
||||
"Center of mass": "Centre of mass",
|
||||
"Customize Toolbar": "Customise Toolbar",
|
||||
"Mass Center": "Mass Centre",
|
||||
"Modeled": "Modelled",
|
||||
"cmd.construct_point_center": "Point At Centre Of Circle/Sphere/Torus",
|
||||
"cmd.inspect_center_of_mass": "Centre of Mass",
|
||||
"cmd.sketch_arc_center_point": "Centre Point Arc",
|
||||
"cmd.sketch_circle_center": "Centre Diameter Circle",
|
||||
"cmd.sketch_rectangle_center": "Centre Rectangle",
|
||||
"cmd.sketch_slot_arc_center": "Centre Point Arc Slot",
|
||||
"cmd.sketch_slot_center_point": "Centre Point Slot",
|
||||
"cmd.sketch_slot_center_to_center": "Centre to Centre Slot",
|
||||
"pattern.alignment_hint": "Counts include the original. Centre balances copies; Flip chooses the extra side for even totals.",
|
||||
"render.opaque_preview_help": "Opaque PBR and simple colours; blended transparency. Refraction, HDRI and final rendering are not available yet.",
|
||||
"sketch_text.align_center": "Centre",
|
||||
"ui.center": "Centre",
|
||||
"ui.centerline": "Centreline",
|
||||
"ui.customizeToolbar": "Customise Toolbar…",
|
||||
"ui.design_properties.license": "Licence",
|
||||
"ui.design_properties.organization": "Organisation",
|
||||
"ui.layout_compact": "Compact",
|
||||
"ui.layout_floating": "Floating",
|
||||
"ui.ribbon_labels": "Show text under ribbon icons",
|
||||
"ui.space_mouse_step_pull": "Pull the cap towards you, hold it, then let go.",
|
||||
"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."
|
||||
}
|
||||
+89
-3
@@ -28,6 +28,7 @@
|
||||
"Also deletes": "Also deletes",
|
||||
"Angle 2": "Angle 2",
|
||||
"Animate": "Animate",
|
||||
"App/system information and command settings are included. Review private details before sharing.": "App/system information and command settings are included. Review private details before sharing.",
|
||||
"Appearance name": "Appearance name",
|
||||
"Applied Radius": "Applied Radius",
|
||||
"Apply To": "Apply To",
|
||||
@@ -83,6 +84,7 @@
|
||||
"Contact": "Contact",
|
||||
"Contact Sets": "Contact Sets",
|
||||
"Contact sets": "Contact sets",
|
||||
"Continue on GitHub": "Continue on GitHub",
|
||||
"Continuity": "Continuity",
|
||||
"Copy Error Message": "Copy Error Message",
|
||||
"Copy Warning Message": "Copy Warning Message",
|
||||
@@ -102,6 +104,7 @@
|
||||
"Curv. Comb": "Curv. Comb",
|
||||
"Curvature (G2)": "Curvature (G2)",
|
||||
"Curvature change (G3)": "Curvature change (G3)",
|
||||
"Custom": "Custom",
|
||||
"Customize Toolbar": "Customize Toolbar",
|
||||
"Delete": "Delete",
|
||||
"Delete all": "Delete all",
|
||||
@@ -110,6 +113,8 @@
|
||||
"Delete the set": "Delete the set",
|
||||
"Density": "Density",
|
||||
"Dependents": "Dependents",
|
||||
"Describe the problem you saw": "Describe the problem you saw",
|
||||
"Describe what happened before continuing.": "Describe what happened before continuing.",
|
||||
"Designs you open or save appear here.": "Designs you open or save appear here.",
|
||||
"Determined by To Object. Choose Angle to edit the angular extent.": "Determined by To Object. Choose Angle to edit the angular extent.",
|
||||
"Direction": "Direction",
|
||||
@@ -127,6 +132,7 @@
|
||||
"Enabled": "Enabled",
|
||||
"End bulge": "End bulge",
|
||||
"End continuity": "End continuity",
|
||||
"Expected result (optional)": "Expected result (optional)",
|
||||
"Explode": "Explode",
|
||||
"Explode Text": "Explode Text",
|
||||
"Export Animation…": "Export Animation…",
|
||||
@@ -167,6 +173,8 @@
|
||||
"HISTORY": "HISTORY",
|
||||
"Height": "Height",
|
||||
"Hidden edges": "Hidden edges",
|
||||
"History layout": "History layout",
|
||||
"History names": "History names",
|
||||
"Holes": "Holes",
|
||||
"Holes (edges on them)": "Holes (edges on them)",
|
||||
"Home view": "Home view",
|
||||
@@ -232,7 +240,8 @@
|
||||
"No crash reports.": "No crash reports.",
|
||||
"No matching appearance presets.": "No matching appearance presets.",
|
||||
"No matching appearances": "No matching appearances",
|
||||
"No visible bodies to export.": "No visible bodies to export.",
|
||||
"No visible bodies or sketches to export.": "No visible bodies or sketches to export.",
|
||||
"Nominal diameter": "Nominal diameter",
|
||||
"None": "None",
|
||||
"Not available yet": "Not available yet",
|
||||
"Notch": "Notch",
|
||||
@@ -241,7 +250,10 @@
|
||||
"Open GitHub draft": "Open GitHub draft",
|
||||
"Open local libraries in the desktop app. You can export this design's library here.": "Open local libraries in the desktop app. You can export this design's library here.",
|
||||
"Open-source parametric 3D CAD in Rust. Licensed MIT OR Apache-2.0.": "Open-source parametric 3D CAD in Rust. Licensed MIT OR Apache-2.0.",
|
||||
"Opens your browser and copies the report text for you to paste into GitHub.": "Opens your browser and copies the report text for you to paste into GitHub.",
|
||||
"Opens your browser. Review and submit your report on GitHub.": "Opens your browser. Review and submit your report on GitHub.",
|
||||
"Operator": "Operator",
|
||||
"Orbit (right drag)": "Orbit (right drag)",
|
||||
"Other side": "Other side",
|
||||
"Other surface (mutual trim)": "Other surface (mutual trim)",
|
||||
"Outside": "Outside",
|
||||
@@ -256,6 +268,7 @@
|
||||
"Pick the tool bodies": "Pick the tool bodies",
|
||||
"Picked": "Picked",
|
||||
"Picked point": "Picked point",
|
||||
"Pitch": "Pitch",
|
||||
"Place at the origin instead": "Place at the origin instead",
|
||||
"Plastic Rules": "Plastic Rules",
|
||||
"Playback": "Playback",
|
||||
@@ -289,6 +302,8 @@
|
||||
"Remove the track": "Remove the track",
|
||||
"Remove this point": "Remove this point",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "Report a bug — review a screenshot and open a GitHub draft",
|
||||
"Report text copied. Paste it into GitHub and submit your report there.": "Report text copied. Paste it into GitHub and submit your report there.",
|
||||
"Reports are public. A GitHub account is required.": "Reports are public. A GitHub account is required.",
|
||||
"Reset Toolbar": "Reset Toolbar",
|
||||
"Resize Part": "Resize Part",
|
||||
"Restore View": "Restore View",
|
||||
@@ -303,6 +318,7 @@
|
||||
"Revert Pos.": "Revert Pos.",
|
||||
"Revert Position": "Revert Position",
|
||||
"Review the report and screenshot before opening GitHub.": "Review the report and screenshot before opening GitHub.",
|
||||
"Review your report on GitHub and click Submit new issue to publish it.": "Review your report on GitHub and click Submit new issue to publish it.",
|
||||
"Rim Edge": "Rim Edge",
|
||||
"Roll History Marker Here": "Roll History Marker Here",
|
||||
"Round": "Round",
|
||||
@@ -313,10 +329,12 @@
|
||||
"SURFACE QUALITY": "SURFACE QUALITY",
|
||||
"Save Rule": "Save Rule",
|
||||
"Save View": "Save View",
|
||||
"Save screenshot…": "Save screenshot…",
|
||||
"Save the study as an animated GIF, seen as the viewport shows it": "Save the study as an animated GIF, seen as the viewport shows it",
|
||||
"Saved preview. Geometry is being checked before editing.": "Saved preview. Geometry is being checked before editing.",
|
||||
"Scale factor": "Scale factor",
|
||||
"Scene": "Scene",
|
||||
"Screenshot (optional)": "Screenshot (optional)",
|
||||
"Search": "Search",
|
||||
"Search commands": "Search commands",
|
||||
"Search this library": "Search this library",
|
||||
@@ -351,17 +369,21 @@
|
||||
"Start continuity": "Start continuity",
|
||||
"Step": "Step",
|
||||
"Steps": "Steps",
|
||||
"Steps to reproduce (optional)": "Steps to reproduce (optional)",
|
||||
"Stitch the kept pieces": "Stitch the kept pieces",
|
||||
"Stitch to both bodies": "Stitch to both bodies",
|
||||
"Study": "Study",
|
||||
"Style": "Style",
|
||||
"Summary": "Summary",
|
||||
"Suppressed": "Suppressed",
|
||||
"Surface": "Surface",
|
||||
"Swap Ends": "Swap Ends",
|
||||
"TRIM": "TRIM",
|
||||
"Tangent (G1)": "Tangent (G1)",
|
||||
"Tangent Chain": "Tangent Chain",
|
||||
"Tap drill diameter": "Tap drill diameter",
|
||||
"Tear": "Tear",
|
||||
"Technical details": "Technical details",
|
||||
"The dashed sketch is a saved plane guide. Select a new face to repair the attachment.": "The dashed sketch is a saved plane guide. Select a new face to repair the attachment.",
|
||||
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "The saved author occurrence is missing. Select a valid author placement before rebinding.",
|
||||
"Then": "Then",
|
||||
@@ -373,6 +395,7 @@
|
||||
"This child is shared. Deleting it removes it from every copy of its parent.": "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 child is shared. Position changes apply to every copy of its parent.",
|
||||
"This face cannot provide a durable attachment. Choose another face or explicitly use a fixed plane.": "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.": "This image is not attached to GitHub yet. Copy or save it, then attach it on GitHub.",
|
||||
"To Offset": "To Offset",
|
||||
"To edges": "To edges",
|
||||
"Tolerance": "Tolerance",
|
||||
@@ -398,6 +421,9 @@
|
||||
"Wall": "Wall",
|
||||
"Wall Location": "Wall Location",
|
||||
"Wall Thickness": "Wall Thickness",
|
||||
"What did you do just before the problem? A short list is enough": "What did you do just before the problem? A short list is enough",
|
||||
"What did you expect to see?": "What did you expect to see?",
|
||||
"What happened?": "What happened?",
|
||||
"When": "When",
|
||||
"Width": "Width",
|
||||
"Will fail": "Will fail",
|
||||
@@ -758,6 +784,7 @@
|
||||
"cmd.view_graphics_preset": "Graphics Preset",
|
||||
"cmd.view_list": "List Named Views",
|
||||
"cmd.view_navigate": "Navigate View",
|
||||
"cmd.view_normal_to": "Normal to Selection",
|
||||
"cmd.view_rename": "Rename Named View",
|
||||
"cmd.view_save": "New Named View",
|
||||
"color.copy": "Click to copy color values",
|
||||
@@ -831,7 +858,7 @@
|
||||
"dxf.progress.projection": "Projecting geometry (3/5)",
|
||||
"dxf.progress.write": "Writing DXF (5/5)",
|
||||
"dxf.tolerance": "Curve tolerance",
|
||||
"dxf.tolerance_help": "Visible bodies only. Curves use straight segments. Smaller tolerance gives finer curves and uses more time and memory.",
|
||||
"dxf.tolerance_help": "Visible bodies and shown sketches only. Curves use straight segments. Smaller tolerance gives finer curves and uses more time and memory.",
|
||||
"file_load.built": "Built so far: {built} of {total} features.",
|
||||
"file_load.converting": "Converting solid {done} of {total}",
|
||||
"file_load.inserting": "Inserting geometry",
|
||||
@@ -855,6 +882,12 @@
|
||||
"help.licences": "Licences",
|
||||
"help.mit": "MIT licence",
|
||||
"help.notice": "NOTICE",
|
||||
"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.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.",
|
||||
"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.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.third_party": "Third-party licences",
|
||||
"home.documentation": "Documentation",
|
||||
"home.in_this_browser": "in this browser",
|
||||
@@ -926,6 +959,8 @@
|
||||
"motion.to_end": "To the end",
|
||||
"motion.to_start": "To the start",
|
||||
"motion.track_name": "Track {number}",
|
||||
"nav.free_orbit.help": "Mouse and 3D mouse rotate through both poles and preserve roll. Shift axis snapping is disabled.",
|
||||
"nav.free_orbit.label": "Free orbit",
|
||||
"nav.touchpad.help": "Hold Shift and move the pointer to pan; hold Alt/Option and move it to orbit. No mouse button is needed. Vertical scroll zooms.",
|
||||
"nav.touchpad.label": "Touchpad",
|
||||
"nav.touchpad.orbit": "Orbit (hold Alt/Option and move; no mouse button)",
|
||||
@@ -1092,6 +1127,7 @@
|
||||
"ui.agent_chat_add": "Attach files",
|
||||
"ui.agent_chat_add_file": "Choose a file…",
|
||||
"ui.agent_chat_allow": "Allow",
|
||||
"ui.agent_chat_allow_once": "Allow once",
|
||||
"ui.agent_chat_allowed": "Allowed",
|
||||
"ui.agent_chat_answered": "Answer: {option}",
|
||||
"ui.agent_chat_approval": "Allow the agent to do this?",
|
||||
@@ -1149,6 +1185,9 @@
|
||||
"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_minimize": "Minimise to a strip",
|
||||
"ui.agent_chat_mode_all": "All",
|
||||
"ui.agent_chat_mode_ask": "Ask",
|
||||
"ui.agent_chat_mode_edits": "Edits",
|
||||
"ui.agent_chat_model_tip": "The model the agent uses. Your choice is remembered for this agent.",
|
||||
"ui.agent_chat_new": "New chat",
|
||||
"ui.agent_chat_new_confirm": "Start a new chat? The current one is cleared.",
|
||||
@@ -1161,6 +1200,7 @@
|
||||
"ui.agent_chat_none_found_short": "No agent found",
|
||||
"ui.agent_chat_not_installed": "Not installed",
|
||||
"ui.agent_chat_not_signed_in": "not signed in",
|
||||
"ui.agent_chat_open_settings": "Open Preferences",
|
||||
"ui.agent_chat_own_window": "Own window",
|
||||
"ui.agent_chat_paste_picture": "Paste a picture",
|
||||
"ui.agent_chat_permission": "The agent asks to run: {title}",
|
||||
@@ -1185,6 +1225,8 @@
|
||||
"ui.agent_chat_send": "Send",
|
||||
"ui.agent_chat_send_now": "Send now",
|
||||
"ui.agent_chat_send_now_tip": "Stop the agent's current step and send this next (Ctrl+Enter)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft talks to a coding agent installed on this computer. Pick one in Preferences › AI.",
|
||||
"ui.agent_chat_setup_title": "Set up an assistant",
|
||||
"ui.agent_chat_side": "Agent chat side",
|
||||
"ui.agent_chat_signed_in": "signed in",
|
||||
"ui.agent_chat_state_auth": "Sign-in needed",
|
||||
@@ -1201,8 +1243,11 @@
|
||||
"ui.agent_chat_undo_label": "Agent chat: {message}",
|
||||
"ui.agent_chat_undone": "(undone)",
|
||||
"ui.agent_chat_view_attached": "View attached",
|
||||
"ui.agent_chat_waiting": "Waiting for you",
|
||||
"ui.agent_cursor": "Agent cursor",
|
||||
"ui.agent_cursor_speed": "Agent cursor speed",
|
||||
"ui.agent_off_thread": "Keep the window responsive while agents run commands",
|
||||
"ui.agent_off_thread_note": "An agent's slow commands (from the AI panel or an MCP client) compute beside the window: you can keep turning and inspecting the view, and stop one from the chip in the view. Off: each runs on the window's thread and the window waits for it.",
|
||||
"ui.ai_add": "Add",
|
||||
"ui.ai_add_agent": "Add agent…",
|
||||
"ui.ai_add_agent_title": "Add agent",
|
||||
@@ -1528,7 +1573,19 @@
|
||||
"ui.hideOrigin": "Hide Origin",
|
||||
"ui.hidePlanes": "Hide Planes",
|
||||
"ui.hideProfile": "Hide Profile",
|
||||
"ui.history_back": "Go back one step",
|
||||
"ui.history_below_browser": "Below browser",
|
||||
"ui.history_edge_hint": "double-click the edge",
|
||||
"ui.history_first": "Go to beginning",
|
||||
"ui.history_forward": "Go forward one step",
|
||||
"ui.history_hide_names": "Hide names",
|
||||
"ui.history_last": "Go to end",
|
||||
"ui.history_marker_tip": "History marker: drag to roll the model back; new features go here",
|
||||
"ui.history_names": "Names",
|
||||
"ui.history_play": "Play to the end",
|
||||
"ui.history_show_names": "Show names",
|
||||
"ui.history_title": "History",
|
||||
"ui.history_toggle_names": "Show or hide feature names",
|
||||
"ui.hole": "Hole",
|
||||
"ui.hole_tap_type": "Hole Tap Type",
|
||||
"ui.hole_type": "Hole Type",
|
||||
@@ -1545,6 +1602,8 @@
|
||||
"ui.joints": "Joints",
|
||||
"ui.keyboard_shortcuts": "Keyboard Shortcuts",
|
||||
"ui.language": "Language",
|
||||
"ui.layout_compact": "Compact",
|
||||
"ui.layout_floating": "Floating",
|
||||
"ui.length_decimals": "Length decimals",
|
||||
"ui.light": "Light",
|
||||
"ui.line": "Line",
|
||||
@@ -1682,6 +1741,7 @@
|
||||
"ui.reverse_zoom_direction": "Reverse zoom direction",
|
||||
"ui.revolve": "Revolve",
|
||||
"ui.ribbon_exit": "Exit",
|
||||
"ui.ribbon_labels": "Show text under ribbon icons",
|
||||
"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",
|
||||
@@ -1737,6 +1797,7 @@
|
||||
"ui.smooth_wheel_zoom": "Smooth wheel zoom",
|
||||
"ui.smooth_wheel_zoom_tip": "Turn off to apply each wheel event immediately, without a smoothing tail",
|
||||
"ui.snap": "Snap",
|
||||
"ui.space_mouse_axes_reassigned": "Axes are reassigned in Preferences, and that still applies on top of this calibration.",
|
||||
"ui.space_mouse_calibrate": "Calibrate…",
|
||||
"ui.space_mouse_calibrate_tip": "Move the cap as the window asks, so SolidCraft learns which way each axis of this 3D mouse goes and how far. Saved for this 3D mouse.",
|
||||
"ui.space_mouse_calibrate_title": "Calibrate 3D mouse",
|
||||
@@ -1746,12 +1807,26 @@
|
||||
"ui.space_mouse_calibration_no_response": "One axis barely moved, so it can't be calibrated. Start over and move the cap further each time.",
|
||||
"ui.space_mouse_dead_zone": "Dead zone",
|
||||
"ui.space_mouse_dead_zone_tip": "How far the cap has to move before the view does, as a share of its full travel. Raise it if the view drifts while the cap is at rest.",
|
||||
"ui.space_mouse_driven_by": "Which calibrated motion of the device drives this axis. Picking a motion another axis already uses swaps the two.",
|
||||
"ui.space_mouse_driven_by_label": "driven by",
|
||||
"ui.space_mouse_driver": "3D mouse driver",
|
||||
"ui.space_mouse_driver_auto": "Automatic",
|
||||
"ui.space_mouse_driver_direct": "Direct (hidraw)",
|
||||
"ui.space_mouse_driver_only_direct": "Reading the device directly; spacenavd is not running",
|
||||
"ui.space_mouse_driver_only_spacenavd": "Reading through spacenavd, the only way available",
|
||||
"ui.space_mouse_forget": "Forget calibration",
|
||||
"ui.space_mouse_hint": "Move and rotate the view together; sketches use pan and zoom only.",
|
||||
"ui.space_mouse_motion_rotation_x": "Rotation X",
|
||||
"ui.space_mouse_motion_rotation_y": "Rotation Y",
|
||||
"ui.space_mouse_motion_rotation_z": "Rotation Z",
|
||||
"ui.space_mouse_motion_translation_x": "Translation X",
|
||||
"ui.space_mouse_motion_translation_y": "Translation Y",
|
||||
"ui.space_mouse_motion_translation_z": "Translation Z",
|
||||
"ui.space_mouse_navigate": "Navigate with SpaceNavigator / SpaceMouse",
|
||||
"ui.space_mouse_none": "No 3D mouse found",
|
||||
"ui.space_mouse_not_calibrated": "Not calibrated",
|
||||
"ui.space_mouse_reading": "Reading",
|
||||
"ui.space_mouse_reset_axes": "Reset axis assignment",
|
||||
"ui.space_mouse_step_lift": "Lift the cap up, hold it, then let go.",
|
||||
"ui.space_mouse_step_press": "Press the cap down, hold it, then let go.",
|
||||
"ui.space_mouse_step_pull": "Pull the cap toward you, hold it, then let go.",
|
||||
@@ -1765,6 +1840,16 @@
|
||||
"ui.space_mouse_step_tilt_right": "Tilt the cap right (right edge down), hold it, then let go.",
|
||||
"ui.space_mouse_step_twist_clockwise": "Twist the cap clockwise, hold it, then let go.",
|
||||
"ui.space_mouse_step_twist_counterclockwise": "Twist the cap anticlockwise, hold it, then let go.",
|
||||
"ui.space_mouse_view_axis": "View axis",
|
||||
"ui.space_mouse_view_off": "Off",
|
||||
"ui.space_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.",
|
||||
"ui.space_mouse_view_orbit_sideways": "Orbit left / right",
|
||||
"ui.space_mouse_view_orbit_up_down": "Orbit up / down",
|
||||
"ui.space_mouse_view_pan_sideways": "Pan left / right",
|
||||
"ui.space_mouse_view_pan_up_down": "Pan up / down",
|
||||
"ui.space_mouse_view_roll": "Roll",
|
||||
"ui.space_mouse_view_unmapped": "Axis {n}",
|
||||
"ui.space_mouse_view_zoom": "Zoom",
|
||||
"ui.spacing": "Spacing",
|
||||
"ui.spacing_2": "Spacing 2",
|
||||
"ui.sphere": "Sphere",
|
||||
@@ -1798,7 +1883,7 @@
|
||||
"ui.tie_ends": "Tie",
|
||||
"ui.tie_ends_tip": "Constrain all untied ends",
|
||||
"ui.timeline_bottom": "Bottom timeline",
|
||||
"ui.timeline_left_note": "The left layout shows the rollback bar above the parts and scene browser.",
|
||||
"ui.timeline_left_note": "The vertical history sits to the left of the browser. Toggle feature names on the strip.",
|
||||
"ui.timeline_left_sidebar": "Left sidebar",
|
||||
"ui.tip_angle": "Tip Angle",
|
||||
"ui.to_object": "To Object",
|
||||
@@ -1836,6 +1921,7 @@
|
||||
"ui.workspace_design_tabs": "Model · Surface · Assemble · Insert · Inspect · Manage · View",
|
||||
"ui.workspace_drawing_description": "2D drawings with views, dimensions and title blocks",
|
||||
"ui.workspace_footer": "Every workspace works on the same design, history and selection; only the ribbon tabs change. The switcher is locked while a sketch or feature is being edited.",
|
||||
"ui.workspace_layout": "Workspace layout",
|
||||
"ui.workspace_manufacture_description": "Slicing, toolpaths and setup",
|
||||
"ui.workspace_planned": "Planned",
|
||||
"ui.workspace_render_description": "Product preview, scene settings and rendered images",
|
||||
|
||||
+45
-2
@@ -167,6 +167,7 @@
|
||||
"HISTORY": "HISTORIAL",
|
||||
"Height": "Altura",
|
||||
"Hidden edges": "Aristas ocultas",
|
||||
"History layout": "Disposición del historial",
|
||||
"Holes": "Agujeros",
|
||||
"Holes (edges on them)": "Agujeros (aristas que contienen)",
|
||||
"Home view": "Vista de inicio",
|
||||
@@ -232,7 +233,7 @@
|
||||
"No crash reports.": "No hay informes de fallos.",
|
||||
"No matching appearance presets.": "No hay preajustes de apariencia coincidentes.",
|
||||
"No matching appearances": "No hay apariencias coincidentes",
|
||||
"No visible bodies to export.": "No hay cuerpos visibles para exportar.",
|
||||
"No visible bodies or sketches to export.": "No hay cuerpos ni bocetos visibles para exportar.",
|
||||
"None": "Ninguno",
|
||||
"Not available yet": "Todavía no disponible",
|
||||
"Notch": "Muesca",
|
||||
@@ -831,7 +832,7 @@
|
||||
"dxf.progress.projection": "Proyectando geometría (3/5)",
|
||||
"dxf.progress.write": "Escribiendo DXF (5/5)",
|
||||
"dxf.tolerance": "Tolerancia de curva",
|
||||
"dxf.tolerance_help": "Solo cuerpos visibles. Las curvas usan segmentos rectos. Una tolerancia menor da curvas más finas y consume más tiempo y memoria.",
|
||||
"dxf.tolerance_help": "Solo cuerpos visibles y bocetos mostrados. Las curvas usan segmentos rectos. Una tolerancia menor da curvas más finas y consume más tiempo y memoria.",
|
||||
"file_load.built": "Construido hasta ahora: {built} de {total} operaciones.",
|
||||
"file_load.converting": "Convirtiendo sólido {done} de {total}",
|
||||
"file_load.inserting": "Insertando geometría",
|
||||
@@ -1092,6 +1093,7 @@
|
||||
"ui.agent_chat_add": "Adjuntar archivos",
|
||||
"ui.agent_chat_add_file": "Elegir un archivo…",
|
||||
"ui.agent_chat_allow": "Permitir",
|
||||
"ui.agent_chat_allow_once": "Permitir una vez",
|
||||
"ui.agent_chat_allowed": "Permitido",
|
||||
"ui.agent_chat_answered": "Respuesta: {option}",
|
||||
"ui.agent_chat_approval": "¿Permitir que el agente haga esto?",
|
||||
@@ -1149,6 +1151,9 @@
|
||||
"ui.agent_chat_left": "Izquierda",
|
||||
"ui.agent_chat_limit": "El agente ha alcanzado el límite de {limit} cambios por mensaje. Envía otro mensaje para que continúe.",
|
||||
"ui.agent_chat_minimize": "Minimizar a una franja",
|
||||
"ui.agent_chat_mode_all": "Todo",
|
||||
"ui.agent_chat_mode_ask": "Preguntar",
|
||||
"ui.agent_chat_mode_edits": "Ediciones",
|
||||
"ui.agent_chat_model_tip": "Modelo que utiliza el agente. Se recuerda tu elección para este agente.",
|
||||
"ui.agent_chat_new": "Nuevo chat",
|
||||
"ui.agent_chat_new_confirm": "¿Iniciar un nuevo chat? Se borrará la conversación actual.",
|
||||
@@ -1161,6 +1166,7 @@
|
||||
"ui.agent_chat_none_found_short": "No se ha encontrado ningún agente",
|
||||
"ui.agent_chat_not_installed": "No instalado",
|
||||
"ui.agent_chat_not_signed_in": "sesión no iniciada",
|
||||
"ui.agent_chat_open_settings": "Abrir preferencias",
|
||||
"ui.agent_chat_own_window": "Ventana propia",
|
||||
"ui.agent_chat_paste_picture": "Pegar una imagen",
|
||||
"ui.agent_chat_permission": "El agente solicita ejecutar: {title}",
|
||||
@@ -1185,6 +1191,8 @@
|
||||
"ui.agent_chat_send": "Enviar",
|
||||
"ui.agent_chat_send_now": "Enviar ahora",
|
||||
"ui.agent_chat_send_now_tip": "Detener el paso actual del agente y enviar esto a continuación (Ctrl+Intro)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft se comunica con un agente de programación instalado en este equipo. Elige uno en Preferencias › AI.",
|
||||
"ui.agent_chat_setup_title": "Configurar un asistente",
|
||||
"ui.agent_chat_side": "Lado del chat del agente",
|
||||
"ui.agent_chat_signed_in": "sesión iniciada",
|
||||
"ui.agent_chat_state_auth": "Se requiere iniciar sesión",
|
||||
@@ -1201,6 +1209,7 @@
|
||||
"ui.agent_chat_undo_label": "Chat del agente: {message}",
|
||||
"ui.agent_chat_undone": "(deshecho)",
|
||||
"ui.agent_chat_view_attached": "Vista adjunta",
|
||||
"ui.agent_chat_waiting": "Esperando tu respuesta",
|
||||
"ui.agent_cursor": "Cursor del agente",
|
||||
"ui.agent_cursor_speed": "Velocidad del cursor del agente",
|
||||
"ui.ai_add": "Añadir",
|
||||
@@ -1528,6 +1537,11 @@
|
||||
"ui.hideOrigin": "Ocultar origen",
|
||||
"ui.hidePlanes": "Ocultar planos",
|
||||
"ui.hideProfile": "Ocultar perfil",
|
||||
"ui.history_back": "Retroceder un paso",
|
||||
"ui.history_below_browser": "Debajo del explorador",
|
||||
"ui.history_first": "Ir al principio",
|
||||
"ui.history_forward": "Avanzar un paso",
|
||||
"ui.history_last": "Ir al final",
|
||||
"ui.history_title": "Historial",
|
||||
"ui.hole": "Agujero",
|
||||
"ui.hole_tap_type": "Tipo de roscado del agujero",
|
||||
@@ -1545,6 +1559,8 @@
|
||||
"ui.joints": "Uniones",
|
||||
"ui.keyboard_shortcuts": "Atajos de teclado",
|
||||
"ui.language": "Idioma",
|
||||
"ui.layout_compact": "Compacta",
|
||||
"ui.layout_floating": "Flotante",
|
||||
"ui.length_decimals": "Decimales de longitud",
|
||||
"ui.light": "Claro",
|
||||
"ui.line": "Línea",
|
||||
@@ -1682,6 +1698,7 @@
|
||||
"ui.reverse_zoom_direction": "Invertir dirección del zoom",
|
||||
"ui.revolve": "Revolución",
|
||||
"ui.ribbon_exit": "Salir",
|
||||
"ui.ribbon_labels": "Mostrar texto bajo los iconos de la cinta",
|
||||
"ui.ribbon_pin_footer": "Los comandos anclados se muestran en la cinta. Pasa el ratón sobre una fila para anclarla o desanclarla.",
|
||||
"ui.ribbon_workspace": "Espacio de trabajo",
|
||||
"ui.right": "Derecha",
|
||||
@@ -1737,6 +1754,7 @@
|
||||
"ui.smooth_wheel_zoom": "Zoom suave con la rueda",
|
||||
"ui.smooth_wheel_zoom_tip": "Desactívalo para aplicar cada movimiento de la rueda inmediatamente, sin suavizado posterior",
|
||||
"ui.snap": "Referencia a objetos",
|
||||
"ui.space_mouse_axes_reassigned": "Los ejes están reasignados en Preferencias, y eso sigue aplicándose sobre esta calibración.",
|
||||
"ui.space_mouse_calibrate": "Calibrar…",
|
||||
"ui.space_mouse_calibrate_tip": "Mueve el capuchón como pide la ventana para que SolidCraft aprenda hacia dónde y hasta dónde va cada eje de este ratón 3D. Se guarda para este ratón 3D.",
|
||||
"ui.space_mouse_calibrate_title": "Calibrar ratón 3D",
|
||||
@@ -1746,12 +1764,26 @@
|
||||
"ui.space_mouse_calibration_no_response": "Un eje apenas se movió, así que no se puede calibrar. Vuelve a empezar y mueve el capuchón más lejos cada vez.",
|
||||
"ui.space_mouse_dead_zone": "Zona muerta",
|
||||
"ui.space_mouse_dead_zone_tip": "Cuánto tiene que moverse el capuchón antes de que se mueva la vista, como parte de su recorrido completo. Auméntala si la vista se desplaza con el capuchón en reposo.",
|
||||
"ui.space_mouse_driven_by": "Qué movimiento calibrado del dispositivo controla este eje. Si eliges uno que ya usa otro eje, se intercambian.",
|
||||
"ui.space_mouse_driven_by_label": "controlado por",
|
||||
"ui.space_mouse_driver": "Controlador del ratón 3D",
|
||||
"ui.space_mouse_driver_auto": "Automático",
|
||||
"ui.space_mouse_driver_direct": "Directo (hidraw)",
|
||||
"ui.space_mouse_driver_only_direct": "Lectura directa del dispositivo; spacenavd no se está ejecutando",
|
||||
"ui.space_mouse_driver_only_spacenavd": "Lectura mediante spacenavd, la única opción disponible",
|
||||
"ui.space_mouse_forget": "Olvidar calibración",
|
||||
"ui.space_mouse_hint": "Desplaza y gira la vista a la vez; los bocetos solo permiten desplazamiento y zoom.",
|
||||
"ui.space_mouse_motion_rotation_x": "Rotación X",
|
||||
"ui.space_mouse_motion_rotation_y": "Rotación Y",
|
||||
"ui.space_mouse_motion_rotation_z": "Rotación Z",
|
||||
"ui.space_mouse_motion_translation_x": "Traslación X",
|
||||
"ui.space_mouse_motion_translation_y": "Traslación Y",
|
||||
"ui.space_mouse_motion_translation_z": "Traslación Z",
|
||||
"ui.space_mouse_navigate": "Navegar con SpaceNavigator / SpaceMouse",
|
||||
"ui.space_mouse_none": "No se encontró ningún ratón 3D",
|
||||
"ui.space_mouse_not_calibrated": "Sin calibrar",
|
||||
"ui.space_mouse_reading": "Lectura",
|
||||
"ui.space_mouse_reset_axes": "Restablecer asignación de ejes",
|
||||
"ui.space_mouse_step_lift": "Levanta el capuchón, mantenlo y suéltalo.",
|
||||
"ui.space_mouse_step_press": "Presiona el capuchón hacia abajo, mantenlo y suéltalo.",
|
||||
"ui.space_mouse_step_pull": "Tira del capuchón hacia ti, mantenlo y suéltalo.",
|
||||
@@ -1765,6 +1797,16 @@
|
||||
"ui.space_mouse_step_tilt_right": "Inclina el capuchón a la derecha (borde derecho abajo), mantenlo y suéltalo.",
|
||||
"ui.space_mouse_step_twist_clockwise": "Gira el capuchón en sentido horario, mantenlo y suéltalo.",
|
||||
"ui.space_mouse_step_twist_counterclockwise": "Gira el capuchón en sentido antihorario, mantenlo y suéltalo.",
|
||||
"ui.space_mouse_view_axis": "Eje de la vista",
|
||||
"ui.space_mouse_view_off": "Desactivado",
|
||||
"ui.space_mouse_view_off_tip": "El perfil del controlador del ratón 3D desactiva este eje, así que su movimiento no mueve la vista. Si eliges aquí el movimiento de otro eje, se intercambian y ese movimiento deja de mover la vista.",
|
||||
"ui.space_mouse_view_orbit_sideways": "Orbitar izquierda / derecha",
|
||||
"ui.space_mouse_view_orbit_up_down": "Orbitar arriba / abajo",
|
||||
"ui.space_mouse_view_pan_sideways": "Desplazar izquierda / derecha",
|
||||
"ui.space_mouse_view_pan_up_down": "Desplazar arriba / abajo",
|
||||
"ui.space_mouse_view_roll": "Girar sobre la vista",
|
||||
"ui.space_mouse_view_unmapped": "Eje {n}",
|
||||
"ui.space_mouse_view_zoom": "Zoom",
|
||||
"ui.spacing": "Espaciado",
|
||||
"ui.spacing_2": "Espaciado 2",
|
||||
"ui.sphere": "Esfera",
|
||||
@@ -1836,6 +1878,7 @@
|
||||
"ui.workspace_design_tabs": "Modelo · Superficie · Ensamblaje · Insertar · Inspeccionar · Gestionar · Vista",
|
||||
"ui.workspace_drawing_description": "Dibujos 2D con vistas, cotas y cajetines",
|
||||
"ui.workspace_footer": "Todos los espacios de trabajo usan el mismo diseño, historial y selección; solo cambian las pestañas de la cinta. El selector está bloqueado mientras se edita un boceto o una operación.",
|
||||
"ui.workspace_layout": "Disposición del espacio de trabajo",
|
||||
"ui.workspace_manufacture_description": "Laminado, trayectorias de herramienta y configuración",
|
||||
"ui.workspace_planned": "Previsto",
|
||||
"ui.workspace_render_description": "Vista previa del producto, ajustes de escena e imágenes renderizadas",
|
||||
|
||||
+51
-2
@@ -167,6 +167,7 @@
|
||||
"HISTORY": "HISTORIQUE",
|
||||
"Height": "Hauteur",
|
||||
"Hidden edges": "Arêtes cachées",
|
||||
"History layout": "Disposition de l’historique",
|
||||
"Holes": "Trous",
|
||||
"Holes (edges on them)": "Trous (par leurs arêtes)",
|
||||
"Home view": "Vue d'accueil",
|
||||
@@ -232,7 +233,7 @@
|
||||
"No crash reports.": "Aucun rapport de plantage.",
|
||||
"No matching appearance presets.": "Aucun préréglage d’apparence correspondant.",
|
||||
"No matching appearances": "Aucune apparence correspondante",
|
||||
"No visible bodies to export.": "Aucun corps visible à exporter.",
|
||||
"No visible bodies or sketches to export.": "Aucun corps ni esquisse visible à exporter.",
|
||||
"None": "Aucun",
|
||||
"Not available yet": "Pas encore disponible",
|
||||
"Notch": "Encoche",
|
||||
@@ -831,7 +832,7 @@
|
||||
"dxf.progress.projection": "Projection de la géométrie (3/5)",
|
||||
"dxf.progress.write": "Écriture du DXF (5/5)",
|
||||
"dxf.tolerance": "Tolérance des courbes",
|
||||
"dxf.tolerance_help": "Corps visibles uniquement. Les courbes sont approchées par des segments de droite. Une tolérance plus faible donne des courbes plus fines, au prix de plus de temps et de mémoire.",
|
||||
"dxf.tolerance_help": "Corps visibles et esquisses affichées uniquement. Les courbes sont approchées par des segments de droite. Une tolérance plus faible donne des courbes plus fines, au prix de plus de temps et de mémoire.",
|
||||
"file_load.built": "Construit jusqu’ici : {built} entités sur {total}.",
|
||||
"file_load.converting": "Conversion du solide {done} sur {total}",
|
||||
"file_load.inserting": "Insertion de la géométrie",
|
||||
@@ -855,6 +856,12 @@
|
||||
"help.licences": "Licences",
|
||||
"help.mit": "Licence MIT",
|
||||
"help.notice": "NOTICE",
|
||||
"help.solid.chamfer": "Coupe les arêtes sélectionnées par un biseau plat de la distance indiquée. À utiliser pour casser des arêtes vives ou faciliter l'entrée d'une goupille ou d'une vis.",
|
||||
"help.solid.extrude": "Pousse le profil d'une esquisse en 3D sur une distance, en ajoutant ou en retirant de la matière. À utiliser pour transformer un contour plat en bloc, en bossage ou en poche.",
|
||||
"help.solid.fillet": "Arrondit les arêtes sélectionnées par un raccord circulaire lisse du rayon indiqué. À utiliser pour adoucir des arêtes vives par souci de solidité, de confort ou d'aspect.",
|
||||
"help.solid.hole": "Perce un trou rond du diamètre indiqué en un point choisi d'une face. À utiliser pour les passages de vis, les goupilles et autres ouvertures rondes simples.",
|
||||
"help.solid.revolve": "Fait tourner le profil d'une esquisse autour d'un axe pour obtenir un solide de révolution. À utiliser pour les arbres, boutons, bols et toute pièce ronde autour d'une ligne centrale.",
|
||||
"help.solid.shell": "Évide un solide en laissant des parois de l'épaisseur indiquée, et peut ouvrir les faces choisies. À utiliser pour les boîtes, couvercles et autres pièces à parois minces.",
|
||||
"help.third_party": "Licences tierces",
|
||||
"home.documentation": "Documentation",
|
||||
"home.in_this_browser": "dans ce navigateur",
|
||||
@@ -1092,6 +1099,7 @@
|
||||
"ui.agent_chat_add": "Joindre des fichiers",
|
||||
"ui.agent_chat_add_file": "Choisir un fichier…",
|
||||
"ui.agent_chat_allow": "Autoriser",
|
||||
"ui.agent_chat_allow_once": "Autoriser une fois",
|
||||
"ui.agent_chat_allowed": "Autorisé",
|
||||
"ui.agent_chat_answered": "Réponse : {option}",
|
||||
"ui.agent_chat_approval": "Autoriser l'agent à effectuer cette action ?",
|
||||
@@ -1149,6 +1157,9 @@
|
||||
"ui.agent_chat_left": "Gauche",
|
||||
"ui.agent_chat_limit": "L'agent a atteint la limite de {limit} modifications pour un message. Envoyez un autre message pour qu'il poursuive.",
|
||||
"ui.agent_chat_minimize": "Réduire en bandeau",
|
||||
"ui.agent_chat_mode_all": "Tout",
|
||||
"ui.agent_chat_mode_ask": "Demander",
|
||||
"ui.agent_chat_mode_edits": "Édition",
|
||||
"ui.agent_chat_model_tip": "Le modèle utilisé par l'agent. Votre choix est mémorisé pour cet agent.",
|
||||
"ui.agent_chat_new": "Nouveau chat",
|
||||
"ui.agent_chat_new_confirm": "Démarrer un nouveau chat ? Le chat actuel sera effacé.",
|
||||
@@ -1161,6 +1172,7 @@
|
||||
"ui.agent_chat_none_found_short": "Aucun agent trouvé",
|
||||
"ui.agent_chat_not_installed": "Non installé",
|
||||
"ui.agent_chat_not_signed_in": "non authentifié",
|
||||
"ui.agent_chat_open_settings": "Ouvrir les préférences",
|
||||
"ui.agent_chat_own_window": "Fenêtre séparée",
|
||||
"ui.agent_chat_paste_picture": "Coller une image",
|
||||
"ui.agent_chat_permission": "L'agent demande à exécuter : {title}",
|
||||
@@ -1185,6 +1197,8 @@
|
||||
"ui.agent_chat_send": "Envoyer",
|
||||
"ui.agent_chat_send_now": "Envoyer maintenant",
|
||||
"ui.agent_chat_send_now_tip": "Arrêter l'étape en cours de l'agent et envoyer celui-ci ensuite (Ctrl+Enter)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft communique avec un agent de programmation installé sur cet ordinateur. Choisissez-en un dans Préférences › IA.",
|
||||
"ui.agent_chat_setup_title": "Configurer un assistant",
|
||||
"ui.agent_chat_side": "Côté du chat de l'agent",
|
||||
"ui.agent_chat_signed_in": "authentifié",
|
||||
"ui.agent_chat_state_auth": "Authentification requise",
|
||||
@@ -1201,6 +1215,7 @@
|
||||
"ui.agent_chat_undo_label": "Chat de l'agent : {message}",
|
||||
"ui.agent_chat_undone": "(annulé)",
|
||||
"ui.agent_chat_view_attached": "Vue jointe",
|
||||
"ui.agent_chat_waiting": "En attente de votre réponse",
|
||||
"ui.agent_cursor": "Curseur de l'agent",
|
||||
"ui.agent_cursor_speed": "Vitesse du curseur de l'agent",
|
||||
"ui.ai_add": "Ajouter",
|
||||
@@ -1528,6 +1543,11 @@
|
||||
"ui.hideOrigin": "Masquer l'origine",
|
||||
"ui.hidePlanes": "Masquer les plans",
|
||||
"ui.hideProfile": "Masquer le profil",
|
||||
"ui.history_back": "Reculer d’une étape",
|
||||
"ui.history_below_browser": "Sous le navigateur",
|
||||
"ui.history_first": "Aller au début",
|
||||
"ui.history_forward": "Avancer d’une étape",
|
||||
"ui.history_last": "Aller à la fin",
|
||||
"ui.history_title": "Historique",
|
||||
"ui.hole": "Perçage",
|
||||
"ui.hole_tap_type": "Type de taraudage",
|
||||
@@ -1545,6 +1565,8 @@
|
||||
"ui.joints": "Liaisons",
|
||||
"ui.keyboard_shortcuts": "Raccourcis clavier",
|
||||
"ui.language": "Langue",
|
||||
"ui.layout_compact": "Compacte",
|
||||
"ui.layout_floating": "Flottante",
|
||||
"ui.length_decimals": "Décimales des longueurs",
|
||||
"ui.light": "Clair",
|
||||
"ui.line": "Ligne",
|
||||
@@ -1682,6 +1704,7 @@
|
||||
"ui.reverse_zoom_direction": "Inverser le sens du zoom",
|
||||
"ui.revolve": "Révolution",
|
||||
"ui.ribbon_exit": "Quitter",
|
||||
"ui.ribbon_labels": "Afficher le texte sous les icônes du ruban",
|
||||
"ui.ribbon_pin_footer": "Les commandes épinglées s’affichent dans le ruban. Survolez une ligne pour l’épingler ou la détacher.",
|
||||
"ui.ribbon_workspace": "Espace de travail",
|
||||
"ui.right": "Droite",
|
||||
@@ -1737,6 +1760,7 @@
|
||||
"ui.smooth_wheel_zoom": "Zoom fluide à la molette",
|
||||
"ui.smooth_wheel_zoom_tip": "Désactiver pour appliquer chaque cran de molette immédiatement, sans lissage",
|
||||
"ui.snap": "Accrochage",
|
||||
"ui.space_mouse_axes_reassigned": "Les axes sont réattribués dans les Préférences, et cela s'applique toujours en plus de ce calibrage.",
|
||||
"ui.space_mouse_calibrate": "Calibrer…",
|
||||
"ui.space_mouse_calibrate_tip": "Déplacez le capuchon comme la fenêtre le demande, pour que SolidCraft apprenne dans quel sens et jusqu'où va chaque axe de cette souris 3D. Enregistré pour cette souris 3D.",
|
||||
"ui.space_mouse_calibrate_title": "Calibrer la souris 3D",
|
||||
@@ -1746,12 +1770,26 @@
|
||||
"ui.space_mouse_calibration_no_response": "Un axe a à peine bougé, il ne peut donc pas être calibré. Recommencez en déplaçant le capuchon plus loin à chaque fois.",
|
||||
"ui.space_mouse_dead_zone": "Zone morte",
|
||||
"ui.space_mouse_dead_zone_tip": "De combien le capuchon doit bouger avant que la vue ne bouge, en part de sa course complète. Augmentez-la si la vue dérive alors que le capuchon est au repos.",
|
||||
"ui.space_mouse_driven_by": "Quel mouvement calibré de l'appareil pilote cet axe. Choisir un mouvement déjà utilisé par un autre axe les échange.",
|
||||
"ui.space_mouse_driven_by_label": "piloté par",
|
||||
"ui.space_mouse_driver": "Pilote de la souris 3D",
|
||||
"ui.space_mouse_driver_auto": "Automatique",
|
||||
"ui.space_mouse_driver_direct": "Direct (hidraw)",
|
||||
"ui.space_mouse_driver_only_direct": "Lecture directe du périphérique ; spacenavd n'est pas lancé",
|
||||
"ui.space_mouse_driver_only_spacenavd": "Lecture via spacenavd, seule possibilité disponible",
|
||||
"ui.space_mouse_forget": "Oublier le calibrage",
|
||||
"ui.space_mouse_hint": "Déplace et fait pivoter la vue en même temps ; dans les esquisses, seuls le panoramique et le zoom sont utilisés.",
|
||||
"ui.space_mouse_motion_rotation_x": "Rotation X",
|
||||
"ui.space_mouse_motion_rotation_y": "Rotation Y",
|
||||
"ui.space_mouse_motion_rotation_z": "Rotation Z",
|
||||
"ui.space_mouse_motion_translation_x": "Translation X",
|
||||
"ui.space_mouse_motion_translation_y": "Translation Y",
|
||||
"ui.space_mouse_motion_translation_z": "Translation Z",
|
||||
"ui.space_mouse_navigate": "Naviguer avec SpaceNavigator / SpaceMouse",
|
||||
"ui.space_mouse_none": "Aucune souris 3D trouvée",
|
||||
"ui.space_mouse_not_calibrated": "Non calibrée",
|
||||
"ui.space_mouse_reading": "Lecture",
|
||||
"ui.space_mouse_reset_axes": "Réinitialiser l'attribution des axes",
|
||||
"ui.space_mouse_step_lift": "Soulevez le capuchon, maintenez, puis relâchez.",
|
||||
"ui.space_mouse_step_press": "Appuyez sur le capuchon, maintenez, puis relâchez.",
|
||||
"ui.space_mouse_step_pull": "Tirez le capuchon vers vous, maintenez, puis relâchez.",
|
||||
@@ -1765,6 +1803,16 @@
|
||||
"ui.space_mouse_step_tilt_right": "Inclinez le capuchon vers la droite (bord droit vers le bas), maintenez, puis relâchez.",
|
||||
"ui.space_mouse_step_twist_clockwise": "Tournez le capuchon dans le sens horaire, maintenez, puis relâchez.",
|
||||
"ui.space_mouse_step_twist_counterclockwise": "Tournez le capuchon dans le sens antihoraire, maintenez, puis relâchez.",
|
||||
"ui.space_mouse_view_axis": "Axe de la vue",
|
||||
"ui.space_mouse_view_off": "Désactivé",
|
||||
"ui.space_mouse_view_off_tip": "Le profil du pilote de la souris 3D désactive cet axe : son mouvement ne déplace pas la vue. Choisir ici le mouvement d'un autre axe les échange, et ce mouvement cesse de déplacer la vue.",
|
||||
"ui.space_mouse_view_orbit_sideways": "Orbite gauche / droite",
|
||||
"ui.space_mouse_view_orbit_up_down": "Orbite haut / bas",
|
||||
"ui.space_mouse_view_pan_sideways": "Panoramique gauche / droite",
|
||||
"ui.space_mouse_view_pan_up_down": "Panoramique haut / bas",
|
||||
"ui.space_mouse_view_roll": "Roulis",
|
||||
"ui.space_mouse_view_unmapped": "Axe {n}",
|
||||
"ui.space_mouse_view_zoom": "Zoom",
|
||||
"ui.spacing": "Espacement",
|
||||
"ui.spacing_2": "Espacement 2",
|
||||
"ui.sphere": "Sphère",
|
||||
@@ -1836,6 +1884,7 @@
|
||||
"ui.workspace_design_tabs": "Modèle · Surface · Assemblage · Insérer · Inspecter · Gérer · Vue",
|
||||
"ui.workspace_drawing_description": "Mises en plan 2D avec vues, cotes et cartouches",
|
||||
"ui.workspace_footer": "Chaque espace de travail s’applique à la même conception, au même historique et à la même sélection ; seuls les onglets du ruban changent. Le sélecteur est verrouillé pendant la modification d’une esquisse ou d’une entité.",
|
||||
"ui.workspace_layout": "Disposition de l’espace de travail",
|
||||
"ui.workspace_manufacture_description": "Tranchage, parcours d’outil et configuration",
|
||||
"ui.workspace_planned": "Prévu",
|
||||
"ui.workspace_render_description": "Aperçu du produit, paramètres de scène et images rendues",
|
||||
|
||||
+45
-2
@@ -167,6 +167,7 @@
|
||||
"HISTORY": "HISTORIAL",
|
||||
"Height": "Altura",
|
||||
"Hidden edges": "Arestas ocultas",
|
||||
"History layout": "Disposición do historial",
|
||||
"Holes": "Furados",
|
||||
"Holes (edges on them)": "Furados (arestas sobre eles)",
|
||||
"Home view": "Vista de inicio",
|
||||
@@ -232,7 +233,7 @@
|
||||
"No crash reports.": "Non hai informes de fallos.",
|
||||
"No matching appearance presets.": "Non hai preaxustes de aparencia coincidentes.",
|
||||
"No matching appearances": "Non hai aparencias coincidentes",
|
||||
"No visible bodies to export.": "Non hai corpos visibles para exportar.",
|
||||
"No visible bodies or sketches to export.": "Non hai corpos nin bosquexos visibles para exportar.",
|
||||
"None": "Ningún",
|
||||
"Not available yet": "Aínda non dispoñible",
|
||||
"Notch": "Entalle",
|
||||
@@ -831,7 +832,7 @@
|
||||
"dxf.progress.projection": "Proxectando a xeometría (3/5)",
|
||||
"dxf.progress.write": "Escribindo o DXF (5/5)",
|
||||
"dxf.tolerance": "Tolerancia da curva",
|
||||
"dxf.tolerance_help": "Só os corpos visibles. As curvas usan segmentos rectos. Unha tolerancia menor dá curvas máis finas e usa máis tempo e memoria.",
|
||||
"dxf.tolerance_help": "Só os corpos visibles e os bosquexos amosados. As curvas usan segmentos rectos. Unha tolerancia menor dá curvas máis finas e usa máis tempo e memoria.",
|
||||
"file_load.built": "Construído ata agora: {built} de {total} operacións.",
|
||||
"file_load.converting": "Convertendo o sólido {done} de {total}",
|
||||
"file_load.inserting": "Inserindo a xeometría",
|
||||
@@ -1092,6 +1093,7 @@
|
||||
"ui.agent_chat_add": "Anexar ficheiros",
|
||||
"ui.agent_chat_add_file": "Escoller un ficheiro…",
|
||||
"ui.agent_chat_allow": "Permitir",
|
||||
"ui.agent_chat_allow_once": "Permitir unha vez",
|
||||
"ui.agent_chat_allowed": "Permitido",
|
||||
"ui.agent_chat_answered": "Resposta: {option}",
|
||||
"ui.agent_chat_approval": "Permitir que o axente faga isto?",
|
||||
@@ -1149,6 +1151,9 @@
|
||||
"ui.agent_chat_left": "Esquerda",
|
||||
"ui.agent_chat_limit": "O axente chegou ao límite de {limit} cambios por mensaxe. Envía outra mensaxe para que continúe.",
|
||||
"ui.agent_chat_minimize": "Minimizar nunha franxa",
|
||||
"ui.agent_chat_mode_all": "Todo",
|
||||
"ui.agent_chat_mode_ask": "Preguntar",
|
||||
"ui.agent_chat_mode_edits": "Edicións",
|
||||
"ui.agent_chat_model_tip": "O modelo que usa o axente. A túa escolla lémbrase para este axente.",
|
||||
"ui.agent_chat_new": "Chat novo",
|
||||
"ui.agent_chat_new_confirm": "Comezar un chat novo? Borrarase o actual.",
|
||||
@@ -1161,6 +1166,7 @@
|
||||
"ui.agent_chat_none_found_short": "Non se atopou ningún axente",
|
||||
"ui.agent_chat_not_installed": "Non instalado",
|
||||
"ui.agent_chat_not_signed_in": "sen sesión iniciada",
|
||||
"ui.agent_chat_open_settings": "Abrir preferencias",
|
||||
"ui.agent_chat_own_window": "Xanela propia",
|
||||
"ui.agent_chat_paste_picture": "Pegar unha imaxe",
|
||||
"ui.agent_chat_permission": "O axente pide executar: {title}",
|
||||
@@ -1185,6 +1191,8 @@
|
||||
"ui.agent_chat_send": "Enviar",
|
||||
"ui.agent_chat_send_now": "Enviar agora",
|
||||
"ui.agent_chat_send_now_tip": "Detén o paso actual do axente e envía isto a continuación (Ctrl+Intro)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft comunícase cun axente de programación instalado neste equipo. Escolle un en Preferencias › IA.",
|
||||
"ui.agent_chat_setup_title": "Configurar un asistente",
|
||||
"ui.agent_chat_side": "Lado do chat co axente",
|
||||
"ui.agent_chat_signed_in": "sesión iniciada",
|
||||
"ui.agent_chat_state_auth": "Hai que iniciar sesión",
|
||||
@@ -1201,6 +1209,7 @@
|
||||
"ui.agent_chat_undo_label": "Chat co axente: {message}",
|
||||
"ui.agent_chat_undone": "(desfeito)",
|
||||
"ui.agent_chat_view_attached": "Vista achegada",
|
||||
"ui.agent_chat_waiting": "Agardando a túa resposta",
|
||||
"ui.agent_cursor": "Cursor do axente",
|
||||
"ui.agent_cursor_speed": "Velocidade do cursor do axente",
|
||||
"ui.ai_add": "Engadir",
|
||||
@@ -1528,6 +1537,11 @@
|
||||
"ui.hideOrigin": "Ocultar a orixe",
|
||||
"ui.hidePlanes": "Ocultar os planos",
|
||||
"ui.hideProfile": "Ocultar o perfil",
|
||||
"ui.history_back": "Retroceder un paso",
|
||||
"ui.history_below_browser": "Debaixo do explorador",
|
||||
"ui.history_first": "Ir ao comezo",
|
||||
"ui.history_forward": "Avanzar un paso",
|
||||
"ui.history_last": "Ir ao final",
|
||||
"ui.history_title": "Historial",
|
||||
"ui.hole": "Furado",
|
||||
"ui.hole_tap_type": "Tipo de roscado do furado",
|
||||
@@ -1545,6 +1559,8 @@
|
||||
"ui.joints": "Unións",
|
||||
"ui.keyboard_shortcuts": "Atallos de teclado",
|
||||
"ui.language": "Idioma",
|
||||
"ui.layout_compact": "Compacta",
|
||||
"ui.layout_floating": "Flotante",
|
||||
"ui.length_decimals": "Decimais das lonxitudes",
|
||||
"ui.light": "Claro",
|
||||
"ui.line": "Liña",
|
||||
@@ -1682,6 +1698,7 @@
|
||||
"ui.reverse_zoom_direction": "Inverter a dirección do zoom",
|
||||
"ui.revolve": "Revolución",
|
||||
"ui.ribbon_exit": "Saír",
|
||||
"ui.ribbon_labels": "Mostrar texto baixo as iconas da cinta",
|
||||
"ui.ribbon_pin_footer": "As ordes fixadas móstranse na cinta. Pasa o cursor sobre unha fila para fixala ou soltala.",
|
||||
"ui.ribbon_workspace": "Espazo de traballo",
|
||||
"ui.right": "Dereita",
|
||||
@@ -1737,6 +1754,7 @@
|
||||
"ui.smooth_wheel_zoom": "Zoom suave coa roda",
|
||||
"ui.smooth_wheel_zoom_tip": "Desactívao para aplicar cada movemento da roda de inmediato, sen suavizado",
|
||||
"ui.snap": "Axuste",
|
||||
"ui.space_mouse_axes_reassigned": "Os eixes están reasignados en Preferencias, e iso segue aplicándose sobre esta calibración.",
|
||||
"ui.space_mouse_calibrate": "Calibrar…",
|
||||
"ui.space_mouse_calibrate_tip": "Move o capuchón como pide a xanela para que SolidCraft aprenda cara a onde e ata onde vai cada eixe deste rato 3D. Gárdase para este rato 3D.",
|
||||
"ui.space_mouse_calibrate_title": "Calibrar o rato 3D",
|
||||
@@ -1746,12 +1764,26 @@
|
||||
"ui.space_mouse_calibration_no_response": "Un eixe case non se moveu, así que non se pode calibrar. Volve comezar e move o capuchón máis lonxe cada vez.",
|
||||
"ui.space_mouse_dead_zone": "Zona morta",
|
||||
"ui.space_mouse_dead_zone_tip": "Canto ten que moverse o capuchón antes de que se mova a vista, como parte do seu percorrido completo. Auméntaa se a vista se despraza co capuchón en repouso.",
|
||||
"ui.space_mouse_driven_by": "Que movemento calibrado do dispositivo controla este eixe. Se escolles un que xa usa outro eixe, intercámbianse.",
|
||||
"ui.space_mouse_driven_by_label": "controlado por",
|
||||
"ui.space_mouse_driver": "Controlador do rato 3D",
|
||||
"ui.space_mouse_driver_auto": "Automático",
|
||||
"ui.space_mouse_driver_direct": "Directo (hidraw)",
|
||||
"ui.space_mouse_driver_only_direct": "Lectura directa do dispositivo; spacenavd non está en execución",
|
||||
"ui.space_mouse_driver_only_spacenavd": "Lectura mediante spacenavd, a única opción dispoñible",
|
||||
"ui.space_mouse_forget": "Esquecer a calibración",
|
||||
"ui.space_mouse_hint": "Move e rota a vista á vez; os bosquexos só usan desprazamento e zoom.",
|
||||
"ui.space_mouse_motion_rotation_x": "Rotación X",
|
||||
"ui.space_mouse_motion_rotation_y": "Rotación Y",
|
||||
"ui.space_mouse_motion_rotation_z": "Rotación Z",
|
||||
"ui.space_mouse_motion_translation_x": "Translación X",
|
||||
"ui.space_mouse_motion_translation_y": "Translación Y",
|
||||
"ui.space_mouse_motion_translation_z": "Translación Z",
|
||||
"ui.space_mouse_navigate": "Navegar con SpaceNavigator / SpaceMouse",
|
||||
"ui.space_mouse_none": "Non se atopou ningún rato 3D",
|
||||
"ui.space_mouse_not_calibrated": "Sen calibrar",
|
||||
"ui.space_mouse_reading": "Lectura",
|
||||
"ui.space_mouse_reset_axes": "Restablecer asignación de eixes",
|
||||
"ui.space_mouse_step_lift": "Levanta o capuchón, mantéño e sóltao.",
|
||||
"ui.space_mouse_step_press": "Preme o capuchón cara abaixo, mantéño e sóltao.",
|
||||
"ui.space_mouse_step_pull": "Tira do capuchón cara a ti, mantéño e sóltao.",
|
||||
@@ -1765,6 +1797,16 @@
|
||||
"ui.space_mouse_step_tilt_right": "Inclina o capuchón á dereita (bordo dereito abaixo), mantéño e sóltao.",
|
||||
"ui.space_mouse_step_twist_clockwise": "Xira o capuchón no sentido horario, mantéño e sóltao.",
|
||||
"ui.space_mouse_step_twist_counterclockwise": "Xira o capuchón no sentido antihorario, mantéño e sóltao.",
|
||||
"ui.space_mouse_view_axis": "Eixe da vista",
|
||||
"ui.space_mouse_view_off": "Desactivado",
|
||||
"ui.space_mouse_view_off_tip": "O perfil do controlador do rato 3D desactiva este eixe, así que o seu movemento non move a vista. Se escolles aquí o movemento doutro eixe, intercámbianse e ese movemento deixa de mover a vista.",
|
||||
"ui.space_mouse_view_orbit_sideways": "Orbitar á esquerda / dereita",
|
||||
"ui.space_mouse_view_orbit_up_down": "Orbitar arriba / abaixo",
|
||||
"ui.space_mouse_view_pan_sideways": "Desprazar á esquerda / dereita",
|
||||
"ui.space_mouse_view_pan_up_down": "Desprazar arriba / abaixo",
|
||||
"ui.space_mouse_view_roll": "Xirar sobre a vista",
|
||||
"ui.space_mouse_view_unmapped": "Eixe {n}",
|
||||
"ui.space_mouse_view_zoom": "Zoom",
|
||||
"ui.spacing": "Espazado",
|
||||
"ui.spacing_2": "Espazado 2",
|
||||
"ui.sphere": "Esfera",
|
||||
@@ -1836,6 +1878,7 @@
|
||||
"ui.workspace_design_tabs": "Modelo · Superficie · Montaxe · Inserir · Inspeccionar · Xestionar · Vista",
|
||||
"ui.workspace_drawing_description": "Planos 2D con vistas, cotas e cartelas",
|
||||
"ui.workspace_footer": "Todos os espazos de traballo usan o mesmo deseño, historial e selección; só cambian as pestanas da cinta. O selector bloquéase mentres se edita un bosquexo ou unha operación.",
|
||||
"ui.workspace_layout": "Disposición do espazo de traballo",
|
||||
"ui.workspace_manufacture_description": "Laminado, traxectorias de ferramenta e configuración",
|
||||
"ui.workspace_planned": "Previsto",
|
||||
"ui.workspace_render_description": "Vista previa do produto, axustes de escena e imaxes renderizadas",
|
||||
|
||||
+18
-1
@@ -167,6 +167,7 @@
|
||||
"HISTORY": "ELŐZMÉNYEK",
|
||||
"Height": "Magasság",
|
||||
"Hidden edges": "Rejtett élek",
|
||||
"History layout": "Előzmények elrendezése",
|
||||
"Holes": "Furatok",
|
||||
"Holes (edges on them)": "Furatok (rajtuk lévő élek)",
|
||||
"Home view": "Kezdőnézet",
|
||||
@@ -232,7 +233,6 @@
|
||||
"No crash reports.": "Nincsenek összeomlási jelentések.",
|
||||
"No matching appearance presets.": "Nincs egyező megjelenés-előbeállítás.",
|
||||
"No matching appearances": "Nincs egyező megjelenés",
|
||||
"No visible bodies to export.": "Nincs exportálható látható test.",
|
||||
"None": "Nincs",
|
||||
"Not available yet": "Még nem érhető el",
|
||||
"Notch": "Bevágás",
|
||||
@@ -1092,6 +1092,7 @@
|
||||
"ui.agent_chat_add": "Fájlok csatolása",
|
||||
"ui.agent_chat_add_file": "Válasszon fájlt…",
|
||||
"ui.agent_chat_allow": "Engedélyezés",
|
||||
"ui.agent_chat_allow_once": "Egyszer engedélyezi",
|
||||
"ui.agent_chat_allowed": "Engedélyezve",
|
||||
"ui.agent_chat_answered": "Válasz: {option}",
|
||||
"ui.agent_chat_approval": "Engedélyezi az ügynöknek ezt?",
|
||||
@@ -1149,6 +1150,9 @@
|
||||
"ui.agent_chat_left": "Bal",
|
||||
"ui.agent_chat_limit": "Az ügynök elérte az egy üzenetre eső {limit} módosítás korlátját. Küldjön újabb üzenetet a folytatáshoz.",
|
||||
"ui.agent_chat_minimize": "Összecsukás sávvá",
|
||||
"ui.agent_chat_mode_all": "Minden",
|
||||
"ui.agent_chat_mode_ask": "Kérdez",
|
||||
"ui.agent_chat_mode_edits": "Szerkesztés",
|
||||
"ui.agent_chat_model_tip": "Az ügynök által használt modell. A választás megmarad ennél az ügynöknél.",
|
||||
"ui.agent_chat_new": "Új csevegés",
|
||||
"ui.agent_chat_new_confirm": "Új csevegést indít? A jelenlegi törlődik.",
|
||||
@@ -1161,6 +1165,7 @@
|
||||
"ui.agent_chat_none_found_short": "Nincs ügynök",
|
||||
"ui.agent_chat_not_installed": "Nincs telepítve",
|
||||
"ui.agent_chat_not_signed_in": "nincs bejelentkezve",
|
||||
"ui.agent_chat_open_settings": "Beállítások megnyitása",
|
||||
"ui.agent_chat_own_window": "Saját ablak",
|
||||
"ui.agent_chat_paste_picture": "Kép beillesztése",
|
||||
"ui.agent_chat_permission": "Az ügynök futtatást kér: {title}",
|
||||
@@ -1185,6 +1190,8 @@
|
||||
"ui.agent_chat_send": "Küldés",
|
||||
"ui.agent_chat_send_now": "Küldés most",
|
||||
"ui.agent_chat_send_now_tip": "Az ügynök jelenlegi lépésének leállítása és ennek küldése következőként (Ctrl+Enter)",
|
||||
"ui.agent_chat_setup_body": "A SolidCraft a számítógépre telepített programozóügynökkel kommunikál. Válasszon egyet itt: Beállítások › AI.",
|
||||
"ui.agent_chat_setup_title": "Asszisztens beállítása",
|
||||
"ui.agent_chat_side": "Ügynök-csevegés oldala",
|
||||
"ui.agent_chat_signed_in": "bejelentkezve",
|
||||
"ui.agent_chat_state_auth": "Bejelentkezés szükséges",
|
||||
@@ -1201,6 +1208,7 @@
|
||||
"ui.agent_chat_undo_label": "Ügynök-csevegés: {message}",
|
||||
"ui.agent_chat_undone": "(visszavonva)",
|
||||
"ui.agent_chat_view_attached": "Nézet csatolva",
|
||||
"ui.agent_chat_waiting": "Önre vár",
|
||||
"ui.agent_cursor": "Ügynök kurzora",
|
||||
"ui.agent_cursor_speed": "Ügynök kurzorának sebessége",
|
||||
"ui.ai_add": "Hozzáadás",
|
||||
@@ -1528,6 +1536,11 @@
|
||||
"ui.hideOrigin": "Origó elrejtése",
|
||||
"ui.hidePlanes": "Síkok elrejtése",
|
||||
"ui.hideProfile": "Profil elrejtése",
|
||||
"ui.history_back": "Egy lépéssel vissza",
|
||||
"ui.history_below_browser": "Böngésző alatt",
|
||||
"ui.history_first": "Ugrás az elejére",
|
||||
"ui.history_forward": "Egy lépéssel előre",
|
||||
"ui.history_last": "Ugrás a végére",
|
||||
"ui.history_title": "Előzmények",
|
||||
"ui.hole": "Furat",
|
||||
"ui.hole_tap_type": "Furat menettípusa",
|
||||
@@ -1545,6 +1558,8 @@
|
||||
"ui.joints": "Csuklók",
|
||||
"ui.keyboard_shortcuts": "Billentyűparancsok",
|
||||
"ui.language": "Nyelv",
|
||||
"ui.layout_compact": "Kompakt",
|
||||
"ui.layout_floating": "Lebegő",
|
||||
"ui.length_decimals": "Hossz tizedesjegyei",
|
||||
"ui.light": "Világos",
|
||||
"ui.line": "Vonal",
|
||||
@@ -1682,6 +1697,7 @@
|
||||
"ui.reverse_zoom_direction": "Nagyítási irány megfordítása",
|
||||
"ui.revolve": "Forgatás",
|
||||
"ui.ribbon_exit": "Kilépés",
|
||||
"ui.ribbon_labels": "Szöveg megjelenítése a menüszalag ikonjai alatt",
|
||||
"ui.ribbon_pin_footer": "A rögzített parancsok megjelennek a menüszalagon. Egy sor fölé víve rögzíthető vagy feloldható.",
|
||||
"ui.ribbon_workspace": "Munkaterület",
|
||||
"ui.right": "Jobb",
|
||||
@@ -1836,6 +1852,7 @@
|
||||
"ui.workspace_design_tabs": "Modell · Felület · Összeszerelés · Beszúrás · Vizsgálat · Kezelés · Nézet",
|
||||
"ui.workspace_drawing_description": "2D rajzok nézetekkel, méretekkel és címblokkokkal",
|
||||
"ui.workspace_footer": "Minden munkaterület ugyanazon a terven, előzményeken és kijelölésen dolgozik; csak a menüszalag lapjai változnak. A váltó zárolva van, amíg vázlatot vagy elemet szerkeszt.",
|
||||
"ui.workspace_layout": "Munkaterület elrendezése",
|
||||
"ui.workspace_manufacture_description": "Szeletelés, szerszámutak és beállítás",
|
||||
"ui.workspace_planned": "Tervezett",
|
||||
"ui.workspace_render_description": "Terméknézet, jelenetbeállítások és renderelt képek",
|
||||
|
||||
+18
-1
@@ -167,6 +167,7 @@
|
||||
"HISTORY": "RIWAYAT",
|
||||
"Height": "Tinggi",
|
||||
"Hidden edges": "Tepi tersembunyi",
|
||||
"History layout": "Tata letak riwayat",
|
||||
"Holes": "Lubang",
|
||||
"Holes (edges on them)": "Lubang (tepi di atasnya)",
|
||||
"Home view": "Tampilan awal",
|
||||
@@ -232,7 +233,6 @@
|
||||
"No crash reports.": "Tidak ada laporan crash.",
|
||||
"No matching appearance presets.": "Tidak ada preset tampilan yang cocok.",
|
||||
"No matching appearances": "Tidak ada tampilan yang cocok",
|
||||
"No visible bodies to export.": "Tidak ada bodi terlihat untuk diekspor.",
|
||||
"None": "Tidak ada",
|
||||
"Not available yet": "Belum tersedia",
|
||||
"Notch": "Takik",
|
||||
@@ -1092,6 +1092,7 @@
|
||||
"ui.agent_chat_add": "Lampirkan file",
|
||||
"ui.agent_chat_add_file": "Pilih file…",
|
||||
"ui.agent_chat_allow": "Izinkan",
|
||||
"ui.agent_chat_allow_once": "Izinkan sekali",
|
||||
"ui.agent_chat_allowed": "Diizinkan",
|
||||
"ui.agent_chat_answered": "Jawaban: {option}",
|
||||
"ui.agent_chat_approval": "Izinkan agen melakukan ini?",
|
||||
@@ -1149,6 +1150,9 @@
|
||||
"ui.agent_chat_left": "Kiri",
|
||||
"ui.agent_chat_limit": "Agen mencapai batas {limit} perubahan untuk satu pesan. Kirim pesan lain agar agen melanjutkan.",
|
||||
"ui.agent_chat_minimize": "Kecilkan menjadi strip",
|
||||
"ui.agent_chat_mode_all": "Semua",
|
||||
"ui.agent_chat_mode_ask": "Tanya",
|
||||
"ui.agent_chat_mode_edits": "Edit",
|
||||
"ui.agent_chat_model_tip": "Model yang dipakai agen. Pilihan Anda diingat untuk agen ini.",
|
||||
"ui.agent_chat_new": "Chat baru",
|
||||
"ui.agent_chat_new_confirm": "Mulai chat baru? Chat saat ini akan dibersihkan.",
|
||||
@@ -1161,6 +1165,7 @@
|
||||
"ui.agent_chat_none_found_short": "Agen tidak ditemukan",
|
||||
"ui.agent_chat_not_installed": "Belum terpasang",
|
||||
"ui.agent_chat_not_signed_in": "belum masuk",
|
||||
"ui.agent_chat_open_settings": "Buka preferensi",
|
||||
"ui.agent_chat_own_window": "Jendela sendiri",
|
||||
"ui.agent_chat_paste_picture": "Tempel gambar",
|
||||
"ui.agent_chat_permission": "Agen meminta menjalankan: {title}",
|
||||
@@ -1185,6 +1190,8 @@
|
||||
"ui.agent_chat_send": "Kirim",
|
||||
"ui.agent_chat_send_now": "Kirim sekarang",
|
||||
"ui.agent_chat_send_now_tip": "Hentikan langkah agen saat ini dan kirim ini berikutnya (Ctrl+Enter)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft berkomunikasi dengan agen pemrograman yang terpasang di komputer ini. Pilih agen di Preferensi › AI.",
|
||||
"ui.agent_chat_setup_title": "Siapkan asisten",
|
||||
"ui.agent_chat_side": "Sisi chat agen",
|
||||
"ui.agent_chat_signed_in": "sudah masuk",
|
||||
"ui.agent_chat_state_auth": "Perlu masuk",
|
||||
@@ -1201,6 +1208,7 @@
|
||||
"ui.agent_chat_undo_label": "Chat agen: {message}",
|
||||
"ui.agent_chat_undone": "(diurungkan)",
|
||||
"ui.agent_chat_view_attached": "Tampilan dilampirkan",
|
||||
"ui.agent_chat_waiting": "Menunggu Anda",
|
||||
"ui.agent_cursor": "Kursor agen",
|
||||
"ui.agent_cursor_speed": "Kecepatan kursor agen",
|
||||
"ui.ai_add": "Tambah",
|
||||
@@ -1528,6 +1536,11 @@
|
||||
"ui.hideOrigin": "Sembunyikan Origin",
|
||||
"ui.hidePlanes": "Sembunyikan Bidang",
|
||||
"ui.hideProfile": "Sembunyikan Profil",
|
||||
"ui.history_back": "Mundur satu langkah",
|
||||
"ui.history_below_browser": "Di bawah browser",
|
||||
"ui.history_first": "Ke awal",
|
||||
"ui.history_forward": "Maju satu langkah",
|
||||
"ui.history_last": "Ke akhir",
|
||||
"ui.history_title": "Riwayat",
|
||||
"ui.hole": "Lubang",
|
||||
"ui.hole_tap_type": "Tipe Tap Lubang",
|
||||
@@ -1545,6 +1558,8 @@
|
||||
"ui.joints": "Sambungan",
|
||||
"ui.keyboard_shortcuts": "Pintasan Keyboard",
|
||||
"ui.language": "Bahasa",
|
||||
"ui.layout_compact": "Ringkas",
|
||||
"ui.layout_floating": "Mengambang",
|
||||
"ui.length_decimals": "Desimal panjang",
|
||||
"ui.light": "Terang",
|
||||
"ui.line": "Garis",
|
||||
@@ -1682,6 +1697,7 @@
|
||||
"ui.reverse_zoom_direction": "Balik arah zoom",
|
||||
"ui.revolve": "Revolve",
|
||||
"ui.ribbon_exit": "Keluar",
|
||||
"ui.ribbon_labels": "Tampilkan teks di bawah ikon pita",
|
||||
"ui.ribbon_pin_footer": "Perintah yang disematkan tampil di ribbon. Arahkan kursor ke baris untuk menyematkan atau melepas sematan.",
|
||||
"ui.ribbon_workspace": "Workspace",
|
||||
"ui.right": "Kanan",
|
||||
@@ -1836,6 +1852,7 @@
|
||||
"ui.workspace_design_tabs": "Model · Permukaan · Rakit · Sisipkan · Inspeksi · Kelola · Tampilan",
|
||||
"ui.workspace_drawing_description": "Gambar kerja 2D dengan tampilan, dimensi, dan title block",
|
||||
"ui.workspace_footer": "Setiap workspace bekerja pada desain, riwayat, dan pilihan yang sama; hanya tab ribbon yang berubah. Pengalih terkunci saat sketsa atau fitur sedang diedit.",
|
||||
"ui.workspace_layout": "Tata letak ruang kerja",
|
||||
"ui.workspace_manufacture_description": "Slicing, toolpath, dan setup",
|
||||
"ui.workspace_planned": "Direncanakan",
|
||||
"ui.workspace_render_description": "Pratinjau produk, pengaturan scene, dan gambar render",
|
||||
|
||||
+48
-2
@@ -102,6 +102,7 @@
|
||||
"Curv. Comb": "Pettine curv.",
|
||||
"Curvature (G2)": "Curvatura (G2)",
|
||||
"Curvature change (G3)": "Variazione di curvatura (G3)",
|
||||
"Custom": "Personalizzato",
|
||||
"Customize Toolbar": "Personalizza barra degli strumenti",
|
||||
"Delete": "Elimina",
|
||||
"Delete all": "Elimina tutto",
|
||||
@@ -167,6 +168,7 @@
|
||||
"HISTORY": "CRONOLOGIA",
|
||||
"Height": "Altezza",
|
||||
"Hidden edges": "Spigoli nascosti",
|
||||
"History layout": "Disposizione cronologia",
|
||||
"Holes": "Fori",
|
||||
"Holes (edges on them)": "Fori (spigoli dei fori)",
|
||||
"Home view": "Vista iniziale",
|
||||
@@ -232,7 +234,8 @@
|
||||
"No crash reports.": "Nessun rapporto di arresto anomalo.",
|
||||
"No matching appearance presets.": "Nessun predefinito di aspetto corrispondente.",
|
||||
"No matching appearances": "Nessun aspetto corrispondente",
|
||||
"No visible bodies to export.": "Nessun corpo visibile da esportare.",
|
||||
"No visible bodies or sketches to export.": "Nessun corpo o schizzo visibile da esportare.",
|
||||
"Nominal diameter": "Diametro nominale",
|
||||
"None": "Nessuno",
|
||||
"Not available yet": "Non ancora disponibile",
|
||||
"Notch": "Intaglio",
|
||||
@@ -256,6 +259,7 @@
|
||||
"Pick the tool bodies": "Seleziona i corpi utensile",
|
||||
"Picked": "Selezionato",
|
||||
"Picked point": "Punto selezionato",
|
||||
"Pitch": "Passo",
|
||||
"Place at the origin instead": "Posiziona invece nell'origine",
|
||||
"Plastic Rules": "Regole per plastica",
|
||||
"Playback": "Riproduzione",
|
||||
@@ -831,7 +835,7 @@
|
||||
"dxf.progress.projection": "Proiezione della geometria (3/5)",
|
||||
"dxf.progress.write": "Scrittura DXF (5/5)",
|
||||
"dxf.tolerance": "Tolleranza delle curve",
|
||||
"dxf.tolerance_help": "Solo corpi visibili. Le curve usano segmenti rettilinei. Una tolleranza minore produce curve più precise e richiede più tempo e memoria.",
|
||||
"dxf.tolerance_help": "Solo corpi visibili e schizzi mostrati. Le curve usano segmenti rettilinei. Una tolleranza minore produce curve più precise e richiede più tempo e memoria.",
|
||||
"file_load.built": "Costruite finora: {built} di {total} feature.",
|
||||
"file_load.converting": "Conversione del solido {done} di {total}",
|
||||
"file_load.inserting": "Inserimento della geometria",
|
||||
@@ -1092,6 +1096,7 @@
|
||||
"ui.agent_chat_add": "Allega file",
|
||||
"ui.agent_chat_add_file": "Scegli un file…",
|
||||
"ui.agent_chat_allow": "Consenti",
|
||||
"ui.agent_chat_allow_once": "Consenti una volta",
|
||||
"ui.agent_chat_allowed": "Consentito",
|
||||
"ui.agent_chat_answered": "Risposta: {option}",
|
||||
"ui.agent_chat_approval": "Consentire questa azione all'agente?",
|
||||
@@ -1149,6 +1154,9 @@
|
||||
"ui.agent_chat_left": "Sinistra",
|
||||
"ui.agent_chat_limit": "L'agente ha raggiunto il limite di {limit} modifiche per messaggio. Invia un altro messaggio per farlo proseguire.",
|
||||
"ui.agent_chat_minimize": "Riduci a una striscia",
|
||||
"ui.agent_chat_mode_all": "Tutto",
|
||||
"ui.agent_chat_mode_ask": "Chiedi",
|
||||
"ui.agent_chat_mode_edits": "Modifiche",
|
||||
"ui.agent_chat_model_tip": "Il modello usato dall'agente. La scelta viene memorizzata per questo agente.",
|
||||
"ui.agent_chat_new": "Nuova chat",
|
||||
"ui.agent_chat_new_confirm": "Avviare una nuova chat? Quella corrente verrà cancellata.",
|
||||
@@ -1161,6 +1169,7 @@
|
||||
"ui.agent_chat_none_found_short": "Nessun agente trovato",
|
||||
"ui.agent_chat_not_installed": "Non installato",
|
||||
"ui.agent_chat_not_signed_in": "accesso non effettuato",
|
||||
"ui.agent_chat_open_settings": "Apri preferenze",
|
||||
"ui.agent_chat_own_window": "Finestra separata",
|
||||
"ui.agent_chat_paste_picture": "Incolla un'immagine",
|
||||
"ui.agent_chat_permission": "L'agente chiede di eseguire: {title}",
|
||||
@@ -1185,6 +1194,8 @@
|
||||
"ui.agent_chat_send": "Invia",
|
||||
"ui.agent_chat_send_now": "Invia subito",
|
||||
"ui.agent_chat_send_now_tip": "Interrompi il passo corrente dell'agente e invia questo messaggio come successivo (Ctrl+Enter)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft comunica con un agente di programmazione installato su questo computer. Scegline uno in Preferenze › IA.",
|
||||
"ui.agent_chat_setup_title": "Configura un assistente",
|
||||
"ui.agent_chat_side": "Lato della chat con l'agente",
|
||||
"ui.agent_chat_signed_in": "accesso effettuato",
|
||||
"ui.agent_chat_state_auth": "Accesso richiesto",
|
||||
@@ -1201,6 +1212,7 @@
|
||||
"ui.agent_chat_undo_label": "Chat con l'agente: {message}",
|
||||
"ui.agent_chat_undone": "(annullato)",
|
||||
"ui.agent_chat_view_attached": "Vista allegata",
|
||||
"ui.agent_chat_waiting": "In attesa della tua risposta",
|
||||
"ui.agent_cursor": "Cursore dell'agente",
|
||||
"ui.agent_cursor_speed": "Velocità del cursore dell'agente",
|
||||
"ui.ai_add": "Aggiungi",
|
||||
@@ -1528,6 +1540,11 @@
|
||||
"ui.hideOrigin": "Nascondi origine",
|
||||
"ui.hidePlanes": "Nascondi piani",
|
||||
"ui.hideProfile": "Nascondi profilo",
|
||||
"ui.history_back": "Indietro di un passaggio",
|
||||
"ui.history_below_browser": "Sotto il browser",
|
||||
"ui.history_first": "Vai all’inizio",
|
||||
"ui.history_forward": "Avanti di un passaggio",
|
||||
"ui.history_last": "Vai alla fine",
|
||||
"ui.history_title": "Cronologia",
|
||||
"ui.hole": "Foro",
|
||||
"ui.hole_tap_type": "Tipo di maschiatura del foro",
|
||||
@@ -1545,6 +1562,8 @@
|
||||
"ui.joints": "Giunti",
|
||||
"ui.keyboard_shortcuts": "Scorciatoie da tastiera",
|
||||
"ui.language": "Lingua",
|
||||
"ui.layout_compact": "Compatta",
|
||||
"ui.layout_floating": "Flottante",
|
||||
"ui.length_decimals": "Decimali delle lunghezze",
|
||||
"ui.light": "Chiaro",
|
||||
"ui.line": "Linea",
|
||||
@@ -1682,6 +1701,7 @@
|
||||
"ui.reverse_zoom_direction": "Inverti direzione dello zoom",
|
||||
"ui.revolve": "Rivoluzione",
|
||||
"ui.ribbon_exit": "Esci",
|
||||
"ui.ribbon_labels": "Mostra il testo sotto le icone della barra multifunzione",
|
||||
"ui.ribbon_pin_footer": "I comandi fissati compaiono sulla barra multifunzione. Passa sopra una riga per fissarla o rimuoverla.",
|
||||
"ui.ribbon_workspace": "Area di lavoro",
|
||||
"ui.right": "Destra",
|
||||
@@ -1737,6 +1757,7 @@
|
||||
"ui.smooth_wheel_zoom": "Zoom fluido con rotellina",
|
||||
"ui.smooth_wheel_zoom_tip": "Disattiva per applicare immediatamente ogni evento della rotellina, senza coda di interpolazione",
|
||||
"ui.snap": "Aggancio",
|
||||
"ui.space_mouse_axes_reassigned": "Gli assi sono riassegnati nelle Preferenze, e questo vale anche dopo questa calibrazione.",
|
||||
"ui.space_mouse_calibrate": "Calibra…",
|
||||
"ui.space_mouse_calibrate_tip": "Muovi la manopola come chiede la finestra, così SolidCraft impara in che direzione e quanto va ogni asse di questo mouse 3D. Viene salvata per questo mouse 3D.",
|
||||
"ui.space_mouse_calibrate_title": "Calibra mouse 3D",
|
||||
@@ -1746,12 +1767,26 @@
|
||||
"ui.space_mouse_calibration_no_response": "Un asse si è mosso appena, quindi non si può calibrare. Ricomincia e muovi la manopola più a fondo ogni volta.",
|
||||
"ui.space_mouse_dead_zone": "Zona morta",
|
||||
"ui.space_mouse_dead_zone_tip": "Quanto deve muoversi la manopola prima che si muova la vista, come quota della sua corsa completa. Aumentala se la vista si sposta con la manopola a riposo.",
|
||||
"ui.space_mouse_driven_by": "Quale movimento calibrato del dispositivo comanda questo asse. Se ne scegli uno già usato da un altro asse, i due si scambiano.",
|
||||
"ui.space_mouse_driven_by_label": "comandato da",
|
||||
"ui.space_mouse_driver": "Driver del mouse 3D",
|
||||
"ui.space_mouse_driver_auto": "Automatico",
|
||||
"ui.space_mouse_driver_direct": "Diretto (hidraw)",
|
||||
"ui.space_mouse_driver_only_direct": "Lettura diretta del dispositivo; spacenavd non è in esecuzione",
|
||||
"ui.space_mouse_driver_only_spacenavd": "Lettura tramite spacenavd, l'unica possibilità disponibile",
|
||||
"ui.space_mouse_forget": "Dimentica calibrazione",
|
||||
"ui.space_mouse_hint": "Sposta e ruota la vista insieme; gli schizzi usano solo panoramica e zoom.",
|
||||
"ui.space_mouse_motion_rotation_x": "Rotazione X",
|
||||
"ui.space_mouse_motion_rotation_y": "Rotazione Y",
|
||||
"ui.space_mouse_motion_rotation_z": "Rotazione Z",
|
||||
"ui.space_mouse_motion_translation_x": "Traslazione X",
|
||||
"ui.space_mouse_motion_translation_y": "Traslazione Y",
|
||||
"ui.space_mouse_motion_translation_z": "Traslazione Z",
|
||||
"ui.space_mouse_navigate": "Naviga con SpaceNavigator / SpaceMouse",
|
||||
"ui.space_mouse_none": "Nessun mouse 3D trovato",
|
||||
"ui.space_mouse_not_calibrated": "Non calibrato",
|
||||
"ui.space_mouse_reading": "Lettura",
|
||||
"ui.space_mouse_reset_axes": "Ripristina assegnazione assi",
|
||||
"ui.space_mouse_step_lift": "Solleva la manopola, tienila, poi lasciala.",
|
||||
"ui.space_mouse_step_press": "Premi la manopola verso il basso, tienila, poi lasciala.",
|
||||
"ui.space_mouse_step_pull": "Tira la manopola verso di te, tienila, poi lasciala.",
|
||||
@@ -1765,6 +1800,16 @@
|
||||
"ui.space_mouse_step_tilt_right": "Inclina la manopola a destra (bordo destro in basso), tienila, poi lasciala.",
|
||||
"ui.space_mouse_step_twist_clockwise": "Ruota la manopola in senso orario, tienila, poi lasciala.",
|
||||
"ui.space_mouse_step_twist_counterclockwise": "Ruota la manopola in senso antiorario, tienila, poi lasciala.",
|
||||
"ui.space_mouse_view_axis": "Asse della vista",
|
||||
"ui.space_mouse_view_off": "Disattivato",
|
||||
"ui.space_mouse_view_off_tip": "Il profilo del driver del mouse 3D disattiva questo asse, quindi il suo movimento non muove la vista. Se qui scegli il movimento di un altro asse, i due si scambiano e quel movimento smette di muovere la vista.",
|
||||
"ui.space_mouse_view_orbit_sideways": "Orbita sinistra / destra",
|
||||
"ui.space_mouse_view_orbit_up_down": "Orbita su / giù",
|
||||
"ui.space_mouse_view_pan_sideways": "Panoramica sinistra / destra",
|
||||
"ui.space_mouse_view_pan_up_down": "Panoramica su / giù",
|
||||
"ui.space_mouse_view_roll": "Rollio",
|
||||
"ui.space_mouse_view_unmapped": "Asse {n}",
|
||||
"ui.space_mouse_view_zoom": "Zoom",
|
||||
"ui.spacing": "Spaziatura",
|
||||
"ui.spacing_2": "Spaziatura 2",
|
||||
"ui.sphere": "Sfera",
|
||||
@@ -1836,6 +1881,7 @@
|
||||
"ui.workspace_design_tabs": "Modello · Superficie · Assembla · Inserisci · Ispeziona · Gestisci · Vista",
|
||||
"ui.workspace_drawing_description": "Disegni 2D con viste, quote e cartigli",
|
||||
"ui.workspace_footer": "Ogni area di lavoro opera sullo stesso progetto, cronologia e selezione; cambiano solo le schede della barra multifunzione. Il selettore è bloccato durante la modifica di uno schizzo o di una feature.",
|
||||
"ui.workspace_layout": "Disposizione dello spazio di lavoro",
|
||||
"ui.workspace_manufacture_description": "Slicing, percorsi utensile e impostazione",
|
||||
"ui.workspace_planned": "Pianificata",
|
||||
"ui.workspace_render_description": "Anteprima del prodotto, impostazioni della scena e immagini renderizzate",
|
||||
|
||||
+89
-3
@@ -28,6 +28,7 @@
|
||||
"Also deletes": "同時に削除される要素:",
|
||||
"Angle 2": "角度 2",
|
||||
"Animate": "アニメーション",
|
||||
"App/system information and command settings are included. Review private details before sharing.": "アプリ・システム情報とコマンド設定が含まれます。共有前に非公開の情報を確認してください。",
|
||||
"Appearance name": "外観名",
|
||||
"Applied Radius": "適用半径",
|
||||
"Apply To": "適用先",
|
||||
@@ -83,6 +84,7 @@
|
||||
"Contact": "接触",
|
||||
"Contact Sets": "接触セット",
|
||||
"Contact sets": "接触セット",
|
||||
"Continue on GitHub": "GitHubで続ける",
|
||||
"Continuity": "連続性",
|
||||
"Copy Error Message": "エラーメッセージをコピー",
|
||||
"Copy Warning Message": "警告メッセージをコピー",
|
||||
@@ -102,6 +104,7 @@
|
||||
"Curv. Comb": "曲率コーム",
|
||||
"Curvature (G2)": "曲率 (G2)",
|
||||
"Curvature change (G3)": "曲率変化 (G3)",
|
||||
"Custom": "カスタム",
|
||||
"Customize Toolbar": "ツールバーのカスタマイズ",
|
||||
"Delete": "削除",
|
||||
"Delete all": "すべて削除",
|
||||
@@ -110,6 +113,8 @@
|
||||
"Delete the set": "セットを削除",
|
||||
"Density": "密度",
|
||||
"Dependents": "依存先",
|
||||
"Describe the problem you saw": "発生した問題を説明してください",
|
||||
"Describe what happened before continuing.": "続ける前に、何が起きたか記入してください。",
|
||||
"Designs you open or save appear here.": "開いたデザインや保存したデザインがここに表示されます。",
|
||||
"Determined by To Object. Choose Angle to edit the angular extent.": "「対象オブジェクトまで」により決定されます。角度範囲を編集するには「角度」を選択してください。",
|
||||
"Direction": "方向",
|
||||
@@ -127,6 +132,7 @@
|
||||
"Enabled": "有効",
|
||||
"End bulge": "終了側の膨らみ",
|
||||
"End continuity": "終了側の連続性",
|
||||
"Expected result (optional)": "期待した結果(任意)",
|
||||
"Explode": "分解",
|
||||
"Explode Text": "テキストを分解",
|
||||
"Export Animation…": "アニメーションをエクスポート…",
|
||||
@@ -167,6 +173,8 @@
|
||||
"HISTORY": "履歴",
|
||||
"Height": "高さ",
|
||||
"Hidden edges": "隠線",
|
||||
"History layout": "履歴のレイアウト",
|
||||
"History names": "履歴名",
|
||||
"Holes": "穴",
|
||||
"Holes (edges on them)": "穴(そのエッジ)",
|
||||
"Home view": "ホームビュー",
|
||||
@@ -232,7 +240,8 @@
|
||||
"No crash reports.": "クラッシュレポートはありません。",
|
||||
"No matching appearance presets.": "一致する外観プリセットはありません。",
|
||||
"No matching appearances": "一致する外観がありません",
|
||||
"No visible bodies to export.": "出力する表示中のボディがありません。",
|
||||
"No visible bodies or sketches to export.": "出力する表示中のボディまたはスケッチがありません。",
|
||||
"Nominal diameter": "呼び径",
|
||||
"None": "なし",
|
||||
"Not available yet": "まだ利用できません",
|
||||
"Notch": "ノッチ",
|
||||
@@ -241,7 +250,10 @@
|
||||
"Open GitHub draft": "GitHubの下書きを開く",
|
||||
"Open local libraries in the desktop app. You can export this design's library here.": "ローカルライブラリはデスクトップアプリで開いてください。このデザインのライブラリはここで書き出せます。",
|
||||
"Open-source parametric 3D CAD in Rust. Licensed MIT OR Apache-2.0.": "Rust製オープンソース・パラメトリック3D CAD。ライセンス: MIT または Apache-2.0。",
|
||||
"Opens your browser and copies the report text for you to paste into GitHub.": "ブラウザーを開き、GitHubに貼り付ける報告本文をコピーします。",
|
||||
"Opens your browser. Review and submit your report on GitHub.": "ブラウザーを開きます。GitHubで内容を確認して投稿してください。",
|
||||
"Operator": "演算子",
|
||||
"Orbit (right drag)": "回転(右ドラッグ)",
|
||||
"Other side": "反対側",
|
||||
"Other surface (mutual trim)": "相手のサーフェス(相互トリム)",
|
||||
"Outside": "外側",
|
||||
@@ -256,6 +268,7 @@
|
||||
"Pick the tool bodies": "ツールボディを選択",
|
||||
"Picked": "選択したもの",
|
||||
"Picked point": "選択した点",
|
||||
"Pitch": "ピッチ",
|
||||
"Place at the origin instead": "代わりに原点に配置",
|
||||
"Plastic Rules": "樹脂ルール",
|
||||
"Playback": "再生",
|
||||
@@ -289,6 +302,8 @@
|
||||
"Remove the track": "トラックを削除",
|
||||
"Remove this point": "この点を削除",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "バグを報告 — スクリーンショットを確認してGitHub下書きを作成",
|
||||
"Report text copied. Paste it into GitHub and submit your report there.": "報告本文をコピーしました。GitHubに貼り付けて投稿してください。",
|
||||
"Reports are public. A GitHub account is required.": "報告は公開されます。GitHubアカウントが必要です。",
|
||||
"Reset Toolbar": "ツールバーをリセット",
|
||||
"Resize Part": "部品サイズ変更",
|
||||
"Restore View": "ビューを復元",
|
||||
@@ -303,6 +318,7 @@
|
||||
"Revert Pos.": "位置を復元",
|
||||
"Revert Position": "位置を元に戻す",
|
||||
"Review the report and screenshot before opening GitHub.": "GitHubを開く前にレポートとスクリーンショットを確認してください。",
|
||||
"Review your report on GitHub and click Submit new issue to publish it.": "GitHubで内容を確認し、Submit new issueをクリックすると公開されます。",
|
||||
"Rim Edge": "リムエッジ",
|
||||
"Roll History Marker Here": "履歴マーカーをここに移動",
|
||||
"Round": "丸め",
|
||||
@@ -313,10 +329,12 @@
|
||||
"SURFACE QUALITY": "サーフェス品質",
|
||||
"Save Rule": "ルールを保存",
|
||||
"Save View": "ビューを保存",
|
||||
"Save screenshot…": "スクリーンショットを保存…",
|
||||
"Save the study as an animated GIF, seen as the viewport shows it": "ビューポートの表示どおりにスタディをアニメーション GIF として保存",
|
||||
"Saved preview. Geometry is being checked before editing.": "保存済みのプレビューです。編集前にジオメトリを検証しています。",
|
||||
"Scale factor": "尺度係数",
|
||||
"Scene": "シーン",
|
||||
"Screenshot (optional)": "スクリーンショット(任意)",
|
||||
"Search": "検索",
|
||||
"Search commands": "コマンドを検索",
|
||||
"Search this library": "このライブラリを検索",
|
||||
@@ -351,17 +369,21 @@
|
||||
"Start continuity": "開始側の連続性",
|
||||
"Step": "ステップ",
|
||||
"Steps": "ステップ数",
|
||||
"Steps to reproduce (optional)": "再現手順(任意)",
|
||||
"Stitch the kept pieces": "残した部分をステッチ",
|
||||
"Stitch to both bodies": "両方のボディにステッチ",
|
||||
"Study": "スタディ",
|
||||
"Style": "スタイル",
|
||||
"Summary": "概要",
|
||||
"Suppressed": "抑制",
|
||||
"Surface": "サーフェス",
|
||||
"Swap Ends": "端点を入れ替え",
|
||||
"TRIM": "トリム",
|
||||
"Tangent (G1)": "接線 (G1)",
|
||||
"Tangent Chain": "正接チェーン",
|
||||
"Tap drill diameter": "タップ下穴径",
|
||||
"Tear": "引き裂き",
|
||||
"Technical details": "技術情報",
|
||||
"The dashed sketch is a saved plane guide. Select a new face to repair the attachment.": "破線のスケッチは保存された平面ガイドです。新しい面を選択してアタッチメントを修復してください。",
|
||||
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "保存された作成元のオカレンスが見つかりません。再バインドする前に有効な作成元配置を選択してください。",
|
||||
"Then": "次を実行 (Then)",
|
||||
@@ -373,6 +395,7 @@
|
||||
"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 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で添付してください。",
|
||||
"To Offset": "オフセットまで",
|
||||
"To edges": "終了側エッジ",
|
||||
"Tolerance": "公差",
|
||||
@@ -398,6 +421,9 @@
|
||||
"Wall": "肉厚",
|
||||
"Wall Location": "壁の位置",
|
||||
"Wall Thickness": "壁の厚さ",
|
||||
"What did you do just before the problem? A short list is enough": "問題の直前に行った操作を簡単に記入してください",
|
||||
"What did you expect to see?": "どのような結果を期待していましたか?",
|
||||
"What happened?": "何が起きましたか?",
|
||||
"When": "条件 (When)",
|
||||
"Width": "幅",
|
||||
"Will fail": "失敗します",
|
||||
@@ -758,6 +784,7 @@
|
||||
"cmd.view_graphics_preset": "グラフィックスプリセット",
|
||||
"cmd.view_list": "名前付きビューを一覧表示",
|
||||
"cmd.view_navigate": "ビューナビゲーション",
|
||||
"cmd.view_normal_to": "選択アイテムに垂直",
|
||||
"cmd.view_rename": "名前付きビューの名前を変更",
|
||||
"cmd.view_save": "新規名前付きビュー",
|
||||
"color.copy": "クリックして色の値をコピー",
|
||||
@@ -831,7 +858,7 @@
|
||||
"dxf.progress.projection": "形状を投影中 (3/5)",
|
||||
"dxf.progress.write": "DXFを書き込み中 (5/5)",
|
||||
"dxf.tolerance": "曲線の許容誤差",
|
||||
"dxf.tolerance_help": "表示中のボディを出力し、曲線は線分で近似します。許容誤差を小さくすると曲線が滑らかになりますが、処理時間とメモリ使用量が増えます。",
|
||||
"dxf.tolerance_help": "表示中のボディとスケッチを出力し、曲線は線分で近似します。許容誤差を小さくすると曲線が滑らかになりますが、処理時間とメモリ使用量が増えます。",
|
||||
"file_load.built": "構築済み: 全{total}フィーチャー中{built}件。",
|
||||
"file_load.converting": "ソリッドを変換中: {done}/{total}",
|
||||
"file_load.inserting": "ジオメトリを挿入中",
|
||||
@@ -855,6 +882,12 @@
|
||||
"help.licences": "ライセンス",
|
||||
"help.mit": "MITライセンス",
|
||||
"help.notice": "NOTICE",
|
||||
"help.solid.chamfer": "選択した辺を、指定した距離で平らに斜めに削ります。鋭い角を落とすときや、ピンやねじを入れやすくするときに使います。",
|
||||
"help.solid.extrude": "スケッチの輪郭を指定した距離だけ立体方向に押し出し、材料を足したり削ったりします。平面の図形をブロック、突起、ポケットにするときに使います。",
|
||||
"help.solid.fillet": "選択した辺を、指定した半径のなめらかな円弧で丸めます。強度や持ちやすさ、見た目のために鋭い角を和らげるときに使います。",
|
||||
"help.solid.hole": "面上の指定した位置に、指定した直径の丸い穴をあけます。ねじのクリアランスやピン穴など、単純な丸い開口に使います。",
|
||||
"help.solid.revolve": "スケッチの輪郭を軸のまわりに回転させ、丸い立体を作ります。軸、つまみ、ボウルなど、中心線まわりに丸い部品に使います。",
|
||||
"help.solid.shell": "立体の中をくり抜き、指定した厚さの壁を残します。選んだ面を開口にすることもできます。箱、カバーなどの薄肉の部品に使います。",
|
||||
"help.third_party": "サードパーティライセンス",
|
||||
"home.documentation": "ドキュメント",
|
||||
"home.in_this_browser": "このブラウザ内",
|
||||
@@ -926,6 +959,8 @@
|
||||
"motion.to_end": "最後へ",
|
||||
"motion.to_start": "最初へ",
|
||||
"motion.track_name": "トラック {number}",
|
||||
"nav.free_orbit.help": "マウスと3Dマウスで、極点を越えて回転し、ロールを保ちます。\nShiftによる軸スナップは無効になります。",
|
||||
"nav.free_orbit.label": "自由回転",
|
||||
"nav.touchpad.help": "Shift を押しながらポインターを動かすとパン、Alt/Option を押しながら動かすと回転します。マウスボタンは不要です。縦スクロールでズームします。",
|
||||
"nav.touchpad.label": "タッチパッド",
|
||||
"nav.touchpad.orbit": "回転(Alt/Option を押しながら移動、マウスボタン不要)",
|
||||
@@ -1092,6 +1127,7 @@
|
||||
"ui.agent_chat_add": "ファイルを添付",
|
||||
"ui.agent_chat_add_file": "ファイルを選択…",
|
||||
"ui.agent_chat_allow": "許可",
|
||||
"ui.agent_chat_allow_once": "今回のみ許可",
|
||||
"ui.agent_chat_allowed": "許可しました",
|
||||
"ui.agent_chat_answered": "回答: {option}",
|
||||
"ui.agent_chat_approval": "エージェントにこの操作を許可しますか?",
|
||||
@@ -1149,6 +1185,9 @@
|
||||
"ui.agent_chat_left": "左",
|
||||
"ui.agent_chat_limit": "エージェントが1メッセージあたりの変更上限 {limit} に達しました。続けさせるには、もう一度メッセージを送ってください。",
|
||||
"ui.agent_chat_minimize": "細いバーに最小化",
|
||||
"ui.agent_chat_mode_all": "すべて",
|
||||
"ui.agent_chat_mode_ask": "確認",
|
||||
"ui.agent_chat_mode_edits": "編集",
|
||||
"ui.agent_chat_model_tip": "エージェントが使うモデル。選択はこのエージェントに対して記憶されます。",
|
||||
"ui.agent_chat_new": "新しいチャット",
|
||||
"ui.agent_chat_new_confirm": "新しいチャットを始めますか? 現在のチャットは消去されます。",
|
||||
@@ -1161,6 +1200,7 @@
|
||||
"ui.agent_chat_none_found_short": "エージェントが見つかりません",
|
||||
"ui.agent_chat_not_installed": "未インストール",
|
||||
"ui.agent_chat_not_signed_in": "未サインイン",
|
||||
"ui.agent_chat_open_settings": "環境設定を開く",
|
||||
"ui.agent_chat_own_window": "独立したウィンドウ",
|
||||
"ui.agent_chat_paste_picture": "画像を貼り付け",
|
||||
"ui.agent_chat_permission": "エージェントが実行の許可を求めています: {title}",
|
||||
@@ -1185,6 +1225,8 @@
|
||||
"ui.agent_chat_send": "送信",
|
||||
"ui.agent_chat_send_now": "今すぐ送信",
|
||||
"ui.agent_chat_send_now_tip": "エージェントの現在のステップを止めて、これを次に送信します(Ctrl+Enter)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft は、このコンピューターにインストールされたコーディングエージェントと連携します。「環境設定 › AI」で選択してください。",
|
||||
"ui.agent_chat_setup_title": "アシスタントを設定",
|
||||
"ui.agent_chat_side": "エージェントチャットの位置",
|
||||
"ui.agent_chat_signed_in": "サインイン済み",
|
||||
"ui.agent_chat_state_auth": "サインインが必要",
|
||||
@@ -1201,8 +1243,11 @@
|
||||
"ui.agent_chat_undo_label": "エージェントチャット: {message}",
|
||||
"ui.agent_chat_undone": "(元に戻しました)",
|
||||
"ui.agent_chat_view_attached": "ビューを添付済み",
|
||||
"ui.agent_chat_waiting": "確認待ち",
|
||||
"ui.agent_cursor": "エージェントカーソル",
|
||||
"ui.agent_cursor_speed": "エージェントカーソル速度",
|
||||
"ui.agent_off_thread": "エージェントのコマンド実行中もウィンドウを操作可能にする",
|
||||
"ui.agent_off_thread_note": "エージェント(AI パネルまたは MCP クライアント)の時間のかかるコマンドをウィンドウとは別に計算します。実行中もビューの回転や確認ができ、ビュー内の表示から停止できます。オフ:各コマンドはウィンドウのスレッドで実行され、終わるまでウィンドウは待ちます。",
|
||||
"ui.ai_add": "追加",
|
||||
"ui.ai_add_agent": "エージェントを追加…",
|
||||
"ui.ai_add_agent_title": "エージェントを追加",
|
||||
@@ -1528,7 +1573,19 @@
|
||||
"ui.hideOrigin": "原点を非表示",
|
||||
"ui.hidePlanes": "平面を非表示",
|
||||
"ui.hideProfile": "プロファイルを非表示",
|
||||
"ui.history_back": "1ステップ戻る",
|
||||
"ui.history_below_browser": "ブラウザーの下",
|
||||
"ui.history_edge_hint": "端をダブルクリック",
|
||||
"ui.history_first": "先頭に移動",
|
||||
"ui.history_forward": "1ステップ進む",
|
||||
"ui.history_hide_names": "名前を非表示",
|
||||
"ui.history_last": "末尾に移動",
|
||||
"ui.history_marker_tip": "履歴マーカー:ドラッグしてモデルをロールバックします。新しいフィーチャーはここに追加されます",
|
||||
"ui.history_names": "名前",
|
||||
"ui.history_play": "最後まで再生",
|
||||
"ui.history_show_names": "名前を表示",
|
||||
"ui.history_title": "履歴",
|
||||
"ui.history_toggle_names": "フィーチャー名を表示・非表示",
|
||||
"ui.hole": "穴",
|
||||
"ui.hole_tap_type": "ねじ穴タイプ",
|
||||
"ui.hole_type": "穴タイプ",
|
||||
@@ -1545,6 +1602,8 @@
|
||||
"ui.joints": "ジョイント",
|
||||
"ui.keyboard_shortcuts": "キーボードショートカット",
|
||||
"ui.language": "言語",
|
||||
"ui.layout_compact": "コンパクト",
|
||||
"ui.layout_floating": "フローティング",
|
||||
"ui.length_decimals": "長さの小数点以下の桁数",
|
||||
"ui.light": "ライト",
|
||||
"ui.line": "線分",
|
||||
@@ -1682,6 +1741,7 @@
|
||||
"ui.reverse_zoom_direction": "ズーム方向を反転",
|
||||
"ui.revolve": "回転",
|
||||
"ui.ribbon_exit": "終了",
|
||||
"ui.ribbon_labels": "リボンのアイコンの下にテキストを表示",
|
||||
"ui.ribbon_pin_footer": "ピン留めしたコマンドはリボンに表示されます。行にポインターを合わせて、ピン留めや解除ができます。",
|
||||
"ui.ribbon_workspace": "ワークスペース",
|
||||
"ui.right": "右側面",
|
||||
@@ -1737,6 +1797,7 @@
|
||||
"ui.smooth_wheel_zoom": "滑らかなホイールズーム",
|
||||
"ui.smooth_wheel_zoom_tip": "オフにすると、ホイール操作のたびにズームが即座に反映され、滑らかな減速効果は適用されません。",
|
||||
"ui.snap": "スナップ",
|
||||
"ui.space_mouse_axes_reassigned": "軸の割り当ては環境設定で変更されており、このキャリブレーションの後も適用されます。",
|
||||
"ui.space_mouse_calibrate": "キャリブレーション…",
|
||||
"ui.space_mouse_calibrate_tip": "ウィンドウの指示どおりにキャップを動かすと、この3Dマウスの各軸の向きと移動量をSolidCraftが学習します。この3Dマウス用に保存されます。",
|
||||
"ui.space_mouse_calibrate_title": "3Dマウスのキャリブレーション",
|
||||
@@ -1746,12 +1807,26 @@
|
||||
"ui.space_mouse_calibration_no_response": "ほとんど動かなかった軸があるため、キャリブレーションできません。最初からやり直し、毎回キャップをもっと大きく動かしてください。",
|
||||
"ui.space_mouse_dead_zone": "デッドゾーン",
|
||||
"ui.space_mouse_dead_zone_tip": "視点が動き出すまでにキャップを動かす量(全ストロークに対する割合)。キャップに触れていないのに視点が動く場合は大きくしてください。",
|
||||
"ui.space_mouse_driven_by": "この軸を操作するデバイスのキャリブレーション済みの動き。他の軸で使われている動きを選ぶと、2つが入れ替わります。",
|
||||
"ui.space_mouse_driven_by_label": "操作元",
|
||||
"ui.space_mouse_driver": "3Dマウスのドライバー",
|
||||
"ui.space_mouse_driver_auto": "自動",
|
||||
"ui.space_mouse_driver_direct": "直接 (hidraw)",
|
||||
"ui.space_mouse_driver_only_direct": "デバイスを直接読み取り中(spacenavd は起動していません)",
|
||||
"ui.space_mouse_driver_only_spacenavd": "spacenavd 経由で読み取り中(他の方法はありません)",
|
||||
"ui.space_mouse_forget": "キャリブレーションを削除",
|
||||
"ui.space_mouse_hint": "視点の移動と回転を同時に行います(スケッチ中はパンとズームのみ)",
|
||||
"ui.space_mouse_motion_rotation_x": "回転 X",
|
||||
"ui.space_mouse_motion_rotation_y": "回転 Y",
|
||||
"ui.space_mouse_motion_rotation_z": "回転 Z",
|
||||
"ui.space_mouse_motion_translation_x": "平行移動 X",
|
||||
"ui.space_mouse_motion_translation_y": "平行移動 Y",
|
||||
"ui.space_mouse_motion_translation_z": "平行移動 Z",
|
||||
"ui.space_mouse_navigate": "SpaceNavigator / SpaceMouseで視点を操作",
|
||||
"ui.space_mouse_none": "3Dマウスが見つかりません",
|
||||
"ui.space_mouse_not_calibrated": "未キャリブレーション",
|
||||
"ui.space_mouse_reading": "読み取り値",
|
||||
"ui.space_mouse_reset_axes": "軸の割り当てをリセット",
|
||||
"ui.space_mouse_step_lift": "キャップを持ち上げて保持し、離してください。",
|
||||
"ui.space_mouse_step_press": "キャップを押し下げて保持し、離してください。",
|
||||
"ui.space_mouse_step_pull": "キャップを手前に引いて保持し、離してください。",
|
||||
@@ -1765,6 +1840,16 @@
|
||||
"ui.space_mouse_step_tilt_right": "キャップを右に傾け(右の縁を下げて)保持し、離してください。",
|
||||
"ui.space_mouse_step_twist_clockwise": "キャップを時計回りにひねって保持し、離してください。",
|
||||
"ui.space_mouse_step_twist_counterclockwise": "キャップを反時計回りにひねって保持し、離してください。",
|
||||
"ui.space_mouse_view_axis": "ビューの軸",
|
||||
"ui.space_mouse_view_off": "オフ",
|
||||
"ui.space_mouse_view_off_tip": "3D マウスのドライバーのプロファイルがこの軸をオフにしているため、この軸の動きではビューは動きません。ここで他の軸の動きを選ぶと 2 つが入れ替わり、その動きではビューが動かなくなります。",
|
||||
"ui.space_mouse_view_orbit_sideways": "左右オービット",
|
||||
"ui.space_mouse_view_orbit_up_down": "上下オービット",
|
||||
"ui.space_mouse_view_pan_sideways": "左右パン",
|
||||
"ui.space_mouse_view_pan_up_down": "上下パン",
|
||||
"ui.space_mouse_view_roll": "ロール",
|
||||
"ui.space_mouse_view_unmapped": "軸 {n}",
|
||||
"ui.space_mouse_view_zoom": "ズーム",
|
||||
"ui.spacing": "間隔",
|
||||
"ui.spacing_2": "間隔 2",
|
||||
"ui.sphere": "球",
|
||||
@@ -1798,7 +1883,7 @@
|
||||
"ui.tie_ends": "拘束",
|
||||
"ui.tie_ends_tip": "未拘束をすべて拘束",
|
||||
"ui.timeline_bottom": "下部タイムライン",
|
||||
"ui.timeline_left_note": "左レイアウトでは、ロールバックバーがパーツとシーンブラウザーの上に表示されます。",
|
||||
"ui.timeline_left_note": "縦の履歴はブラウザーの左に表示されます。ストリップでフィーチャー名の表示を切り替えられます。",
|
||||
"ui.timeline_left_sidebar": "左サイドバー",
|
||||
"ui.tip_angle": "先端角度",
|
||||
"ui.to_object": "オブジェクトまで",
|
||||
@@ -1836,6 +1921,7 @@
|
||||
"ui.workspace_design_tabs": "モデル · サーフェス · アセンブリ · 挿入 · 検査 · 管理 · 表示",
|
||||
"ui.workspace_drawing_description": "ビュー、寸法、表題欄を含む2D図面",
|
||||
"ui.workspace_footer": "すべてのワークスペースで同じデザイン、履歴、選択を使用し、リボンのタブだけが変わります。スケッチやフィーチャーの編集中は切り替えできません。",
|
||||
"ui.workspace_layout": "ワークスペースのレイアウト",
|
||||
"ui.workspace_manufacture_description": "スライス、ツールパス、セットアップ",
|
||||
"ui.workspace_planned": "予定",
|
||||
"ui.workspace_render_description": "製品プレビュー、シーン設定、レンダリング画像",
|
||||
|
||||
+49
-2
@@ -167,6 +167,7 @@
|
||||
"HISTORY": "이력",
|
||||
"Height": "높이",
|
||||
"Hidden edges": "숨겨진 모서리",
|
||||
"History layout": "기록 레이아웃",
|
||||
"Holes": "구멍",
|
||||
"Holes (edges on them)": "구멍 (구멍의 모서리)",
|
||||
"Home view": "홈 뷰",
|
||||
@@ -232,7 +233,7 @@
|
||||
"No crash reports.": "충돌 보고서가 없습니다.",
|
||||
"No matching appearance presets.": "일치하는 외관 프리셋이 없습니다.",
|
||||
"No matching appearances": "일치하는 외관 없음",
|
||||
"No visible bodies to export.": "표시된 바디가 없어 내보낼 수 없습니다.",
|
||||
"No visible bodies or sketches to export.": "표시된 바디나 스케치가 없어 내보낼 수 없습니다.",
|
||||
"None": "없음",
|
||||
"Not available yet": "아직 사용할 수 없음",
|
||||
"Notch": "노치",
|
||||
@@ -242,6 +243,7 @@
|
||||
"Open local libraries in the desktop app. You can export this design's library here.": "로컬 라이브러리는 데스크톱 앱에서 여세요. 이 디자인의 라이브러리는 여기서 내보낼 수 있습니다.",
|
||||
"Open-source parametric 3D CAD in Rust. Licensed MIT OR Apache-2.0.": "Rust로 만든 오픈 소스 매개변수 3D CAD. 라이선스: MIT OR Apache-2.0.",
|
||||
"Operator": "연산자",
|
||||
"Orbit (right drag)": "회전 (오른쪽 드래그)",
|
||||
"Other side": "반대쪽",
|
||||
"Other surface (mutual trim)": "다른 곡면(상호 트림)",
|
||||
"Outside": "바깥쪽",
|
||||
@@ -758,6 +760,7 @@
|
||||
"cmd.view_graphics_preset": "그래픽 프리셋",
|
||||
"cmd.view_list": "이름 있는 보기 목록",
|
||||
"cmd.view_navigate": "보기 탐색",
|
||||
"cmd.view_normal_to": "선택 항목에 수직",
|
||||
"cmd.view_rename": "이름 있는 보기 이름 변경",
|
||||
"cmd.view_save": "새 이름 있는 보기",
|
||||
"color.copy": "클릭하여 색상 값 복사",
|
||||
@@ -831,7 +834,7 @@
|
||||
"dxf.progress.projection": "형상 투영 중 (3/5)",
|
||||
"dxf.progress.write": "DXF 쓰는 중 (5/5)",
|
||||
"dxf.tolerance": "곡선 허용 오차",
|
||||
"dxf.tolerance_help": "표시된 바디만 내보내며 곡선은 직선 구간으로 근사합니다. 허용 오차가 작을수록 곡선이 매끄러워지지만 처리 시간과 메모리 사용량이 늘어납니다.",
|
||||
"dxf.tolerance_help": "표시된 바디와 스케치만 내보내며 곡선은 직선 구간으로 근사합니다. 허용 오차가 작을수록 곡선이 매끄러워지지만 처리 시간과 메모리 사용량이 늘어납니다.",
|
||||
"file_load.built": "지금까지 빌드됨: 피처 {total}개 중 {built}개.",
|
||||
"file_load.converting": "솔리드 변환 중 {done}/{total}",
|
||||
"file_load.inserting": "기하 삽입 중",
|
||||
@@ -926,6 +929,8 @@
|
||||
"motion.to_end": "끝으로",
|
||||
"motion.to_start": "처음으로",
|
||||
"motion.track_name": "트랙 {number}",
|
||||
"nav.free_orbit.help": "마우스와 3D 마우스로 롤을 유지하며 양 극점을 넘어 회전합니다. Shift 축 스냅은 비활성화됩니다.",
|
||||
"nav.free_orbit.label": "자유 회전",
|
||||
"nav.touchpad.help": "Shift를 누른 채 포인터를 움직이면 화면이 이동하고, Alt/Option을 누른 채 움직이면 회전합니다. 마우스 버튼은 필요 없습니다. 세로 스크롤로 확대/축소합니다.",
|
||||
"nav.touchpad.label": "터치패드",
|
||||
"nav.touchpad.orbit": "회전 (Alt/Option을 누르고 이동, 마우스 버튼 불필요)",
|
||||
@@ -1092,6 +1097,7 @@
|
||||
"ui.agent_chat_add": "파일 첨부",
|
||||
"ui.agent_chat_add_file": "파일 선택…",
|
||||
"ui.agent_chat_allow": "허용",
|
||||
"ui.agent_chat_allow_once": "한 번 허용",
|
||||
"ui.agent_chat_allowed": "허용함",
|
||||
"ui.agent_chat_answered": "응답: {option}",
|
||||
"ui.agent_chat_approval": "에이전트가 이 작업을 하도록 허용할까요?",
|
||||
@@ -1149,6 +1155,9 @@
|
||||
"ui.agent_chat_left": "왼쪽",
|
||||
"ui.agent_chat_limit": "에이전트가 메시지당 변경 한도 {limit}에 도달했습니다. 계속하게 하려면 메시지를 하나 더 보내세요.",
|
||||
"ui.agent_chat_minimize": "얇은 막대로 최소화",
|
||||
"ui.agent_chat_mode_all": "모두",
|
||||
"ui.agent_chat_mode_ask": "확인",
|
||||
"ui.agent_chat_mode_edits": "편집",
|
||||
"ui.agent_chat_model_tip": "에이전트가 사용하는 모델입니다. 선택은 이 에이전트에 기억됩니다.",
|
||||
"ui.agent_chat_new": "새 채팅",
|
||||
"ui.agent_chat_new_confirm": "새 채팅을 시작할까요? 현재 채팅은 지워집니다.",
|
||||
@@ -1161,6 +1170,7 @@
|
||||
"ui.agent_chat_none_found_short": "에이전트를 찾지 못함",
|
||||
"ui.agent_chat_not_installed": "설치되지 않음",
|
||||
"ui.agent_chat_not_signed_in": "로그인 안 됨",
|
||||
"ui.agent_chat_open_settings": "환경 설정 열기",
|
||||
"ui.agent_chat_own_window": "별도 창",
|
||||
"ui.agent_chat_paste_picture": "이미지 붙여넣기",
|
||||
"ui.agent_chat_permission": "에이전트가 실행 허락을 요청합니다: {title}",
|
||||
@@ -1185,6 +1195,8 @@
|
||||
"ui.agent_chat_send": "보내기",
|
||||
"ui.agent_chat_send_now": "지금 보내기",
|
||||
"ui.agent_chat_send_now_tip": "에이전트의 현재 단계를 중지하고 이것을 다음으로 보냅니다 (Ctrl+Enter)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft는 이 컴퓨터에 설치된 코딩 에이전트와 통신합니다. 환경 설정 › AI에서 에이전트를 선택하세요.",
|
||||
"ui.agent_chat_setup_title": "어시스턴트 설정",
|
||||
"ui.agent_chat_side": "에이전트 채팅 위치",
|
||||
"ui.agent_chat_signed_in": "로그인됨",
|
||||
"ui.agent_chat_state_auth": "로그인 필요",
|
||||
@@ -1201,6 +1213,7 @@
|
||||
"ui.agent_chat_undo_label": "에이전트 채팅: {message}",
|
||||
"ui.agent_chat_undone": "(취소됨)",
|
||||
"ui.agent_chat_view_attached": "뷰 첨부됨",
|
||||
"ui.agent_chat_waiting": "확인 대기 중",
|
||||
"ui.agent_cursor": "에이전트 커서",
|
||||
"ui.agent_cursor_speed": "에이전트 커서 속도",
|
||||
"ui.ai_add": "추가",
|
||||
@@ -1528,6 +1541,11 @@
|
||||
"ui.hideOrigin": "원점 숨기기",
|
||||
"ui.hidePlanes": "평면 숨기기",
|
||||
"ui.hideProfile": "윤곽 숨기기",
|
||||
"ui.history_back": "한 단계 뒤로",
|
||||
"ui.history_below_browser": "브라우저 아래",
|
||||
"ui.history_first": "처음으로 이동",
|
||||
"ui.history_forward": "한 단계 앞으로",
|
||||
"ui.history_last": "끝으로 이동",
|
||||
"ui.history_title": "기록",
|
||||
"ui.hole": "구멍",
|
||||
"ui.hole_tap_type": "탭 유형",
|
||||
@@ -1545,6 +1563,8 @@
|
||||
"ui.joints": "조인트",
|
||||
"ui.keyboard_shortcuts": "키보드 단축키",
|
||||
"ui.language": "언어",
|
||||
"ui.layout_compact": "컴팩트",
|
||||
"ui.layout_floating": "플로팅",
|
||||
"ui.length_decimals": "길이 소수점 자릿수",
|
||||
"ui.light": "밝게",
|
||||
"ui.line": "선",
|
||||
@@ -1682,6 +1702,7 @@
|
||||
"ui.reverse_zoom_direction": "확대/축소 방향 반전",
|
||||
"ui.revolve": "회전",
|
||||
"ui.ribbon_exit": "종료",
|
||||
"ui.ribbon_labels": "리본 아이콘 아래에 텍스트 표시",
|
||||
"ui.ribbon_pin_footer": "고정된 명령은 리본에 표시됩니다. 행 위에 마우스를 올려 고정하거나 해제하세요.",
|
||||
"ui.ribbon_workspace": "작업 공간",
|
||||
"ui.right": "우측면",
|
||||
@@ -1737,6 +1758,7 @@
|
||||
"ui.smooth_wheel_zoom": "부드러운 휠 확대/축소",
|
||||
"ui.smooth_wheel_zoom_tip": "끄면 휠 이벤트를 부드러운 꼬리 없이 즉시 적용합니다",
|
||||
"ui.snap": "스냅",
|
||||
"ui.space_mouse_axes_reassigned": "축이 환경설정에서 다시 지정되어 있으며, 이 보정 후에도 적용됩니다.",
|
||||
"ui.space_mouse_calibrate": "보정…",
|
||||
"ui.space_mouse_calibrate_tip": "창의 안내대로 캡을 움직이면 SolidCraft가 이 3D 마우스의 각 축이 어느 방향으로 얼마나 움직이는지 학습합니다. 이 3D 마우스에 저장됩니다.",
|
||||
"ui.space_mouse_calibrate_title": "3D 마우스 보정",
|
||||
@@ -1746,12 +1768,26 @@
|
||||
"ui.space_mouse_calibration_no_response": "거의 움직이지 않은 축이 있어 보정할 수 없습니다. 처음부터 다시 하고 매번 캡을 더 멀리 움직이세요.",
|
||||
"ui.space_mouse_dead_zone": "데드존",
|
||||
"ui.space_mouse_dead_zone_tip": "보기가 움직이기 전에 캡이 움직여야 하는 정도로, 전체 이동 범위에 대한 비율입니다. 캡을 건드리지 않았는데 보기가 움직이면 높이세요.",
|
||||
"ui.space_mouse_driven_by": "이 축을 움직이는 장치의 보정된 동작입니다. 다른 축이 이미 쓰는 동작을 고르면 둘이 서로 바뀝니다.",
|
||||
"ui.space_mouse_driven_by_label": "제어 동작",
|
||||
"ui.space_mouse_driver": "3D 마우스 드라이버",
|
||||
"ui.space_mouse_driver_auto": "자동",
|
||||
"ui.space_mouse_driver_direct": "직접 (hidraw)",
|
||||
"ui.space_mouse_driver_only_direct": "장치를 직접 읽는 중 (spacenavd가 실행 중이 아님)",
|
||||
"ui.space_mouse_driver_only_spacenavd": "spacenavd로 읽는 중 (다른 방법이 없음)",
|
||||
"ui.space_mouse_forget": "보정 삭제",
|
||||
"ui.space_mouse_hint": "뷰를 함께 이동하고 회전합니다. 스케치는 이동과 확대/축소만 사용합니다.",
|
||||
"ui.space_mouse_motion_rotation_x": "회전 X",
|
||||
"ui.space_mouse_motion_rotation_y": "회전 Y",
|
||||
"ui.space_mouse_motion_rotation_z": "회전 Z",
|
||||
"ui.space_mouse_motion_translation_x": "이동 X",
|
||||
"ui.space_mouse_motion_translation_y": "이동 Y",
|
||||
"ui.space_mouse_motion_translation_z": "이동 Z",
|
||||
"ui.space_mouse_navigate": "SpaceNavigator / SpaceMouse로 탐색",
|
||||
"ui.space_mouse_none": "3D 마우스를 찾을 수 없음",
|
||||
"ui.space_mouse_not_calibrated": "보정 안 됨",
|
||||
"ui.space_mouse_reading": "읽은 값",
|
||||
"ui.space_mouse_reset_axes": "축 지정 초기화",
|
||||
"ui.space_mouse_step_lift": "캡을 위로 들어 유지한 다음 놓으세요.",
|
||||
"ui.space_mouse_step_press": "캡을 아래로 눌러 유지한 다음 놓으세요.",
|
||||
"ui.space_mouse_step_pull": "캡을 몸 쪽으로 당겨 유지한 다음 놓으세요.",
|
||||
@@ -1765,6 +1801,16 @@
|
||||
"ui.space_mouse_step_tilt_right": "캡을 오른쪽으로 기울여(오른쪽 가장자리를 아래로) 유지한 다음 놓으세요.",
|
||||
"ui.space_mouse_step_twist_clockwise": "캡을 시계 방향으로 돌려 유지한 다음 놓으세요.",
|
||||
"ui.space_mouse_step_twist_counterclockwise": "캡을 시계 반대 방향으로 돌려 유지한 다음 놓으세요.",
|
||||
"ui.space_mouse_view_axis": "보기 축",
|
||||
"ui.space_mouse_view_off": "끔",
|
||||
"ui.space_mouse_view_off_tip": "3D 마우스 드라이버 프로필이 이 축을 끄므로, 이 축의 동작으로는 뷰가 움직이지 않습니다. 여기서 다른 축의 동작을 고르면 둘이 바뀌고, 그 동작은 더 이상 뷰를 움직이지 않습니다.",
|
||||
"ui.space_mouse_view_orbit_sideways": "좌우 궤도",
|
||||
"ui.space_mouse_view_orbit_up_down": "상하 궤도",
|
||||
"ui.space_mouse_view_pan_sideways": "좌우 이동",
|
||||
"ui.space_mouse_view_pan_up_down": "상하 이동",
|
||||
"ui.space_mouse_view_roll": "롤",
|
||||
"ui.space_mouse_view_unmapped": "축 {n}",
|
||||
"ui.space_mouse_view_zoom": "확대/축소",
|
||||
"ui.spacing": "간격",
|
||||
"ui.spacing_2": "간격 2",
|
||||
"ui.sphere": "구",
|
||||
@@ -1836,6 +1882,7 @@
|
||||
"ui.workspace_design_tabs": "모델 · 곡면 · 조립 · 삽입 · 검사 · 관리 · 보기",
|
||||
"ui.workspace_drawing_description": "뷰, 치수, 표제란이 있는 2D 도면",
|
||||
"ui.workspace_footer": "모든 작업 공간은 같은 디자인, 히스토리, 선택 항목에서 작동하며 리본 탭만 바뀝니다. 스케치나 피처를 편집하는 동안에는 전환기가 잠깁니다.",
|
||||
"ui.workspace_layout": "작업 공간 레이아웃",
|
||||
"ui.workspace_manufacture_description": "슬라이싱, 툴패스 및 설정",
|
||||
"ui.workspace_planned": "예정",
|
||||
"ui.workspace_render_description": "제품 미리보기, 장면 설정 및 렌더링 이미지",
|
||||
|
||||
+20
-2
@@ -167,6 +167,7 @@
|
||||
"HISTORY": "SEJARAH",
|
||||
"Height": "Ketinggian",
|
||||
"Hidden edges": "Tepi tersembunyi",
|
||||
"History layout": "Susun atur sejarah",
|
||||
"Holes": "Lubang",
|
||||
"Holes (edges on them)": "Lubang (tepi lubang)",
|
||||
"Home view": "Pandangan utama",
|
||||
@@ -232,7 +233,7 @@
|
||||
"No crash reports.": "Tiada laporan ranap.",
|
||||
"No matching appearance presets.": "Tiada pratetap penampilan yang sepadan.",
|
||||
"No matching appearances": "Tiada penampilan yang sepadan",
|
||||
"No visible bodies to export.": "Tiada jasad kelihatan untuk dieksport.",
|
||||
"No visible bodies or sketches to export.": "Tiada jasad atau lakaran kelihatan untuk dieksport.",
|
||||
"None": "Tiada",
|
||||
"Not available yet": "Belum tersedia",
|
||||
"Notch": "Takuk",
|
||||
@@ -831,7 +832,7 @@
|
||||
"dxf.progress.projection": "Mengunjurkan geometri (3/5)",
|
||||
"dxf.progress.write": "Menulis DXF (5/5)",
|
||||
"dxf.tolerance": "Toleransi lengkung",
|
||||
"dxf.tolerance_help": "Jasad kelihatan sahaja. Lengkung guna segmen lurus. Toleransi kecil lebih halus tetapi guna lebih masa dan memori.",
|
||||
"dxf.tolerance_help": "Jasad kelihatan dan lakaran yang ditunjukkan sahaja. Lengkung guna segmen lurus. Toleransi kecil lebih halus tetapi guna lebih masa dan memori.",
|
||||
"file_load.built": "Dibina setakat ini: {built} daripada {total} ciri.",
|
||||
"file_load.converting": "Menukar pepejal {done} daripada {total}",
|
||||
"file_load.inserting": "Menyisip geometri",
|
||||
@@ -1092,6 +1093,7 @@
|
||||
"ui.agent_chat_add": "Lampirkan fail",
|
||||
"ui.agent_chat_add_file": "Pilih fail…",
|
||||
"ui.agent_chat_allow": "Benarkan",
|
||||
"ui.agent_chat_allow_once": "Benarkan sekali",
|
||||
"ui.agent_chat_allowed": "Dibenarkan",
|
||||
"ui.agent_chat_answered": "Jawapan: {option}",
|
||||
"ui.agent_chat_approval": "Benarkan ejen melakukan ini?",
|
||||
@@ -1149,6 +1151,9 @@
|
||||
"ui.agent_chat_left": "Kiri",
|
||||
"ui.agent_chat_limit": "Ejen telah mencapai had {limit} perubahan untuk satu mesej. Hantar mesej lain untuk meneruskan.",
|
||||
"ui.agent_chat_minimize": "Kecilkan kepada bar",
|
||||
"ui.agent_chat_mode_all": "Semua",
|
||||
"ui.agent_chat_mode_ask": "Tanya",
|
||||
"ui.agent_chat_mode_edits": "Suntingan",
|
||||
"ui.agent_chat_model_tip": "Model yang digunakan oleh ejen. Pilihan anda disimpan untuk ejen ini.",
|
||||
"ui.agent_chat_new": "Sembang baharu",
|
||||
"ui.agent_chat_new_confirm": "Mulakan sembang baharu? Sembang semasa akan dikosongkan.",
|
||||
@@ -1161,6 +1166,7 @@
|
||||
"ui.agent_chat_none_found_short": "Tiada ejen ditemui",
|
||||
"ui.agent_chat_not_installed": "Tidak dipasang",
|
||||
"ui.agent_chat_not_signed_in": "belum log masuk",
|
||||
"ui.agent_chat_open_settings": "Buka tetapan",
|
||||
"ui.agent_chat_own_window": "Tetingkap sendiri",
|
||||
"ui.agent_chat_paste_picture": "Tampal gambar",
|
||||
"ui.agent_chat_permission": "Benarkan ejen jalankan: {title}",
|
||||
@@ -1185,6 +1191,8 @@
|
||||
"ui.agent_chat_send": "Hantar",
|
||||
"ui.agent_chat_send_now": "Hantar terus",
|
||||
"ui.agent_chat_send_now_tip": "Hentikan langkah semasa ejen dan hantar mesej ini selepas itu (Ctrl+Enter)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft berkomunikasi dengan ejen pengekodan yang dipasang pada komputer ini. Pilih ejen dalam Tetapan › AI.",
|
||||
"ui.agent_chat_setup_title": "Sediakan pembantu",
|
||||
"ui.agent_chat_side": "Sisi sembang ejen",
|
||||
"ui.agent_chat_signed_in": "sudah log masuk",
|
||||
"ui.agent_chat_state_auth": "Perlu log masuk",
|
||||
@@ -1201,6 +1209,7 @@
|
||||
"ui.agent_chat_undo_label": "Sembang ejen: {message}",
|
||||
"ui.agent_chat_undone": "(dibatalkan)",
|
||||
"ui.agent_chat_view_attached": "Paparan dilampirkan",
|
||||
"ui.agent_chat_waiting": "Menunggu anda",
|
||||
"ui.agent_cursor": "Kursor ejen",
|
||||
"ui.agent_cursor_speed": "Kelajuan kursor ejen",
|
||||
"ui.ai_add": "Tambah",
|
||||
@@ -1528,6 +1537,11 @@
|
||||
"ui.hideOrigin": "Sembunyi Titik Asal",
|
||||
"ui.hidePlanes": "Sembunyi Satah",
|
||||
"ui.hideProfile": "Sembunyi Profil",
|
||||
"ui.history_back": "Undur satu langkah",
|
||||
"ui.history_below_browser": "Di bawah pelayar",
|
||||
"ui.history_first": "Pergi ke permulaan",
|
||||
"ui.history_forward": "Maju satu langkah",
|
||||
"ui.history_last": "Pergi ke penghujung",
|
||||
"ui.history_title": "Sejarah",
|
||||
"ui.hole": "Lubang",
|
||||
"ui.hole_tap_type": "Jenis Bebenang Lubang",
|
||||
@@ -1545,6 +1559,8 @@
|
||||
"ui.joints": "Sendi",
|
||||
"ui.keyboard_shortcuts": "Pintasan Kekunci",
|
||||
"ui.language": "Bahasa",
|
||||
"ui.layout_compact": "Padat",
|
||||
"ui.layout_floating": "Terapung",
|
||||
"ui.length_decimals": "Perpuluhan panjang",
|
||||
"ui.light": "Cerah",
|
||||
"ui.line": "Garis",
|
||||
@@ -1682,6 +1698,7 @@
|
||||
"ui.reverse_zoom_direction": "Songsangkan arah zum",
|
||||
"ui.revolve": "Putar",
|
||||
"ui.ribbon_exit": "Keluar",
|
||||
"ui.ribbon_labels": "Tunjukkan teks di bawah ikon reben",
|
||||
"ui.ribbon_pin_footer": "Arahan tersemat dipaparkan pada reben. Lalukan tetikus pada baris untuk menyemat atau menyahsemat.",
|
||||
"ui.ribbon_workspace": "Ruang kerja",
|
||||
"ui.right": "Kanan",
|
||||
@@ -1836,6 +1853,7 @@
|
||||
"ui.workspace_design_tabs": "Model · Permukaan · Pasang · Sisip · Periksa · Urus · Pandangan",
|
||||
"ui.workspace_drawing_description": "Lukisan 2D dengan pandangan, dimensi dan blok tajuk",
|
||||
"ui.workspace_footer": "Setiap ruang kerja berfungsi pada reka bentuk, sejarah dan pilihan yang sama; hanya tab reben berubah. Penukar dikunci semasa lakaran atau ciri sedang disunting.",
|
||||
"ui.workspace_layout": "Susun atur ruang kerja",
|
||||
"ui.workspace_manufacture_description": "Pemotongan lapisan, laluan alat dan persediaan",
|
||||
"ui.workspace_planned": "Dirancang",
|
||||
"ui.workspace_render_description": "Pratonton produk, tetapan adegan dan imej render",
|
||||
|
||||
+18
-1
@@ -167,6 +167,7 @@
|
||||
"HISTORY": "GESCHIEDENIS",
|
||||
"Height": "Hoogte",
|
||||
"Hidden edges": "Verborgen randen",
|
||||
"History layout": "Geschiedenisindeling",
|
||||
"Holes": "Gaten",
|
||||
"Holes (edges on them)": "Gaten (randen erop)",
|
||||
"Home view": "Beginweergave",
|
||||
@@ -232,7 +233,6 @@
|
||||
"No crash reports.": "Geen crashrapporten.",
|
||||
"No matching appearance presets.": "Geen overeenkomende uiterlijkvoorinstellingen.",
|
||||
"No matching appearances": "Geen overeenkomende uiterlijken",
|
||||
"No visible bodies to export.": "Geen zichtbare lichamen om te exporteren.",
|
||||
"None": "Geen",
|
||||
"Not available yet": "Nog niet beschikbaar",
|
||||
"Notch": "Inkeping",
|
||||
@@ -1092,6 +1092,7 @@
|
||||
"ui.agent_chat_add": "Bestanden bijvoegen",
|
||||
"ui.agent_chat_add_file": "Een bestand kiezen…",
|
||||
"ui.agent_chat_allow": "Toestaan",
|
||||
"ui.agent_chat_allow_once": "Eenmalig toestaan",
|
||||
"ui.agent_chat_allowed": "Toegestaan",
|
||||
"ui.agent_chat_answered": "Antwoord: {option}",
|
||||
"ui.agent_chat_approval": "De agent dit laten doen?",
|
||||
@@ -1149,6 +1150,9 @@
|
||||
"ui.agent_chat_left": "Links",
|
||||
"ui.agent_chat_limit": "De agent heeft de limiet van {limit} wijzigingen voor één bericht bereikt. Stuur nog een bericht om hem verder te laten gaan.",
|
||||
"ui.agent_chat_minimize": "Minimaliseren tot een strook",
|
||||
"ui.agent_chat_mode_all": "Alles",
|
||||
"ui.agent_chat_mode_ask": "Vragen",
|
||||
"ui.agent_chat_mode_edits": "Bewerken",
|
||||
"ui.agent_chat_model_tip": "Het model dat de agent gebruikt. Uw keuze wordt voor deze agent onthouden.",
|
||||
"ui.agent_chat_new": "Nieuwe chat",
|
||||
"ui.agent_chat_new_confirm": "Een nieuwe chat starten? De huidige wordt gewist.",
|
||||
@@ -1161,6 +1165,7 @@
|
||||
"ui.agent_chat_none_found_short": "Geen agent gevonden",
|
||||
"ui.agent_chat_not_installed": "Niet geïnstalleerd",
|
||||
"ui.agent_chat_not_signed_in": "niet aangemeld",
|
||||
"ui.agent_chat_open_settings": "Voorkeuren openen",
|
||||
"ui.agent_chat_own_window": "Eigen venster",
|
||||
"ui.agent_chat_paste_picture": "Een afbeelding plakken",
|
||||
"ui.agent_chat_permission": "De agent vraagt om uit te voeren: {title}",
|
||||
@@ -1185,6 +1190,8 @@
|
||||
"ui.agent_chat_send": "Verzenden",
|
||||
"ui.agent_chat_send_now": "Nu verzenden",
|
||||
"ui.agent_chat_send_now_tip": "De huidige stap van de agent stoppen en dit nu verzenden (Ctrl+Enter)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft communiceert met een programmeeragent die op deze computer is geïnstalleerd. Kies er een via Voorkeuren › AI.",
|
||||
"ui.agent_chat_setup_title": "Een assistent instellen",
|
||||
"ui.agent_chat_side": "Zijde van agentchat",
|
||||
"ui.agent_chat_signed_in": "aangemeld",
|
||||
"ui.agent_chat_state_auth": "Aanmelden vereist",
|
||||
@@ -1201,6 +1208,7 @@
|
||||
"ui.agent_chat_undo_label": "Agentchat: {message}",
|
||||
"ui.agent_chat_undone": "(ongedaan gemaakt)",
|
||||
"ui.agent_chat_view_attached": "Weergave bijgevoegd",
|
||||
"ui.agent_chat_waiting": "Wacht op jou",
|
||||
"ui.agent_cursor": "Agentcursor",
|
||||
"ui.agent_cursor_speed": "Snelheid agentcursor",
|
||||
"ui.ai_add": "Toevoegen",
|
||||
@@ -1528,6 +1536,11 @@
|
||||
"ui.hideOrigin": "Oorsprong verbergen",
|
||||
"ui.hidePlanes": "Vlakken verbergen",
|
||||
"ui.hideProfile": "Profiel verbergen",
|
||||
"ui.history_back": "Eén stap terug",
|
||||
"ui.history_below_browser": "Onder de browser",
|
||||
"ui.history_first": "Naar het begin",
|
||||
"ui.history_forward": "Eén stap vooruit",
|
||||
"ui.history_last": "Naar het einde",
|
||||
"ui.history_title": "Geschiedenis",
|
||||
"ui.hole": "Gat",
|
||||
"ui.hole_tap_type": "Type tapgat",
|
||||
@@ -1545,6 +1558,8 @@
|
||||
"ui.joints": "Verbindingen",
|
||||
"ui.keyboard_shortcuts": "Sneltoetsen",
|
||||
"ui.language": "Taal",
|
||||
"ui.layout_compact": "Compact",
|
||||
"ui.layout_floating": "Zwevend",
|
||||
"ui.length_decimals": "Lengtedecimalen",
|
||||
"ui.light": "Licht",
|
||||
"ui.line": "Lijn",
|
||||
@@ -1682,6 +1697,7 @@
|
||||
"ui.reverse_zoom_direction": "Zoomrichting omkeren",
|
||||
"ui.revolve": "Revolve",
|
||||
"ui.ribbon_exit": "Afsluiten",
|
||||
"ui.ribbon_labels": "Tekst onder lintpictogrammen tonen",
|
||||
"ui.ribbon_pin_footer": "Vastgepinde opdrachten verschijnen in het lint. Beweeg over een rij om vast te pinnen of los te maken.",
|
||||
"ui.ribbon_workspace": "Werkruimte",
|
||||
"ui.right": "Rechts",
|
||||
@@ -1836,6 +1852,7 @@
|
||||
"ui.workspace_design_tabs": "Model · Oppervlak · Assembleren · Invoegen · Inspecteren · Beheren · Beeld",
|
||||
"ui.workspace_drawing_description": "2D-tekeningen met weergaven, maatvoering en titelblokken",
|
||||
"ui.workspace_footer": "Elke werkruimte werkt op hetzelfde ontwerp, dezelfde geschiedenis en selectie; alleen de lintabbladen veranderen. De schakelaar is vergrendeld terwijl een schets of kenmerk wordt bewerkt.",
|
||||
"ui.workspace_layout": "Werkruimte-indeling",
|
||||
"ui.workspace_manufacture_description": "Slicen, gereedschapspaden en opstelling",
|
||||
"ui.workspace_planned": "Gepland",
|
||||
"ui.workspace_render_description": "Productvoorbeeld, sceneinstellingen en gerenderde afbeeldingen",
|
||||
|
||||
+20
-2
@@ -167,6 +167,7 @@
|
||||
"HISTORY": "HISTORIA",
|
||||
"Height": "Wysokość",
|
||||
"Hidden edges": "Krawędzie niewidoczne",
|
||||
"History layout": "Układ historii",
|
||||
"Holes": "Otwory",
|
||||
"Holes (edges on them)": "Otwory (krawędzie na nich)",
|
||||
"Home view": "Widok główny",
|
||||
@@ -232,7 +233,7 @@
|
||||
"No crash reports.": "Brak raportów o awariach.",
|
||||
"No matching appearance presets.": "Brak pasujących ustawień wyglądu.",
|
||||
"No matching appearances": "Brak pasujących wyglądów",
|
||||
"No visible bodies to export.": "Brak widocznych brył do wyeksportowania.",
|
||||
"No visible bodies or sketches to export.": "Brak widocznych brył lub szkiców do wyeksportowania.",
|
||||
"None": "Brak",
|
||||
"Not available yet": "Jeszcze niedostępne",
|
||||
"Notch": "Wycięcie",
|
||||
@@ -831,7 +832,7 @@
|
||||
"dxf.progress.projection": "Rzutowanie geometrii (3/5)",
|
||||
"dxf.progress.write": "Zapisywanie DXF (5/5)",
|
||||
"dxf.tolerance": "Tolerancja krzywych",
|
||||
"dxf.tolerance_help": "Tylko widoczne bryły. Krzywe są przybliżane odcinkami. Mniejsza tolerancja daje dokładniejsze krzywe, ale zajmuje więcej czasu i pamięci.",
|
||||
"dxf.tolerance_help": "Tylko widoczne bryły i pokazane szkice. Krzywe są przybliżane odcinkami. Mniejsza tolerancja daje dokładniejsze krzywe, ale zajmuje więcej czasu i pamięci.",
|
||||
"file_load.built": "Dotąd zbudowano: {built} z {total} elementów.",
|
||||
"file_load.converting": "Konwertowanie bryły {done} z {total}",
|
||||
"file_load.inserting": "Wstawianie geometrii",
|
||||
@@ -1092,6 +1093,7 @@
|
||||
"ui.agent_chat_add": "Załącz pliki",
|
||||
"ui.agent_chat_add_file": "Wybierz plik…",
|
||||
"ui.agent_chat_allow": "Zezwól",
|
||||
"ui.agent_chat_allow_once": "Zezwól raz",
|
||||
"ui.agent_chat_allowed": "Zezwolono",
|
||||
"ui.agent_chat_answered": "Odpowiedź: {option}",
|
||||
"ui.agent_chat_approval": "Zezwolić agentowi na to działanie?",
|
||||
@@ -1149,6 +1151,9 @@
|
||||
"ui.agent_chat_left": "Lewa",
|
||||
"ui.agent_chat_limit": "Agent osiągnął limit {limit} zmian na jedną wiadomość. Wyślij kolejną wiadomość, aby mógł kontynuować.",
|
||||
"ui.agent_chat_minimize": "Zminimalizuj do paska",
|
||||
"ui.agent_chat_mode_all": "Wszystko",
|
||||
"ui.agent_chat_mode_ask": "Pytaj",
|
||||
"ui.agent_chat_mode_edits": "Edycje",
|
||||
"ui.agent_chat_model_tip": "Model używany przez agenta. Twój wybór jest zapamiętywany dla tego agenta.",
|
||||
"ui.agent_chat_new": "Nowy czat",
|
||||
"ui.agent_chat_new_confirm": "Rozpocząć nowy czat? Bieżący zostanie wyczyszczony.",
|
||||
@@ -1161,6 +1166,7 @@
|
||||
"ui.agent_chat_none_found_short": "Nie znaleziono agenta",
|
||||
"ui.agent_chat_not_installed": "Nie zainstalowano",
|
||||
"ui.agent_chat_not_signed_in": "nie zalogowano",
|
||||
"ui.agent_chat_open_settings": "Otwórz preferencje",
|
||||
"ui.agent_chat_own_window": "Osobne okno",
|
||||
"ui.agent_chat_paste_picture": "Wklej obraz",
|
||||
"ui.agent_chat_permission": "Agent prosi o uruchomienie: {title}",
|
||||
@@ -1185,6 +1191,8 @@
|
||||
"ui.agent_chat_send": "Wyślij",
|
||||
"ui.agent_chat_send_now": "Wyślij teraz",
|
||||
"ui.agent_chat_send_now_tip": "Zatrzymaj bieżący krok agenta i wyślij tę wiadomość jako następną (Ctrl+Enter)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft komunikuje się z agentem programistycznym zainstalowanym na tym komputerze. Wybierz go w Preferencje › AI.",
|
||||
"ui.agent_chat_setup_title": "Skonfiguruj asystenta",
|
||||
"ui.agent_chat_side": "Strona czatu z agentem",
|
||||
"ui.agent_chat_signed_in": "zalogowano",
|
||||
"ui.agent_chat_state_auth": "Wymagane logowanie",
|
||||
@@ -1201,6 +1209,7 @@
|
||||
"ui.agent_chat_undo_label": "Czat z agentem: {message}",
|
||||
"ui.agent_chat_undone": "(cofnięto)",
|
||||
"ui.agent_chat_view_attached": "Dołączono widok",
|
||||
"ui.agent_chat_waiting": "Czeka na Ciebie",
|
||||
"ui.agent_cursor": "Kursor agenta",
|
||||
"ui.agent_cursor_speed": "Szybkość kursora agenta",
|
||||
"ui.ai_add": "Dodaj",
|
||||
@@ -1528,6 +1537,11 @@
|
||||
"ui.hideOrigin": "Ukryj początek układu",
|
||||
"ui.hidePlanes": "Ukryj płaszczyzny",
|
||||
"ui.hideProfile": "Ukryj profil",
|
||||
"ui.history_back": "Cofnij o jeden krok",
|
||||
"ui.history_below_browser": "Pod przeglądarką",
|
||||
"ui.history_first": "Przejdź do początku",
|
||||
"ui.history_forward": "Przejdź o jeden krok do przodu",
|
||||
"ui.history_last": "Przejdź do końca",
|
||||
"ui.history_title": "Historia",
|
||||
"ui.hole": "Otwór",
|
||||
"ui.hole_tap_type": "Typ gwintu otworu",
|
||||
@@ -1545,6 +1559,8 @@
|
||||
"ui.joints": "Złącza",
|
||||
"ui.keyboard_shortcuts": "Skróty klawiszowe",
|
||||
"ui.language": "Język",
|
||||
"ui.layout_compact": "Kompaktowy",
|
||||
"ui.layout_floating": "Pływający",
|
||||
"ui.length_decimals": "Miejsca dziesiętne długości",
|
||||
"ui.light": "Jasny",
|
||||
"ui.line": "Linia",
|
||||
@@ -1682,6 +1698,7 @@
|
||||
"ui.reverse_zoom_direction": "Odwróć kierunek powiększania",
|
||||
"ui.revolve": "Obrót",
|
||||
"ui.ribbon_exit": "Wyjdź",
|
||||
"ui.ribbon_labels": "Pokazuj tekst pod ikonami wstążki",
|
||||
"ui.ribbon_pin_footer": "Przypięte polecenia są widoczne na wstążce. Najedź na wiersz, aby go przypiąć lub odpiąć.",
|
||||
"ui.ribbon_workspace": "Przestrzeń robocza",
|
||||
"ui.right": "Prawy",
|
||||
@@ -1836,6 +1853,7 @@
|
||||
"ui.workspace_design_tabs": "Model · Powierzchnia · Złożenie · Wstaw · Sprawdź · Zarządzaj · Widok",
|
||||
"ui.workspace_drawing_description": "Rysunki 2D z widokami, wymiarami i tabliczkami rysunkowymi",
|
||||
"ui.workspace_footer": "Każda przestrzeń robocza działa na tym samym projekcie, historii i zaznaczeniu; zmieniają się tylko karty wstążki. Przełącznik jest zablokowany podczas edycji szkicu lub elementu.",
|
||||
"ui.workspace_layout": "Układ obszaru roboczego",
|
||||
"ui.workspace_manufacture_description": "Cięcie na warstwy, ścieżki narzędzia i przygotowanie",
|
||||
"ui.workspace_planned": "Planowane",
|
||||
"ui.workspace_render_description": "Podgląd produktu, ustawienia sceny i renderowane obrazy",
|
||||
|
||||
@@ -167,6 +167,7 @@
|
||||
"HISTORY": "HISTÓRICO",
|
||||
"Height": "Altura",
|
||||
"Hidden edges": "Arestas ocultas",
|
||||
"History layout": "Layout do histórico",
|
||||
"Holes": "Furos",
|
||||
"Holes (edges on them)": "Furos (arestas neles)",
|
||||
"Home view": "Vista inicial",
|
||||
@@ -232,7 +233,7 @@
|
||||
"No crash reports.": "Nenhum relatório de falha.",
|
||||
"No matching appearance presets.": "Nenhuma predefinição de aparência correspondente.",
|
||||
"No matching appearances": "Nenhuma aparência correspondente",
|
||||
"No visible bodies to export.": "Nenhum corpo visível para exportar.",
|
||||
"No visible bodies or sketches to export.": "Nenhum corpo ou esboço visível para exportar.",
|
||||
"None": "Nenhum",
|
||||
"Not available yet": "Ainda não disponível",
|
||||
"Notch": "Entalhe",
|
||||
@@ -1092,6 +1093,7 @@
|
||||
"ui.agent_chat_add": "Anexar arquivos",
|
||||
"ui.agent_chat_add_file": "Escolher um arquivo…",
|
||||
"ui.agent_chat_allow": "Permitir",
|
||||
"ui.agent_chat_allow_once": "Permitir uma vez",
|
||||
"ui.agent_chat_allowed": "Permitido",
|
||||
"ui.agent_chat_answered": "Resposta: {option}",
|
||||
"ui.agent_chat_approval": "Permitir que o agente faça isto?",
|
||||
@@ -1149,6 +1151,9 @@
|
||||
"ui.agent_chat_left": "Esquerda",
|
||||
"ui.agent_chat_limit": "O agente atingiu o limite de {limit} alterações por mensagem. Envie outra mensagem para que ele continue.",
|
||||
"ui.agent_chat_minimize": "Minimizar para uma faixa",
|
||||
"ui.agent_chat_mode_all": "Tudo",
|
||||
"ui.agent_chat_mode_ask": "Perguntar",
|
||||
"ui.agent_chat_mode_edits": "Edições",
|
||||
"ui.agent_chat_model_tip": "O modelo que o agente usa. Sua escolha é lembrada para este agente.",
|
||||
"ui.agent_chat_new": "Novo chat",
|
||||
"ui.agent_chat_new_confirm": "Começar um novo chat? O atual será apagado.",
|
||||
@@ -1161,6 +1166,7 @@
|
||||
"ui.agent_chat_none_found_short": "Nenhum agente encontrado",
|
||||
"ui.agent_chat_not_installed": "Não instalado",
|
||||
"ui.agent_chat_not_signed_in": "não conectado",
|
||||
"ui.agent_chat_open_settings": "Abrir preferências",
|
||||
"ui.agent_chat_own_window": "Janela própria",
|
||||
"ui.agent_chat_paste_picture": "Colar uma imagem",
|
||||
"ui.agent_chat_permission": "O agente pede para executar: {title}",
|
||||
@@ -1185,6 +1191,8 @@
|
||||
"ui.agent_chat_send": "Enviar",
|
||||
"ui.agent_chat_send_now": "Enviar agora",
|
||||
"ui.agent_chat_send_now_tip": "Parar o passo atual do agente e enviar esta em seguida (Ctrl+Enter)",
|
||||
"ui.agent_chat_setup_body": "O SolidCraft se comunica com um agente de programação instalado neste computador. Escolha um em Preferências › IA.",
|
||||
"ui.agent_chat_setup_title": "Configurar um assistente",
|
||||
"ui.agent_chat_side": "Lado do chat com agente",
|
||||
"ui.agent_chat_signed_in": "conectado",
|
||||
"ui.agent_chat_state_auth": "É preciso entrar",
|
||||
@@ -1201,6 +1209,7 @@
|
||||
"ui.agent_chat_undo_label": "Chat com agente: {message}",
|
||||
"ui.agent_chat_undone": "(desfeito)",
|
||||
"ui.agent_chat_view_attached": "Vista anexada",
|
||||
"ui.agent_chat_waiting": "Aguardando sua resposta",
|
||||
"ui.agent_cursor": "Cursor do agente",
|
||||
"ui.agent_cursor_speed": "Velocidade do cursor do agente",
|
||||
"ui.ai_add": "Adicionar",
|
||||
@@ -1528,6 +1537,11 @@
|
||||
"ui.hideOrigin": "Ocultar origem",
|
||||
"ui.hidePlanes": "Ocultar planos",
|
||||
"ui.hideProfile": "Ocultar perfil",
|
||||
"ui.history_back": "Voltar uma etapa",
|
||||
"ui.history_below_browser": "Abaixo do navegador",
|
||||
"ui.history_first": "Ir para o início",
|
||||
"ui.history_forward": "Avançar uma etapa",
|
||||
"ui.history_last": "Ir para o fim",
|
||||
"ui.history_title": "Histórico",
|
||||
"ui.hole": "Furo",
|
||||
"ui.hole_tap_type": "Tipo de rosca do furo",
|
||||
@@ -1545,6 +1559,8 @@
|
||||
"ui.joints": "Juntas",
|
||||
"ui.keyboard_shortcuts": "Atalhos de teclado",
|
||||
"ui.language": "Idioma",
|
||||
"ui.layout_compact": "Compacto",
|
||||
"ui.layout_floating": "Flutuante",
|
||||
"ui.length_decimals": "Casas decimais de comprimento",
|
||||
"ui.light": "Claro",
|
||||
"ui.line": "Linha",
|
||||
@@ -1682,6 +1698,7 @@
|
||||
"ui.reverse_zoom_direction": "Inverter direção do zoom",
|
||||
"ui.revolve": "Revolução",
|
||||
"ui.ribbon_exit": "Sair",
|
||||
"ui.ribbon_labels": "Mostrar texto abaixo dos ícones da faixa de opções",
|
||||
"ui.ribbon_pin_footer": "Os comandos fixados aparecem na faixa de opções. Passe o mouse sobre uma linha para fixar ou desafixar.",
|
||||
"ui.ribbon_workspace": "Espaço de trabalho",
|
||||
"ui.right": "Direita",
|
||||
@@ -1737,6 +1754,7 @@
|
||||
"ui.smooth_wheel_zoom": "Zoom suave com a roda",
|
||||
"ui.smooth_wheel_zoom_tip": "Desative para aplicar cada evento da roda na hora, sem rastro de suavização",
|
||||
"ui.snap": "Encaixe",
|
||||
"ui.space_mouse_axes_reassigned": "Os eixos estão reatribuídos nas Preferências, e isso continua valendo sobre esta calibração.",
|
||||
"ui.space_mouse_calibrate": "Calibrar…",
|
||||
"ui.space_mouse_calibrate_tip": "Mova a tampa como a janela pedir, para que o SolidCraft aprenda para onde e até onde vai cada eixo deste mouse 3D. Salvo para este mouse 3D.",
|
||||
"ui.space_mouse_calibrate_title": "Calibrar mouse 3D",
|
||||
@@ -1746,12 +1764,26 @@
|
||||
"ui.space_mouse_calibration_no_response": "Um eixo quase não se moveu, então não dá para calibrá-lo. Recomece e mova a tampa mais longe a cada vez.",
|
||||
"ui.space_mouse_dead_zone": "Zona morta",
|
||||
"ui.space_mouse_dead_zone_tip": "Quanto a tampa precisa se mover antes de a vista se mover, como fração do curso completo. Aumente se a vista se mexer com a tampa parada.",
|
||||
"ui.space_mouse_driven_by": "Qual movimento calibrado do dispositivo controla este eixo. Escolher um que outro eixo já usa troca os dois.",
|
||||
"ui.space_mouse_driven_by_label": "controlado por",
|
||||
"ui.space_mouse_driver": "Driver do mouse 3D",
|
||||
"ui.space_mouse_driver_auto": "Automático",
|
||||
"ui.space_mouse_driver_direct": "Direto (hidraw)",
|
||||
"ui.space_mouse_driver_only_direct": "Lendo o dispositivo diretamente; o spacenavd não está em execução",
|
||||
"ui.space_mouse_driver_only_spacenavd": "Lendo pelo spacenavd, a única opção disponível",
|
||||
"ui.space_mouse_forget": "Esquecer calibração",
|
||||
"ui.space_mouse_hint": "Move e gira a vista juntos; esboços usam apenas deslocamento e zoom.",
|
||||
"ui.space_mouse_motion_rotation_x": "Rotação X",
|
||||
"ui.space_mouse_motion_rotation_y": "Rotação Y",
|
||||
"ui.space_mouse_motion_rotation_z": "Rotação Z",
|
||||
"ui.space_mouse_motion_translation_x": "Translação X",
|
||||
"ui.space_mouse_motion_translation_y": "Translação Y",
|
||||
"ui.space_mouse_motion_translation_z": "Translação Z",
|
||||
"ui.space_mouse_navigate": "Navegar com SpaceNavigator / SpaceMouse",
|
||||
"ui.space_mouse_none": "Nenhum mouse 3D encontrado",
|
||||
"ui.space_mouse_not_calibrated": "Não calibrado",
|
||||
"ui.space_mouse_reading": "Leitura",
|
||||
"ui.space_mouse_reset_axes": "Redefinir atribuição de eixos",
|
||||
"ui.space_mouse_step_lift": "Levante a tampa, segure e solte.",
|
||||
"ui.space_mouse_step_press": "Pressione a tampa para baixo, segure e solte.",
|
||||
"ui.space_mouse_step_pull": "Puxe a tampa na sua direção, segure e solte.",
|
||||
@@ -1765,6 +1797,16 @@
|
||||
"ui.space_mouse_step_tilt_right": "Incline a tampa para a direita (borda direita para baixo), segure e solte.",
|
||||
"ui.space_mouse_step_twist_clockwise": "Gire a tampa no sentido horário, segure e solte.",
|
||||
"ui.space_mouse_step_twist_counterclockwise": "Gire a tampa no sentido anti-horário, segure e solte.",
|
||||
"ui.space_mouse_view_axis": "Eixo da vista",
|
||||
"ui.space_mouse_view_off": "Desligado",
|
||||
"ui.space_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.",
|
||||
"ui.space_mouse_view_orbit_sideways": "Orbitar esquerda / direita",
|
||||
"ui.space_mouse_view_orbit_up_down": "Orbitar cima / baixo",
|
||||
"ui.space_mouse_view_pan_sideways": "Deslocar esquerda / direita",
|
||||
"ui.space_mouse_view_pan_up_down": "Deslocar cima / baixo",
|
||||
"ui.space_mouse_view_roll": "Girar a vista",
|
||||
"ui.space_mouse_view_unmapped": "Eixo {n}",
|
||||
"ui.space_mouse_view_zoom": "Zoom",
|
||||
"ui.spacing": "Espaçamento",
|
||||
"ui.spacing_2": "Espaçamento 2",
|
||||
"ui.sphere": "Esfera",
|
||||
@@ -1836,6 +1878,7 @@
|
||||
"ui.workspace_design_tabs": "Modelo · Superfície · Montagem · Inserir · Inspecionar · Gerenciar · Vista",
|
||||
"ui.workspace_drawing_description": "Desenhos 2D com vistas, cotas e legendas",
|
||||
"ui.workspace_footer": "Todo espaço de trabalho usa o mesmo projeto, histórico e seleção; só as guias da faixa de opções mudam. O seletor fica bloqueado enquanto um esboço ou recurso está sendo editado.",
|
||||
"ui.workspace_layout": "Layout do espaço de trabalho",
|
||||
"ui.workspace_manufacture_description": "Fatiamento, trajetórias de ferramenta e configuração",
|
||||
"ui.workspace_planned": "Planejado",
|
||||
"ui.workspace_render_description": "Pré-visualização do produto, configurações de cena e imagens renderizadas",
|
||||
|
||||
+18
-1
@@ -167,6 +167,7 @@
|
||||
"HISTORY": "ISTORIC",
|
||||
"Height": "Înălțime",
|
||||
"Hidden edges": "Muchii ascunse",
|
||||
"History layout": "Aspectul istoricului",
|
||||
"Holes": "Găuri",
|
||||
"Holes (edges on them)": "Găuri (muchiile lor)",
|
||||
"Home view": "Vizualizare de start",
|
||||
@@ -232,7 +233,6 @@
|
||||
"No crash reports.": "Niciun raport de blocare.",
|
||||
"No matching appearance presets.": "Niciun preset de aspect corespunzător.",
|
||||
"No matching appearances": "Niciun aspect corespunzător",
|
||||
"No visible bodies to export.": "Niciun corp vizibil de exportat.",
|
||||
"None": "Niciunul",
|
||||
"Not available yet": "Indisponibil deocamdată",
|
||||
"Notch": "Crestătură",
|
||||
@@ -1092,6 +1092,7 @@
|
||||
"ui.agent_chat_add": "Atașează fișiere",
|
||||
"ui.agent_chat_add_file": "Alegeți un fișier…",
|
||||
"ui.agent_chat_allow": "Permite",
|
||||
"ui.agent_chat_allow_once": "Permite o dată",
|
||||
"ui.agent_chat_allowed": "Permis",
|
||||
"ui.agent_chat_answered": "Răspuns: {option}",
|
||||
"ui.agent_chat_approval": "Permiteți agentului să facă asta?",
|
||||
@@ -1149,6 +1150,9 @@
|
||||
"ui.agent_chat_left": "Stânga",
|
||||
"ui.agent_chat_limit": "Agentul a atins limita de {limit} modificări pentru un mesaj. Trimiteți alt mesaj pentru a-l lăsa să continue.",
|
||||
"ui.agent_chat_minimize": "Minimizează într-o bandă",
|
||||
"ui.agent_chat_mode_all": "Tot",
|
||||
"ui.agent_chat_mode_ask": "Întreabă",
|
||||
"ui.agent_chat_mode_edits": "Modificări",
|
||||
"ui.agent_chat_model_tip": "Modelul folosit de agent. Alegerea dumneavoastră este reținută pentru acest agent.",
|
||||
"ui.agent_chat_new": "Chat nou",
|
||||
"ui.agent_chat_new_confirm": "Începeți un chat nou? Cel curent este șters.",
|
||||
@@ -1161,6 +1165,7 @@
|
||||
"ui.agent_chat_none_found_short": "Niciun agent găsit",
|
||||
"ui.agent_chat_not_installed": "Neinstalat",
|
||||
"ui.agent_chat_not_signed_in": "neautentificat",
|
||||
"ui.agent_chat_open_settings": "Deschide preferințele",
|
||||
"ui.agent_chat_own_window": "Fereastră proprie",
|
||||
"ui.agent_chat_paste_picture": "Lipește o imagine",
|
||||
"ui.agent_chat_permission": "Agentul cere să ruleze: {title}",
|
||||
@@ -1185,6 +1190,8 @@
|
||||
"ui.agent_chat_send": "Trimite",
|
||||
"ui.agent_chat_send_now": "Trimite acum",
|
||||
"ui.agent_chat_send_now_tip": "Oprește pasul curent al agentului și trimite acest mesaj următorul (Ctrl+Enter)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft comunică cu un agent de programare instalat pe acest computer. Alege unul în Preferințe › AI.",
|
||||
"ui.agent_chat_setup_title": "Configurează un asistent",
|
||||
"ui.agent_chat_side": "Partea chatului cu agentul",
|
||||
"ui.agent_chat_signed_in": "autentificat",
|
||||
"ui.agent_chat_state_auth": "Autentificare necesară",
|
||||
@@ -1201,6 +1208,7 @@
|
||||
"ui.agent_chat_undo_label": "Chat cu agentul: {message}",
|
||||
"ui.agent_chat_undone": "(anulat)",
|
||||
"ui.agent_chat_view_attached": "Vizualizare atașată",
|
||||
"ui.agent_chat_waiting": "Așteaptă răspunsul tău",
|
||||
"ui.agent_cursor": "Cursorul agentului",
|
||||
"ui.agent_cursor_speed": "Viteza cursorului agentului",
|
||||
"ui.ai_add": "Adaugă",
|
||||
@@ -1528,6 +1536,11 @@
|
||||
"ui.hideOrigin": "Ascunde originea",
|
||||
"ui.hidePlanes": "Ascunde planele",
|
||||
"ui.hideProfile": "Ascunde profilul",
|
||||
"ui.history_back": "Un pas înapoi",
|
||||
"ui.history_below_browser": "Sub navigator",
|
||||
"ui.history_first": "Mergi la început",
|
||||
"ui.history_forward": "Un pas înainte",
|
||||
"ui.history_last": "Mergi la sfârșit",
|
||||
"ui.history_title": "Istoric",
|
||||
"ui.hole": "Gaură",
|
||||
"ui.hole_tap_type": "Tip de filetare gaură",
|
||||
@@ -1545,6 +1558,8 @@
|
||||
"ui.joints": "Îmbinări",
|
||||
"ui.keyboard_shortcuts": "Scurtături de tastatură",
|
||||
"ui.language": "Limbă",
|
||||
"ui.layout_compact": "Compact",
|
||||
"ui.layout_floating": "Plutitor",
|
||||
"ui.length_decimals": "Zecimale lungime",
|
||||
"ui.light": "Luminos",
|
||||
"ui.line": "Linie",
|
||||
@@ -1682,6 +1697,7 @@
|
||||
"ui.reverse_zoom_direction": "Inversează direcția zoomului",
|
||||
"ui.revolve": "Revoluție",
|
||||
"ui.ribbon_exit": "Ieșire",
|
||||
"ui.ribbon_labels": "Afișează text sub pictogramele panglicii",
|
||||
"ui.ribbon_pin_footer": "Comenzile fixate apar pe bandă. Treceți cursorul peste un rând pentru a-l fixa sau defixa.",
|
||||
"ui.ribbon_workspace": "Spațiu de lucru",
|
||||
"ui.right": "Dreapta",
|
||||
@@ -1836,6 +1852,7 @@
|
||||
"ui.workspace_design_tabs": "Model · Suprafață · Asamblare · Inserare · Inspectare · Gestionare · Vizualizare",
|
||||
"ui.workspace_drawing_description": "Desene 2D cu vederi, cote și indicatoare",
|
||||
"ui.workspace_footer": "Fiecare spațiu de lucru lucrează pe același proiect, istoric și selecție; se schimbă doar filele benzii. Comutatorul este blocat cât timp se editează o schiță sau o operație.",
|
||||
"ui.workspace_layout": "Aspectul spațiului de lucru",
|
||||
"ui.workspace_manufacture_description": "Feliere, trasee de sculă și configurare",
|
||||
"ui.workspace_planned": "Planificat",
|
||||
"ui.workspace_render_description": "Previzualizare produs, setări de scenă și imagini randate",
|
||||
|
||||
+43
-1
@@ -167,6 +167,7 @@
|
||||
"HISTORY": "ИСТОРИЯ",
|
||||
"Height": "Высота",
|
||||
"Hidden edges": "Скрытые рёбра",
|
||||
"History layout": "Расположение истории",
|
||||
"Holes": "Отверстия",
|
||||
"Holes (edges on them)": "Отверстия (рёбра на них)",
|
||||
"Home view": "Главный вид",
|
||||
@@ -232,7 +233,6 @@
|
||||
"No crash reports.": "Нет отчётов о сбоях.",
|
||||
"No matching appearance presets.": "Нет подходящих наборов внешнего вида.",
|
||||
"No matching appearances": "Нет подходящих внешних видов",
|
||||
"No visible bodies to export.": "Нет видимых тел для экспорта.",
|
||||
"None": "Нет",
|
||||
"Not available yet": "Пока недоступно",
|
||||
"Notch": "Вырез",
|
||||
@@ -1092,6 +1092,7 @@
|
||||
"ui.agent_chat_add": "Прикрепить файлы",
|
||||
"ui.agent_chat_add_file": "Выбрать файл…",
|
||||
"ui.agent_chat_allow": "Разрешить",
|
||||
"ui.agent_chat_allow_once": "Разрешить один раз",
|
||||
"ui.agent_chat_allowed": "Разрешено",
|
||||
"ui.agent_chat_answered": "Ответ: {option}",
|
||||
"ui.agent_chat_approval": "Разрешить агенту сделать это?",
|
||||
@@ -1149,6 +1150,9 @@
|
||||
"ui.agent_chat_left": "Слева",
|
||||
"ui.agent_chat_limit": "Агент достиг предела в {limit} изменений на одно сообщение. Отправьте ещё одно сообщение, чтобы он продолжил.",
|
||||
"ui.agent_chat_minimize": "Свернуть в полосу",
|
||||
"ui.agent_chat_mode_all": "Всё",
|
||||
"ui.agent_chat_mode_ask": "Спросить",
|
||||
"ui.agent_chat_mode_edits": "Правки",
|
||||
"ui.agent_chat_model_tip": "Модель, которую использует агент. Ваш выбор запоминается для этого агента.",
|
||||
"ui.agent_chat_new": "Новый чат",
|
||||
"ui.agent_chat_new_confirm": "Начать новый чат? Текущий будет очищен.",
|
||||
@@ -1161,6 +1165,7 @@
|
||||
"ui.agent_chat_none_found_short": "Агент не найден",
|
||||
"ui.agent_chat_not_installed": "Не установлен",
|
||||
"ui.agent_chat_not_signed_in": "вход не выполнен",
|
||||
"ui.agent_chat_open_settings": "Открыть настройки",
|
||||
"ui.agent_chat_own_window": "Отдельное окно",
|
||||
"ui.agent_chat_paste_picture": "Вставить изображение",
|
||||
"ui.agent_chat_permission": "Агент просит выполнить: {title}",
|
||||
@@ -1185,6 +1190,8 @@
|
||||
"ui.agent_chat_send": "Отправить",
|
||||
"ui.agent_chat_send_now": "Отправить сейчас",
|
||||
"ui.agent_chat_send_now_tip": "Остановить текущий шаг агента и отправить это следующим (Ctrl+Enter)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft взаимодействует с агентом программирования, установленным на этом компьютере. Выберите его в разделе «Настройки › ИИ».",
|
||||
"ui.agent_chat_setup_title": "Настроить ассистента",
|
||||
"ui.agent_chat_side": "Сторона чата с агентом",
|
||||
"ui.agent_chat_signed_in": "вход выполнен",
|
||||
"ui.agent_chat_state_auth": "Требуется вход",
|
||||
@@ -1201,6 +1208,7 @@
|
||||
"ui.agent_chat_undo_label": "Чат с агентом: {message}",
|
||||
"ui.agent_chat_undone": "(отменено)",
|
||||
"ui.agent_chat_view_attached": "Вид прикреплён",
|
||||
"ui.agent_chat_waiting": "Ожидает вашего ответа",
|
||||
"ui.agent_cursor": "Курсор агента",
|
||||
"ui.agent_cursor_speed": "Скорость курсора агента",
|
||||
"ui.ai_add": "Добавить",
|
||||
@@ -1528,6 +1536,11 @@
|
||||
"ui.hideOrigin": "Скрыть начало координат",
|
||||
"ui.hidePlanes": "Скрыть плоскости",
|
||||
"ui.hideProfile": "Скрыть профиль",
|
||||
"ui.history_back": "На один шаг назад",
|
||||
"ui.history_below_browser": "Под браузером",
|
||||
"ui.history_first": "Перейти в начало",
|
||||
"ui.history_forward": "На один шаг вперёд",
|
||||
"ui.history_last": "Перейти в конец",
|
||||
"ui.history_title": "История",
|
||||
"ui.hole": "Отверстие",
|
||||
"ui.hole_tap_type": "Тип резьбы отверстия",
|
||||
@@ -1545,6 +1558,8 @@
|
||||
"ui.joints": "Сопряжения",
|
||||
"ui.keyboard_shortcuts": "Сочетания клавиш",
|
||||
"ui.language": "Язык",
|
||||
"ui.layout_compact": "Компактная",
|
||||
"ui.layout_floating": "Плавающая",
|
||||
"ui.length_decimals": "Знаков после запятой (длины)",
|
||||
"ui.light": "Светлая",
|
||||
"ui.line": "Линия",
|
||||
@@ -1682,6 +1697,7 @@
|
||||
"ui.reverse_zoom_direction": "Обратить направление масштабирования",
|
||||
"ui.revolve": "Вращение",
|
||||
"ui.ribbon_exit": "Выход",
|
||||
"ui.ribbon_labels": "Показывать текст под значками ленты",
|
||||
"ui.ribbon_pin_footer": "Закреплённые команды отображаются на ленте. Наведите курсор на строку, чтобы закрепить или открепить её.",
|
||||
"ui.ribbon_workspace": "Рабочая область",
|
||||
"ui.right": "Справа",
|
||||
@@ -1737,6 +1753,7 @@
|
||||
"ui.smooth_wheel_zoom": "Плавное масштабирование колесом",
|
||||
"ui.smooth_wheel_zoom_tip": "Отключите, чтобы каждое движение колеса применялось сразу, без сглаживания",
|
||||
"ui.snap": "Привязка",
|
||||
"ui.space_mouse_axes_reassigned": "Оси переназначены в настройках, и это действует и поверх этой калибровки.",
|
||||
"ui.space_mouse_calibrate": "Калибровать…",
|
||||
"ui.space_mouse_calibrate_tip": "Двигайте колпачок так, как просит окно, чтобы SolidCraft узнал, куда и насколько далеко движется каждая ось этой 3D-мыши. Сохраняется для этой 3D-мыши.",
|
||||
"ui.space_mouse_calibrate_title": "Калибровка 3D-мыши",
|
||||
@@ -1746,12 +1763,26 @@
|
||||
"ui.space_mouse_calibration_no_response": "Одна ось почти не сдвинулась, поэтому её нельзя откалибровать. Начните заново и каждый раз двигайте колпачок дальше.",
|
||||
"ui.space_mouse_dead_zone": "Мёртвая зона",
|
||||
"ui.space_mouse_dead_zone_tip": "Насколько нужно сдвинуть колпачок, прежде чем сдвинется вид, в доле от полного хода. Увеличьте, если вид смещается, когда колпачок в покое.",
|
||||
"ui.space_mouse_driven_by": "Какое откалиброванное движение устройства управляет этой осью. Если выбрать движение, которое уже использует другая ось, они поменяются местами.",
|
||||
"ui.space_mouse_driven_by_label": "управляется",
|
||||
"ui.space_mouse_driver": "Драйвер 3D-мыши",
|
||||
"ui.space_mouse_driver_auto": "Автоматически",
|
||||
"ui.space_mouse_driver_direct": "Напрямую (hidraw)",
|
||||
"ui.space_mouse_driver_only_direct": "Чтение напрямую с устройства; spacenavd не запущен",
|
||||
"ui.space_mouse_driver_only_spacenavd": "Чтение через spacenavd, других вариантов нет",
|
||||
"ui.space_mouse_forget": "Забыть калибровку",
|
||||
"ui.space_mouse_hint": "Совместное перемещение и вращение вида; в эскизе — только панорама и масштаб.",
|
||||
"ui.space_mouse_motion_rotation_x": "Вращение X",
|
||||
"ui.space_mouse_motion_rotation_y": "Вращение Y",
|
||||
"ui.space_mouse_motion_rotation_z": "Вращение Z",
|
||||
"ui.space_mouse_motion_translation_x": "Смещение X",
|
||||
"ui.space_mouse_motion_translation_y": "Смещение Y",
|
||||
"ui.space_mouse_motion_translation_z": "Смещение Z",
|
||||
"ui.space_mouse_navigate": "Навигация через SpaceNavigator / SpaceMouse",
|
||||
"ui.space_mouse_none": "3D-мышь не найдена",
|
||||
"ui.space_mouse_not_calibrated": "Не откалибрована",
|
||||
"ui.space_mouse_reading": "Показания",
|
||||
"ui.space_mouse_reset_axes": "Сбросить назначение осей",
|
||||
"ui.space_mouse_step_lift": "Поднимите колпачок вверх, удержите и отпустите.",
|
||||
"ui.space_mouse_step_press": "Нажмите на колпачок вниз, удержите и отпустите.",
|
||||
"ui.space_mouse_step_pull": "Потяните колпачок на себя, удержите и отпустите.",
|
||||
@@ -1765,6 +1796,16 @@
|
||||
"ui.space_mouse_step_tilt_right": "Наклоните колпачок вправо (правый край вниз), удержите и отпустите.",
|
||||
"ui.space_mouse_step_twist_clockwise": "Поверните колпачок по часовой стрелке, удержите и отпустите.",
|
||||
"ui.space_mouse_step_twist_counterclockwise": "Поверните колпачок против часовой стрелки, удержите и отпустите.",
|
||||
"ui.space_mouse_view_axis": "Ось вида",
|
||||
"ui.space_mouse_view_off": "Выкл.",
|
||||
"ui.space_mouse_view_off_tip": "Профиль драйвера 3D-мыши отключает эту ось, поэтому её движение не двигает вид. Если выбрать здесь движение другой оси, они поменяются местами, и это движение перестанет двигать вид.",
|
||||
"ui.space_mouse_view_orbit_sideways": "Орбита влево / вправо",
|
||||
"ui.space_mouse_view_orbit_up_down": "Орбита вверх / вниз",
|
||||
"ui.space_mouse_view_pan_sideways": "Сдвиг влево / вправо",
|
||||
"ui.space_mouse_view_pan_up_down": "Сдвиг вверх / вниз",
|
||||
"ui.space_mouse_view_roll": "Крен",
|
||||
"ui.space_mouse_view_unmapped": "Ось {n}",
|
||||
"ui.space_mouse_view_zoom": "Масштаб",
|
||||
"ui.spacing": "Интервал",
|
||||
"ui.spacing_2": "Интервал 2",
|
||||
"ui.sphere": "Сфера",
|
||||
@@ -1836,6 +1877,7 @@
|
||||
"ui.workspace_design_tabs": "Модель · Поверхность · Сборка · Вставка · Проверка · Управление · Вид",
|
||||
"ui.workspace_drawing_description": "2D-чертежи с видами, размерами и основными надписями",
|
||||
"ui.workspace_footer": "Все рабочие среды работают с одним дизайном, историей и выбором; меняются только вкладки ленты. Переключатель заблокирован, пока редактируется эскиз или операция.",
|
||||
"ui.workspace_layout": "Компоновка рабочей области",
|
||||
"ui.workspace_manufacture_description": "Слайсинг, траектории инструмента и наладка",
|
||||
"ui.workspace_planned": "Запланировано",
|
||||
"ui.workspace_render_description": "Предпросмотр изделия, настройки сцены и отрендеренные изображения",
|
||||
|
||||
+18
-1
@@ -167,6 +167,7 @@
|
||||
"HISTORY": "HISTORIK",
|
||||
"Height": "Höjd",
|
||||
"Hidden edges": "Dolda kanter",
|
||||
"History layout": "Historiklayout",
|
||||
"Holes": "Hål",
|
||||
"Holes (edges on them)": "Hål (kanter på dem)",
|
||||
"Home view": "Hemvy",
|
||||
@@ -232,7 +233,6 @@
|
||||
"No crash reports.": "Inga kraschrapporter.",
|
||||
"No matching appearance presets.": "Inga matchande utseendeförinställningar.",
|
||||
"No matching appearances": "Inga matchande utseenden",
|
||||
"No visible bodies to export.": "Inga synliga kroppar att exportera.",
|
||||
"None": "Ingen",
|
||||
"Not available yet": "Inte tillgänglig ännu",
|
||||
"Notch": "Urtag",
|
||||
@@ -1092,6 +1092,7 @@
|
||||
"ui.agent_chat_add": "Bifoga filer",
|
||||
"ui.agent_chat_add_file": "Välj en fil…",
|
||||
"ui.agent_chat_allow": "Tillåt",
|
||||
"ui.agent_chat_allow_once": "Tillåt en gång",
|
||||
"ui.agent_chat_allowed": "Tillåten",
|
||||
"ui.agent_chat_answered": "Svar: {option}",
|
||||
"ui.agent_chat_approval": "Tillåta agenten att göra detta?",
|
||||
@@ -1149,6 +1150,9 @@
|
||||
"ui.agent_chat_left": "Vänster",
|
||||
"ui.agent_chat_limit": "Agenten nådde gränsen på {limit} ändringar för ett meddelande. Skicka ett nytt meddelande så att den kan fortsätta.",
|
||||
"ui.agent_chat_minimize": "Minimera till ett fält",
|
||||
"ui.agent_chat_mode_all": "Allt",
|
||||
"ui.agent_chat_mode_ask": "Fråga",
|
||||
"ui.agent_chat_mode_edits": "Ändringar",
|
||||
"ui.agent_chat_model_tip": "Modellen som agenten använder. Ditt val kommer ihåg för den här agenten.",
|
||||
"ui.agent_chat_new": "Ny chatt",
|
||||
"ui.agent_chat_new_confirm": "Starta en ny chatt? Den nuvarande rensas.",
|
||||
@@ -1161,6 +1165,7 @@
|
||||
"ui.agent_chat_none_found_short": "Ingen agent hittades",
|
||||
"ui.agent_chat_not_installed": "Inte installerad",
|
||||
"ui.agent_chat_not_signed_in": "inte inloggad",
|
||||
"ui.agent_chat_open_settings": "Öppna inställningar",
|
||||
"ui.agent_chat_own_window": "Eget fönster",
|
||||
"ui.agent_chat_paste_picture": "Klistra in en bild",
|
||||
"ui.agent_chat_permission": "Agenten ber om att få köra: {title}",
|
||||
@@ -1185,6 +1190,8 @@
|
||||
"ui.agent_chat_send": "Skicka",
|
||||
"ui.agent_chat_send_now": "Skicka nu",
|
||||
"ui.agent_chat_send_now_tip": "Stoppa agentens nuvarande steg och skicka detta härnäst (Ctrl+Enter)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft kommunicerar med en kodningsagent som är installerad på den här datorn. Välj en under Inställningar › AI.",
|
||||
"ui.agent_chat_setup_title": "Konfigurera en assistent",
|
||||
"ui.agent_chat_side": "Agentchattens sida",
|
||||
"ui.agent_chat_signed_in": "inloggad",
|
||||
"ui.agent_chat_state_auth": "Inloggning krävs",
|
||||
@@ -1201,6 +1208,7 @@
|
||||
"ui.agent_chat_undo_label": "Agentchatt: {message}",
|
||||
"ui.agent_chat_undone": "(ångrad)",
|
||||
"ui.agent_chat_view_attached": "Vy bifogad",
|
||||
"ui.agent_chat_waiting": "Väntar på dig",
|
||||
"ui.agent_cursor": "Agentmarkör",
|
||||
"ui.agent_cursor_speed": "Agentmarkörens hastighet",
|
||||
"ui.ai_add": "Lägg till",
|
||||
@@ -1528,6 +1536,11 @@
|
||||
"ui.hideOrigin": "Dölj origo",
|
||||
"ui.hidePlanes": "Dölj plan",
|
||||
"ui.hideProfile": "Dölj profil",
|
||||
"ui.history_back": "Gå ett steg bakåt",
|
||||
"ui.history_below_browser": "Under webbläsaren",
|
||||
"ui.history_first": "Gå till början",
|
||||
"ui.history_forward": "Gå ett steg framåt",
|
||||
"ui.history_last": "Gå till slutet",
|
||||
"ui.history_title": "Historik",
|
||||
"ui.hole": "Hål",
|
||||
"ui.hole_tap_type": "Typ av gängning för hål",
|
||||
@@ -1545,6 +1558,8 @@
|
||||
"ui.joints": "Leder",
|
||||
"ui.keyboard_shortcuts": "Kortkommandon",
|
||||
"ui.language": "Språk",
|
||||
"ui.layout_compact": "Kompakt",
|
||||
"ui.layout_floating": "Flytande",
|
||||
"ui.length_decimals": "Längddecimaler",
|
||||
"ui.light": "Ljust",
|
||||
"ui.line": "Linje",
|
||||
@@ -1682,6 +1697,7 @@
|
||||
"ui.reverse_zoom_direction": "Vänd zoomriktning",
|
||||
"ui.revolve": "Rotation",
|
||||
"ui.ribbon_exit": "Avsluta",
|
||||
"ui.ribbon_labels": "Visa text under menyfliksikoner",
|
||||
"ui.ribbon_pin_footer": "Fästa kommandon visas i menyfliken. Håll muspekaren över en rad för att fästa eller lossa den.",
|
||||
"ui.ribbon_workspace": "Arbetsyta",
|
||||
"ui.right": "Höger",
|
||||
@@ -1836,6 +1852,7 @@
|
||||
"ui.workspace_design_tabs": "Modell · Yta · Montera · Infoga · Granska · Hantera · Vy",
|
||||
"ui.workspace_drawing_description": "2D-ritningar med vyer, mått och titelrutor",
|
||||
"ui.workspace_footer": "Varje arbetsyta arbetar med samma konstruktion, historik och markering; endast menyflikarna ändras. Växlaren är låst medan en skiss eller funktion redigeras.",
|
||||
"ui.workspace_layout": "Arbetsytans layout",
|
||||
"ui.workspace_manufacture_description": "Slicing, verktygsbanor och uppsättning",
|
||||
"ui.workspace_planned": "Planerad",
|
||||
"ui.workspace_render_description": "Produktförhandsvisning, sceninställningar och renderade bilder",
|
||||
|
||||
+43
-1
@@ -167,6 +167,7 @@
|
||||
"HISTORY": "GEÇMİŞ",
|
||||
"Height": "Yükseklik",
|
||||
"Hidden edges": "Gizli kenarlar",
|
||||
"History layout": "Geçmiş düzeni",
|
||||
"Holes": "Delikler",
|
||||
"Holes (edges on them)": "Delikler (kenarları)",
|
||||
"Home view": "Başlangıç görünümü",
|
||||
@@ -232,7 +233,6 @@
|
||||
"No crash reports.": "Çökme raporu yok.",
|
||||
"No matching appearance presets.": "Eşleşen görünüm ön ayarı yok.",
|
||||
"No matching appearances": "Eşleşen görünüm yok",
|
||||
"No visible bodies to export.": "Dışa aktarılacak görünür gövde yok.",
|
||||
"None": "Hiçbiri",
|
||||
"Not available yet": "Henüz kullanılamıyor",
|
||||
"Notch": "Çentik",
|
||||
@@ -1092,6 +1092,7 @@
|
||||
"ui.agent_chat_add": "Dosya ekle",
|
||||
"ui.agent_chat_add_file": "Dosya seç…",
|
||||
"ui.agent_chat_allow": "İzin ver",
|
||||
"ui.agent_chat_allow_once": "Bir kez izin ver",
|
||||
"ui.agent_chat_allowed": "İzin verildi",
|
||||
"ui.agent_chat_answered": "Yanıt: {option}",
|
||||
"ui.agent_chat_approval": "Ajanın bunu yapmasına izin verilsin mi?",
|
||||
@@ -1149,6 +1150,9 @@
|
||||
"ui.agent_chat_left": "Sol",
|
||||
"ui.agent_chat_limit": "Ajan, bir mesaj için {limit} değişiklik sınırına ulaştı. Devam etmesi için başka bir mesaj gönderin.",
|
||||
"ui.agent_chat_minimize": "Bir çubuğa küçült",
|
||||
"ui.agent_chat_mode_all": "Tümü",
|
||||
"ui.agent_chat_mode_ask": "Sor",
|
||||
"ui.agent_chat_mode_edits": "Düzenle",
|
||||
"ui.agent_chat_model_tip": "Ajanın kullandığı model. Seçiminiz bu ajan için hatırlanır.",
|
||||
"ui.agent_chat_new": "Yeni sohbet",
|
||||
"ui.agent_chat_new_confirm": "Yeni bir sohbet başlatılsın mı? Geçerli sohbet temizlenir.",
|
||||
@@ -1161,6 +1165,7 @@
|
||||
"ui.agent_chat_none_found_short": "Ajan bulunamadı",
|
||||
"ui.agent_chat_not_installed": "Yüklü değil",
|
||||
"ui.agent_chat_not_signed_in": "oturum açık değil",
|
||||
"ui.agent_chat_open_settings": "Ayarları aç",
|
||||
"ui.agent_chat_own_window": "Ayrı pencere",
|
||||
"ui.agent_chat_paste_picture": "Görsel yapıştır",
|
||||
"ui.agent_chat_permission": "Ajan şunu çalıştırmak istiyor: {title}",
|
||||
@@ -1185,6 +1190,8 @@
|
||||
"ui.agent_chat_send": "Gönder",
|
||||
"ui.agent_chat_send_now": "Şimdi gönder",
|
||||
"ui.agent_chat_send_now_tip": "Ajanın geçerli adımını durdurur ve bunu sonraki olarak gönderir (Ctrl+Enter)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft, bu bilgisayarda yüklü bir kodlama aracısıyla iletişim kurar. Ayarlar › AI bölümünden birini seçin.",
|
||||
"ui.agent_chat_setup_title": "Asistan ayarla",
|
||||
"ui.agent_chat_side": "Ajan sohbeti konumu",
|
||||
"ui.agent_chat_signed_in": "oturum açık",
|
||||
"ui.agent_chat_state_auth": "Oturum açılması gerekiyor",
|
||||
@@ -1201,6 +1208,7 @@
|
||||
"ui.agent_chat_undo_label": "Ajan sohbeti: {message}",
|
||||
"ui.agent_chat_undone": "(geri alındı)",
|
||||
"ui.agent_chat_view_attached": "Görünüm eklendi",
|
||||
"ui.agent_chat_waiting": "Yanıtınız bekleniyor",
|
||||
"ui.agent_cursor": "Ajan imleci",
|
||||
"ui.agent_cursor_speed": "Ajan imleci hızı",
|
||||
"ui.ai_add": "Ekle",
|
||||
@@ -1528,6 +1536,11 @@
|
||||
"ui.hideOrigin": "Orijini gizle",
|
||||
"ui.hidePlanes": "Düzlemleri gizle",
|
||||
"ui.hideProfile": "Profili gizle",
|
||||
"ui.history_back": "Bir adım geri git",
|
||||
"ui.history_below_browser": "Tarayıcının altında",
|
||||
"ui.history_first": "Başlangıca git",
|
||||
"ui.history_forward": "Bir adım ileri git",
|
||||
"ui.history_last": "Sona git",
|
||||
"ui.history_title": "Geçmiş",
|
||||
"ui.hole": "Delik",
|
||||
"ui.hole_tap_type": "Diş türü",
|
||||
@@ -1545,6 +1558,8 @@
|
||||
"ui.joints": "Bağlantılar",
|
||||
"ui.keyboard_shortcuts": "Klavye kısayolları",
|
||||
"ui.language": "Dil",
|
||||
"ui.layout_compact": "Kompakt",
|
||||
"ui.layout_floating": "Kayan",
|
||||
"ui.length_decimals": "Uzunluk ondalık basamakları",
|
||||
"ui.light": "Açık",
|
||||
"ui.line": "Çizgi",
|
||||
@@ -1682,6 +1697,7 @@
|
||||
"ui.reverse_zoom_direction": "Yakınlaştırma yönünü tersine çevir",
|
||||
"ui.revolve": "Döndür",
|
||||
"ui.ribbon_exit": "Çıkış",
|
||||
"ui.ribbon_labels": "Şerit simgelerinin altında metin göster",
|
||||
"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ğ",
|
||||
@@ -1737,6 +1753,7 @@
|
||||
"ui.smooth_wheel_zoom": "Yumuşak tekerlek yakınlaştırması",
|
||||
"ui.smooth_wheel_zoom_tip": "Her tekerlek hareketini yumuşatma olmadan hemen uygulamak için kapatın",
|
||||
"ui.snap": "Yakala",
|
||||
"ui.space_mouse_axes_reassigned": "Eksenler Tercihler'de yeniden atanmış; bu, bu kalibrasyonun üstüne de uygulanır.",
|
||||
"ui.space_mouse_calibrate": "Kalibre et…",
|
||||
"ui.space_mouse_calibrate_tip": "Pencerenin istediği gibi başlığı hareket ettirin; SolidCraft bu 3B farenin her ekseninin hangi yöne ve ne kadar gittiğini öğrensin. Bu 3B fare için kaydedilir.",
|
||||
"ui.space_mouse_calibrate_title": "3B fareyi kalibre et",
|
||||
@@ -1746,12 +1763,26 @@
|
||||
"ui.space_mouse_calibration_no_response": "Bir eksen neredeyse hiç hareket etmedi, bu yüzden kalibre edilemiyor. Baştan başlayın ve başlığı her seferinde daha fazla hareket ettirin.",
|
||||
"ui.space_mouse_dead_zone": "Ölü bölge",
|
||||
"ui.space_mouse_dead_zone_tip": "Görünüm hareket etmeden önce başlığın ne kadar hareket etmesi gerektiği, tam hareket aralığının oranı olarak. Başlık dururken görünüm kayıyorsa artırın.",
|
||||
"ui.space_mouse_driven_by": "Bu ekseni cihazın hangi kalibre edilmiş hareketinin yönettiği. Başka bir eksenin zaten kullandığını seçerseniz ikisi yer değiştirir.",
|
||||
"ui.space_mouse_driven_by_label": "kumanda eden",
|
||||
"ui.space_mouse_driver": "3B fare sürücüsü",
|
||||
"ui.space_mouse_driver_auto": "Otomatik",
|
||||
"ui.space_mouse_driver_direct": "Doğrudan (hidraw)",
|
||||
"ui.space_mouse_driver_only_direct": "Aygıt doğrudan okunuyor; spacenavd çalışmıyor",
|
||||
"ui.space_mouse_driver_only_spacenavd": "spacenavd üzerinden okunuyor, tek seçenek bu",
|
||||
"ui.space_mouse_forget": "Kalibrasyonu unut",
|
||||
"ui.space_mouse_hint": "Görünümü aynı anda kaydırın ve döndürün; eskizlerde yalnızca kaydırma ve yakınlaştırma kullanılır.",
|
||||
"ui.space_mouse_motion_rotation_x": "Dönme X",
|
||||
"ui.space_mouse_motion_rotation_y": "Dönme Y",
|
||||
"ui.space_mouse_motion_rotation_z": "Dönme Z",
|
||||
"ui.space_mouse_motion_translation_x": "Öteleme X",
|
||||
"ui.space_mouse_motion_translation_y": "Öteleme Y",
|
||||
"ui.space_mouse_motion_translation_z": "Öteleme Z",
|
||||
"ui.space_mouse_navigate": "SpaceNavigator / SpaceMouse ile gezin",
|
||||
"ui.space_mouse_none": "3B fare bulunamadı",
|
||||
"ui.space_mouse_not_calibrated": "Kalibre edilmedi",
|
||||
"ui.space_mouse_reading": "Okunan değer",
|
||||
"ui.space_mouse_reset_axes": "Eksen atamasını sıfırla",
|
||||
"ui.space_mouse_step_lift": "Başlığı yukarı kaldırın, tutun, sonra bırakın.",
|
||||
"ui.space_mouse_step_press": "Başlığı aşağı bastırın, tutun, sonra bırakın.",
|
||||
"ui.space_mouse_step_pull": "Başlığı kendinize doğru çekin, tutun, sonra bırakın.",
|
||||
@@ -1765,6 +1796,16 @@
|
||||
"ui.space_mouse_step_tilt_right": "Başlığı sağa eğin (sağ kenar aşağı), tutun, sonra bırakın.",
|
||||
"ui.space_mouse_step_twist_clockwise": "Başlığı saat yönünde çevirin, tutun, sonra bırakın.",
|
||||
"ui.space_mouse_step_twist_counterclockwise": "Başlığı saat yönünün tersine çevirin, tutun, sonra bırakın.",
|
||||
"ui.space_mouse_view_axis": "Görünüm ekseni",
|
||||
"ui.space_mouse_view_off": "Kapalı",
|
||||
"ui.space_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.",
|
||||
"ui.space_mouse_view_orbit_sideways": "Sola / sağa yörünge",
|
||||
"ui.space_mouse_view_orbit_up_down": "Yukarı / aşağı yörünge",
|
||||
"ui.space_mouse_view_pan_sideways": "Sola / sağa kaydır",
|
||||
"ui.space_mouse_view_pan_up_down": "Yukarı / aşağı kaydır",
|
||||
"ui.space_mouse_view_roll": "Yuvarlanma",
|
||||
"ui.space_mouse_view_unmapped": "Eksen {n}",
|
||||
"ui.space_mouse_view_zoom": "Yakınlaştır",
|
||||
"ui.spacing": "Aralık",
|
||||
"ui.spacing_2": "Aralık 2",
|
||||
"ui.sphere": "Küre",
|
||||
@@ -1836,6 +1877,7 @@
|
||||
"ui.workspace_design_tabs": "Modelle · Yüzey · Montajla · Ekle · İncele · Yönet · Görünüm",
|
||||
"ui.workspace_drawing_description": "Görünümler, ölçüler ve başlık bloklarıyla 2B teknik resimler",
|
||||
"ui.workspace_footer": "Her çalışma alanı aynı tasarım, geçmiş ve seçim üzerinde çalışır; yalnızca şerit sekmeleri değişir. Bir eskiz veya özellik düzenlenirken geçiş kilitlidir.",
|
||||
"ui.workspace_layout": "Çalışma alanı düzeni",
|
||||
"ui.workspace_manufacture_description": "Dilimleme, takım yolları ve kurulum",
|
||||
"ui.workspace_planned": "Planlanan",
|
||||
"ui.workspace_render_description": "Ürün önizlemesi, sahne ayarları ve render görüntüleri",
|
||||
|
||||
+45
-2
@@ -167,6 +167,7 @@
|
||||
"HISTORY": "ІСТОРІЯ",
|
||||
"Height": "Висота",
|
||||
"Hidden edges": "Приховані ребра",
|
||||
"History layout": "Розташування історії",
|
||||
"Holes": "Отвори",
|
||||
"Holes (edges on them)": "Отвори (ребра на них)",
|
||||
"Home view": "Головний вид",
|
||||
@@ -232,7 +233,7 @@
|
||||
"No crash reports.": "Немає звітів про збої.",
|
||||
"No matching appearance presets.": "Немає відповідних наборів вигляду.",
|
||||
"No matching appearances": "Немає відповідних виглядів",
|
||||
"No visible bodies to export.": "Немає видимих тіл для експорту.",
|
||||
"No visible bodies or sketches to export.": "Немає видимих тіл або ескізів для експорту.",
|
||||
"None": "Немає",
|
||||
"Not available yet": "Ще недоступно",
|
||||
"Notch": "Виріз",
|
||||
@@ -831,7 +832,7 @@
|
||||
"dxf.progress.projection": "Проєктування геометрії (3/5)",
|
||||
"dxf.progress.write": "Запис DXF (5/5)",
|
||||
"dxf.tolerance": "Допуск кривої",
|
||||
"dxf.tolerance_help": "Лише видимі тіла. Криві подаються відрізками. Менший допуск дає точніші криві, але потребує більше часу й пам'яті.",
|
||||
"dxf.tolerance_help": "Лише видимі тіла та показані ескізи. Криві подаються відрізками. Менший допуск дає точніші криві, але потребує більше часу й пам'яті.",
|
||||
"file_load.built": "Побудовано: {built} із {total} елементів.",
|
||||
"file_load.converting": "Перетворення тіла {done} із {total}",
|
||||
"file_load.inserting": "Вставлення геометрії",
|
||||
@@ -1092,6 +1093,7 @@
|
||||
"ui.agent_chat_add": "Прикріпити файли",
|
||||
"ui.agent_chat_add_file": "Вибрати файл…",
|
||||
"ui.agent_chat_allow": "Дозволити",
|
||||
"ui.agent_chat_allow_once": "Дозволити один раз",
|
||||
"ui.agent_chat_allowed": "Дозволено",
|
||||
"ui.agent_chat_answered": "Відповідь: {option}",
|
||||
"ui.agent_chat_approval": "Дозволити агенту це зробити?",
|
||||
@@ -1149,6 +1151,9 @@
|
||||
"ui.agent_chat_left": "Ліворуч",
|
||||
"ui.agent_chat_limit": "Агент досяг межі {limit} змін на одне повідомлення. Надішліть інше повідомлення, щоб дозволити йому продовжити.",
|
||||
"ui.agent_chat_minimize": "Згорнути до смужки",
|
||||
"ui.agent_chat_mode_all": "Усе",
|
||||
"ui.agent_chat_mode_ask": "Запит",
|
||||
"ui.agent_chat_mode_edits": "Зміни",
|
||||
"ui.agent_chat_model_tip": "Модель, яку використовує агент. Ваш вибір запам'ятовується для цього агента.",
|
||||
"ui.agent_chat_new": "Новий чат",
|
||||
"ui.agent_chat_new_confirm": "Почати новий чат? Поточний буде очищено.",
|
||||
@@ -1161,6 +1166,7 @@
|
||||
"ui.agent_chat_none_found_short": "Агента не знайдено",
|
||||
"ui.agent_chat_not_installed": "Не встановлено",
|
||||
"ui.agent_chat_not_signed_in": "не виконано вхід",
|
||||
"ui.agent_chat_open_settings": "Відкрити налаштування",
|
||||
"ui.agent_chat_own_window": "Власне вікно",
|
||||
"ui.agent_chat_paste_picture": "Вставити зображення",
|
||||
"ui.agent_chat_permission": "Агент просить виконати: {title}",
|
||||
@@ -1185,6 +1191,8 @@
|
||||
"ui.agent_chat_send": "Надіслати",
|
||||
"ui.agent_chat_send_now": "Надіслати зараз",
|
||||
"ui.agent_chat_send_now_tip": "Зупинити поточний крок агента й надіслати це наступним (Ctrl+Enter)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft взаємодіє з агентом програмування, установленим на цьому комп’ютері. Виберіть його в розділі «Налаштування › ШІ».",
|
||||
"ui.agent_chat_setup_title": "Налаштувати асистента",
|
||||
"ui.agent_chat_side": "Сторона чату з агентом",
|
||||
"ui.agent_chat_signed_in": "виконано вхід",
|
||||
"ui.agent_chat_state_auth": "Потрібен вхід",
|
||||
@@ -1201,6 +1209,7 @@
|
||||
"ui.agent_chat_undo_label": "Чат з агентом: {message}",
|
||||
"ui.agent_chat_undone": "(скасовано)",
|
||||
"ui.agent_chat_view_attached": "Вид прикріплено",
|
||||
"ui.agent_chat_waiting": "Очікує на вашу відповідь",
|
||||
"ui.agent_cursor": "Курсор агента",
|
||||
"ui.agent_cursor_speed": "Швидкість курсора агента",
|
||||
"ui.ai_add": "Додати",
|
||||
@@ -1528,6 +1537,11 @@
|
||||
"ui.hideOrigin": "Приховати початок координат",
|
||||
"ui.hidePlanes": "Приховати площини",
|
||||
"ui.hideProfile": "Приховати профіль",
|
||||
"ui.history_back": "На один крок назад",
|
||||
"ui.history_below_browser": "Під браузером",
|
||||
"ui.history_first": "Перейти на початок",
|
||||
"ui.history_forward": "На один крок уперед",
|
||||
"ui.history_last": "Перейти в кінець",
|
||||
"ui.history_title": "Історія",
|
||||
"ui.hole": "Отвір",
|
||||
"ui.hole_tap_type": "Тип різьби отвору",
|
||||
@@ -1545,6 +1559,8 @@
|
||||
"ui.joints": "З'єднання",
|
||||
"ui.keyboard_shortcuts": "Комбінації клавіш",
|
||||
"ui.language": "Мова",
|
||||
"ui.layout_compact": "Компактне",
|
||||
"ui.layout_floating": "Плаваюче",
|
||||
"ui.length_decimals": "Знаки після коми для довжин",
|
||||
"ui.light": "Світла",
|
||||
"ui.line": "Пряма",
|
||||
@@ -1682,6 +1698,7 @@
|
||||
"ui.reverse_zoom_direction": "Змінити напрямок масштабування",
|
||||
"ui.revolve": "Обертання",
|
||||
"ui.ribbon_exit": "Вихід",
|
||||
"ui.ribbon_labels": "Показувати текст під піктограмами стрічки",
|
||||
"ui.ribbon_pin_footer": "Закріплені команди відображаються на стрічці. Наведіть курсор на рядок, щоб закріпити або відкріпити його.",
|
||||
"ui.ribbon_workspace": "Робоча область",
|
||||
"ui.right": "Справа",
|
||||
@@ -1737,6 +1754,7 @@
|
||||
"ui.smooth_wheel_zoom": "Плавне масштабування колесом",
|
||||
"ui.smooth_wheel_zoom_tip": "Вимкніть, щоб застосовувати кожну подію колеса негайно, без плавного завершення",
|
||||
"ui.snap": "Прив'язка",
|
||||
"ui.space_mouse_axes_reassigned": "Осі перепризначено в налаштуваннях, і це діє й поверх цього калібрування.",
|
||||
"ui.space_mouse_calibrate": "Калібрувати…",
|
||||
"ui.space_mouse_calibrate_tip": "Рухайте ковпачок так, як просить вікно, щоб SolidCraft дізнався, куди й наскільки далеко рухається кожна вісь цієї 3D-миші. Зберігається для цієї 3D-миші.",
|
||||
"ui.space_mouse_calibrate_title": "Калібрування 3D-миші",
|
||||
@@ -1746,12 +1764,26 @@
|
||||
"ui.space_mouse_calibration_no_response": "Одна вісь майже не зрушила, тому її не можна відкалібрувати. Почніть спочатку й щоразу рухайте ковпачок далі.",
|
||||
"ui.space_mouse_dead_zone": "Мертва зона",
|
||||
"ui.space_mouse_dead_zone_tip": "Наскільки треба зсунути ковпачок, перш ніж зрушить вигляд, у частці від повного ходу. Збільште, якщо вигляд зміщується, коли ковпачок у спокої.",
|
||||
"ui.space_mouse_driven_by": "Який відкалібрований рух пристрою керує цією віссю. Якщо вибрати рух, який уже використовує інша вісь, вони поміняються місцями.",
|
||||
"ui.space_mouse_driven_by_label": "керується",
|
||||
"ui.space_mouse_driver": "Драйвер 3D-миші",
|
||||
"ui.space_mouse_driver_auto": "Автоматично",
|
||||
"ui.space_mouse_driver_direct": "Напряму (hidraw)",
|
||||
"ui.space_mouse_driver_only_direct": "Читання напряму з пристрою; spacenavd не запущено",
|
||||
"ui.space_mouse_driver_only_spacenavd": "Читання через spacenavd, інших варіантів немає",
|
||||
"ui.space_mouse_forget": "Забути калібрування",
|
||||
"ui.space_mouse_hint": "Переміщуйте й обертайте вид разом; в ескізах доступні лише панорамування та масштаб.",
|
||||
"ui.space_mouse_motion_rotation_x": "Обертання X",
|
||||
"ui.space_mouse_motion_rotation_y": "Обертання Y",
|
||||
"ui.space_mouse_motion_rotation_z": "Обертання Z",
|
||||
"ui.space_mouse_motion_translation_x": "Зміщення X",
|
||||
"ui.space_mouse_motion_translation_y": "Зміщення Y",
|
||||
"ui.space_mouse_motion_translation_z": "Зміщення Z",
|
||||
"ui.space_mouse_navigate": "Навігація за допомогою SpaceNavigator / SpaceMouse",
|
||||
"ui.space_mouse_none": "3D-мишу не знайдено",
|
||||
"ui.space_mouse_not_calibrated": "Не відкалібровано",
|
||||
"ui.space_mouse_reading": "Показники",
|
||||
"ui.space_mouse_reset_axes": "Скинути призначення осей",
|
||||
"ui.space_mouse_step_lift": "Підніміть ковпачок угору, утримайте й відпустіть.",
|
||||
"ui.space_mouse_step_press": "Натисніть ковпачок униз, утримайте й відпустіть.",
|
||||
"ui.space_mouse_step_pull": "Потягніть ковпачок до себе, утримайте й відпустіть.",
|
||||
@@ -1765,6 +1797,16 @@
|
||||
"ui.space_mouse_step_tilt_right": "Нахиліть ковпачок праворуч (правий край униз), утримайте й відпустіть.",
|
||||
"ui.space_mouse_step_twist_clockwise": "Поверніть ковпачок за годинниковою стрілкою, утримайте й відпустіть.",
|
||||
"ui.space_mouse_step_twist_counterclockwise": "Поверніть ковпачок проти годинникової стрілки, утримайте й відпустіть.",
|
||||
"ui.space_mouse_view_axis": "Вісь вигляду",
|
||||
"ui.space_mouse_view_off": "Вимк.",
|
||||
"ui.space_mouse_view_off_tip": "Профіль драйвера 3D-миші вимикає цю вісь, тож її рух не рухає вигляд. Якщо вибрати тут рух іншої осі, вони поміняються місцями, і цей рух перестане рухати вигляд.",
|
||||
"ui.space_mouse_view_orbit_sideways": "Орбіта ліворуч / праворуч",
|
||||
"ui.space_mouse_view_orbit_up_down": "Орбіта вгору / вниз",
|
||||
"ui.space_mouse_view_pan_sideways": "Зсув ліворуч / праворуч",
|
||||
"ui.space_mouse_view_pan_up_down": "Зсув угору / вниз",
|
||||
"ui.space_mouse_view_roll": "Крен",
|
||||
"ui.space_mouse_view_unmapped": "Вісь {n}",
|
||||
"ui.space_mouse_view_zoom": "Масштаб",
|
||||
"ui.spacing": "Крок",
|
||||
"ui.spacing_2": "Крок 2",
|
||||
"ui.sphere": "Куля",
|
||||
@@ -1836,6 +1878,7 @@
|
||||
"ui.workspace_design_tabs": "Модель · Поверхня · Збірка · Вставка · Перевірка · Керування · Вигляд",
|
||||
"ui.workspace_drawing_description": "2D-креслення з видами, розмірами та штампами",
|
||||
"ui.workspace_footer": "Кожне робоче середовище працює з тим самим проєктом, історією та вибором; змінюються лише вкладки стрічки. Перемикач заблоковано, поки редагується ескіз або елемент.",
|
||||
"ui.workspace_layout": "Компонування робочої області",
|
||||
"ui.workspace_manufacture_description": "Нарізання, траєкторії та налаштування",
|
||||
"ui.workspace_planned": "Заплановано",
|
||||
"ui.workspace_render_description": "Перегляд виробу, налаштування сцени та відтворені зображення",
|
||||
|
||||
+20
-2
@@ -167,6 +167,7 @@
|
||||
"HISTORY": "LỊCH SỬ",
|
||||
"Height": "Chiều cao",
|
||||
"Hidden edges": "Nét khuất",
|
||||
"History layout": "Bố cục lịch sử",
|
||||
"Holes": "Lỗ",
|
||||
"Holes (edges on them)": "Lỗ (các cạnh trên lỗ)",
|
||||
"Home view": "Góc nhìn chuẩn (Home)",
|
||||
@@ -232,7 +233,7 @@
|
||||
"No crash reports.": "Không có báo cáo sự cố.",
|
||||
"No matching appearance presets.": "Không có cài đặt sẵn vật liệu bề mặt phù hợp.",
|
||||
"No matching appearances": "Không có màu sắc/vật liệu phù hợp",
|
||||
"No visible bodies to export.": "Không có thân khối hiển thị nào để xuất.",
|
||||
"No visible bodies or sketches to export.": "Không có thân khối hoặc phác thảo hiển thị nào để xuất.",
|
||||
"None": "Không có",
|
||||
"Not available yet": "Chưa khả dụng",
|
||||
"Notch": "Rãnh khía chữ V",
|
||||
@@ -831,7 +832,7 @@
|
||||
"dxf.progress.projection": "Đang chiếu hình học (3/5)",
|
||||
"dxf.progress.write": "Đang ghi tệp DXF (5/5)",
|
||||
"dxf.tolerance": "Dung sai đường cong",
|
||||
"dxf.tolerance_help": "Chỉ áp dụng cho các thân khối hiển thị. Đường cong sử dụng các đoạn thẳng. Dung sai nhỏ hơn cho đường cong mịn hơn nhưng tốn nhiều thời gian và bộ nhớ hơn.",
|
||||
"dxf.tolerance_help": "Chỉ áp dụng cho các thân khối và phác thảo hiển thị. Đường cong sử dụng các đoạn thẳng. Dung sai nhỏ hơn cho đường cong mịn hơn nhưng tốn nhiều thời gian và bộ nhớ hơn.",
|
||||
"file_load.built": "Đã dựng được: {built} trên {total} tính năng.",
|
||||
"file_load.converting": "Đang chuyển đổi khối đặc {done} trên {total}",
|
||||
"file_load.inserting": "Đang chèn hình học",
|
||||
@@ -1092,6 +1093,7 @@
|
||||
"ui.agent_chat_add": "Đính kèm tệp",
|
||||
"ui.agent_chat_add_file": "Chọn một tệp…",
|
||||
"ui.agent_chat_allow": "Cho phép",
|
||||
"ui.agent_chat_allow_once": "Cho phép một lần",
|
||||
"ui.agent_chat_allowed": "Đã cho phép",
|
||||
"ui.agent_chat_answered": "Câu trả lời: {option}",
|
||||
"ui.agent_chat_approval": "Cho phép AI thực hiện thao tác này?",
|
||||
@@ -1149,6 +1151,9 @@
|
||||
"ui.agent_chat_left": "Trái",
|
||||
"ui.agent_chat_limit": "AI Agent đã đạt giới hạn {limit} thay đổi cho một tin nhắn. Hãy gửi tin nhắn tiếp theo để tiếp tục.",
|
||||
"ui.agent_chat_minimize": "Thu nhỏ thành thanh công cụ",
|
||||
"ui.agent_chat_mode_all": "Tất cả",
|
||||
"ui.agent_chat_mode_ask": "Hỏi",
|
||||
"ui.agent_chat_mode_edits": "Chỉnh sửa",
|
||||
"ui.agent_chat_model_tip": "Mô hình AI đang sử dụng. Lựa chọn của bạn sẽ được ghi nhớ cho Agent này.",
|
||||
"ui.agent_chat_new": "Cuộc trò chuyện mới",
|
||||
"ui.agent_chat_new_confirm": "Bắt đầu cuộc trò chuyện mới? Cuộc trò chuyện hiện tại sẽ bị xóa.",
|
||||
@@ -1161,6 +1166,7 @@
|
||||
"ui.agent_chat_none_found_short": "Không tìm thấy tác tử nào",
|
||||
"ui.agent_chat_not_installed": "Chưa cài đặt",
|
||||
"ui.agent_chat_not_signed_in": "chưa đăng nhập",
|
||||
"ui.agent_chat_open_settings": "Mở tùy chọn cài đặt",
|
||||
"ui.agent_chat_own_window": "Cửa sổ riêng",
|
||||
"ui.agent_chat_paste_picture": "Dán hình ảnh",
|
||||
"ui.agent_chat_permission": "AI Agent yêu cầu thực thi: {title}",
|
||||
@@ -1185,6 +1191,8 @@
|
||||
"ui.agent_chat_send": "Gửi",
|
||||
"ui.agent_chat_send_now": "Gửi ngay",
|
||||
"ui.agent_chat_send_now_tip": "Dừng bước hiện tại của tác tử và gửi tin nhắn này ngay (Ctrl+Enter)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft giao tiếp với một tác nhân lập trình được cài đặt trên máy tính này. Chọn một tác nhân trong Tùy chọn cài đặt › AI.",
|
||||
"ui.agent_chat_setup_title": "Thiết lập trợ lý",
|
||||
"ui.agent_chat_side": "Vị trí bảng trò chuyện AI",
|
||||
"ui.agent_chat_signed_in": "đã đăng nhập",
|
||||
"ui.agent_chat_state_auth": "Cần đăng nhập",
|
||||
@@ -1201,6 +1209,7 @@
|
||||
"ui.agent_chat_undo_label": "Trò chuyện AI: {message}",
|
||||
"ui.agent_chat_undone": "(hoàn tácne)",
|
||||
"ui.agent_chat_view_attached": "Đã đính kèm góc nhìn",
|
||||
"ui.agent_chat_waiting": "Đang chờ bạn",
|
||||
"ui.agent_cursor": "Con trỏ AI",
|
||||
"ui.agent_cursor_speed": "Tốc độ con trỏ AI",
|
||||
"ui.ai_add": "Thêm",
|
||||
@@ -1528,6 +1537,11 @@
|
||||
"ui.hideOrigin": "Ẩn Gốc tọa độ",
|
||||
"ui.hidePlanes": "Ẩn các mặt phẳng",
|
||||
"ui.hideProfile": "Ẩn biên dạng",
|
||||
"ui.history_back": "Lùi một bước",
|
||||
"ui.history_below_browser": "Bên dưới trình duyệt",
|
||||
"ui.history_first": "Về đầu",
|
||||
"ui.history_forward": "Tiến một bước",
|
||||
"ui.history_last": "Đến cuối",
|
||||
"ui.history_title": "Lịch sử",
|
||||
"ui.hole": "Tạo lỗ",
|
||||
"ui.hole_tap_type": "Kiểu ta-rô ren lỗ",
|
||||
@@ -1545,6 +1559,8 @@
|
||||
"ui.joints": "Các khớp nối",
|
||||
"ui.keyboard_shortcuts": "Phím tắt bàn phím",
|
||||
"ui.language": "Ngôn ngữ (Language)",
|
||||
"ui.layout_compact": "Gọn",
|
||||
"ui.layout_floating": "Nổi",
|
||||
"ui.length_decimals": "Số chữ số thập phân cho chiều dài",
|
||||
"ui.light": "Sáng",
|
||||
"ui.line": "Đoạn thẳng",
|
||||
@@ -1682,6 +1698,7 @@
|
||||
"ui.reverse_zoom_direction": "Đảo chiều thu phóng",
|
||||
"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_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)",
|
||||
@@ -1836,6 +1853,7 @@
|
||||
"ui.workspace_design_tabs": "Mô hình · Bề mặt · Lắp ráp · Chèn · Kiểm tra · Quản lý · Xem",
|
||||
"ui.workspace_drawing_description": "Bản vẽ 2D với các hình chiếu, kích thước và khung tên",
|
||||
"ui.workspace_footer": "Mọi không gian làm việc đều dùng chung một thiết kế, lịch sử và vùng chọn; chỉ các thẻ ruy-băng thay đổi. Bộ chuyển đổi bị khóa khi đang chỉnh sửa phác thảo hoặc tính năng.",
|
||||
"ui.workspace_layout": "Bố cục không gian làm việc",
|
||||
"ui.workspace_manufacture_description": "Cắt lớp, đường chạy dao và thiết lập",
|
||||
"ui.workspace_planned": "Dự kiến",
|
||||
"ui.workspace_render_description": "Xem trước sản phẩm, cài đặt cảnh và hình ảnh kết xuất",
|
||||
|
||||
@@ -167,6 +167,7 @@
|
||||
"HISTORY": "歷程記錄",
|
||||
"Height": "高度",
|
||||
"Hidden edges": "隱藏線",
|
||||
"History layout": "歷程配置",
|
||||
"Holes": "孔",
|
||||
"Holes (edges on them)": "孔(孔上的邊)",
|
||||
"Home view": "主檢視",
|
||||
@@ -232,7 +233,6 @@
|
||||
"No crash reports.": "沒有當機報告。",
|
||||
"No matching appearance presets.": "沒有符合的外觀預設。",
|
||||
"No matching appearances": "沒有符合的外觀",
|
||||
"No visible bodies to export.": "沒有可匯出的可見實體。",
|
||||
"None": "無",
|
||||
"Not available yet": "暫不可用",
|
||||
"Notch": "缺口",
|
||||
@@ -1092,6 +1092,7 @@
|
||||
"ui.agent_chat_add": "附加檔案",
|
||||
"ui.agent_chat_add_file": "選擇檔案…",
|
||||
"ui.agent_chat_allow": "允許",
|
||||
"ui.agent_chat_allow_once": "僅允許一次",
|
||||
"ui.agent_chat_allowed": "已允許",
|
||||
"ui.agent_chat_answered": "回答:{option}",
|
||||
"ui.agent_chat_approval": "允許代理執行此操作嗎?",
|
||||
@@ -1149,6 +1150,9 @@
|
||||
"ui.agent_chat_left": "左側",
|
||||
"ui.agent_chat_limit": "代理已達到每條訊息 {limit} 項變更的上限。再傳送一條訊息可讓它繼續。",
|
||||
"ui.agent_chat_minimize": "最小化為細條",
|
||||
"ui.agent_chat_mode_all": "全部",
|
||||
"ui.agent_chat_mode_ask": "詢問",
|
||||
"ui.agent_chat_mode_edits": "編輯",
|
||||
"ui.agent_chat_model_tip": "代理使用的模型。您的選擇會為此代理記住。",
|
||||
"ui.agent_chat_new": "新對話",
|
||||
"ui.agent_chat_new_confirm": "開始新對話?目前對話將被清除。",
|
||||
@@ -1161,6 +1165,7 @@
|
||||
"ui.agent_chat_none_found_short": "未找到代理",
|
||||
"ui.agent_chat_not_installed": "未安裝",
|
||||
"ui.agent_chat_not_signed_in": "未登入",
|
||||
"ui.agent_chat_open_settings": "開啟偏好設定",
|
||||
"ui.agent_chat_own_window": "獨立視窗",
|
||||
"ui.agent_chat_paste_picture": "貼上圖片",
|
||||
"ui.agent_chat_permission": "代理請求執行:{title}",
|
||||
@@ -1185,6 +1190,8 @@
|
||||
"ui.agent_chat_send": "傳送",
|
||||
"ui.agent_chat_send_now": "立即傳送",
|
||||
"ui.agent_chat_send_now_tip": "停止代理目前的步驟,並接著傳送這條訊息(Ctrl+Enter)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft 會與安裝在這部電腦上的程式設計代理程式通訊。請在「偏好設定 › AI」中選擇一個。",
|
||||
"ui.agent_chat_setup_title": "設定助理",
|
||||
"ui.agent_chat_side": "代理聊天位置",
|
||||
"ui.agent_chat_signed_in": "已登入",
|
||||
"ui.agent_chat_state_auth": "需要登入",
|
||||
@@ -1201,6 +1208,7 @@
|
||||
"ui.agent_chat_undo_label": "代理聊天:{message}",
|
||||
"ui.agent_chat_undone": "(已復原)",
|
||||
"ui.agent_chat_view_attached": "已附加檢視",
|
||||
"ui.agent_chat_waiting": "等待您的回應",
|
||||
"ui.agent_cursor": "代理游標",
|
||||
"ui.agent_cursor_speed": "代理游標速度",
|
||||
"ui.ai_add": "新增",
|
||||
@@ -1528,6 +1536,11 @@
|
||||
"ui.hideOrigin": "隱藏原點",
|
||||
"ui.hidePlanes": "隱藏平面",
|
||||
"ui.hideProfile": "隱藏輪廓",
|
||||
"ui.history_back": "後退一步",
|
||||
"ui.history_below_browser": "瀏覽器下方",
|
||||
"ui.history_first": "移至開頭",
|
||||
"ui.history_forward": "前進一步",
|
||||
"ui.history_last": "移至結尾",
|
||||
"ui.history_title": "歷程記錄",
|
||||
"ui.hole": "孔",
|
||||
"ui.hole_tap_type": "攻牙類型",
|
||||
@@ -1545,6 +1558,8 @@
|
||||
"ui.joints": "接頭",
|
||||
"ui.keyboard_shortcuts": "鍵盤快捷鍵",
|
||||
"ui.language": "語言",
|
||||
"ui.layout_compact": "精簡",
|
||||
"ui.layout_floating": "浮動",
|
||||
"ui.length_decimals": "長度小數位數",
|
||||
"ui.light": "淺色",
|
||||
"ui.line": "直線",
|
||||
@@ -1682,6 +1697,7 @@
|
||||
"ui.reverse_zoom_direction": "反轉縮放方向",
|
||||
"ui.revolve": "旋轉",
|
||||
"ui.ribbon_exit": "結束",
|
||||
"ui.ribbon_labels": "顯示功能區圖示下的文字",
|
||||
"ui.ribbon_pin_footer": "固定的命令會顯示在功能區中。將指標停留在某列上即可固定或取消固定。",
|
||||
"ui.ribbon_workspace": "工作區",
|
||||
"ui.right": "右視",
|
||||
@@ -1836,6 +1852,7 @@
|
||||
"ui.workspace_design_tabs": "模型 · 曲面 · 組裝 · 插入 · 檢查 · 管理 · 檢視",
|
||||
"ui.workspace_drawing_description": "含檢視、尺寸和標題欄的 2D 工程圖",
|
||||
"ui.workspace_footer": "所有工作區使用同一設計、歷程記錄和選擇,僅功能區索引標籤會變化。編輯草圖或特徵時,切換器將被鎖定。",
|
||||
"ui.workspace_layout": "工作區配置",
|
||||
"ui.workspace_manufacture_description": "切片、刀具路徑和設定",
|
||||
"ui.workspace_planned": "規劃中",
|
||||
"ui.workspace_render_description": "產品預覽、場景設定和彩現影像",
|
||||
|
||||
+89
-3
@@ -28,6 +28,7 @@
|
||||
"Also deletes": "同时删除",
|
||||
"Angle 2": "角度 2",
|
||||
"Animate": "动画",
|
||||
"App/system information and command settings are included. Review private details before sharing.": "报告包含应用和系统信息以及命令设置。共享前请检查私人信息。",
|
||||
"Appearance name": "外观名称",
|
||||
"Applied Radius": "应用半径",
|
||||
"Apply To": "应用于",
|
||||
@@ -83,6 +84,7 @@
|
||||
"Contact": "接触",
|
||||
"Contact Sets": "接触集",
|
||||
"Contact sets": "接触集",
|
||||
"Continue on GitHub": "在GitHub中继续",
|
||||
"Continuity": "连续性",
|
||||
"Copy Error Message": "复制错误消息",
|
||||
"Copy Warning Message": "复制警告消息",
|
||||
@@ -102,6 +104,7 @@
|
||||
"Curv. Comb": "曲率梳",
|
||||
"Curvature (G2)": "曲率 (G2)",
|
||||
"Curvature change (G3)": "曲率变化 (G3)",
|
||||
"Custom": "自定义",
|
||||
"Customize Toolbar": "自定义工具栏",
|
||||
"Delete": "删除",
|
||||
"Delete all": "全部删除",
|
||||
@@ -110,6 +113,8 @@
|
||||
"Delete the set": "删除此集合",
|
||||
"Density": "密度",
|
||||
"Dependents": "依赖项",
|
||||
"Describe the problem you saw": "请描述您遇到的问题",
|
||||
"Describe what happened before continuing.": "继续之前,请描述发生了什么。",
|
||||
"Designs you open or save appear here.": "你打开或保存的设计会显示在此处。",
|
||||
"Determined by To Object. Choose Angle to edit the angular extent.": "由“到对象”确定。选择“角度”可编辑角度范围。",
|
||||
"Direction": "方向",
|
||||
@@ -127,6 +132,7 @@
|
||||
"Enabled": "已启用",
|
||||
"End bulge": "终止鼓度",
|
||||
"End continuity": "终止连续性",
|
||||
"Expected result (optional)": "预期结果(可选)",
|
||||
"Explode": "爆炸",
|
||||
"Explode Text": "分解文字",
|
||||
"Export Animation…": "导出动画…",
|
||||
@@ -167,6 +173,8 @@
|
||||
"HISTORY": "历史",
|
||||
"Height": "高度",
|
||||
"Hidden edges": "隐藏线",
|
||||
"History layout": "历史布局",
|
||||
"History names": "历史名称",
|
||||
"Holes": "孔",
|
||||
"Holes (edges on them)": "孔(孔上的边)",
|
||||
"Home view": "主视图",
|
||||
@@ -232,7 +240,8 @@
|
||||
"No crash reports.": "没有崩溃报告。",
|
||||
"No matching appearance presets.": "没有匹配的外观预设。",
|
||||
"No matching appearances": "没有匹配的外观",
|
||||
"No visible bodies to export.": "没有可导出的可见实体。",
|
||||
"No visible bodies or sketches to export.": "没有可导出的可见实体或草图。",
|
||||
"Nominal diameter": "公称直径",
|
||||
"None": "无",
|
||||
"Not available yet": "暂不可用",
|
||||
"Notch": "缺口",
|
||||
@@ -241,7 +250,10 @@
|
||||
"Open GitHub draft": "打开 GitHub 草稿",
|
||||
"Open local libraries in the desktop app. You can export this design's library here.": "请在桌面应用中打开本地库。您可以在此导出当前设计的库。",
|
||||
"Open-source parametric 3D CAD in Rust. Licensed MIT OR Apache-2.0.": "使用 Rust 编写的开源参数化 3D CAD。基于 MIT OR Apache-2.0 许可。",
|
||||
"Opens your browser and copies the report text for you to paste into GitHub.": "打开浏览器,并复制报告文本以便粘贴到GitHub。",
|
||||
"Opens your browser. Review and submit your report on GitHub.": "打开浏览器。请在GitHub中检查并提交报告。",
|
||||
"Operator": "运算符",
|
||||
"Orbit (right drag)": "旋转(右键拖动)",
|
||||
"Other side": "另一侧",
|
||||
"Other surface (mutual trim)": "另一曲面(相互修剪)",
|
||||
"Outside": "外侧",
|
||||
@@ -256,6 +268,7 @@
|
||||
"Pick the tool bodies": "选择工具实体",
|
||||
"Picked": "所选",
|
||||
"Picked point": "选取的点",
|
||||
"Pitch": "螺距",
|
||||
"Place at the origin instead": "改为放置在原点",
|
||||
"Plastic Rules": "塑料规则",
|
||||
"Playback": "播放",
|
||||
@@ -289,6 +302,8 @@
|
||||
"Remove the track": "移除轨迹",
|
||||
"Remove this point": "移除此点",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "报告缺陷 — 复核截图并开启 GitHub 草稿",
|
||||
"Report text copied. Paste it into GitHub and submit your report there.": "报告文本已复制。请在GitHub中粘贴并提交。",
|
||||
"Reports are public. A GitHub account is required.": "报告将公开发布。您需要GitHub账户。",
|
||||
"Reset Toolbar": "重置工具栏",
|
||||
"Resize Part": "调整零件大小",
|
||||
"Restore View": "恢复视图",
|
||||
@@ -303,6 +318,7 @@
|
||||
"Revert Pos.": "还原位置",
|
||||
"Revert Position": "恢复位置",
|
||||
"Review the report and screenshot before opening GitHub.": "打开 GitHub 前请审阅报告和截图。",
|
||||
"Review your report on GitHub and click Submit new issue to publish it.": "请在GitHub中检查报告,然后点击Submit new issue发布。",
|
||||
"Rim Edge": "轮缘边",
|
||||
"Roll History Marker Here": "将历史标记滚动到此处",
|
||||
"Round": "圆角",
|
||||
@@ -313,10 +329,12 @@
|
||||
"SURFACE QUALITY": "曲面质量",
|
||||
"Save Rule": "保存规则",
|
||||
"Save View": "保存视图",
|
||||
"Save screenshot…": "保存屏幕截图…",
|
||||
"Save the study as an animated GIF, seen as the viewport shows it": "按视口中的显示将研究保存为动画 GIF",
|
||||
"Saved preview. Geometry is being checked before editing.": "已保存预览。编辑前正在检查几何。",
|
||||
"Scale factor": "比例因子",
|
||||
"Scene": "场景",
|
||||
"Screenshot (optional)": "屏幕截图(可选)",
|
||||
"Search": "搜索",
|
||||
"Search commands": "搜索命令",
|
||||
"Search this library": "搜索此库",
|
||||
@@ -351,17 +369,21 @@
|
||||
"Start continuity": "起始连续性",
|
||||
"Step": "步骤",
|
||||
"Steps": "步数",
|
||||
"Steps to reproduce (optional)": "重现步骤(可选)",
|
||||
"Stitch the kept pieces": "缝合保留的部分",
|
||||
"Stitch to both bodies": "缝合到两个实体",
|
||||
"Study": "研究",
|
||||
"Style": "样式",
|
||||
"Summary": "摘要",
|
||||
"Suppressed": "已抑制",
|
||||
"Surface": "曲面",
|
||||
"Swap Ends": "交换端点",
|
||||
"TRIM": "修剪",
|
||||
"Tangent (G1)": "相切 (G1)",
|
||||
"Tangent Chain": "相切链",
|
||||
"Tap drill diameter": "攻丝底孔直径",
|
||||
"Tear": "撕裂",
|
||||
"Technical details": "技术详情",
|
||||
"The dashed sketch is a saved plane guide. Select a new face to repair the attachment.": "虚线草图是已保存的平面引导。选择一个新面以修复附着。",
|
||||
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "保存的作者实例缺失。重新绑定前请选择有效的作者放置。",
|
||||
"Then": "操作",
|
||||
@@ -373,6 +395,7 @@
|
||||
"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 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中附加。",
|
||||
"To Offset": "偏移",
|
||||
"To edges": "终止边",
|
||||
"Tolerance": "公差",
|
||||
@@ -398,6 +421,9 @@
|
||||
"Wall": "壁厚",
|
||||
"Wall Location": "壁位置",
|
||||
"Wall Thickness": "壁厚度",
|
||||
"What did you do just before the problem? A short list is enough": "问题发生前您进行了哪些操作?简要列出即可",
|
||||
"What did you expect to see?": "您期望看到什么结果?",
|
||||
"What happened?": "发生了什么?",
|
||||
"When": "条件",
|
||||
"Width": "宽度",
|
||||
"Will fail": "将失败",
|
||||
@@ -758,6 +784,7 @@
|
||||
"cmd.view_graphics_preset": "图形预设",
|
||||
"cmd.view_list": "列出命名视图",
|
||||
"cmd.view_navigate": "导航视图",
|
||||
"cmd.view_normal_to": "垂直于所选面",
|
||||
"cmd.view_rename": "重命名命名视图",
|
||||
"cmd.view_save": "新建命名视图",
|
||||
"color.copy": "单击复制颜色值",
|
||||
@@ -831,7 +858,7 @@
|
||||
"dxf.progress.projection": "正在投影几何体 (3/5)",
|
||||
"dxf.progress.write": "正在写入 DXF (5/5)",
|
||||
"dxf.tolerance": "曲线公差",
|
||||
"dxf.tolerance_help": "仅导出可见实体。曲线使用直线段近似。公差越小,曲线越精细,但需要更多时间和内存。",
|
||||
"dxf.tolerance_help": "仅导出可见实体和显示的草图。曲线使用直线段近似。公差越小,曲线越精细,但需要更多时间和内存。",
|
||||
"file_load.built": "已构建:{built}/{total} 个特征。",
|
||||
"file_load.converting": "正在转换实体 {done}/{total}",
|
||||
"file_load.inserting": "正在插入几何体",
|
||||
@@ -855,6 +882,12 @@
|
||||
"help.licences": "许可证",
|
||||
"help.mit": "MIT 许可证",
|
||||
"help.notice": "NOTICE",
|
||||
"help.solid.chamfer": "用指定的距离在所选边上切出一个平直的斜面。用于去除锐边,或使销钉、螺钉更容易插入。",
|
||||
"help.solid.extrude": "将草图轮廓按指定距离拉伸为三维形体,可以增加或去除材料。用于把平面轮廓变成方块、凸台或凹槽。",
|
||||
"help.solid.fillet": "用指定半径的平滑圆弧将所选的边倒圆。用于出于强度、手感或外观的考虑而柔化锐边。",
|
||||
"help.solid.hole": "在面上的指定位置钻出指定直径的圆孔。用于螺钉过孔、销孔等简单的圆形开口。",
|
||||
"help.solid.revolve": "将草图轮廓绕一条轴旋转,生成圆形的实体。用于轴、旋钮、碗等围绕中心线呈圆形的零件。",
|
||||
"help.solid.shell": "将实体掏空,留下指定厚度的壁,还可以打开所选的面。用于盒子、盖子等薄壁零件。",
|
||||
"help.third_party": "第三方许可证",
|
||||
"home.documentation": "文档",
|
||||
"home.in_this_browser": "在此浏览器中",
|
||||
@@ -926,6 +959,8 @@
|
||||
"motion.to_end": "到结尾",
|
||||
"motion.to_start": "到开头",
|
||||
"motion.track_name": "轨道 {number}",
|
||||
"nav.free_orbit.help": "鼠标和3D鼠标可保持滚转并越过两个极点旋转。Shift轴吸附将被禁用。",
|
||||
"nav.free_orbit.label": "自由旋转",
|
||||
"nav.touchpad.help": "按住 Shift 并移动指针可平移;按住 Alt/Option 并移动可旋转。无需按鼠标按钮。垂直滚动可缩放。",
|
||||
"nav.touchpad.label": "触控板",
|
||||
"nav.touchpad.orbit": "旋转(按住 Alt/Option 并移动,无需鼠标按钮)",
|
||||
@@ -1092,6 +1127,7 @@
|
||||
"ui.agent_chat_add": "附加文件",
|
||||
"ui.agent_chat_add_file": "选择文件…",
|
||||
"ui.agent_chat_allow": "允许",
|
||||
"ui.agent_chat_allow_once": "仅允许一次",
|
||||
"ui.agent_chat_allowed": "已允许",
|
||||
"ui.agent_chat_answered": "回答:{option}",
|
||||
"ui.agent_chat_approval": "允许智能体执行此操作吗?",
|
||||
@@ -1149,6 +1185,9 @@
|
||||
"ui.agent_chat_left": "左侧",
|
||||
"ui.agent_chat_limit": "智能体已达到每条消息 {limit} 项更改的上限。再发送一条消息可让它继续。",
|
||||
"ui.agent_chat_minimize": "最小化为细条",
|
||||
"ui.agent_chat_mode_all": "全部",
|
||||
"ui.agent_chat_mode_ask": "询问",
|
||||
"ui.agent_chat_mode_edits": "编辑",
|
||||
"ui.agent_chat_model_tip": "智能体使用的模型。你的选择会为此智能体记住。",
|
||||
"ui.agent_chat_new": "新对话",
|
||||
"ui.agent_chat_new_confirm": "开始新对话?当前对话将被清除。",
|
||||
@@ -1161,6 +1200,7 @@
|
||||
"ui.agent_chat_none_found_short": "未找到智能体",
|
||||
"ui.agent_chat_not_installed": "未安装",
|
||||
"ui.agent_chat_not_signed_in": "未登录",
|
||||
"ui.agent_chat_open_settings": "打开首选项",
|
||||
"ui.agent_chat_own_window": "独立窗口",
|
||||
"ui.agent_chat_paste_picture": "粘贴图片",
|
||||
"ui.agent_chat_permission": "智能体请求运行:{title}",
|
||||
@@ -1185,6 +1225,8 @@
|
||||
"ui.agent_chat_send": "发送",
|
||||
"ui.agent_chat_send_now": "立即发送",
|
||||
"ui.agent_chat_send_now_tip": "停止智能体当前的步骤,并接着发送这条消息(Ctrl+Enter)",
|
||||
"ui.agent_chat_setup_body": "SolidCraft 会与安装在此计算机上的编程代理通信。请在“首选项 › AI”中选择一个。",
|
||||
"ui.agent_chat_setup_title": "设置助手",
|
||||
"ui.agent_chat_side": "智能体聊天位置",
|
||||
"ui.agent_chat_signed_in": "已登录",
|
||||
"ui.agent_chat_state_auth": "需要登录",
|
||||
@@ -1201,8 +1243,11 @@
|
||||
"ui.agent_chat_undo_label": "智能体聊天:{message}",
|
||||
"ui.agent_chat_undone": "(已撤销)",
|
||||
"ui.agent_chat_view_attached": "已附加视图",
|
||||
"ui.agent_chat_waiting": "等待您的回复",
|
||||
"ui.agent_cursor": "代理光标",
|
||||
"ui.agent_cursor_speed": "代理光标速度",
|
||||
"ui.agent_off_thread": "代理运行命令时保持窗口响应",
|
||||
"ui.agent_off_thread_note": "代理(来自 AI 面板或 MCP 客户端)的耗时命令在窗口之外计算:运行时仍可旋转和查看视图,并可在视图中的提示上停止它。关闭:每条命令在窗口线程上运行,窗口会等待其完成。",
|
||||
"ui.ai_add": "添加",
|
||||
"ui.ai_add_agent": "添加智能体…",
|
||||
"ui.ai_add_agent_title": "添加智能体",
|
||||
@@ -1528,7 +1573,19 @@
|
||||
"ui.hideOrigin": "隐藏原点",
|
||||
"ui.hidePlanes": "隐藏平面",
|
||||
"ui.hideProfile": "隐藏轮廓",
|
||||
"ui.history_back": "后退一步",
|
||||
"ui.history_below_browser": "浏览器下方",
|
||||
"ui.history_edge_hint": "双击边缘",
|
||||
"ui.history_first": "转到开头",
|
||||
"ui.history_forward": "前进一步",
|
||||
"ui.history_hide_names": "隐藏名称",
|
||||
"ui.history_last": "转到末尾",
|
||||
"ui.history_marker_tip": "历史标记:拖动以回退模型;新特征将在此处添加",
|
||||
"ui.history_names": "名称",
|
||||
"ui.history_play": "播放至末尾",
|
||||
"ui.history_show_names": "显示名称",
|
||||
"ui.history_title": "历史",
|
||||
"ui.history_toggle_names": "显示或隐藏特征名称",
|
||||
"ui.hole": "孔",
|
||||
"ui.hole_tap_type": "孔攻丝类型",
|
||||
"ui.hole_type": "孔类型",
|
||||
@@ -1545,6 +1602,8 @@
|
||||
"ui.joints": "关节",
|
||||
"ui.keyboard_shortcuts": "键盘快捷键",
|
||||
"ui.language": "语言",
|
||||
"ui.layout_compact": "紧凑",
|
||||
"ui.layout_floating": "浮动",
|
||||
"ui.length_decimals": "长度小数位",
|
||||
"ui.light": "浅色",
|
||||
"ui.line": "直线",
|
||||
@@ -1682,6 +1741,7 @@
|
||||
"ui.reverse_zoom_direction": "反转缩放方向",
|
||||
"ui.revolve": "旋转",
|
||||
"ui.ribbon_exit": "退出",
|
||||
"ui.ribbon_labels": "显示功能区图标下的文字",
|
||||
"ui.ribbon_pin_footer": "固定的命令显示在功能区中。将指针悬停在行上可固定或取消固定。",
|
||||
"ui.ribbon_workspace": "工作区",
|
||||
"ui.right": "右视",
|
||||
@@ -1737,6 +1797,7 @@
|
||||
"ui.smooth_wheel_zoom": "平滑滚轮缩放",
|
||||
"ui.smooth_wheel_zoom_tip": "关闭后每个滚轮事件立即生效,不做平滑延迟",
|
||||
"ui.snap": "捕捉",
|
||||
"ui.space_mouse_axes_reassigned": "轴已在偏好设置中重新分配,在此次校准之上仍然生效。",
|
||||
"ui.space_mouse_calibrate": "校准…",
|
||||
"ui.space_mouse_calibrate_tip": "按窗口提示移动摇杆帽,SolidCraft 会记住这个 3D 鼠标每个轴的方向和行程。校准结果按此 3D 鼠标保存。",
|
||||
"ui.space_mouse_calibrate_title": "校准 3D 鼠标",
|
||||
@@ -1746,12 +1807,26 @@
|
||||
"ui.space_mouse_calibration_no_response": "有一个轴几乎没有移动,因此无法校准。请重新开始,并且每次把摇杆帽移得更远。",
|
||||
"ui.space_mouse_dead_zone": "死区",
|
||||
"ui.space_mouse_dead_zone_tip": "视图开始移动前摇杆帽需要移动的距离,以其全行程的比例表示。如果摇杆帽静止时视图仍在漂移,请调大。",
|
||||
"ui.space_mouse_driven_by": "由设备的哪个校准动作控制此轴。选择另一个轴已在使用的动作时,两者会互换。",
|
||||
"ui.space_mouse_driven_by_label": "控制来源",
|
||||
"ui.space_mouse_driver": "3D 鼠标驱动",
|
||||
"ui.space_mouse_driver_auto": "自动",
|
||||
"ui.space_mouse_driver_direct": "直接 (hidraw)",
|
||||
"ui.space_mouse_driver_only_direct": "直接读取设备(spacenavd 未运行)",
|
||||
"ui.space_mouse_driver_only_spacenavd": "通过 spacenavd 读取(没有其他方式)",
|
||||
"ui.space_mouse_forget": "清除校准",
|
||||
"ui.space_mouse_hint": "同时移动和旋转视图;草图中仅支持平移和缩放。",
|
||||
"ui.space_mouse_motion_rotation_x": "旋转 X",
|
||||
"ui.space_mouse_motion_rotation_y": "旋转 Y",
|
||||
"ui.space_mouse_motion_rotation_z": "旋转 Z",
|
||||
"ui.space_mouse_motion_translation_x": "平移 X",
|
||||
"ui.space_mouse_motion_translation_y": "平移 Y",
|
||||
"ui.space_mouse_motion_translation_z": "平移 Z",
|
||||
"ui.space_mouse_navigate": "使用 SpaceNavigator / SpaceMouse 导航",
|
||||
"ui.space_mouse_none": "未找到 3D 鼠标",
|
||||
"ui.space_mouse_not_calibrated": "未校准",
|
||||
"ui.space_mouse_reading": "读数",
|
||||
"ui.space_mouse_reset_axes": "重置轴分配",
|
||||
"ui.space_mouse_step_lift": "向上提起摇杆帽,保持住,然后松开。",
|
||||
"ui.space_mouse_step_press": "向下按压摇杆帽,保持住,然后松开。",
|
||||
"ui.space_mouse_step_pull": "把摇杆帽拉向自己,保持住,然后松开。",
|
||||
@@ -1765,6 +1840,16 @@
|
||||
"ui.space_mouse_step_tilt_right": "向右倾斜摇杆帽(右缘向下),保持住,然后松开。",
|
||||
"ui.space_mouse_step_twist_clockwise": "顺时针扭转摇杆帽,保持住,然后松开。",
|
||||
"ui.space_mouse_step_twist_counterclockwise": "逆时针扭转摇杆帽,保持住,然后松开。",
|
||||
"ui.space_mouse_view_axis": "视图轴",
|
||||
"ui.space_mouse_view_off": "关闭",
|
||||
"ui.space_mouse_view_off_tip": "3D 鼠标驱动程序的配置文件关闭了此轴,因此它的动作不会移动视图。在此选择另一个轴的动作会将两者互换,该动作将不再移动视图。",
|
||||
"ui.space_mouse_view_orbit_sideways": "左右环绕",
|
||||
"ui.space_mouse_view_orbit_up_down": "上下环绕",
|
||||
"ui.space_mouse_view_pan_sideways": "左右平移",
|
||||
"ui.space_mouse_view_pan_up_down": "上下平移",
|
||||
"ui.space_mouse_view_roll": "滚转",
|
||||
"ui.space_mouse_view_unmapped": "轴 {n}",
|
||||
"ui.space_mouse_view_zoom": "缩放",
|
||||
"ui.spacing": "间距",
|
||||
"ui.spacing_2": "间距 2",
|
||||
"ui.sphere": "球体",
|
||||
@@ -1798,7 +1883,7 @@
|
||||
"ui.tie_ends": "约束",
|
||||
"ui.tie_ends_tip": "约束全部未约束端点",
|
||||
"ui.timeline_bottom": "底部时间轴",
|
||||
"ui.timeline_left_note": "左侧布局下,回退条显示在零件和场景浏览器的上方。",
|
||||
"ui.timeline_left_note": "垂直历史位于浏览器左侧。可在历史条上切换特征名称的显示。",
|
||||
"ui.timeline_left_sidebar": "左侧边栏",
|
||||
"ui.tip_angle": "尖端角度",
|
||||
"ui.to_object": "到对象",
|
||||
@@ -1836,6 +1921,7 @@
|
||||
"ui.workspace_design_tabs": "模型 · 曲面 · 装配 · 插入 · 检查 · 管理 · 视图",
|
||||
"ui.workspace_drawing_description": "包含视图、尺寸和标题栏的二维图纸",
|
||||
"ui.workspace_footer": "所有工作区使用同一设计、历史记录和选择,仅功能区选项卡发生变化。编辑草图或特征时,切换器将被锁定。",
|
||||
"ui.workspace_layout": "工作区布局",
|
||||
"ui.workspace_manufacture_description": "切片、刀具路径和设置",
|
||||
"ui.workspace_planned": "计划中",
|
||||
"ui.workspace_render_description": "产品预览、场景设置和渲染图像",
|
||||
|
||||
@@ -107,7 +107,6 @@ impl Tokens {
|
||||
error: Color32::from_rgb(214, 54, 54),
|
||||
warning: Color32::from_rgb(222, 150, 30),
|
||||
icon: Color32::from_rgb(70, 78, 92),
|
||||
icon_fill: Color32::from_rgb(178, 196, 222),
|
||||
timeline: Color32::from_rgb(232, 235, 240),
|
||||
hover: Color32::from_rgb(226, 234, 246),
|
||||
sel_face: Color32::from_rgb(76, 127, 203),
|
||||
@@ -157,8 +156,6 @@ impl Tokens {
|
||||
dialog_bg: Color32::from_rgb(250, 251, 252),
|
||||
dialog_border: Color32::from_rgb(213, 219, 227),
|
||||
chip: Color32::from_rgb(38, 120, 218),
|
||||
heading_bg: Color32::from_rgb(218, 223, 231),
|
||||
subheading_bg: Color32::from_rgb(234, 237, 242),
|
||||
profile_fill: Color32::from_rgba_unmultiplied_const(255, 196, 120, 70),
|
||||
glyph_bg: Color32::WHITE,
|
||||
glyph_edge: Color32::from_rgb(190, 110, 100),
|
||||
@@ -203,30 +200,7 @@ impl Tokens {
|
||||
Color32::from_rgb(204, 0, 140),
|
||||
],
|
||||
analysis: AnalysisColors::DEFAULT,
|
||||
icons: IconColors {
|
||||
sheet: Color32::from_rgb(170, 200, 240),
|
||||
sheet_2: Color32::from_rgb(150, 185, 235),
|
||||
sheet_light: Color32::from_rgb(214, 229, 248),
|
||||
sheet_line: Color32::from_rgb(127, 166, 218),
|
||||
highlight: Color32::WHITE,
|
||||
glint: Color32::from_rgba_premultiplied(160, 160, 160, 160),
|
||||
folder: Color32::from_rgb(236, 200, 110),
|
||||
plane: Color32::from_rgb(244, 214, 150),
|
||||
fold: Color32::from_rgb(130, 140, 160),
|
||||
stripe: Color32::from_gray(30),
|
||||
sky: Color32::from_rgb(190, 205, 230),
|
||||
floor: Color32::from_rgb(60, 62, 70),
|
||||
paint: [Color32::from_rgb(220, 70, 60), Color32::from_rgb(70, 150, 230), Color32::from_rgb(240, 200, 60)],
|
||||
axes: [Color32::from_rgb(210, 60, 60), Color32::from_rgb(60, 160, 60), Color32::from_rgb(60, 100, 220)],
|
||||
access: [Color32::from_rgb(70, 180, 90), Color32::from_rgb(220, 70, 60)],
|
||||
draft: [Color32::from_rgb(70, 180, 90), Color32::from_rgb(235, 200, 50)],
|
||||
curvature: [Color32::from_rgb(60, 110, 230), Color32::from_rgb(70, 190, 90), Color32::from_rgb(225, 60, 45)],
|
||||
interference: Color32::from_rgb(225, 70, 60),
|
||||
warning: Color32::from_rgb(250, 200, 60),
|
||||
agent: Color32::from_rgb(168, 85, 247),
|
||||
marker_halo: Color32::from_black_alpha(150),
|
||||
},
|
||||
app_bar_icon: [Color32::from_rgb(225, 230, 238), Color32::from_rgb(120, 140, 170), Color32::from_rgb(120, 190, 255)],
|
||||
icons: IconColors { highlight: Color32::WHITE, agent: Color32::from_rgb(168, 85, 247), marker_halo: Color32::from_black_alpha(150) },
|
||||
logo: [Color32::from_rgb(96, 158, 236), Color32::from_rgb(58, 136, 238), Color32::from_rgb(55, 108, 197), Color32::WHITE],
|
||||
}
|
||||
}
|
||||
@@ -306,7 +280,6 @@ impl Tokens {
|
||||
t.error = Color32::from_rgb(240, 100, 96);
|
||||
t.warning = Color32::from_rgb(236, 170, 70);
|
||||
t.icon = Color32::from_rgb(208, 214, 226);
|
||||
t.icon_fill = Color32::from_rgb(86, 112, 150);
|
||||
t.timeline = Color32::from_rgb(33, 37, 45);
|
||||
t.hover = Color32::from_rgb(54, 61, 74);
|
||||
t.hover_edge_halo = Color32::from_rgb(203, 203, 203);
|
||||
@@ -328,8 +301,6 @@ impl Tokens {
|
||||
t.dialog_bg = Color32::from_rgb(59, 68, 83);
|
||||
t.dialog_border = Color32::from_rgb(72, 83, 99);
|
||||
t.chip = Color32::from_rgb(59, 101, 168);
|
||||
t.heading_bg = Color32::from_rgb(38, 44, 55);
|
||||
t.subheading_bg = Color32::from_rgb(48, 55, 68);
|
||||
t.glyph_bg = Color32::from_rgb(44, 50, 60);
|
||||
t.glyph_edge = Color32::from_rgb(200, 130, 120);
|
||||
t.glyph_text = icon_primary(crate::icon_svg::CONSTRAINT_PRIMARY[1]);
|
||||
@@ -373,15 +344,8 @@ impl Tokens {
|
||||
if !dark {
|
||||
t.form_success = Color32::from_rgb(39, 88, 78);
|
||||
}
|
||||
// The icons that show the same meanings follow; on the dark toolbar the blue is a little
|
||||
// stronger, to stay clear of the icon fill.
|
||||
let good = if dark { Color32::from_rgb(35, 115, 245) } else { Color32::from_rgb(51, 115, 230) };
|
||||
t.icons.axes[0] = t.triad[0];
|
||||
t.icons.axes[1] = t.triad[1];
|
||||
t.icons.access = [good, Color32::from_rgb(217, 102, 13)];
|
||||
t.construction_geom = icon_primary(crate::icon_svg::CONSTRUCTION_RED_GREEN[dark as usize]);
|
||||
t.glyph_text = icon_primary(crate::icon_svg::CONSTRAINT_RED_GREEN[dark as usize]);
|
||||
t.icons.draft = [good, Color32::from_rgb(242, 230, 153)];
|
||||
t.surface_accent = if dark { Color32::from_rgb(200, 135, 55) } else { Color32::from_rgb(164, 80, 0) };
|
||||
t.mesh_accent = if dark { Color32::from_rgb(190, 175, 70) } else { Color32::from_rgb(130, 103, 0) };
|
||||
t.form_accent = if dark { Color32::from_rgb(165, 135, 220) } else { Color32::from_rgb(115, 73, 205) };
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
{
|
||||
"base": "dark",
|
||||
"colors": {
|
||||
"accent": "#ffbc48",
|
||||
"accent_soft": "#3c290d",
|
||||
"agent_busy": "#ffad38",
|
||||
"agent_ok": "#d4902f",
|
||||
"app_bar": "#090602",
|
||||
"app_bar_hover": "#ffad3818",
|
||||
"app_bar_hover_button": "#ffad3820",
|
||||
"app_bar_hover_strong": "#ffad3830",
|
||||
"app_bar_hover_subtle": "#ffad3810",
|
||||
"app_bar_icon": [
|
||||
"#efa335",
|
||||
"#9b6922",
|
||||
"#ffad38"
|
||||
],
|
||||
"app_bar_separator": "#533812",
|
||||
"app_bar_tab_active": "#1d1406",
|
||||
"app_bar_text": "#f3ca87",
|
||||
"axis_x": "#f3a535",
|
||||
"axis_y": "#ffad38",
|
||||
"axis_z": "#9b6922",
|
||||
"bend_down": "#ba7e29",
|
||||
"bend_up": "#d4902f",
|
||||
"body": "#382309",
|
||||
"body_edge": "#ffbc48",
|
||||
"body_selected": "#543615",
|
||||
"border": "#533812",
|
||||
"box_crossing": "#d4902f",
|
||||
"box_window": "#ffad38",
|
||||
"chip": "#4a3210",
|
||||
"construction_geom": "#ad7626",
|
||||
"construction_plane": "#c6872c18",
|
||||
"continuity": [
|
||||
"#c5862b",
|
||||
"#c6872c",
|
||||
"#d38f2e",
|
||||
"#b07827"
|
||||
],
|
||||
"control_border": "#533812",
|
||||
"control_fill": "#1d1406",
|
||||
"cube_border": "#533812",
|
||||
"cube_face": "#36250c",
|
||||
"cube_hover": "#563a13",
|
||||
"cube_text": "#efa335",
|
||||
"dialog_bg": "#100b04",
|
||||
"dialog_body": "#1d1406",
|
||||
"dialog_border": "#533812",
|
||||
"dialog_footer": "#100b04",
|
||||
"dialog_label": "#f3ca87",
|
||||
"dialog_rule": "#3b280d",
|
||||
"dimension": "#ffe2a5",
|
||||
"dimension_driven": "#9b6922",
|
||||
"error": "#fff0ca",
|
||||
"field": "#100b04",
|
||||
"form_accent": "#d6922f",
|
||||
"form_dim": "#9b6922",
|
||||
"form_error": "#fff0ca",
|
||||
"form_error_fill": "#46300f",
|
||||
"form_fill": "#7e561c",
|
||||
"form_ink": "#f3ca87",
|
||||
"form_primary": "#ffad38",
|
||||
"form_soft": "#291c09",
|
||||
"form_success": "#d4902f",
|
||||
"form_success_fill": "#3b280d",
|
||||
"form_tab_ink": "#f0a335",
|
||||
"gizmo": [
|
||||
"#f3a535",
|
||||
"#ffad38",
|
||||
"#9b6922"
|
||||
],
|
||||
"glyph_bg": "#201607",
|
||||
"glyph_edge": "#bd812a",
|
||||
"glyph_text": "#fff0ca",
|
||||
"grid": "#21170b",
|
||||
"grid_major": "#362511",
|
||||
"heading_bg": "#080602",
|
||||
"hover": "#3c290d",
|
||||
"hover_edge": "#ffbc48",
|
||||
"hover_edge_halo": "#170d03",
|
||||
"hover_profile": "#ffbc4826",
|
||||
"hover_profile_edge": "#ffbc48",
|
||||
"icon": "#f3ca87",
|
||||
"icon_fill": "#b87d28",
|
||||
"icons.access": [
|
||||
"#c5862b",
|
||||
"#ac7526"
|
||||
],
|
||||
"icons.agent": "#cb8a2d",
|
||||
"icons.axes": [
|
||||
"#f3a535",
|
||||
"#ffad38",
|
||||
"#9b6922"
|
||||
],
|
||||
"icons.curvature": [
|
||||
"#c0832a",
|
||||
"#c5862b",
|
||||
"#ac7526"
|
||||
],
|
||||
"icons.draft": [
|
||||
"#cb8a2d",
|
||||
"#d38f2e"
|
||||
],
|
||||
"icons.floor": "#76501a",
|
||||
"icons.fold": "#9f6c23",
|
||||
"icons.folder": "#d38f2e",
|
||||
"icons.glint": "#f4a63699",
|
||||
"icons.highlight": "#f6a736",
|
||||
"icons.interference": "#fff0ca",
|
||||
"icons.marker_halo": "#171005cc",
|
||||
"icons.paint": [
|
||||
"#cc8b2d",
|
||||
"#c6872c",
|
||||
"#eea234"
|
||||
],
|
||||
"icons.plane": "#d38f2e",
|
||||
"icons.sheet": "#c3852b",
|
||||
"icons.sheet_2": "#a26e24",
|
||||
"icons.sheet_light": "#eca034",
|
||||
"icons.sheet_line": "#f4a636",
|
||||
"icons.sky": "#654516",
|
||||
"icons.stripe": "#171005",
|
||||
"icons.warning": "#ffd274",
|
||||
"interference": "#fff0ca",
|
||||
"logo": [
|
||||
"#ffad38",
|
||||
"#eda134",
|
||||
"#855a1d",
|
||||
"#ffad38"
|
||||
],
|
||||
"manipulator": "#ffad38",
|
||||
"mesh_accent": "#d6922f",
|
||||
"mesh_fill": "#7e561c",
|
||||
"mesh_ink": "#f0a335",
|
||||
"mesh_soft": "#291c09",
|
||||
"on_accent": "#170d03",
|
||||
"origin_plane": "#c6872c20",
|
||||
"origin_plane_edge": "#c6872c80",
|
||||
"origin_plane_hover": "#ffad3840",
|
||||
"origin_point": "#c48b32",
|
||||
"overlay": "#100b04",
|
||||
"panel": "#100b04",
|
||||
"panel_header": "#1d1406",
|
||||
"preview_add": "#8e5c20",
|
||||
"preview_cut": "#ffbc4855",
|
||||
"preview_new": "#9f6c28",
|
||||
"preview_sketch": "#d4902f",
|
||||
"profile_fill": "#be812f20",
|
||||
"render_accent": "#d6922f",
|
||||
"render_fill": "#7e561c",
|
||||
"render_ink": "#f0a335",
|
||||
"render_soft": "#291c09",
|
||||
"rubber_band": "#ffad38",
|
||||
"section_cap": "#ba7e29",
|
||||
"sel_edge": "#ffbc48",
|
||||
"sel_edge_rim": "#9d6a24",
|
||||
"sel_face": "#66431b",
|
||||
"sel_profile": "#5e3d14",
|
||||
"sel_profile_edge": "#ffbc48",
|
||||
"sel_vertex": "#ffbc48",
|
||||
"selected_bg": "#3c290d",
|
||||
"selected_ink": "#ffe8b3",
|
||||
"sheet_fill": "#7e561c",
|
||||
"sheet_ink": "#f0a335",
|
||||
"sheet_soft": "#291c09",
|
||||
"sketch": "#ffbc48",
|
||||
"sketch_accent": "#d6922f",
|
||||
"sketch_centerline": "#ba7e29",
|
||||
"sketch_construction": "#ad7626",
|
||||
"sketch_fixed": "#c68b2f",
|
||||
"sketch_ink": "#f0a335",
|
||||
"sketch_locked": "#d4902f",
|
||||
"sketch_lost": "#fff0ca",
|
||||
"sketch_point": "#ffbc48",
|
||||
"sketch_point_free": "#ffbc48",
|
||||
"sketch_projected": "#cb8a2d",
|
||||
"sketch_soft": "#291c09",
|
||||
"subheading_bg": "#0c0803",
|
||||
"surface_accent": "#d6922f",
|
||||
"surface_fill": "#7e561c",
|
||||
"surface_ink": "#f0a335",
|
||||
"surface_soft": "#291c09",
|
||||
"tab_on_fill_dark": "#170d03",
|
||||
"text": "#f3ca87",
|
||||
"text_dim": "#9b6922",
|
||||
"timeline": "#090602",
|
||||
"timeline_item": "#1d1406",
|
||||
"timeline_item_error": "#46300f",
|
||||
"timeline_marker": "#9b6922",
|
||||
"toolbar": "#100b04",
|
||||
"triad": [
|
||||
"#f3a535",
|
||||
"#ffad38",
|
||||
"#9b6922"
|
||||
],
|
||||
"viewport_bottom": "#090603",
|
||||
"viewport_top": "#090603",
|
||||
"warning": "#ffd274"
|
||||
},
|
||||
"format": "solidcraft-theme/1",
|
||||
"name": "Amber Phosphor"
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
{
|
||||
"base": "dark",
|
||||
"colors": {
|
||||
"accent": "#66ddff",
|
||||
"accent_soft": "#153c60",
|
||||
"agent_busy": "#66ddff",
|
||||
"agent_ok": "#6ee0bb",
|
||||
"app_bar": "#07192f",
|
||||
"app_bar_hover": "#c6ecff18",
|
||||
"app_bar_hover_button": "#c6ecff20",
|
||||
"app_bar_hover_strong": "#c6ecff30",
|
||||
"app_bar_hover_subtle": "#c6ecff10",
|
||||
"app_bar_icon": [
|
||||
"#e3f0fa",
|
||||
"#94b2cb",
|
||||
"#66ddff"
|
||||
],
|
||||
"app_bar_separator": "#315777",
|
||||
"app_bar_tab_active": "#112d4b",
|
||||
"app_bar_text": "#e3f0fa",
|
||||
"axis_x": "#ec9e99",
|
||||
"axis_y": "#9ddab7",
|
||||
"axis_z": "#91c8ff",
|
||||
"bend_down": "#e7b374",
|
||||
"bend_up": "#6ee0bb",
|
||||
"body": "#8baccb",
|
||||
"body_edge": "#eef8ff",
|
||||
"body_selected": "#58bbdf",
|
||||
"border": "#315777",
|
||||
"box_crossing": "#6ee0bb",
|
||||
"box_window": "#66ddff",
|
||||
"chip": "#1f5278",
|
||||
"construction_geom": "#78b8dc",
|
||||
"construction_plane": "#b7dcf01c",
|
||||
"continuity": [
|
||||
"#6ee0bb",
|
||||
"#66ddff",
|
||||
"#f0d18c",
|
||||
"#b6a9e4"
|
||||
],
|
||||
"control_border": "#315777",
|
||||
"control_fill": "#112d4b",
|
||||
"cube_border": "#315777",
|
||||
"cube_face": "#163b60",
|
||||
"cube_hover": "#285f89",
|
||||
"cube_text": "#e3f0fa",
|
||||
"dialog_bg": "#0c223c",
|
||||
"dialog_body": "#112d4b",
|
||||
"dialog_border": "#315777",
|
||||
"dialog_footer": "#0c223c",
|
||||
"dialog_label": "#e3f0fa",
|
||||
"dialog_rule": "#254764",
|
||||
"dimension": "#e3f0fa",
|
||||
"dimension_driven": "#94b2cb",
|
||||
"error": "#ff968d",
|
||||
"field": "#071d35",
|
||||
"form_accent": "#78c6e8",
|
||||
"form_dim": "#94b2cb",
|
||||
"form_error": "#ff968d",
|
||||
"form_error_fill": "#422d40",
|
||||
"form_fill": "#4386ac",
|
||||
"form_ink": "#c3e6f6",
|
||||
"form_primary": "#66ddff",
|
||||
"form_soft": "#132f4c",
|
||||
"form_success": "#6ee0bb",
|
||||
"form_success_fill": "#153e47",
|
||||
"form_tab_ink": "#c3e6f6",
|
||||
"gizmo": [
|
||||
"#ec9e99",
|
||||
"#9ddab7",
|
||||
"#91c8ff"
|
||||
],
|
||||
"glyph_bg": "#0a233e",
|
||||
"glyph_edge": "#e6b3a6",
|
||||
"glyph_text": "#e6b3a6",
|
||||
"grid": "#214975",
|
||||
"grid_major": "#315d89",
|
||||
"heading_bg": "#06111e",
|
||||
"hover": "#153c60",
|
||||
"hover_edge": "#eef8ff",
|
||||
"hover_edge_halo": "#092c50",
|
||||
"hover_profile": "#66ddff26",
|
||||
"hover_profile_edge": "#66ddff",
|
||||
"icon": "#e3f0fa",
|
||||
"icon_fill": "#a4c1dc",
|
||||
"icons.access": [
|
||||
"#6ee0bb",
|
||||
"#ff968d"
|
||||
],
|
||||
"icons.agent": "#9cd6ef",
|
||||
"icons.axes": [
|
||||
"#ec9e99",
|
||||
"#9ddab7",
|
||||
"#91c8ff"
|
||||
],
|
||||
"icons.curvature": [
|
||||
"#91c8ff",
|
||||
"#6ee0bb",
|
||||
"#ff968d"
|
||||
],
|
||||
"icons.draft": [
|
||||
"#9ddab7",
|
||||
"#f0d18c"
|
||||
],
|
||||
"icons.floor": "#557c9e",
|
||||
"icons.fold": "#7fa5c5",
|
||||
"icons.folder": "#b9d8ec",
|
||||
"icons.glint": "#eaf6ff99",
|
||||
"icons.highlight": "#eef8ff",
|
||||
"icons.interference": "#ff968d",
|
||||
"icons.marker_halo": "#07192fcc",
|
||||
"icons.paint": [
|
||||
"#a5d4ec",
|
||||
"#66ddff",
|
||||
"#e3f0fa"
|
||||
],
|
||||
"icons.plane": "#bad8ec",
|
||||
"icons.sheet": "#a4c9e5",
|
||||
"icons.sheet_2": "#7ea9cb",
|
||||
"icons.sheet_light": "#deeffb",
|
||||
"icons.sheet_line": "#eaf6ff",
|
||||
"icons.sky": "#416b92",
|
||||
"icons.stripe": "#07192f",
|
||||
"icons.warning": "#f0d18c",
|
||||
"interference": "#ff968d",
|
||||
"logo": [
|
||||
"#d8f4ff",
|
||||
"#8edcf5",
|
||||
"#4386ac",
|
||||
"#eef8ff"
|
||||
],
|
||||
"manipulator": "#66ddff",
|
||||
"mesh_accent": "#78c6e8",
|
||||
"mesh_fill": "#4386ac",
|
||||
"mesh_ink": "#c3e6f6",
|
||||
"mesh_soft": "#132f4c",
|
||||
"on_accent": "#07192f",
|
||||
"origin_plane": "#b7dcf024",
|
||||
"origin_plane_edge": "#a3cce480",
|
||||
"origin_plane_hover": "#66ddff45",
|
||||
"origin_point": "#aad0ed",
|
||||
"overlay": "#0c223c",
|
||||
"panel": "#0c223c",
|
||||
"panel_header": "#112d4b",
|
||||
"preview_add": "#a9def2",
|
||||
"preview_cut": "#ff968d55",
|
||||
"preview_new": "#d2e8f7",
|
||||
"preview_sketch": "#6ee0bb",
|
||||
"profile_fill": "#c9eaff12",
|
||||
"render_accent": "#78c6e8",
|
||||
"render_fill": "#4386ac",
|
||||
"render_ink": "#c3e6f6",
|
||||
"render_soft": "#132f4c",
|
||||
"rubber_band": "#66ddff",
|
||||
"section_cap": "#e7b374",
|
||||
"sel_edge": "#66ddff",
|
||||
"sel_edge_rim": "#c5f3ff",
|
||||
"sel_face": "#58bbdf",
|
||||
"sel_profile": "#1c799e",
|
||||
"sel_profile_edge": "#66ddff",
|
||||
"sel_vertex": "#66ddff",
|
||||
"selected_bg": "#153c60",
|
||||
"selected_ink": "#b6f2ff",
|
||||
"sheet_fill": "#4386ac",
|
||||
"sheet_ink": "#c3e6f6",
|
||||
"sheet_soft": "#132f4c",
|
||||
"sketch": "#eaf6ff",
|
||||
"sketch_accent": "#78c6e8",
|
||||
"sketch_centerline": "#e7b374",
|
||||
"sketch_construction": "#78b8dc",
|
||||
"sketch_fixed": "#e3f0fa",
|
||||
"sketch_ink": "#c3e6f6",
|
||||
"sketch_locked": "#6ee0bb",
|
||||
"sketch_lost": "#ff968d",
|
||||
"sketch_point": "#e3f0fa",
|
||||
"sketch_point_free": "#66ddff",
|
||||
"sketch_projected": "#9cd6ef",
|
||||
"sketch_soft": "#132f4c",
|
||||
"subheading_bg": "#091a2d",
|
||||
"surface_accent": "#78c6e8",
|
||||
"surface_fill": "#4386ac",
|
||||
"surface_ink": "#c3e6f6",
|
||||
"surface_soft": "#132f4c",
|
||||
"tab_on_fill_dark": "#07192f",
|
||||
"text": "#e3f0fa",
|
||||
"text_dim": "#94b2cb",
|
||||
"timeline": "#07192f",
|
||||
"timeline_item": "#112d4b",
|
||||
"timeline_item_error": "#422d40",
|
||||
"timeline_marker": "#94b2cb",
|
||||
"toolbar": "#0c223c",
|
||||
"triad": [
|
||||
"#ec9e99",
|
||||
"#9ddab7",
|
||||
"#91c8ff"
|
||||
],
|
||||
"viewport_bottom": "#143b67",
|
||||
"viewport_top": "#143b67",
|
||||
"warning": "#f0d18c"
|
||||
},
|
||||
"format": "solidcraft-theme/1",
|
||||
"name": "Blueprint"
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
{
|
||||
"base": "dark",
|
||||
"colors": {
|
||||
"accent": "#cba6f7",
|
||||
"accent_soft": "#302b46",
|
||||
"agent_busy": "#cba6f7",
|
||||
"agent_ok": "#a6e3a1",
|
||||
"app_bar": "#181825",
|
||||
"app_bar_hover": "#cdd6f418",
|
||||
"app_bar_hover_button": "#cdd6f422",
|
||||
"app_bar_hover_strong": "#cdd6f430",
|
||||
"app_bar_hover_subtle": "#cdd6f410",
|
||||
"app_bar_icon": [
|
||||
"#cdd6f4",
|
||||
"#a6adc8",
|
||||
"#cba6f7"
|
||||
],
|
||||
"app_bar_separator": "#45475a",
|
||||
"app_bar_tab_active": "#313244",
|
||||
"app_bar_text": "#cdd6f4",
|
||||
"axis_x": "#f38ba8",
|
||||
"axis_y": "#a6e3a1",
|
||||
"axis_z": "#477dc8",
|
||||
"bend_down": "#f9e2af",
|
||||
"bend_up": "#a6e3a1",
|
||||
"body": "#586382",
|
||||
"body_edge": "#477dc8",
|
||||
"body_selected": "#365280",
|
||||
"border": "#45475a",
|
||||
"box_crossing": "#a6e3a1",
|
||||
"box_window": "#cba6f7",
|
||||
"chip": "#cba6f7",
|
||||
"construction_geom": "#f9e2af",
|
||||
"construction_plane": "#f9e2af22",
|
||||
"continuity": [
|
||||
"#a6e3a1",
|
||||
"#477dc8",
|
||||
"#f9e2af",
|
||||
"#cba6f7"
|
||||
],
|
||||
"control_border": "#45475a",
|
||||
"control_fill": "#313244",
|
||||
"cube_border": "#45475a",
|
||||
"cube_face": "#313244",
|
||||
"cube_hover": "#302b46",
|
||||
"cube_text": "#cdd6f4",
|
||||
"dialog_bg": "#1e1e2e",
|
||||
"dialog_body": "#1e1e2e",
|
||||
"dialog_border": "#45475a",
|
||||
"dialog_footer": "#1e1e2e",
|
||||
"dialog_label": "#cdd6f4",
|
||||
"dialog_rule": "#45475a",
|
||||
"dimension": "#cdd6f4",
|
||||
"dimension_driven": "#a6adc8",
|
||||
"error": "#f38ba8",
|
||||
"field": "#181825",
|
||||
"form_accent": "#cba6f7",
|
||||
"form_dim": "#a6adc8",
|
||||
"form_error": "#f38ba8",
|
||||
"form_error_fill": "#493446",
|
||||
"form_fill": "#477dc8",
|
||||
"form_ink": "#cdd6f4",
|
||||
"form_primary": "#cba6f7",
|
||||
"form_soft": "#302b46",
|
||||
"form_success": "#a6e3a1",
|
||||
"form_success_fill": "#353f42",
|
||||
"form_tab_ink": "#cdd6f4",
|
||||
"gizmo": [
|
||||
"#f38ba8",
|
||||
"#a6e3a1",
|
||||
"#477dc8"
|
||||
],
|
||||
"glyph_bg": "#313244",
|
||||
"glyph_edge": "#f38ba8",
|
||||
"glyph_text": "#f38ba8",
|
||||
"grid": "#242531",
|
||||
"grid_major": "#363846",
|
||||
"heading_bg": "#0f0f17",
|
||||
"hover": "#302b46",
|
||||
"hover_edge": "#477dc8",
|
||||
"hover_edge_halo": "#11111b",
|
||||
"hover_profile": "#cba6f728",
|
||||
"hover_profile_edge": "#477dc8",
|
||||
"icon": "#cdd6f4",
|
||||
"icon_fill": "#a6adc8",
|
||||
"icons.access": [
|
||||
"#a6e3a1",
|
||||
"#f38ba8"
|
||||
],
|
||||
"icons.agent": "#cba6f7",
|
||||
"icons.axes": [
|
||||
"#f38ba8",
|
||||
"#a6e3a1",
|
||||
"#477dc8"
|
||||
],
|
||||
"icons.curvature": [
|
||||
"#477dc8",
|
||||
"#a6e3a1",
|
||||
"#f38ba8"
|
||||
],
|
||||
"icons.draft": [
|
||||
"#a6e3a1",
|
||||
"#f9e2af"
|
||||
],
|
||||
"icons.floor": "#a6adc8",
|
||||
"icons.fold": "#a6adc8",
|
||||
"icons.folder": "#f9e2af",
|
||||
"icons.glint": "#cdd6f480",
|
||||
"icons.highlight": "#cdd6f4",
|
||||
"icons.interference": "#f38ba8",
|
||||
"icons.marker_halo": "#181825cc",
|
||||
"icons.paint": [
|
||||
"#f38ba8",
|
||||
"#477dc8",
|
||||
"#f9e2af"
|
||||
],
|
||||
"icons.plane": "#f9e2af",
|
||||
"icons.sheet": "#586382",
|
||||
"icons.sheet_2": "#586382",
|
||||
"icons.sheet_light": "#cdd6f4",
|
||||
"icons.sheet_line": "#477dc8",
|
||||
"icons.sky": "#586382",
|
||||
"icons.stripe": "#a6adc8",
|
||||
"icons.warning": "#f9e2af",
|
||||
"interference": "#f38ba8",
|
||||
"logo": [
|
||||
"#cba6f7",
|
||||
"#477dc8",
|
||||
"#586382",
|
||||
"#cdd6f4"
|
||||
],
|
||||
"manipulator": "#cba6f7",
|
||||
"mesh_accent": "#cba6f7",
|
||||
"mesh_fill": "#477dc8",
|
||||
"mesh_ink": "#cdd6f4",
|
||||
"mesh_soft": "#302b46",
|
||||
"on_accent": "#06121b",
|
||||
"origin_plane": "#f9e2af28",
|
||||
"origin_plane_edge": "#f9e2af88",
|
||||
"origin_plane_hover": "#cba6f740",
|
||||
"origin_point": "#a6adc8",
|
||||
"overlay": "#1e1e2e",
|
||||
"panel": "#1e1e2e",
|
||||
"panel_header": "#313244",
|
||||
"preview_add": "#477dc8",
|
||||
"preview_cut": "#f38ba866",
|
||||
"preview_new": "#f9e2af",
|
||||
"preview_sketch": "#a6e3a1",
|
||||
"profile_fill": "#477dc822",
|
||||
"render_accent": "#cba6f7",
|
||||
"render_fill": "#477dc8",
|
||||
"render_ink": "#cdd6f4",
|
||||
"render_soft": "#302b46",
|
||||
"rubber_band": "#cba6f7",
|
||||
"section_cap": "#f9e2af",
|
||||
"sel_edge": "#477dc8",
|
||||
"sel_edge_rim": "#477dc8",
|
||||
"sel_face": "#365280",
|
||||
"sel_profile": "#365280",
|
||||
"sel_profile_edge": "#477dc8",
|
||||
"sel_vertex": "#477dc8",
|
||||
"selected_bg": "#365280",
|
||||
"selected_ink": "#cdd6f4",
|
||||
"sheet_fill": "#477dc8",
|
||||
"sheet_ink": "#cdd6f4",
|
||||
"sheet_soft": "#302b46",
|
||||
"sketch": "#477dc8",
|
||||
"sketch_accent": "#cba6f7",
|
||||
"sketch_centerline": "#f9e2af",
|
||||
"sketch_construction": "#f9e2af",
|
||||
"sketch_fixed": "#cdd6f4",
|
||||
"sketch_ink": "#cdd6f4",
|
||||
"sketch_locked": "#a6e3a1",
|
||||
"sketch_lost": "#f38ba8",
|
||||
"sketch_point": "#cdd6f4",
|
||||
"sketch_point_free": "#477dc8",
|
||||
"sketch_projected": "#cba6f7",
|
||||
"sketch_soft": "#302b46",
|
||||
"subheading_bg": "#161622",
|
||||
"surface_accent": "#cba6f7",
|
||||
"surface_fill": "#477dc8",
|
||||
"surface_ink": "#cdd6f4",
|
||||
"surface_soft": "#302b46",
|
||||
"tab_on_fill_dark": "#181825",
|
||||
"text": "#cdd6f4",
|
||||
"text_dim": "#a6adc8",
|
||||
"timeline": "#313244",
|
||||
"timeline_item": "#313244",
|
||||
"timeline_item_error": "#51384b",
|
||||
"timeline_marker": "#a6adc8",
|
||||
"toolbar": "#1e1e2e",
|
||||
"triad": [
|
||||
"#f38ba8",
|
||||
"#a6e3a1",
|
||||
"#477dc8"
|
||||
],
|
||||
"viewport_bottom": "#11111b",
|
||||
"viewport_top": "#11111b",
|
||||
"warning": "#f9e2af"
|
||||
},
|
||||
"format": "solidcraft-theme/1",
|
||||
"name": "Catppuccin Mocha"
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
{
|
||||
"base": "light",
|
||||
"colors": {
|
||||
"accent": "#007aff",
|
||||
"accent_soft": "#dcecff",
|
||||
"agent_busy": "#329ade",
|
||||
"agent_ok": "#299b45",
|
||||
"app_bar": "#dcdcdc",
|
||||
"app_bar_hover": "#0000000a",
|
||||
"app_bar_hover_button": "#ffffff80",
|
||||
"app_bar_hover_strong": "#00000018",
|
||||
"app_bar_hover_subtle": "#ffffff40",
|
||||
"app_bar_icon": [
|
||||
"#48484a",
|
||||
"#8e8e93",
|
||||
"#007aff"
|
||||
],
|
||||
"app_bar_separator": "#b4b4b8",
|
||||
"app_bar_tab_active": "#f4f4f4",
|
||||
"app_bar_text": "#242426",
|
||||
"axis_x": "#ff0000",
|
||||
"axis_y": "#00ff00",
|
||||
"axis_z": "#0202f8",
|
||||
"bend_down": "#bd843f",
|
||||
"bend_up": "#36934d",
|
||||
"body": "#b6bbc3",
|
||||
"body_edge": "#393d44",
|
||||
"body_selected": "#79afea",
|
||||
"border": "#c6c6c8",
|
||||
"box_crossing": "#39a75b",
|
||||
"box_window": "#007aff",
|
||||
"chip": "#007aff",
|
||||
"construction_geom": "#9a712b",
|
||||
"construction_plane": "#efd3a146",
|
||||
"continuity": [
|
||||
"#009e73",
|
||||
"#0072b2",
|
||||
"#f0c800",
|
||||
"#cc008c"
|
||||
],
|
||||
"control_border": "#b0b0b4",
|
||||
"control_fill": "#ececec",
|
||||
"cube_border": "#9a9aa0",
|
||||
"cube_face": "#f2f2f4",
|
||||
"cube_hover": "#cce5ff",
|
||||
"cube_text": "#262628",
|
||||
"dialog_bg": "#ececec",
|
||||
"dialog_body": "#f3f3f3",
|
||||
"dialog_border": "#c6c6c8",
|
||||
"dialog_footer": "#ececec",
|
||||
"dialog_label": "#262628",
|
||||
"dialog_rule": "#c6c6c8",
|
||||
"dimension": "#262628",
|
||||
"dimension_driven": "#6e6e73",
|
||||
"error": "#d53137",
|
||||
"field": "#ffffff",
|
||||
"form_accent": "#8055be",
|
||||
"form_dim": "#6e6e73",
|
||||
"form_error": "#c02b31",
|
||||
"form_error_fill": "#fcebea",
|
||||
"form_fill": "#946bc9",
|
||||
"form_ink": "#262628",
|
||||
"form_primary": "#0071e3",
|
||||
"form_soft": "#eee8f7",
|
||||
"form_success": "#237b38",
|
||||
"form_success_fill": "#e6f3e8",
|
||||
"form_tab_ink": "#7445af",
|
||||
"gizmo": [
|
||||
"#ed514b",
|
||||
"#36ad59",
|
||||
"#2184e9"
|
||||
],
|
||||
"glyph_bg": "#ffffff",
|
||||
"glyph_edge": "#ce8d85",
|
||||
"glyph_text": "#c34136",
|
||||
"grid": "#d0d2d6",
|
||||
"grid_major": "#b5b8be",
|
||||
"heading_bg": "#e2e2e2",
|
||||
"hover": "#dcecff",
|
||||
"hover_edge": "#222226",
|
||||
"hover_edge_halo": "#ffffff",
|
||||
"hover_profile": "#007aff26",
|
||||
"hover_profile_edge": "#007aff",
|
||||
"icon": "#505054",
|
||||
"icon_fill": "#b5c8e1",
|
||||
"icons.access": [
|
||||
"#34a853",
|
||||
"#e94e44"
|
||||
],
|
||||
"icons.agent": "#a467d6",
|
||||
"icons.axes": [
|
||||
"#e95049",
|
||||
"#35a754",
|
||||
"#297fdb"
|
||||
],
|
||||
"icons.curvature": [
|
||||
"#297fdb",
|
||||
"#35ad59",
|
||||
"#e95049"
|
||||
],
|
||||
"icons.draft": [
|
||||
"#35ad59",
|
||||
"#e4bb31"
|
||||
],
|
||||
"icons.floor": "#606068",
|
||||
"icons.fold": "#9a9aa2",
|
||||
"icons.folder": "#70bce8",
|
||||
"icons.glint": "#ffffffb3",
|
||||
"icons.highlight": "#ffffff",
|
||||
"icons.interference": "#ed514b",
|
||||
"icons.marker_halo": "#00000080",
|
||||
"icons.paint": [
|
||||
"#ed514b",
|
||||
"#3599e4",
|
||||
"#edc53d"
|
||||
],
|
||||
"icons.plane": "#ebd3a2",
|
||||
"icons.sheet": "#bbd7ef",
|
||||
"icons.sheet_2": "#a5c8e7",
|
||||
"icons.sheet_light": "#e5eff8",
|
||||
"icons.sheet_line": "#84aace",
|
||||
"icons.sky": "#d1e2f0",
|
||||
"icons.stripe": "#38383c",
|
||||
"icons.warning": "#f5c437",
|
||||
"interference": "#ed443b",
|
||||
"logo": [
|
||||
"#85b9ef",
|
||||
"#4096e9",
|
||||
"#2b71be",
|
||||
"#ffffff"
|
||||
],
|
||||
"manipulator": "#007aff",
|
||||
"mesh_accent": "#927300",
|
||||
"mesh_fill": "#d0ac35",
|
||||
"mesh_ink": "#856700",
|
||||
"mesh_soft": "#fbf5df",
|
||||
"on_accent": "#ffffff",
|
||||
"origin_plane": "#f0c58a55",
|
||||
"origin_plane_edge": "#bf915ac8",
|
||||
"origin_plane_hover": "#8eaee680",
|
||||
"origin_point": "#d8dadd",
|
||||
"overlay": "#f6f6f6f5",
|
||||
"panel": "#ececec",
|
||||
"panel_header": "#e2e2e2",
|
||||
"preview_add": "#78aae1",
|
||||
"preview_cut": "#eb634d66",
|
||||
"preview_new": "#e9d875",
|
||||
"preview_sketch": "#3ab86b",
|
||||
"profile_fill": "#f3c88a3d",
|
||||
"render_accent": "#af4083",
|
||||
"render_fill": "#bd5b92",
|
||||
"render_ink": "#9f3676",
|
||||
"render_soft": "#f6e8f0",
|
||||
"rubber_band": "#007aff",
|
||||
"section_cap": "#c79769",
|
||||
"sel_edge": "#007aff",
|
||||
"sel_edge_rim": "#a6d2ff",
|
||||
"sel_face": "#669bd6",
|
||||
"sel_profile": "#215da3",
|
||||
"sel_profile_edge": "#007aff",
|
||||
"sel_vertex": "#316cae",
|
||||
"selected_bg": "#dcecff",
|
||||
"selected_ink": "#004b9b",
|
||||
"sheet_fill": "#3884a5",
|
||||
"sheet_ink": "#226b89",
|
||||
"sheet_soft": "#e6f1f6",
|
||||
"sketch": "#1264bf",
|
||||
"sketch_accent": "#288049",
|
||||
"sketch_centerline": "#b77c47",
|
||||
"sketch_construction": "#cb8b37",
|
||||
"sketch_fixed": "#262628",
|
||||
"sketch_ink": "#23723f",
|
||||
"sketch_locked": "#318348",
|
||||
"sketch_lost": "#c94f68",
|
||||
"sketch_point": "#262628",
|
||||
"sketch_point_free": "#1264bf",
|
||||
"sketch_projected": "#9258b7",
|
||||
"sketch_soft": "#eaf4ed",
|
||||
"subheading_bg": "#e8e8e8",
|
||||
"surface_accent": "#996a28",
|
||||
"surface_fill": "#b5833e",
|
||||
"surface_ink": "#8b5c1d",
|
||||
"surface_soft": "#f8efdf",
|
||||
"tab_on_fill_dark": "#242426",
|
||||
"text": "#262628",
|
||||
"text_dim": "#6e6e73",
|
||||
"timeline": "#e2e2e2",
|
||||
"timeline_item": "#ffffff",
|
||||
"timeline_item_error": "#f8dedb",
|
||||
"timeline_marker": "#63636b",
|
||||
"toolbar": "#ececec",
|
||||
"triad": [
|
||||
"#e95049",
|
||||
"#35a754",
|
||||
"#297fdb"
|
||||
],
|
||||
"viewport_bottom": "#d9dce2",
|
||||
"viewport_top": "#f5f5f7",
|
||||
"warning": "#ad7400"
|
||||
},
|
||||
"format": "solidcraft-theme/1",
|
||||
"name": "Cupertino"
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
{
|
||||
"base": "dark",
|
||||
"colors": {
|
||||
"accent": "#4aa3d8",
|
||||
"accent_soft": "#293c49",
|
||||
"agent_busy": "#4aa3d8",
|
||||
"agent_ok": "#83c69b",
|
||||
"app_bar": "#1c1e21",
|
||||
"app_bar_hover": "#e0e2e518",
|
||||
"app_bar_hover_button": "#e0e2e522",
|
||||
"app_bar_hover_strong": "#e0e2e530",
|
||||
"app_bar_hover_subtle": "#e0e2e510",
|
||||
"app_bar_icon": [
|
||||
"#e0e2e5",
|
||||
"#a0a6b0",
|
||||
"#4aa3d8"
|
||||
],
|
||||
"app_bar_separator": "#484d55",
|
||||
"app_bar_tab_active": "#303338",
|
||||
"app_bar_text": "#e0e2e5",
|
||||
"axis_x": "#ed7777",
|
||||
"axis_y": "#83c69b",
|
||||
"axis_z": "#2878a9",
|
||||
"bend_down": "#dbb879",
|
||||
"bend_up": "#83c69b",
|
||||
"body": "#68717d",
|
||||
"body_edge": "#2878a9",
|
||||
"body_selected": "#245674",
|
||||
"border": "#484d55",
|
||||
"box_crossing": "#83c69b",
|
||||
"box_window": "#4aa3d8",
|
||||
"chip": "#4aa3d8",
|
||||
"construction_geom": "#dbb879",
|
||||
"construction_plane": "#dbb87922",
|
||||
"continuity": [
|
||||
"#83c69b",
|
||||
"#2878a9",
|
||||
"#dbb879",
|
||||
"#ad9cce"
|
||||
],
|
||||
"control_border": "#484d55",
|
||||
"control_fill": "#303338",
|
||||
"cube_border": "#484d55",
|
||||
"cube_face": "#303338",
|
||||
"cube_hover": "#293c49",
|
||||
"cube_text": "#e0e2e5",
|
||||
"dialog_bg": "#25272b",
|
||||
"dialog_body": "#25272b",
|
||||
"dialog_border": "#484d55",
|
||||
"dialog_footer": "#25272b",
|
||||
"dialog_label": "#e0e2e5",
|
||||
"dialog_rule": "#484d55",
|
||||
"dimension": "#e0e2e5",
|
||||
"dimension_driven": "#a0a6b0",
|
||||
"error": "#ed7777",
|
||||
"field": "#1c1e21",
|
||||
"form_accent": "#4aa3d8",
|
||||
"form_dim": "#a0a6b0",
|
||||
"form_error": "#ed7777",
|
||||
"form_error_fill": "#4d373a",
|
||||
"form_fill": "#2878a9",
|
||||
"form_ink": "#e0e2e5",
|
||||
"form_primary": "#4aa3d8",
|
||||
"form_soft": "#293c49",
|
||||
"form_success": "#83c69b",
|
||||
"form_success_fill": "#35423e",
|
||||
"form_tab_ink": "#e0e2e5",
|
||||
"gizmo": [
|
||||
"#ed7777",
|
||||
"#83c69b",
|
||||
"#2878a9"
|
||||
],
|
||||
"glyph_bg": "#303338",
|
||||
"glyph_edge": "#ed7777",
|
||||
"glyph_text": "#ed7777",
|
||||
"grid": "#292b30",
|
||||
"grid_major": "#393c42",
|
||||
"heading_bg": "#121416",
|
||||
"hover": "#293c49",
|
||||
"hover_edge": "#2878a9",
|
||||
"hover_edge_halo": "#17191d",
|
||||
"hover_profile": "#4aa3d828",
|
||||
"hover_profile_edge": "#2878a9",
|
||||
"icon": "#e0e2e5",
|
||||
"icon_fill": "#a0a6b0",
|
||||
"icons.access": [
|
||||
"#83c69b",
|
||||
"#ed7777"
|
||||
],
|
||||
"icons.agent": "#ad9cce",
|
||||
"icons.axes": [
|
||||
"#ed7777",
|
||||
"#83c69b",
|
||||
"#2878a9"
|
||||
],
|
||||
"icons.curvature": [
|
||||
"#2878a9",
|
||||
"#83c69b",
|
||||
"#ed7777"
|
||||
],
|
||||
"icons.draft": [
|
||||
"#83c69b",
|
||||
"#dbb879"
|
||||
],
|
||||
"icons.floor": "#a0a6b0",
|
||||
"icons.fold": "#a0a6b0",
|
||||
"icons.folder": "#dbb879",
|
||||
"icons.glint": "#e0e2e580",
|
||||
"icons.highlight": "#e0e2e5",
|
||||
"icons.interference": "#ed7777",
|
||||
"icons.marker_halo": "#1c1e21cc",
|
||||
"icons.paint": [
|
||||
"#ed7777",
|
||||
"#2878a9",
|
||||
"#dbb879"
|
||||
],
|
||||
"icons.plane": "#dbb879",
|
||||
"icons.sheet": "#68717d",
|
||||
"icons.sheet_2": "#68717d",
|
||||
"icons.sheet_light": "#e0e2e5",
|
||||
"icons.sheet_line": "#2878a9",
|
||||
"icons.sky": "#68717d",
|
||||
"icons.stripe": "#a0a6b0",
|
||||
"icons.warning": "#dbb879",
|
||||
"interference": "#ed7777",
|
||||
"logo": [
|
||||
"#4aa3d8",
|
||||
"#2878a9",
|
||||
"#68717d",
|
||||
"#e0e2e5"
|
||||
],
|
||||
"manipulator": "#4aa3d8",
|
||||
"mesh_accent": "#4aa3d8",
|
||||
"mesh_fill": "#2878a9",
|
||||
"mesh_ink": "#e0e2e5",
|
||||
"mesh_soft": "#293c49",
|
||||
"on_accent": "#06121b",
|
||||
"origin_plane": "#dbb87928",
|
||||
"origin_plane_edge": "#dbb87988",
|
||||
"origin_plane_hover": "#4aa3d840",
|
||||
"origin_point": "#a0a6b0",
|
||||
"overlay": "#25272b",
|
||||
"panel": "#25272b",
|
||||
"panel_header": "#303338",
|
||||
"preview_add": "#2878a9",
|
||||
"preview_cut": "#ed777766",
|
||||
"preview_new": "#dbb879",
|
||||
"preview_sketch": "#83c69b",
|
||||
"profile_fill": "#2878a922",
|
||||
"render_accent": "#4aa3d8",
|
||||
"render_fill": "#2878a9",
|
||||
"render_ink": "#e0e2e5",
|
||||
"render_soft": "#293c49",
|
||||
"rubber_band": "#4aa3d8",
|
||||
"section_cap": "#dbb879",
|
||||
"sel_edge": "#2878a9",
|
||||
"sel_edge_rim": "#2878a9",
|
||||
"sel_face": "#245674",
|
||||
"sel_profile": "#245674",
|
||||
"sel_profile_edge": "#2878a9",
|
||||
"sel_vertex": "#2878a9",
|
||||
"selected_bg": "#245674",
|
||||
"selected_ink": "#e0e2e5",
|
||||
"sheet_fill": "#2878a9",
|
||||
"sheet_ink": "#e0e2e5",
|
||||
"sheet_soft": "#293c49",
|
||||
"sketch": "#2878a9",
|
||||
"sketch_accent": "#4aa3d8",
|
||||
"sketch_centerline": "#dbb879",
|
||||
"sketch_construction": "#dbb879",
|
||||
"sketch_fixed": "#e0e2e5",
|
||||
"sketch_ink": "#e0e2e5",
|
||||
"sketch_locked": "#83c69b",
|
||||
"sketch_lost": "#ed7777",
|
||||
"sketch_point": "#e0e2e5",
|
||||
"sketch_point_free": "#2878a9",
|
||||
"sketch_projected": "#ad9cce",
|
||||
"sketch_soft": "#293c49",
|
||||
"subheading_bg": "#1c1d20",
|
||||
"surface_accent": "#4aa3d8",
|
||||
"surface_fill": "#2878a9",
|
||||
"surface_ink": "#e0e2e5",
|
||||
"surface_soft": "#293c49",
|
||||
"tab_on_fill_dark": "#1c1e21",
|
||||
"text": "#e0e2e5",
|
||||
"text_dim": "#a0a6b0",
|
||||
"timeline": "#303338",
|
||||
"timeline_item": "#303338",
|
||||
"timeline_item_error": "#553a3d",
|
||||
"timeline_marker": "#a0a6b0",
|
||||
"toolbar": "#25272b",
|
||||
"triad": [
|
||||
"#ed7777",
|
||||
"#83c69b",
|
||||
"#2878a9"
|
||||
],
|
||||
"viewport_bottom": "#17191d",
|
||||
"viewport_top": "#17191d",
|
||||
"warning": "#dbb879"
|
||||
},
|
||||
"format": "solidcraft-theme/1",
|
||||
"name": "Graphite"
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
{
|
||||
"base": "dark",
|
||||
"colors": {
|
||||
"accent": "#4dff79",
|
||||
"accent_soft": "#123c20",
|
||||
"agent_busy": "#4dff79",
|
||||
"agent_ok": "#39d45c",
|
||||
"app_bar": "#020904",
|
||||
"app_bar_hover": "#79ff9b18",
|
||||
"app_bar_hover_button": "#79ff9b20",
|
||||
"app_bar_hover_strong": "#79ff9b30",
|
||||
"app_bar_hover_subtle": "#79ff9b10",
|
||||
"app_bar_icon": [
|
||||
"#8def9f",
|
||||
"#5b9b6a",
|
||||
"#4dff79"
|
||||
],
|
||||
"app_bar_separator": "#285333",
|
||||
"app_bar_tab_active": "#0b1d10",
|
||||
"app_bar_text": "#8def9f",
|
||||
"axis_x": "#c1f3ba",
|
||||
"axis_y": "#4dff79",
|
||||
"axis_z": "#599b70",
|
||||
"bend_down": "#32ba50",
|
||||
"bend_up": "#39d45c",
|
||||
"body": "#0b3818",
|
||||
"body_edge": "#4dff79",
|
||||
"body_selected": "#18542b",
|
||||
"border": "#285333",
|
||||
"box_crossing": "#39d45c",
|
||||
"box_window": "#4dff79",
|
||||
"chip": "#144a20",
|
||||
"construction_geom": "#2fad4a",
|
||||
"construction_plane": "#72c68b18",
|
||||
"continuity": [
|
||||
"#35c555",
|
||||
"#36c655",
|
||||
"#39d35b",
|
||||
"#30b04c"
|
||||
],
|
||||
"control_border": "#285333",
|
||||
"control_fill": "#0b1d10",
|
||||
"cube_border": "#285333",
|
||||
"cube_face": "#0f3617",
|
||||
"cube_hover": "#175625",
|
||||
"cube_text": "#40ee66",
|
||||
"dialog_bg": "#061009",
|
||||
"dialog_body": "#0b1d10",
|
||||
"dialog_border": "#285333",
|
||||
"dialog_footer": "#061009",
|
||||
"dialog_label": "#8def9f",
|
||||
"dialog_rule": "#173b22",
|
||||
"dimension": "#8def9f",
|
||||
"dimension_driven": "#5b9b6a",
|
||||
"error": "#d8ffdf",
|
||||
"field": "#061009",
|
||||
"form_accent": "#59d67b",
|
||||
"form_dim": "#5b9b6a",
|
||||
"form_error": "#d8ffdf",
|
||||
"form_error_fill": "#21462a",
|
||||
"form_fill": "#287e43",
|
||||
"form_ink": "#9bf0b1",
|
||||
"form_primary": "#4dff79",
|
||||
"form_soft": "#0e2918",
|
||||
"form_success": "#39d45c",
|
||||
"form_success_fill": "#133b20",
|
||||
"form_tab_ink": "#9bf0b1",
|
||||
"gizmo": [
|
||||
"#c1f3ba",
|
||||
"#4dff79",
|
||||
"#599b70"
|
||||
],
|
||||
"glyph_bg": "#09200e",
|
||||
"glyph_edge": "#33bd51",
|
||||
"glyph_text": "#d8ffdf",
|
||||
"grid": "#091b0f",
|
||||
"grid_major": "#122d1a",
|
||||
"heading_bg": "#030804",
|
||||
"hover": "#123c20",
|
||||
"hover_edge": "#4dff79",
|
||||
"hover_edge_halo": "#021007",
|
||||
"hover_profile": "#4dff7926",
|
||||
"hover_profile_edge": "#4dff79",
|
||||
"icon": "#8def9f",
|
||||
"icon_fill": "#57b875",
|
||||
"icons.access": [
|
||||
"#35c555",
|
||||
"#2eac4a"
|
||||
],
|
||||
"icons.agent": "#37cb57",
|
||||
"icons.axes": [
|
||||
"#c1f3ba",
|
||||
"#4dff79",
|
||||
"#599b70"
|
||||
],
|
||||
"icons.curvature": [
|
||||
"#34c053",
|
||||
"#35c555",
|
||||
"#2eac4a"
|
||||
],
|
||||
"icons.draft": [
|
||||
"#37cb57",
|
||||
"#39d35b"
|
||||
],
|
||||
"icons.floor": "#207633",
|
||||
"icons.fold": "#2b9f44",
|
||||
"icons.folder": "#39d35b",
|
||||
"icons.glint": "#42f46999",
|
||||
"icons.highlight": "#43f66a",
|
||||
"icons.interference": "#d8ffdf",
|
||||
"icons.marker_halo": "#06170acc",
|
||||
"icons.paint": [
|
||||
"#37cc58",
|
||||
"#36c655",
|
||||
"#40ee66"
|
||||
],
|
||||
"icons.plane": "#39d35b",
|
||||
"icons.sheet": "#35c354",
|
||||
"icons.sheet_2": "#2ca246",
|
||||
"icons.sheet_light": "#40ec66",
|
||||
"icons.sheet_line": "#42f469",
|
||||
"icons.sky": "#1b652b",
|
||||
"icons.stripe": "#06170a",
|
||||
"icons.warning": "#d1ffad",
|
||||
"interference": "#d8ffdf",
|
||||
"logo": [
|
||||
"#b3ffc4",
|
||||
"#79ed95",
|
||||
"#34854b",
|
||||
"#d8ffdf"
|
||||
],
|
||||
"manipulator": "#4dff79",
|
||||
"mesh_accent": "#59d67b",
|
||||
"mesh_fill": "#287e43",
|
||||
"mesh_ink": "#9bf0b1",
|
||||
"mesh_soft": "#0e2918",
|
||||
"on_accent": "#031207",
|
||||
"origin_plane": "#72c68b20",
|
||||
"origin_plane_edge": "#72c68b80",
|
||||
"origin_plane_hover": "#4dff7940",
|
||||
"origin_point": "#37ca57",
|
||||
"overlay": "#061009",
|
||||
"panel": "#061009",
|
||||
"panel_header": "#0b1d10",
|
||||
"preview_add": "#278e43",
|
||||
"preview_cut": "#4dff7955",
|
||||
"preview_new": "#319f4b",
|
||||
"preview_sketch": "#39d45c",
|
||||
"profile_fill": "#2fbe5320",
|
||||
"render_accent": "#59d67b",
|
||||
"render_fill": "#287e43",
|
||||
"render_ink": "#9bf0b1",
|
||||
"render_soft": "#0e2918",
|
||||
"rubber_band": "#4dff79",
|
||||
"section_cap": "#32ba50",
|
||||
"sel_edge": "#4dff79",
|
||||
"sel_edge_rim": "#249d48",
|
||||
"sel_face": "#216638",
|
||||
"sel_profile": "#1a5e2c",
|
||||
"sel_profile_edge": "#4dff79",
|
||||
"sel_vertex": "#4dff79",
|
||||
"selected_bg": "#123c20",
|
||||
"selected_ink": "#b3ffc4",
|
||||
"sheet_fill": "#287e43",
|
||||
"sheet_ink": "#9bf0b1",
|
||||
"sheet_soft": "#0e2918",
|
||||
"sketch": "#4dff79",
|
||||
"sketch_accent": "#59d67b",
|
||||
"sketch_centerline": "#32ba50",
|
||||
"sketch_construction": "#2fad4a",
|
||||
"sketch_fixed": "#8def9f",
|
||||
"sketch_ink": "#9bf0b1",
|
||||
"sketch_locked": "#39d45c",
|
||||
"sketch_lost": "#d8ffdf",
|
||||
"sketch_point": "#8def9f",
|
||||
"sketch_point_free": "#4dff79",
|
||||
"sketch_projected": "#37cb57",
|
||||
"sketch_soft": "#0e2918",
|
||||
"subheading_bg": "#040c07",
|
||||
"surface_accent": "#59d67b",
|
||||
"surface_fill": "#287e43",
|
||||
"surface_ink": "#9bf0b1",
|
||||
"surface_soft": "#0e2918",
|
||||
"tab_on_fill_dark": "#031207",
|
||||
"text": "#8def9f",
|
||||
"text_dim": "#5b9b6a",
|
||||
"timeline": "#020904",
|
||||
"timeline_item": "#0b1d10",
|
||||
"timeline_item_error": "#21462a",
|
||||
"timeline_marker": "#5b9b6a",
|
||||
"toolbar": "#061009",
|
||||
"triad": [
|
||||
"#c1f3ba",
|
||||
"#4dff79",
|
||||
"#599b70"
|
||||
],
|
||||
"viewport_bottom": "#010703",
|
||||
"viewport_top": "#010703",
|
||||
"warning": "#d1ffad"
|
||||
},
|
||||
"format": "solidcraft-theme/1",
|
||||
"name": "Green Phosphor"
|
||||
}
|
||||
@@ -16,7 +16,6 @@
|
||||
"error": "#fb4934",
|
||||
"warning": "#fabd2f",
|
||||
"icon": "#d5c4a1",
|
||||
"icon_fill": "#458588",
|
||||
"timeline": "#141617",
|
||||
"hover": "#46403e",
|
||||
"sel_face": "#458588",
|
||||
@@ -62,8 +61,6 @@
|
||||
"dialog_bg": "#3c3836",
|
||||
"dialog_border": "#504945",
|
||||
"chip": "#458588",
|
||||
"heading_bg": "#1d2021",
|
||||
"subheading_bg": "#2c2c2c",
|
||||
"section_cap": "#d65d0e",
|
||||
"profile_fill": "#83a59828",
|
||||
"glyph_bg": "#2c2c2c",
|
||||
@@ -96,24 +93,6 @@
|
||||
"#b8bb26",
|
||||
"#83a598"
|
||||
],
|
||||
"app_bar_icon": [
|
||||
"#ebdbb2",
|
||||
"#928374",
|
||||
"#83a598"
|
||||
],
|
||||
"icons.folder": "#d79921",
|
||||
"icons.axes": [
|
||||
"#fb4934",
|
||||
"#b8bb26",
|
||||
"#83a598"
|
||||
],
|
||||
"icons.agent": "#d3869b",
|
||||
"icons.warning": "#fabd2f",
|
||||
"icons.interference": "#fb4934",
|
||||
"icons.paint": [
|
||||
"#fb4934",
|
||||
"#83a598",
|
||||
"#fabd2f"
|
||||
]
|
||||
"icons.agent": "#d3869b"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
"error": "#fb4934",
|
||||
"warning": "#fabd2f",
|
||||
"icon": "#d5c4a1",
|
||||
"icon_fill": "#458588",
|
||||
"timeline": "#1d2021",
|
||||
"hover": "#46403e",
|
||||
"sel_face": "#458588",
|
||||
@@ -62,8 +61,6 @@
|
||||
"dialog_bg": "#3c3836",
|
||||
"dialog_border": "#504945",
|
||||
"chip": "#458588",
|
||||
"heading_bg": "#32302f",
|
||||
"subheading_bg": "#373432",
|
||||
"section_cap": "#d65d0e",
|
||||
"profile_fill": "#83a59828",
|
||||
"glyph_bg": "#373432",
|
||||
@@ -96,24 +93,6 @@
|
||||
"#b8bb26",
|
||||
"#83a598"
|
||||
],
|
||||
"app_bar_icon": [
|
||||
"#ebdbb2",
|
||||
"#928374",
|
||||
"#83a598"
|
||||
],
|
||||
"icons.folder": "#d79921",
|
||||
"icons.axes": [
|
||||
"#fb4934",
|
||||
"#b8bb26",
|
||||
"#83a598"
|
||||
],
|
||||
"icons.agent": "#d3869b",
|
||||
"icons.warning": "#fabd2f",
|
||||
"icons.interference": "#fb4934",
|
||||
"icons.paint": [
|
||||
"#fb4934",
|
||||
"#83a598",
|
||||
"#fabd2f"
|
||||
]
|
||||
"icons.agent": "#d3869b"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
"error": "#fb4934",
|
||||
"warning": "#fabd2f",
|
||||
"icon": "#d5c4a1",
|
||||
"icon_fill": "#458588",
|
||||
"timeline": "#1d2021",
|
||||
"hover": "#46403e",
|
||||
"sel_face": "#458588",
|
||||
@@ -62,8 +61,6 @@
|
||||
"dialog_bg": "#3c3836",
|
||||
"dialog_border": "#504945",
|
||||
"chip": "#458588",
|
||||
"heading_bg": "#282828",
|
||||
"subheading_bg": "#32302f",
|
||||
"section_cap": "#d65d0e",
|
||||
"profile_fill": "#83a59828",
|
||||
"glyph_bg": "#32302f",
|
||||
@@ -96,24 +93,6 @@
|
||||
"#b8bb26",
|
||||
"#83a598"
|
||||
],
|
||||
"app_bar_icon": [
|
||||
"#ebdbb2",
|
||||
"#928374",
|
||||
"#83a598"
|
||||
],
|
||||
"icons.folder": "#d79921",
|
||||
"icons.axes": [
|
||||
"#fb4934",
|
||||
"#b8bb26",
|
||||
"#83a598"
|
||||
],
|
||||
"icons.agent": "#d3869b",
|
||||
"icons.warning": "#fabd2f",
|
||||
"icons.interference": "#fb4934",
|
||||
"icons.paint": [
|
||||
"#fb4934",
|
||||
"#83a598",
|
||||
"#fabd2f"
|
||||
]
|
||||
"icons.agent": "#d3869b"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
"error": "#9d0006",
|
||||
"warning": "#b57614",
|
||||
"icon": "#504945",
|
||||
"icon_fill": "#bdae93",
|
||||
"timeline": "#ebdbb2",
|
||||
"hover": "#f1e5c1",
|
||||
"sel_face": "#458588",
|
||||
@@ -62,8 +61,6 @@
|
||||
"dialog_bg": "#f9f5d7",
|
||||
"dialog_border": "#d5c4a1",
|
||||
"chip": "#458588",
|
||||
"heading_bg": "#d5c4a1",
|
||||
"subheading_bg": "#ebdbb2",
|
||||
"section_cap": "#d65d0e",
|
||||
"profile_fill": "#fabd2f46",
|
||||
"glyph_bg": "#fcfaeb",
|
||||
@@ -96,24 +93,6 @@
|
||||
"#98971a",
|
||||
"#458588"
|
||||
],
|
||||
"app_bar_icon": [
|
||||
"#fbf1c7",
|
||||
"#a89984",
|
||||
"#83a598"
|
||||
],
|
||||
"icons.folder": "#d79921",
|
||||
"icons.axes": [
|
||||
"#cc241d",
|
||||
"#98971a",
|
||||
"#458588"
|
||||
],
|
||||
"icons.agent": "#8f3f71",
|
||||
"icons.warning": "#d79921",
|
||||
"icons.interference": "#cc241d",
|
||||
"icons.paint": [
|
||||
"#cc241d",
|
||||
"#458588",
|
||||
"#d79921"
|
||||
]
|
||||
"icons.agent": "#8f3f71"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
"error": "#9d0006",
|
||||
"warning": "#b57614",
|
||||
"icon": "#504945",
|
||||
"icon_fill": "#bdae93",
|
||||
"timeline": "#ebdbb2",
|
||||
"hover": "#eedfb6",
|
||||
"sel_face": "#458588",
|
||||
@@ -62,8 +61,6 @@
|
||||
"dialog_bg": "#f2e5bc",
|
||||
"dialog_border": "#d5c4a1",
|
||||
"chip": "#458588",
|
||||
"heading_bg": "#d5c4a1",
|
||||
"subheading_bg": "#ebdbb2",
|
||||
"section_cap": "#d65d0e",
|
||||
"profile_fill": "#fabd2f46",
|
||||
"glyph_bg": "#f8f2de",
|
||||
@@ -96,24 +93,6 @@
|
||||
"#98971a",
|
||||
"#458588"
|
||||
],
|
||||
"app_bar_icon": [
|
||||
"#fbf1c7",
|
||||
"#a89984",
|
||||
"#83a598"
|
||||
],
|
||||
"icons.folder": "#d79921",
|
||||
"icons.axes": [
|
||||
"#cc241d",
|
||||
"#98971a",
|
||||
"#458588"
|
||||
],
|
||||
"icons.agent": "#8f3f71",
|
||||
"icons.warning": "#d79921",
|
||||
"icons.interference": "#cc241d",
|
||||
"icons.paint": [
|
||||
"#cc241d",
|
||||
"#458588",
|
||||
"#d79921"
|
||||
]
|
||||
"icons.agent": "#8f3f71"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
"error": "#9d0006",
|
||||
"warning": "#b57614",
|
||||
"icon": "#504945",
|
||||
"icon_fill": "#bdae93",
|
||||
"timeline": "#ebdbb2",
|
||||
"hover": "#f1e4ba",
|
||||
"sel_face": "#458588",
|
||||
@@ -62,8 +61,6 @@
|
||||
"dialog_bg": "#fbf1c7",
|
||||
"dialog_border": "#d5c4a1",
|
||||
"chip": "#458588",
|
||||
"heading_bg": "#d5c4a1",
|
||||
"subheading_bg": "#ebdbb2",
|
||||
"section_cap": "#d65d0e",
|
||||
"profile_fill": "#fabd2f46",
|
||||
"glyph_bg": "#fdf8e3",
|
||||
@@ -96,24 +93,6 @@
|
||||
"#98971a",
|
||||
"#458588"
|
||||
],
|
||||
"app_bar_icon": [
|
||||
"#fbf1c7",
|
||||
"#a89984",
|
||||
"#83a598"
|
||||
],
|
||||
"icons.folder": "#d79921",
|
||||
"icons.axes": [
|
||||
"#cc241d",
|
||||
"#98971a",
|
||||
"#458588"
|
||||
],
|
||||
"icons.agent": "#8f3f71",
|
||||
"icons.warning": "#d79921",
|
||||
"icons.interference": "#cc241d",
|
||||
"icons.paint": [
|
||||
"#cc241d",
|
||||
"#458588",
|
||||
"#d79921"
|
||||
]
|
||||
"icons.agent": "#8f3f71"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
{
|
||||
"base": "dark",
|
||||
"colors": {
|
||||
"accent": "#00bfff",
|
||||
"accent_soft": "#002c45",
|
||||
"agent_busy": "#00bfff",
|
||||
"agent_ok": "#40e080",
|
||||
"app_bar": "#000000",
|
||||
"app_bar_hover": "#ffffff18",
|
||||
"app_bar_hover_button": "#ffffff22",
|
||||
"app_bar_hover_strong": "#ffffff30",
|
||||
"app_bar_hover_subtle": "#ffffff10",
|
||||
"app_bar_icon": [
|
||||
"#ffffff",
|
||||
"#cccccc",
|
||||
"#00bfff"
|
||||
],
|
||||
"app_bar_separator": "#a0a0a0",
|
||||
"app_bar_tab_active": "#151515",
|
||||
"app_bar_text": "#ffffff",
|
||||
"axis_x": "#ff6666",
|
||||
"axis_y": "#40e080",
|
||||
"axis_z": "#0070d0",
|
||||
"bend_down": "#ffcc00",
|
||||
"bend_up": "#40e080",
|
||||
"body": "#555555",
|
||||
"body_edge": "#0070d0",
|
||||
"body_selected": "#004c80",
|
||||
"border": "#a0a0a0",
|
||||
"box_crossing": "#40e080",
|
||||
"box_window": "#00bfff",
|
||||
"chip": "#00bfff",
|
||||
"construction_geom": "#ffcc00",
|
||||
"construction_plane": "#ffcc0022",
|
||||
"continuity": [
|
||||
"#40e080",
|
||||
"#0070d0",
|
||||
"#ffcc00",
|
||||
"#c080ff"
|
||||
],
|
||||
"control_border": "#a0a0a0",
|
||||
"control_fill": "#151515",
|
||||
"cube_border": "#a0a0a0",
|
||||
"cube_face": "#151515",
|
||||
"cube_hover": "#002c45",
|
||||
"cube_text": "#ffffff",
|
||||
"dialog_bg": "#0c0c0c",
|
||||
"dialog_body": "#000000",
|
||||
"dialog_border": "#a0a0a0",
|
||||
"dialog_footer": "#000000",
|
||||
"dialog_label": "#ffffff",
|
||||
"dialog_rule": "#a0a0a0",
|
||||
"dimension": "#ffffff",
|
||||
"dimension_driven": "#cccccc",
|
||||
"error": "#ff6666",
|
||||
"field": "#000000",
|
||||
"form_accent": "#00bfff",
|
||||
"form_dim": "#cccccc",
|
||||
"form_error": "#ff6666",
|
||||
"form_error_fill": "#331414",
|
||||
"form_fill": "#0070d0",
|
||||
"form_ink": "#ffffff",
|
||||
"form_primary": "#00bfff",
|
||||
"form_soft": "#002c45",
|
||||
"form_success": "#40e080",
|
||||
"form_success_fill": "#0b2616",
|
||||
"form_tab_ink": "#ffffff",
|
||||
"gizmo": [
|
||||
"#ff6666",
|
||||
"#40e080",
|
||||
"#0070d0"
|
||||
],
|
||||
"glyph_bg": "#151515",
|
||||
"glyph_edge": "#ff6666",
|
||||
"glyph_text": "#ff6666",
|
||||
"grid": "#10202b",
|
||||
"grid_major": "#234054",
|
||||
"heading_bg": "#000000",
|
||||
"hover": "#002c45",
|
||||
"hover_edge": "#ffcc00",
|
||||
"hover_edge_halo": "#000000",
|
||||
"hover_profile": "#00bfff28",
|
||||
"hover_profile_edge": "#0070d0",
|
||||
"icon": "#ffffff",
|
||||
"icon_fill": "#cccccc",
|
||||
"icons.access": [
|
||||
"#40e080",
|
||||
"#ff6666"
|
||||
],
|
||||
"icons.agent": "#c080ff",
|
||||
"icons.axes": [
|
||||
"#ff6666",
|
||||
"#40e080",
|
||||
"#0070d0"
|
||||
],
|
||||
"icons.curvature": [
|
||||
"#0070d0",
|
||||
"#40e080",
|
||||
"#ff6666"
|
||||
],
|
||||
"icons.draft": [
|
||||
"#40e080",
|
||||
"#ffcc00"
|
||||
],
|
||||
"icons.floor": "#cccccc",
|
||||
"icons.fold": "#cccccc",
|
||||
"icons.folder": "#ffcc00",
|
||||
"icons.glint": "#ffffff80",
|
||||
"icons.highlight": "#ffffff",
|
||||
"icons.interference": "#ff6666",
|
||||
"icons.marker_halo": "#000000cc",
|
||||
"icons.paint": [
|
||||
"#ff6666",
|
||||
"#0070d0",
|
||||
"#ffcc00"
|
||||
],
|
||||
"icons.plane": "#ffcc00",
|
||||
"icons.sheet": "#555555",
|
||||
"icons.sheet_2": "#555555",
|
||||
"icons.sheet_light": "#ffffff",
|
||||
"icons.sheet_line": "#0070d0",
|
||||
"icons.sky": "#555555",
|
||||
"icons.stripe": "#cccccc",
|
||||
"icons.warning": "#ffcc00",
|
||||
"interference": "#ff6666",
|
||||
"logo": [
|
||||
"#00bfff",
|
||||
"#0070d0",
|
||||
"#555555",
|
||||
"#ffffff"
|
||||
],
|
||||
"manipulator": "#00bfff",
|
||||
"mesh_accent": "#00bfff",
|
||||
"mesh_fill": "#0070d0",
|
||||
"mesh_ink": "#ffffff",
|
||||
"mesh_soft": "#002c45",
|
||||
"on_accent": "#06121b",
|
||||
"origin_plane": "#ffcc0028",
|
||||
"origin_plane_edge": "#ffcc0088",
|
||||
"origin_plane_hover": "#00bfff40",
|
||||
"origin_point": "#cccccc",
|
||||
"overlay": "#000000",
|
||||
"panel": "#000000",
|
||||
"panel_header": "#151515",
|
||||
"preview_add": "#0070d0",
|
||||
"preview_cut": "#ff666666",
|
||||
"preview_new": "#ffcc00",
|
||||
"preview_sketch": "#40e080",
|
||||
"profile_fill": "#0070d022",
|
||||
"render_accent": "#00bfff",
|
||||
"render_fill": "#0070d0",
|
||||
"render_ink": "#ffffff",
|
||||
"render_soft": "#002c45",
|
||||
"rubber_band": "#00bfff",
|
||||
"section_cap": "#ffcc00",
|
||||
"sel_edge": "#00bfff",
|
||||
"sel_edge_rim": "#0070d0",
|
||||
"sel_face": "#004c80",
|
||||
"sel_profile": "#004c80",
|
||||
"sel_profile_edge": "#0070d0",
|
||||
"sel_vertex": "#0070d0",
|
||||
"selected_bg": "#004c80",
|
||||
"selected_ink": "#ffffff",
|
||||
"sheet_fill": "#0070d0",
|
||||
"sheet_ink": "#ffffff",
|
||||
"sheet_soft": "#002c45",
|
||||
"sketch": "#0070d0",
|
||||
"sketch_accent": "#00bfff",
|
||||
"sketch_centerline": "#ffcc00",
|
||||
"sketch_construction": "#ffcc00",
|
||||
"sketch_fixed": "#ffffff",
|
||||
"sketch_ink": "#ffffff",
|
||||
"sketch_locked": "#40e080",
|
||||
"sketch_lost": "#ff6666",
|
||||
"sketch_point": "#ffffff",
|
||||
"sketch_point_free": "#0070d0",
|
||||
"sketch_projected": "#c080ff",
|
||||
"sketch_soft": "#002c45",
|
||||
"subheading_bg": "#060606",
|
||||
"surface_accent": "#00bfff",
|
||||
"surface_fill": "#0070d0",
|
||||
"surface_ink": "#ffffff",
|
||||
"surface_soft": "#002c45",
|
||||
"tab_on_fill_dark": "#000000",
|
||||
"text": "#ffffff",
|
||||
"text_dim": "#cccccc",
|
||||
"timeline": "#151515",
|
||||
"timeline_item": "#151515",
|
||||
"timeline_item_error": "#3d1818",
|
||||
"timeline_marker": "#cccccc",
|
||||
"toolbar": "#000000",
|
||||
"triad": [
|
||||
"#ff6666",
|
||||
"#40e080",
|
||||
"#0070d0"
|
||||
],
|
||||
"viewport_bottom": "#000000",
|
||||
"viewport_top": "#000000",
|
||||
"warning": "#ffcc00"
|
||||
},
|
||||
"format": "solidcraft-theme/1",
|
||||
"name": "High Contrast"
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
{
|
||||
"base": "dark",
|
||||
"colors": {
|
||||
"accent": "#88c0d0",
|
||||
"accent_soft": "#35495b",
|
||||
"agent_busy": "#88c0d0",
|
||||
"agent_ok": "#a3be8c",
|
||||
"app_bar": "#242933",
|
||||
"app_bar_hover": "#eceff418",
|
||||
"app_bar_hover_button": "#eceff422",
|
||||
"app_bar_hover_strong": "#eceff430",
|
||||
"app_bar_hover_subtle": "#eceff410",
|
||||
"app_bar_icon": [
|
||||
"#eceff4",
|
||||
"#a4b0c2",
|
||||
"#88c0d0"
|
||||
],
|
||||
"app_bar_separator": "#4c566a",
|
||||
"app_bar_tab_active": "#3b4252",
|
||||
"app_bar_text": "#eceff4",
|
||||
"axis_x": "#bf616a",
|
||||
"axis_y": "#a3be8c",
|
||||
"axis_z": "#5e81ac",
|
||||
"bend_down": "#ebcb8b",
|
||||
"bend_up": "#a3be8c",
|
||||
"body": "#657991",
|
||||
"body_edge": "#5e81ac",
|
||||
"body_selected": "#34607a",
|
||||
"border": "#4c566a",
|
||||
"box_crossing": "#a3be8c",
|
||||
"box_window": "#88c0d0",
|
||||
"chip": "#88c0d0",
|
||||
"construction_geom": "#ebcb8b",
|
||||
"construction_plane": "#ebcb8b22",
|
||||
"continuity": [
|
||||
"#a3be8c",
|
||||
"#5e81ac",
|
||||
"#ebcb8b",
|
||||
"#b48ead"
|
||||
],
|
||||
"control_border": "#4c566a",
|
||||
"control_fill": "#3b4252",
|
||||
"cube_border": "#4c566a",
|
||||
"cube_face": "#3b4252",
|
||||
"cube_hover": "#35495b",
|
||||
"cube_text": "#eceff4",
|
||||
"dialog_bg": "#2e3440",
|
||||
"dialog_body": "#2e3440",
|
||||
"dialog_border": "#4c566a",
|
||||
"dialog_footer": "#2e3440",
|
||||
"dialog_label": "#eceff4",
|
||||
"dialog_rule": "#4c566a",
|
||||
"dimension": "#eceff4",
|
||||
"dimension_driven": "#a4b0c2",
|
||||
"error": "#bf616a",
|
||||
"field": "#242933",
|
||||
"form_accent": "#88c0d0",
|
||||
"form_dim": "#a4b0c2",
|
||||
"form_error": "#bf616a",
|
||||
"form_error_fill": "#4b3d48",
|
||||
"form_fill": "#5e81ac",
|
||||
"form_ink": "#eceff4",
|
||||
"form_primary": "#88c0d0",
|
||||
"form_soft": "#35495b",
|
||||
"form_success": "#a3be8c",
|
||||
"form_success_fill": "#424b4d",
|
||||
"form_tab_ink": "#eceff4",
|
||||
"gizmo": [
|
||||
"#bf616a",
|
||||
"#a3be8c",
|
||||
"#5e81ac"
|
||||
],
|
||||
"glyph_bg": "#3b4252",
|
||||
"glyph_edge": "#bf616a",
|
||||
"glyph_text": "#bf616a",
|
||||
"grid": "#354051",
|
||||
"grid_major": "#444f60",
|
||||
"heading_bg": "#171a20",
|
||||
"hover": "#35495b",
|
||||
"hover_edge": "#5e81ac",
|
||||
"hover_edge_halo": "#242f40",
|
||||
"hover_profile": "#88c0d028",
|
||||
"hover_profile_edge": "#5e81ac",
|
||||
"icon": "#eceff4",
|
||||
"icon_fill": "#a4b0c2",
|
||||
"icons.access": [
|
||||
"#a3be8c",
|
||||
"#bf616a"
|
||||
],
|
||||
"icons.agent": "#b48ead",
|
||||
"icons.axes": [
|
||||
"#bf616a",
|
||||
"#a3be8c",
|
||||
"#5e81ac"
|
||||
],
|
||||
"icons.curvature": [
|
||||
"#5e81ac",
|
||||
"#a3be8c",
|
||||
"#bf616a"
|
||||
],
|
||||
"icons.draft": [
|
||||
"#a3be8c",
|
||||
"#ebcb8b"
|
||||
],
|
||||
"icons.floor": "#a4b0c2",
|
||||
"icons.fold": "#a4b0c2",
|
||||
"icons.folder": "#ebcb8b",
|
||||
"icons.glint": "#eceff480",
|
||||
"icons.highlight": "#eceff4",
|
||||
"icons.interference": "#bf616a",
|
||||
"icons.marker_halo": "#242933cc",
|
||||
"icons.paint": [
|
||||
"#bf616a",
|
||||
"#5e81ac",
|
||||
"#ebcb8b"
|
||||
],
|
||||
"icons.plane": "#ebcb8b",
|
||||
"icons.sheet": "#657991",
|
||||
"icons.sheet_2": "#657991",
|
||||
"icons.sheet_light": "#eceff4",
|
||||
"icons.sheet_line": "#5e81ac",
|
||||
"icons.sky": "#657991",
|
||||
"icons.stripe": "#a4b0c2",
|
||||
"icons.warning": "#ebcb8b",
|
||||
"interference": "#bf616a",
|
||||
"logo": [
|
||||
"#88c0d0",
|
||||
"#5e81ac",
|
||||
"#657991",
|
||||
"#eceff4"
|
||||
],
|
||||
"manipulator": "#88c0d0",
|
||||
"mesh_accent": "#88c0d0",
|
||||
"mesh_fill": "#5e81ac",
|
||||
"mesh_ink": "#eceff4",
|
||||
"mesh_soft": "#35495b",
|
||||
"on_accent": "#06121b",
|
||||
"origin_plane": "#ebcb8b28",
|
||||
"origin_plane_edge": "#ebcb8b88",
|
||||
"origin_plane_hover": "#88c0d040",
|
||||
"origin_point": "#a4b0c2",
|
||||
"overlay": "#2e3440",
|
||||
"panel": "#2e3440",
|
||||
"panel_header": "#3b4252",
|
||||
"preview_add": "#5e81ac",
|
||||
"preview_cut": "#bf616a66",
|
||||
"preview_new": "#ebcb8b",
|
||||
"preview_sketch": "#a3be8c",
|
||||
"profile_fill": "#5e81ac22",
|
||||
"render_accent": "#88c0d0",
|
||||
"render_fill": "#5e81ac",
|
||||
"render_ink": "#eceff4",
|
||||
"render_soft": "#35495b",
|
||||
"rubber_band": "#88c0d0",
|
||||
"section_cap": "#ebcb8b",
|
||||
"sel_edge": "#5e81ac",
|
||||
"sel_edge_rim": "#5e81ac",
|
||||
"sel_face": "#34607a",
|
||||
"sel_profile": "#34607a",
|
||||
"sel_profile_edge": "#5e81ac",
|
||||
"sel_vertex": "#5e81ac",
|
||||
"selected_bg": "#34607a",
|
||||
"selected_ink": "#eceff4",
|
||||
"sheet_fill": "#5e81ac",
|
||||
"sheet_ink": "#eceff4",
|
||||
"sheet_soft": "#35495b",
|
||||
"sketch": "#5e81ac",
|
||||
"sketch_accent": "#88c0d0",
|
||||
"sketch_centerline": "#ebcb8b",
|
||||
"sketch_construction": "#ebcb8b",
|
||||
"sketch_fixed": "#eceff4",
|
||||
"sketch_ink": "#eceff4",
|
||||
"sketch_locked": "#a3be8c",
|
||||
"sketch_lost": "#bf616a",
|
||||
"sketch_point": "#eceff4",
|
||||
"sketch_point_free": "#5e81ac",
|
||||
"sketch_projected": "#b48ead",
|
||||
"sketch_soft": "#35495b",
|
||||
"subheading_bg": "#222730",
|
||||
"surface_accent": "#88c0d0",
|
||||
"surface_fill": "#5e81ac",
|
||||
"surface_ink": "#eceff4",
|
||||
"surface_soft": "#35495b",
|
||||
"tab_on_fill_dark": "#242933",
|
||||
"text": "#eceff4",
|
||||
"text_dim": "#a4b0c2",
|
||||
"timeline": "#3b4252",
|
||||
"timeline_item": "#3b4252",
|
||||
"timeline_item_error": "#513f4a",
|
||||
"timeline_marker": "#a4b0c2",
|
||||
"toolbar": "#2e3440",
|
||||
"triad": [
|
||||
"#bf616a",
|
||||
"#a3be8c",
|
||||
"#5e81ac"
|
||||
],
|
||||
"viewport_bottom": "#242f40",
|
||||
"viewport_top": "#242f40",
|
||||
"warning": "#ebcb8b"
|
||||
},
|
||||
"format": "solidcraft-theme/1",
|
||||
"name": "Nord"
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
{
|
||||
"base": "dark",
|
||||
"colors": {
|
||||
"accent": "#bd93f9",
|
||||
"accent_soft": "#393149",
|
||||
"agent_busy": "#bd93f9",
|
||||
"agent_ok": "#50fa7b",
|
||||
"app_bar": "#21222c",
|
||||
"app_bar_hover": "#f8f8f218",
|
||||
"app_bar_hover_button": "#f8f8f222",
|
||||
"app_bar_hover_strong": "#f8f8f230",
|
||||
"app_bar_hover_subtle": "#f8f8f210",
|
||||
"app_bar_icon": [
|
||||
"#f8f8f2",
|
||||
"#a1a4c4",
|
||||
"#bd93f9"
|
||||
],
|
||||
"app_bar_separator": "#50546d",
|
||||
"app_bar_tab_active": "#343746",
|
||||
"app_bar_text": "#f8f8f2",
|
||||
"axis_x": "#ff5555",
|
||||
"axis_y": "#50fa7b",
|
||||
"axis_z": "#8456b8",
|
||||
"bend_down": "#f1fa8c",
|
||||
"bend_up": "#50fa7b",
|
||||
"body": "#656581",
|
||||
"body_edge": "#8456b8",
|
||||
"body_selected": "#563c79",
|
||||
"border": "#50546d",
|
||||
"box_crossing": "#50fa7b",
|
||||
"box_window": "#bd93f9",
|
||||
"chip": "#bd93f9",
|
||||
"construction_geom": "#f1fa8c",
|
||||
"construction_plane": "#f1fa8c22",
|
||||
"continuity": [
|
||||
"#50fa7b",
|
||||
"#8456b8",
|
||||
"#f1fa8c",
|
||||
"#bd93f9"
|
||||
],
|
||||
"control_border": "#50546d",
|
||||
"control_fill": "#343746",
|
||||
"cube_border": "#50546d",
|
||||
"cube_face": "#343746",
|
||||
"cube_hover": "#393149",
|
||||
"cube_text": "#f8f8f2",
|
||||
"dialog_bg": "#282a36",
|
||||
"dialog_body": "#282a36",
|
||||
"dialog_border": "#50546d",
|
||||
"dialog_footer": "#282a36",
|
||||
"dialog_label": "#f8f8f2",
|
||||
"dialog_rule": "#50546d",
|
||||
"dimension": "#f8f8f2",
|
||||
"dimension_driven": "#a1a4c4",
|
||||
"error": "#ff5555",
|
||||
"field": "#21222c",
|
||||
"form_accent": "#bd93f9",
|
||||
"form_dim": "#a1a4c4",
|
||||
"form_error": "#ff5555",
|
||||
"form_error_fill": "#53333c",
|
||||
"form_fill": "#8456b8",
|
||||
"form_ink": "#f8f8f2",
|
||||
"form_primary": "#bd93f9",
|
||||
"form_soft": "#393149",
|
||||
"form_success": "#50fa7b",
|
||||
"form_success_fill": "#2f4d42",
|
||||
"form_tab_ink": "#f8f8f2",
|
||||
"gizmo": [
|
||||
"#ff5555",
|
||||
"#50fa7b",
|
||||
"#8456b8"
|
||||
],
|
||||
"glyph_bg": "#343746",
|
||||
"glyph_edge": "#ff5555",
|
||||
"glyph_text": "#ff5555",
|
||||
"grid": "#2e2f3d",
|
||||
"grid_major": "#3e4050",
|
||||
"heading_bg": "#14151b",
|
||||
"hover": "#393149",
|
||||
"hover_edge": "#8456b8",
|
||||
"hover_edge_halo": "#1d1e29",
|
||||
"hover_profile": "#bd93f928",
|
||||
"hover_profile_edge": "#8456b8",
|
||||
"icon": "#f8f8f2",
|
||||
"icon_fill": "#a1a4c4",
|
||||
"icons.access": [
|
||||
"#50fa7b",
|
||||
"#ff5555"
|
||||
],
|
||||
"icons.agent": "#bd93f9",
|
||||
"icons.axes": [
|
||||
"#ff5555",
|
||||
"#50fa7b",
|
||||
"#8456b8"
|
||||
],
|
||||
"icons.curvature": [
|
||||
"#8456b8",
|
||||
"#50fa7b",
|
||||
"#ff5555"
|
||||
],
|
||||
"icons.draft": [
|
||||
"#50fa7b",
|
||||
"#f1fa8c"
|
||||
],
|
||||
"icons.floor": "#a1a4c4",
|
||||
"icons.fold": "#a1a4c4",
|
||||
"icons.folder": "#f1fa8c",
|
||||
"icons.glint": "#f8f8f280",
|
||||
"icons.highlight": "#f8f8f2",
|
||||
"icons.interference": "#ff5555",
|
||||
"icons.marker_halo": "#21222ccc",
|
||||
"icons.paint": [
|
||||
"#ff5555",
|
||||
"#8456b8",
|
||||
"#f1fa8c"
|
||||
],
|
||||
"icons.plane": "#f1fa8c",
|
||||
"icons.sheet": "#656581",
|
||||
"icons.sheet_2": "#656581",
|
||||
"icons.sheet_light": "#f8f8f2",
|
||||
"icons.sheet_line": "#8456b8",
|
||||
"icons.sky": "#656581",
|
||||
"icons.stripe": "#a1a4c4",
|
||||
"icons.warning": "#f1fa8c",
|
||||
"interference": "#ff5555",
|
||||
"logo": [
|
||||
"#bd93f9",
|
||||
"#8456b8",
|
||||
"#656581",
|
||||
"#f8f8f2"
|
||||
],
|
||||
"manipulator": "#bd93f9",
|
||||
"mesh_accent": "#bd93f9",
|
||||
"mesh_fill": "#8456b8",
|
||||
"mesh_ink": "#f8f8f2",
|
||||
"mesh_soft": "#393149",
|
||||
"on_accent": "#06121b",
|
||||
"origin_plane": "#f1fa8c28",
|
||||
"origin_plane_edge": "#f1fa8c88",
|
||||
"origin_plane_hover": "#bd93f940",
|
||||
"origin_point": "#a1a4c4",
|
||||
"overlay": "#282a36",
|
||||
"panel": "#282a36",
|
||||
"panel_header": "#343746",
|
||||
"preview_add": "#8456b8",
|
||||
"preview_cut": "#ff555566",
|
||||
"preview_new": "#f1fa8c",
|
||||
"preview_sketch": "#50fa7b",
|
||||
"profile_fill": "#8456b822",
|
||||
"render_accent": "#bd93f9",
|
||||
"render_fill": "#8456b8",
|
||||
"render_ink": "#f8f8f2",
|
||||
"render_soft": "#393149",
|
||||
"rubber_band": "#bd93f9",
|
||||
"section_cap": "#f1fa8c",
|
||||
"sel_edge": "#8456b8",
|
||||
"sel_edge_rim": "#8456b8",
|
||||
"sel_face": "#563c79",
|
||||
"sel_profile": "#563c79",
|
||||
"sel_profile_edge": "#8456b8",
|
||||
"sel_vertex": "#8456b8",
|
||||
"selected_bg": "#563c79",
|
||||
"selected_ink": "#f8f8f2",
|
||||
"sheet_fill": "#8456b8",
|
||||
"sheet_ink": "#f8f8f2",
|
||||
"sheet_soft": "#393149",
|
||||
"sketch": "#8456b8",
|
||||
"sketch_accent": "#bd93f9",
|
||||
"sketch_centerline": "#f1fa8c",
|
||||
"sketch_construction": "#f1fa8c",
|
||||
"sketch_fixed": "#f8f8f2",
|
||||
"sketch_ink": "#f8f8f2",
|
||||
"sketch_locked": "#50fa7b",
|
||||
"sketch_lost": "#ff5555",
|
||||
"sketch_point": "#f8f8f2",
|
||||
"sketch_point_free": "#8456b8",
|
||||
"sketch_projected": "#bd93f9",
|
||||
"sketch_soft": "#393149",
|
||||
"subheading_bg": "#1e2028",
|
||||
"surface_accent": "#bd93f9",
|
||||
"surface_fill": "#8456b8",
|
||||
"surface_ink": "#f8f8f2",
|
||||
"surface_soft": "#393149",
|
||||
"tab_on_fill_dark": "#21222c",
|
||||
"text": "#f8f8f2",
|
||||
"text_dim": "#a1a4c4",
|
||||
"timeline": "#343746",
|
||||
"timeline_item": "#343746",
|
||||
"timeline_item_error": "#5c343d",
|
||||
"timeline_marker": "#a1a4c4",
|
||||
"toolbar": "#282a36",
|
||||
"triad": [
|
||||
"#ff5555",
|
||||
"#50fa7b",
|
||||
"#8456b8"
|
||||
],
|
||||
"viewport_bottom": "#1d1e29",
|
||||
"viewport_top": "#1d1e29",
|
||||
"warning": "#f1fa8c"
|
||||
},
|
||||
"format": "solidcraft-theme/1",
|
||||
"name": "Nosferatu"
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
{
|
||||
"base": "dark",
|
||||
"colors": {
|
||||
"accent": "#61afef",
|
||||
"accent_soft": "#344b65",
|
||||
"agent_busy": "#6cb4f0",
|
||||
"agent_ok": "#4cc27a",
|
||||
"app_bar": "#21252b",
|
||||
"app_bar_hover": "#adb6bf1c",
|
||||
"app_bar_hover_button": "#ffffff1e",
|
||||
"app_bar_hover_strong": "#ffffff28",
|
||||
"app_bar_hover_subtle": "#ffffff12",
|
||||
"app_bar_icon": [
|
||||
"#abb2bf",
|
||||
"#5c6370",
|
||||
"#61afef"
|
||||
],
|
||||
"app_bar_separator": "#8891a0",
|
||||
"app_bar_tab_active": "#353b47",
|
||||
"app_bar_text": "#abb2bf",
|
||||
"axis_x": "#e06c75",
|
||||
"axis_y": "#98c379",
|
||||
"axis_z": "#61afef",
|
||||
"bend_down": "#d19a66",
|
||||
"bend_up": "#98c379",
|
||||
"body": "#abb2bf",
|
||||
"body_edge": "#181a1f",
|
||||
"body_selected": "#61afef",
|
||||
"border": "#3e4451",
|
||||
"box_crossing": "#98c379",
|
||||
"box_window": "#61afef",
|
||||
"chip": "#344b65",
|
||||
"construction_geom": "#d19a66",
|
||||
"construction_plane": "#d0996646",
|
||||
"continuity": [
|
||||
"#98c379",
|
||||
"#61afef",
|
||||
"#e5c07b",
|
||||
"#c678dd"
|
||||
],
|
||||
"control_border": "#4a5262",
|
||||
"control_fill": "#272b34",
|
||||
"cube_border": "#5c6370",
|
||||
"cube_face": "#abb2bf",
|
||||
"cube_hover": "#61afef",
|
||||
"cube_text": "#21252b",
|
||||
"dialog_bg": "#282c34",
|
||||
"dialog_body": "#3b4453",
|
||||
"dialog_border": "#4b5263",
|
||||
"dialog_footer": "#2f343e",
|
||||
"dialog_label": "#b4bbc8",
|
||||
"dialog_rule": "#353b47",
|
||||
"dimension": "#abb2bf",
|
||||
"dimension_driven": "#7f848e",
|
||||
"error": "#e06c75",
|
||||
"field": "#1e2227",
|
||||
"form_accent": "#7b63a6",
|
||||
"form_dim": "#949ba8",
|
||||
"form_error": "#f06460",
|
||||
"form_error_fill": "#6e3236",
|
||||
"form_fill": "#8a6fcc",
|
||||
"form_ink": "#dee2e8",
|
||||
"form_primary": "#3a88ee",
|
||||
"form_soft": "#372f4f",
|
||||
"form_success": "#8fdb9a",
|
||||
"form_success_fill": "#2c4a33",
|
||||
"form_tab_ink": "#b39bee",
|
||||
"gizmo": [
|
||||
"#e06c75",
|
||||
"#98c379",
|
||||
"#61afef"
|
||||
],
|
||||
"glyph_bg": "#282c34",
|
||||
"glyph_edge": "#e06c75",
|
||||
"glyph_text": "#e06c75",
|
||||
"grid": "#323842",
|
||||
"grid_major": "#3e4451",
|
||||
"heading_bg": "#14161a",
|
||||
"hover": "#3e4451",
|
||||
"hover_edge": "#181a1f",
|
||||
"hover_edge_halo": "#abb2bf",
|
||||
"hover_profile": "#60afef30",
|
||||
"hover_profile_edge": "#61afef",
|
||||
"icon": "#abb2bf",
|
||||
"icon_fill": "#5c6370",
|
||||
"icons.access": [
|
||||
"#98c379",
|
||||
"#e06c75"
|
||||
],
|
||||
"icons.agent": "#c678dd",
|
||||
"icons.axes": [
|
||||
"#e06c75",
|
||||
"#98c379",
|
||||
"#61afef"
|
||||
],
|
||||
"icons.curvature": [
|
||||
"#61afef",
|
||||
"#98c379",
|
||||
"#e06c75"
|
||||
],
|
||||
"icons.draft": [
|
||||
"#98c379",
|
||||
"#e5c07b"
|
||||
],
|
||||
"icons.floor": "#3e4451",
|
||||
"icons.fold": "#7f848e",
|
||||
"icons.folder": "#e5c07b",
|
||||
"icons.glint": "#ffffffa0",
|
||||
"icons.highlight": "#ffffff",
|
||||
"icons.interference": "#e06c75",
|
||||
"icons.marker_halo": "#181a1f96",
|
||||
"icons.paint": [
|
||||
"#e06c75",
|
||||
"#61afef",
|
||||
"#e5c07b"
|
||||
],
|
||||
"icons.plane": "#e5c07b",
|
||||
"icons.sheet": "#61afef",
|
||||
"icons.sheet_2": "#56b6c2",
|
||||
"icons.sheet_light": "#abb2bf",
|
||||
"icons.sheet_line": "#61afef",
|
||||
"icons.sky": "#abb2bf",
|
||||
"icons.stripe": "#21252b",
|
||||
"icons.warning": "#e5c07b",
|
||||
"interference": "#e06c75",
|
||||
"logo": [
|
||||
"#61afef",
|
||||
"#56b6c2",
|
||||
"#c678dd",
|
||||
"#abb2bf"
|
||||
],
|
||||
"manipulator": "#61afef",
|
||||
"mesh_accent": "#857100",
|
||||
"mesh_fill": "#c6a800",
|
||||
"mesh_ink": "#d4b726",
|
||||
"mesh_soft": "#41391a",
|
||||
"on_accent": "#ffffff",
|
||||
"origin_plane": "#d19b675e",
|
||||
"origin_plane_edge": "#d19a66c8",
|
||||
"origin_plane_hover": "#c579dd96",
|
||||
"origin_point": "#abb2bf",
|
||||
"overlay": "#21252bf0",
|
||||
"panel": "#21252b",
|
||||
"panel_header": "#282c34",
|
||||
"preview_add": "#61afef",
|
||||
"preview_cut": "#e16d746e",
|
||||
"preview_new": "#e5c07b",
|
||||
"preview_sketch": "#98c379",
|
||||
"profile_fill": "#60acec28",
|
||||
"render_accent": "#e074ae",
|
||||
"render_fill": "#c8588f",
|
||||
"render_ink": "#e074ae",
|
||||
"render_soft": "#402336",
|
||||
"rubber_band": "#61afef",
|
||||
"section_cap": "#d19a66",
|
||||
"sel_edge": "#61afef",
|
||||
"sel_edge_rim": "#a6d4fa",
|
||||
"sel_face": "#4d78a6",
|
||||
"sel_profile": "#2c4b6b",
|
||||
"sel_profile_edge": "#61afef",
|
||||
"sel_vertex": "#61afef",
|
||||
"selected_bg": "#2a466e",
|
||||
"selected_ink": "#9cc7f5",
|
||||
"sheet_fill": "#2a7fa8",
|
||||
"sheet_ink": "#4fb3d9",
|
||||
"sheet_soft": "#1f3a47",
|
||||
"sketch": "#61afef",
|
||||
"sketch_accent": "#56b6c2",
|
||||
"sketch_centerline": "#d19a66",
|
||||
"sketch_construction": "#d19a66",
|
||||
"sketch_fixed": "#abb2bf",
|
||||
"sketch_ink": "#46be78",
|
||||
"sketch_locked": "#98c379",
|
||||
"sketch_lost": "#e06c75",
|
||||
"sketch_point": "#abb2bf",
|
||||
"sketch_point_free": "#61afef",
|
||||
"sketch_projected": "#c678dd",
|
||||
"sketch_soft": "#1f3b2b",
|
||||
"subheading_bg": "#1e2127",
|
||||
"surface_accent": "#95682d",
|
||||
"surface_fill": "#b26a14",
|
||||
"surface_ink": "#e0914a",
|
||||
"surface_soft": "#45331e",
|
||||
"tab_on_fill_dark": "#000000",
|
||||
"text": "#abb2bf",
|
||||
"text_dim": "#7f848e",
|
||||
"timeline": "#1e2227",
|
||||
"timeline_item": "#323842",
|
||||
"timeline_item_error": "#5c343d",
|
||||
"timeline_marker": "#abb2bf",
|
||||
"toolbar": "#21252b",
|
||||
"triad": [
|
||||
"#e06c75",
|
||||
"#98c379",
|
||||
"#61afef"
|
||||
],
|
||||
"viewport_bottom": "#282c34",
|
||||
"viewport_top": "#282c34",
|
||||
"warning": "#e5c07b"
|
||||
},
|
||||
"format": "solidcraft-theme/1",
|
||||
"name": "One Dark"
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
{
|
||||
"base": "light",
|
||||
"colors": {
|
||||
"accent": "#555599",
|
||||
"accent_soft": "#ccccff",
|
||||
"agent_busy": "#8888bb",
|
||||
"agent_ok": "#558855",
|
||||
"app_bar": "#c5c5c5",
|
||||
"app_bar_hover": "#ffffff40",
|
||||
"app_bar_hover_button": "#ffffff66",
|
||||
"app_bar_hover_strong": "#ffffff80",
|
||||
"app_bar_hover_subtle": "#ffffff26",
|
||||
"app_bar_icon": [
|
||||
"#333333",
|
||||
"#777777",
|
||||
"#666699"
|
||||
],
|
||||
"app_bar_separator": "#aaaaaa",
|
||||
"app_bar_tab_active": "#e6e6e6",
|
||||
"app_bar_text": "#222222",
|
||||
"axis_x": "#ff0000",
|
||||
"axis_y": "#00ff00",
|
||||
"axis_z": "#0202f8",
|
||||
"bend_down": "#c85a14",
|
||||
"bend_up": "#1e8c32",
|
||||
"body": "#adadb6",
|
||||
"body_edge": "#292933",
|
||||
"body_selected": "#9999cc",
|
||||
"border": "#888888",
|
||||
"box_crossing": "#28a050",
|
||||
"box_window": "#555599",
|
||||
"chip": "#555599",
|
||||
"construction_geom": "#9c5600",
|
||||
"construction_plane": "#ccccdd33",
|
||||
"continuity": [
|
||||
"#009e73",
|
||||
"#0072b2",
|
||||
"#f0c800",
|
||||
"#cc008c"
|
||||
],
|
||||
"control_border": "#888888",
|
||||
"control_fill": "#dddddd",
|
||||
"cube_border": "#888888",
|
||||
"cube_face": "#eeeeee",
|
||||
"cube_hover": "#ccccff",
|
||||
"cube_text": "#222222",
|
||||
"dialog_bg": "#dddddd",
|
||||
"dialog_body": "#eeeeee",
|
||||
"dialog_border": "#888888",
|
||||
"dialog_footer": "#dddddd",
|
||||
"dialog_label": "#222222",
|
||||
"dialog_rule": "#aaaaaa",
|
||||
"dimension": "#222222",
|
||||
"dimension_driven": "#666666",
|
||||
"error": "#bb3333",
|
||||
"field": "#ffffff",
|
||||
"form_accent": "#666699",
|
||||
"form_dim": "#666666",
|
||||
"form_error": "#aa2222",
|
||||
"form_error_fill": "#f2dddd",
|
||||
"form_fill": "#8888bb",
|
||||
"form_ink": "#444477",
|
||||
"form_primary": "#555599",
|
||||
"form_soft": "#e6e6f2",
|
||||
"form_success": "#336633",
|
||||
"form_success_fill": "#e2ecdf",
|
||||
"form_tab_ink": "#555599",
|
||||
"gizmo": [
|
||||
"#e1463c",
|
||||
"#50be50",
|
||||
"#4682f0"
|
||||
],
|
||||
"glyph_bg": "#ffffff",
|
||||
"glyph_edge": "#be6e64",
|
||||
"glyph_text": "#ca473d",
|
||||
"grid": "#e2e2e2",
|
||||
"grid_major": "#c8c8c8",
|
||||
"heading_bg": "#cccccc",
|
||||
"hover": "#ccccff",
|
||||
"hover_edge": "#000000",
|
||||
"hover_edge_halo": "#ffffff",
|
||||
"hover_profile": "#9999cc33",
|
||||
"hover_profile_edge": "#666699",
|
||||
"icon": "#222222",
|
||||
"icon_fill": "#666699",
|
||||
"icons.access": [
|
||||
"#46b45a",
|
||||
"#dc463c"
|
||||
],
|
||||
"icons.agent": "#8888bb",
|
||||
"icons.axes": [
|
||||
"#d23c3c",
|
||||
"#3ca03c",
|
||||
"#3c64dc"
|
||||
],
|
||||
"icons.curvature": [
|
||||
"#3c6ee6",
|
||||
"#46be5a",
|
||||
"#e13c2d"
|
||||
],
|
||||
"icons.draft": [
|
||||
"#46b45a",
|
||||
"#ebc832"
|
||||
],
|
||||
"icons.floor": "#666666",
|
||||
"icons.fold": "#999999",
|
||||
"icons.folder": "#aaaaee",
|
||||
"icons.glint": "#ffffffa0",
|
||||
"icons.highlight": "#ffffff",
|
||||
"icons.interference": "#e1463c",
|
||||
"icons.marker_halo": "#00000096",
|
||||
"icons.paint": [
|
||||
"#dc463c",
|
||||
"#4696e6",
|
||||
"#f0c83c"
|
||||
],
|
||||
"icons.plane": "#f4d696",
|
||||
"icons.sheet": "#ccccdd",
|
||||
"icons.sheet_2": "#aaaabb",
|
||||
"icons.sheet_light": "#eeeeff",
|
||||
"icons.sheet_line": "#777799",
|
||||
"icons.sky": "#ccccdd",
|
||||
"icons.stripe": "#1e1e1e",
|
||||
"icons.warning": "#fac83c",
|
||||
"interference": "#eb2d28",
|
||||
"logo": [
|
||||
"#ccccff",
|
||||
"#9999cc",
|
||||
"#666699",
|
||||
"#ffffff"
|
||||
],
|
||||
"manipulator": "#555599",
|
||||
"mesh_accent": "#666699",
|
||||
"mesh_fill": "#8888bb",
|
||||
"mesh_ink": "#444477",
|
||||
"mesh_soft": "#e6e6f2",
|
||||
"on_accent": "#ffffff",
|
||||
"origin_plane": "#ccccdd44",
|
||||
"origin_plane_edge": "#888899aa",
|
||||
"origin_plane_hover": "#9999cc66",
|
||||
"origin_point": "#aaaaaa",
|
||||
"overlay": "#dddddd",
|
||||
"panel": "#dddddd",
|
||||
"panel_header": "#cccccc",
|
||||
"preview_add": "#9999cc",
|
||||
"preview_cut": "#e8532c6e",
|
||||
"preview_new": "#f0e169",
|
||||
"preview_sketch": "#20be60",
|
||||
"profile_fill": "#ccccff33",
|
||||
"render_accent": "#666699",
|
||||
"render_fill": "#8888bb",
|
||||
"render_ink": "#444477",
|
||||
"render_soft": "#e6e6f2",
|
||||
"rubber_band": "#555599",
|
||||
"section_cap": "#d68048",
|
||||
"sel_edge": "#555599",
|
||||
"sel_edge_rim": "#ccccff",
|
||||
"sel_face": "#9999cc",
|
||||
"sel_profile": "#444477",
|
||||
"sel_profile_edge": "#555599",
|
||||
"sel_vertex": "#555599",
|
||||
"selected_bg": "#ccccff",
|
||||
"selected_ink": "#333366",
|
||||
"sheet_fill": "#8888bb",
|
||||
"sheet_ink": "#444477",
|
||||
"sheet_soft": "#e6e6f2",
|
||||
"sketch": "#444488",
|
||||
"sketch_accent": "#666699",
|
||||
"sketch_centerline": "#c86e3c",
|
||||
"sketch_construction": "#e68228",
|
||||
"sketch_fixed": "#222222",
|
||||
"sketch_ink": "#444477",
|
||||
"sketch_locked": "#447744",
|
||||
"sketch_lost": "#c83c5a",
|
||||
"sketch_point": "#222222",
|
||||
"sketch_point_free": "#444488",
|
||||
"sketch_projected": "#963cbe",
|
||||
"sketch_soft": "#e6e6f2",
|
||||
"subheading_bg": "#d7d7d7",
|
||||
"surface_accent": "#666699",
|
||||
"surface_fill": "#8888bb",
|
||||
"surface_ink": "#444477",
|
||||
"surface_soft": "#e6e6f2",
|
||||
"tab_on_fill_dark": "#222222",
|
||||
"text": "#222222",
|
||||
"text_dim": "#666666",
|
||||
"timeline": "#cccccc",
|
||||
"timeline_item": "#eeeeee",
|
||||
"timeline_item_error": "#f2dddd",
|
||||
"timeline_marker": "#666666",
|
||||
"toolbar": "#dddddd",
|
||||
"triad": [
|
||||
"#dc3c3c",
|
||||
"#3caa3c",
|
||||
"#3c64e6"
|
||||
],
|
||||
"viewport_bottom": "#ffffff",
|
||||
"viewport_top": "#ffffff",
|
||||
"warning": "#997722"
|
||||
},
|
||||
"format": "solidcraft-theme/1",
|
||||
"name": "Platinum 9"
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
{
|
||||
"base": "light",
|
||||
"colors": {
|
||||
"accent": "#268bd2",
|
||||
"accent_soft": "#e1e8d9",
|
||||
"agent_busy": "#268bd2",
|
||||
"agent_ok": "#859900",
|
||||
"app_bar": "#fdf6e3",
|
||||
"app_bar_hover": "#586e7518",
|
||||
"app_bar_hover_button": "#586e7522",
|
||||
"app_bar_hover_strong": "#586e7530",
|
||||
"app_bar_hover_subtle": "#586e7510",
|
||||
"app_bar_icon": [
|
||||
"#586e75",
|
||||
"#657b83",
|
||||
"#268bd2"
|
||||
],
|
||||
"app_bar_separator": "#b6b3a1",
|
||||
"app_bar_tab_active": "#e3ddc9",
|
||||
"app_bar_text": "#586e75",
|
||||
"axis_x": "#dc322f",
|
||||
"axis_y": "#859900",
|
||||
"axis_z": "#346e88",
|
||||
"bend_down": "#b58900",
|
||||
"bend_up": "#859900",
|
||||
"body": "#b5b6a0",
|
||||
"body_edge": "#346e88",
|
||||
"body_selected": "#bdd9da",
|
||||
"border": "#b6b3a1",
|
||||
"box_crossing": "#859900",
|
||||
"box_window": "#268bd2",
|
||||
"chip": "#268bd2",
|
||||
"construction_geom": "#b58900",
|
||||
"construction_plane": "#b5890022",
|
||||
"continuity": [
|
||||
"#859900",
|
||||
"#346e88",
|
||||
"#b58900",
|
||||
"#6c71c4"
|
||||
],
|
||||
"control_border": "#b6b3a1",
|
||||
"control_fill": "#e3ddc9",
|
||||
"cube_border": "#b6b3a1",
|
||||
"cube_face": "#e3ddc9",
|
||||
"cube_hover": "#e1e8d9",
|
||||
"cube_text": "#586e75",
|
||||
"dialog_bg": "#eee8d5",
|
||||
"dialog_body": "#eee8d5",
|
||||
"dialog_border": "#b6b3a1",
|
||||
"dialog_footer": "#eee8d5",
|
||||
"dialog_label": "#586e75",
|
||||
"dialog_rule": "#b6b3a1",
|
||||
"dimension": "#586e75",
|
||||
"dimension_driven": "#657b83",
|
||||
"error": "#dc322f",
|
||||
"field": "#fdf6e3",
|
||||
"form_accent": "#268bd2",
|
||||
"form_dim": "#657b83",
|
||||
"form_error": "#dc322f",
|
||||
"form_error_fill": "#eac4b4",
|
||||
"form_fill": "#346e88",
|
||||
"form_ink": "#586e75",
|
||||
"form_primary": "#268bd2",
|
||||
"form_soft": "#e1e8d9",
|
||||
"form_success": "#859900",
|
||||
"form_success_fill": "#dcdbb1",
|
||||
"form_tab_ink": "#346e88",
|
||||
"gizmo": [
|
||||
"#dc322f",
|
||||
"#859900",
|
||||
"#346e88"
|
||||
],
|
||||
"glyph_bg": "#e3ddc9",
|
||||
"glyph_edge": "#dc322f",
|
||||
"glyph_text": "#dc322f",
|
||||
"grid": "#dfddd0",
|
||||
"grid_major": "#c8cbc1",
|
||||
"heading_bg": "#e3ddc9",
|
||||
"hover": "#e1e8d9",
|
||||
"hover_edge": "#346e88",
|
||||
"hover_edge_halo": "#fdf6e3",
|
||||
"hover_profile": "#268bd228",
|
||||
"hover_profile_edge": "#346e88",
|
||||
"icon": "#586e75",
|
||||
"icon_fill": "#657b83",
|
||||
"icons.access": [
|
||||
"#859900",
|
||||
"#dc322f"
|
||||
],
|
||||
"icons.agent": "#6c71c4",
|
||||
"icons.axes": [
|
||||
"#dc322f",
|
||||
"#859900",
|
||||
"#346e88"
|
||||
],
|
||||
"icons.curvature": [
|
||||
"#346e88",
|
||||
"#859900",
|
||||
"#dc322f"
|
||||
],
|
||||
"icons.draft": [
|
||||
"#859900",
|
||||
"#b58900"
|
||||
],
|
||||
"icons.floor": "#657b83",
|
||||
"icons.fold": "#657b83",
|
||||
"icons.folder": "#b58900",
|
||||
"icons.glint": "#586e7580",
|
||||
"icons.highlight": "#586e75",
|
||||
"icons.interference": "#dc322f",
|
||||
"icons.marker_halo": "#fdf6e3cc",
|
||||
"icons.paint": [
|
||||
"#dc322f",
|
||||
"#346e88",
|
||||
"#b58900"
|
||||
],
|
||||
"icons.plane": "#b58900",
|
||||
"icons.sheet": "#b5b6a0",
|
||||
"icons.sheet_2": "#b5b6a0",
|
||||
"icons.sheet_light": "#586e75",
|
||||
"icons.sheet_line": "#346e88",
|
||||
"icons.sky": "#b5b6a0",
|
||||
"icons.stripe": "#657b83",
|
||||
"icons.warning": "#b58900",
|
||||
"interference": "#dc322f",
|
||||
"logo": [
|
||||
"#268bd2",
|
||||
"#346e88",
|
||||
"#b5b6a0",
|
||||
"#586e75"
|
||||
],
|
||||
"manipulator": "#268bd2",
|
||||
"mesh_accent": "#268bd2",
|
||||
"mesh_fill": "#346e88",
|
||||
"mesh_ink": "#586e75",
|
||||
"mesh_soft": "#e1e8d9",
|
||||
"on_accent": "#ffffff",
|
||||
"origin_plane": "#b5890028",
|
||||
"origin_plane_edge": "#b5890088",
|
||||
"origin_plane_hover": "#268bd240",
|
||||
"origin_point": "#657b83",
|
||||
"overlay": "#eee8d5",
|
||||
"panel": "#eee8d5",
|
||||
"panel_header": "#e3ddc9",
|
||||
"preview_add": "#346e88",
|
||||
"preview_cut": "#dc322f66",
|
||||
"preview_new": "#b58900",
|
||||
"preview_sketch": "#859900",
|
||||
"profile_fill": "#346e8822",
|
||||
"render_accent": "#268bd2",
|
||||
"render_fill": "#346e88",
|
||||
"render_ink": "#586e75",
|
||||
"render_soft": "#e1e8d9",
|
||||
"rubber_band": "#268bd2",
|
||||
"section_cap": "#b58900",
|
||||
"sel_edge": "#346e88",
|
||||
"sel_edge_rim": "#346e88",
|
||||
"sel_face": "#bdd9da",
|
||||
"sel_profile": "#bdd9da",
|
||||
"sel_profile_edge": "#346e88",
|
||||
"sel_vertex": "#346e88",
|
||||
"selected_bg": "#bdd9da",
|
||||
"selected_ink": "#234a55",
|
||||
"sheet_fill": "#346e88",
|
||||
"sheet_ink": "#586e75",
|
||||
"sheet_soft": "#e1e8d9",
|
||||
"sketch": "#346e88",
|
||||
"sketch_accent": "#268bd2",
|
||||
"sketch_centerline": "#b58900",
|
||||
"sketch_construction": "#b58900",
|
||||
"sketch_fixed": "#586e75",
|
||||
"sketch_ink": "#586e75",
|
||||
"sketch_locked": "#859900",
|
||||
"sketch_lost": "#dc322f",
|
||||
"sketch_point": "#586e75",
|
||||
"sketch_point_free": "#346e88",
|
||||
"sketch_projected": "#6c71c4",
|
||||
"sketch_soft": "#e1e8d9",
|
||||
"subheading_bg": "#eae4d1",
|
||||
"surface_accent": "#268bd2",
|
||||
"surface_fill": "#346e88",
|
||||
"surface_ink": "#586e75",
|
||||
"surface_soft": "#e1e8d9",
|
||||
"tab_on_fill_dark": "#fdf6e3",
|
||||
"text": "#586e75",
|
||||
"text_dim": "#657b83",
|
||||
"timeline": "#e3ddc9",
|
||||
"timeline_item": "#e3ddc9",
|
||||
"timeline_item_error": "#eabcad",
|
||||
"timeline_marker": "#657b83",
|
||||
"toolbar": "#eee8d5",
|
||||
"triad": [
|
||||
"#dc322f",
|
||||
"#859900",
|
||||
"#346e88"
|
||||
],
|
||||
"viewport_bottom": "#fdf6e3",
|
||||
"viewport_top": "#fdf6e3",
|
||||
"warning": "#b58900"
|
||||
},
|
||||
"format": "solidcraft-theme/1",
|
||||
"name": "Solarized Light"
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
{
|
||||
"base": "light",
|
||||
"colors": {
|
||||
"accent": "#000080",
|
||||
"accent_soft": "#c0c0c0",
|
||||
"agent_busy": "#ffffff",
|
||||
"agent_ok": "#008000",
|
||||
"app_bar": "#000080",
|
||||
"app_bar_hover": "#ffffff33",
|
||||
"app_bar_hover_button": "#ffffff33",
|
||||
"app_bar_hover_strong": "#ffffff4d",
|
||||
"app_bar_hover_subtle": "#ffffff1a",
|
||||
"app_bar_icon": [
|
||||
"#ffffff",
|
||||
"#c0c0c0",
|
||||
"#00ffff"
|
||||
],
|
||||
"app_bar_separator": "#808080",
|
||||
"app_bar_tab_active": "#000080",
|
||||
"app_bar_text": "#ffffff",
|
||||
"axis_x": "#ff0000",
|
||||
"axis_y": "#00ff00",
|
||||
"axis_z": "#0000ff",
|
||||
"bend_down": "#808000",
|
||||
"bend_up": "#008000",
|
||||
"body": "#c0c0c0",
|
||||
"body_edge": "#000000",
|
||||
"body_selected": "#8080ff",
|
||||
"border": "#808080",
|
||||
"box_crossing": "#008000",
|
||||
"box_window": "#000080",
|
||||
"chip": "#000080",
|
||||
"construction_geom": "#808000",
|
||||
"construction_plane": "#ffff0046",
|
||||
"continuity": [
|
||||
"#008000",
|
||||
"#0000ff",
|
||||
"#ffff00",
|
||||
"#800080"
|
||||
],
|
||||
"control_border": "#808080",
|
||||
"control_fill": "#c0c0c0",
|
||||
"cube_border": "#808080",
|
||||
"cube_face": "#c0c0c0",
|
||||
"cube_hover": "#8080ff",
|
||||
"cube_text": "#000000",
|
||||
"dialog_bg": "#c0c0c0",
|
||||
"dialog_body": "#c0c0c0",
|
||||
"dialog_border": "#808080",
|
||||
"dialog_footer": "#c0c0c0",
|
||||
"dialog_label": "#000000",
|
||||
"dialog_rule": "#808080",
|
||||
"dimension": "#000000",
|
||||
"dimension_driven": "#808080",
|
||||
"error": "#ff0000",
|
||||
"field": "#ffffff",
|
||||
"form_accent": "#800080",
|
||||
"form_dim": "#808080",
|
||||
"form_error": "#800000",
|
||||
"form_error_fill": "#ffffc0",
|
||||
"form_fill": "#800080",
|
||||
"form_ink": "#000000",
|
||||
"form_primary": "#000080",
|
||||
"form_soft": "#c0c0c0",
|
||||
"form_success": "#008000",
|
||||
"form_success_fill": "#c0dcc0",
|
||||
"form_tab_ink": "#800080",
|
||||
"gizmo": [
|
||||
"#ff0000",
|
||||
"#00ff00",
|
||||
"#0000ff"
|
||||
],
|
||||
"glyph_bg": "#ffffff",
|
||||
"glyph_edge": "#800000",
|
||||
"glyph_text": "#800000",
|
||||
"grid": "#40a0a0",
|
||||
"grid_major": "#808080",
|
||||
"heading_bg": "#a0a0a0",
|
||||
"hover": "#c0c0c0",
|
||||
"hover_edge": "#000000",
|
||||
"hover_edge_halo": "#ffffff",
|
||||
"hover_profile": "#00008030",
|
||||
"hover_profile_edge": "#0000ff",
|
||||
"icon": "#000000",
|
||||
"icon_fill": "#c0c0c0",
|
||||
"icons.access": [
|
||||
"#808080",
|
||||
"#ff0000"
|
||||
],
|
||||
"icons.agent": "#800080",
|
||||
"icons.axes": [
|
||||
"#ff0000",
|
||||
"#008000",
|
||||
"#0000ff"
|
||||
],
|
||||
"icons.curvature": [
|
||||
"#008080",
|
||||
"#808080",
|
||||
"#ff0000"
|
||||
],
|
||||
"icons.draft": [
|
||||
"#808080",
|
||||
"#ffff00"
|
||||
],
|
||||
"icons.floor": "#000000",
|
||||
"icons.fold": "#808080",
|
||||
"icons.folder": "#ffff00",
|
||||
"icons.glint": "#ffffffa0",
|
||||
"icons.highlight": "#ffffff",
|
||||
"icons.interference": "#ff0000",
|
||||
"icons.marker_halo": "#00000096",
|
||||
"icons.paint": [
|
||||
"#ff0000",
|
||||
"#0000ff",
|
||||
"#ffff00"
|
||||
],
|
||||
"icons.plane": "#ffff00",
|
||||
"icons.sheet": "#00ffff",
|
||||
"icons.sheet_2": "#008080",
|
||||
"icons.sheet_light": "#ffffff",
|
||||
"icons.sheet_line": "#000080",
|
||||
"icons.sky": "#008080",
|
||||
"icons.stripe": "#000000",
|
||||
"icons.warning": "#ffff00",
|
||||
"interference": "#ff0000",
|
||||
"logo": [
|
||||
"#ffffff",
|
||||
"#c0c0c0",
|
||||
"#808080",
|
||||
"#ffffff"
|
||||
],
|
||||
"manipulator": "#000080",
|
||||
"mesh_accent": "#000080",
|
||||
"mesh_fill": "#808000",
|
||||
"mesh_ink": "#808000",
|
||||
"mesh_soft": "#ffffc0",
|
||||
"on_accent": "#ffffff",
|
||||
"origin_plane": "#ffff005e",
|
||||
"origin_plane_edge": "#808000c8",
|
||||
"origin_plane_hover": "#00008080",
|
||||
"origin_point": "#c0c0c0",
|
||||
"overlay": "#c0c0c0ff",
|
||||
"panel": "#c0c0c0",
|
||||
"panel_header": "#c0c0c0",
|
||||
"preview_add": "#000080",
|
||||
"preview_cut": "#ff00006e",
|
||||
"preview_new": "#ffff00",
|
||||
"preview_sketch": "#008000",
|
||||
"profile_fill": "#ffff0033",
|
||||
"render_accent": "#000080",
|
||||
"render_fill": "#800080",
|
||||
"render_ink": "#800080",
|
||||
"render_soft": "#c0c0c0",
|
||||
"rubber_band": "#000080",
|
||||
"section_cap": "#808000",
|
||||
"sel_edge": "#0000ff",
|
||||
"sel_edge_rim": "#8080ff",
|
||||
"sel_face": "#8080ff",
|
||||
"sel_profile": "#000080",
|
||||
"sel_profile_edge": "#000080",
|
||||
"sel_vertex": "#000080",
|
||||
"selected_bg": "#000080",
|
||||
"selected_ink": "#ffffff",
|
||||
"sheet_fill": "#008080",
|
||||
"sheet_ink": "#000080",
|
||||
"sheet_soft": "#c0c0c0",
|
||||
"sketch": "#000080",
|
||||
"sketch_accent": "#008000",
|
||||
"sketch_centerline": "#808000",
|
||||
"sketch_construction": "#808000",
|
||||
"sketch_fixed": "#000000",
|
||||
"sketch_ink": "#008000",
|
||||
"sketch_locked": "#008000",
|
||||
"sketch_lost": "#800080",
|
||||
"sketch_point": "#000000",
|
||||
"sketch_point_free": "#000080",
|
||||
"sketch_projected": "#800080",
|
||||
"sketch_soft": "#c0dcc0",
|
||||
"subheading_bg": "#b0b0b0",
|
||||
"surface_accent": "#000080",
|
||||
"surface_fill": "#808000",
|
||||
"surface_ink": "#808000",
|
||||
"surface_soft": "#ffffc0",
|
||||
"tab_on_fill_dark": "#000000",
|
||||
"text": "#000000",
|
||||
"text_dim": "#808080",
|
||||
"timeline": "#c0c0c0",
|
||||
"timeline_item": "#c0c0c0",
|
||||
"timeline_item_error": "#ffffc0",
|
||||
"timeline_marker": "#000000",
|
||||
"toolbar": "#c0c0c0",
|
||||
"triad": [
|
||||
"#ff0000",
|
||||
"#00ff00",
|
||||
"#0000ff"
|
||||
],
|
||||
"viewport_bottom": "#008080",
|
||||
"viewport_top": "#008080",
|
||||
"warning": "#ffff00"
|
||||
},
|
||||
"format": "solidcraft-theme/1",
|
||||
"name": "SolidCraft 95"
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
{
|
||||
"base": "dark",
|
||||
"colors": {
|
||||
"accent": "#7aa2f7",
|
||||
"accent_soft": "#253452",
|
||||
"agent_busy": "#7aa2f7",
|
||||
"agent_ok": "#9ece6a",
|
||||
"app_bar": "#16161e",
|
||||
"app_bar_hover": "#c0caf518",
|
||||
"app_bar_hover_button": "#c0caf522",
|
||||
"app_bar_hover_strong": "#c0caf530",
|
||||
"app_bar_hover_subtle": "#c0caf510",
|
||||
"app_bar_icon": [
|
||||
"#c0caf5",
|
||||
"#9aa5ce",
|
||||
"#7aa2f7"
|
||||
],
|
||||
"app_bar_separator": "#414868",
|
||||
"app_bar_tab_active": "#24283b",
|
||||
"app_bar_text": "#c0caf5",
|
||||
"axis_x": "#f7768e",
|
||||
"axis_y": "#9ece6a",
|
||||
"axis_z": "#4775ce",
|
||||
"bend_down": "#e0af68",
|
||||
"bend_up": "#9ece6a",
|
||||
"body": "#4f6089",
|
||||
"body_edge": "#4775ce",
|
||||
"body_selected": "#304e7e",
|
||||
"border": "#414868",
|
||||
"box_crossing": "#9ece6a",
|
||||
"box_window": "#7aa2f7",
|
||||
"chip": "#7aa2f7",
|
||||
"construction_geom": "#e0af68",
|
||||
"construction_plane": "#e0af6822",
|
||||
"continuity": [
|
||||
"#9ece6a",
|
||||
"#4775ce",
|
||||
"#e0af68",
|
||||
"#bb9af7"
|
||||
],
|
||||
"control_border": "#414868",
|
||||
"control_fill": "#24283b",
|
||||
"cube_border": "#414868",
|
||||
"cube_face": "#24283b",
|
||||
"cube_hover": "#253452",
|
||||
"cube_text": "#c0caf5",
|
||||
"dialog_bg": "#1a1b26",
|
||||
"dialog_body": "#1a1b26",
|
||||
"dialog_border": "#414868",
|
||||
"dialog_footer": "#1a1b26",
|
||||
"dialog_label": "#c0caf5",
|
||||
"dialog_rule": "#414868",
|
||||
"dimension": "#c0caf5",
|
||||
"dimension_driven": "#9aa5ce",
|
||||
"error": "#f7768e",
|
||||
"field": "#16161e",
|
||||
"form_accent": "#7aa2f7",
|
||||
"form_dim": "#9aa5ce",
|
||||
"form_error": "#f7768e",
|
||||
"form_error_fill": "#462d3b",
|
||||
"form_fill": "#4775ce",
|
||||
"form_ink": "#c0caf5",
|
||||
"form_primary": "#7aa2f7",
|
||||
"form_soft": "#253452",
|
||||
"form_success": "#9ece6a",
|
||||
"form_success_fill": "#303932",
|
||||
"form_tab_ink": "#c0caf5",
|
||||
"gizmo": [
|
||||
"#f7768e",
|
||||
"#9ece6a",
|
||||
"#4775ce"
|
||||
],
|
||||
"glyph_bg": "#24283b",
|
||||
"glyph_edge": "#f7768e",
|
||||
"glyph_text": "#f7768e",
|
||||
"grid": "#262938",
|
||||
"grid_major": "#363a4d",
|
||||
"heading_bg": "#0d0e13",
|
||||
"hover": "#253452",
|
||||
"hover_edge": "#4775ce",
|
||||
"hover_edge_halo": "#151722",
|
||||
"hover_profile": "#7aa2f728",
|
||||
"hover_profile_edge": "#4775ce",
|
||||
"icon": "#c0caf5",
|
||||
"icon_fill": "#9aa5ce",
|
||||
"icons.access": [
|
||||
"#9ece6a",
|
||||
"#f7768e"
|
||||
],
|
||||
"icons.agent": "#bb9af7",
|
||||
"icons.axes": [
|
||||
"#f7768e",
|
||||
"#9ece6a",
|
||||
"#4775ce"
|
||||
],
|
||||
"icons.curvature": [
|
||||
"#4775ce",
|
||||
"#9ece6a",
|
||||
"#f7768e"
|
||||
],
|
||||
"icons.draft": [
|
||||
"#9ece6a",
|
||||
"#e0af68"
|
||||
],
|
||||
"icons.floor": "#9aa5ce",
|
||||
"icons.fold": "#9aa5ce",
|
||||
"icons.folder": "#e0af68",
|
||||
"icons.glint": "#c0caf580",
|
||||
"icons.highlight": "#c0caf5",
|
||||
"icons.interference": "#f7768e",
|
||||
"icons.marker_halo": "#16161ecc",
|
||||
"icons.paint": [
|
||||
"#f7768e",
|
||||
"#4775ce",
|
||||
"#e0af68"
|
||||
],
|
||||
"icons.plane": "#e0af68",
|
||||
"icons.sheet": "#4f6089",
|
||||
"icons.sheet_2": "#4f6089",
|
||||
"icons.sheet_light": "#c0caf5",
|
||||
"icons.sheet_line": "#4775ce",
|
||||
"icons.sky": "#4f6089",
|
||||
"icons.stripe": "#9aa5ce",
|
||||
"icons.warning": "#e0af68",
|
||||
"interference": "#f7768e",
|
||||
"logo": [
|
||||
"#7aa2f7",
|
||||
"#4775ce",
|
||||
"#4f6089",
|
||||
"#c0caf5"
|
||||
],
|
||||
"manipulator": "#7aa2f7",
|
||||
"mesh_accent": "#7aa2f7",
|
||||
"mesh_fill": "#4775ce",
|
||||
"mesh_ink": "#c0caf5",
|
||||
"mesh_soft": "#253452",
|
||||
"on_accent": "#06121b",
|
||||
"origin_plane": "#e0af6828",
|
||||
"origin_plane_edge": "#e0af6888",
|
||||
"origin_plane_hover": "#7aa2f740",
|
||||
"origin_point": "#9aa5ce",
|
||||
"overlay": "#1a1b26",
|
||||
"panel": "#1a1b26",
|
||||
"panel_header": "#24283b",
|
||||
"preview_add": "#4775ce",
|
||||
"preview_cut": "#f7768e66",
|
||||
"preview_new": "#e0af68",
|
||||
"preview_sketch": "#9ece6a",
|
||||
"profile_fill": "#4775ce22",
|
||||
"render_accent": "#7aa2f7",
|
||||
"render_fill": "#4775ce",
|
||||
"render_ink": "#c0caf5",
|
||||
"render_soft": "#253452",
|
||||
"rubber_band": "#7aa2f7",
|
||||
"section_cap": "#e0af68",
|
||||
"sel_edge": "#4775ce",
|
||||
"sel_edge_rim": "#4775ce",
|
||||
"sel_face": "#304e7e",
|
||||
"sel_profile": "#304e7e",
|
||||
"sel_profile_edge": "#4775ce",
|
||||
"sel_vertex": "#4775ce",
|
||||
"selected_bg": "#304e7e",
|
||||
"selected_ink": "#c0caf5",
|
||||
"sheet_fill": "#4775ce",
|
||||
"sheet_ink": "#c0caf5",
|
||||
"sheet_soft": "#253452",
|
||||
"sketch": "#4775ce",
|
||||
"sketch_accent": "#7aa2f7",
|
||||
"sketch_centerline": "#e0af68",
|
||||
"sketch_construction": "#e0af68",
|
||||
"sketch_fixed": "#c0caf5",
|
||||
"sketch_ink": "#c0caf5",
|
||||
"sketch_locked": "#9ece6a",
|
||||
"sketch_lost": "#f7768e",
|
||||
"sketch_point": "#c0caf5",
|
||||
"sketch_point_free": "#4775ce",
|
||||
"sketch_projected": "#bb9af7",
|
||||
"sketch_soft": "#253452",
|
||||
"subheading_bg": "#14141c",
|
||||
"surface_accent": "#7aa2f7",
|
||||
"surface_fill": "#4775ce",
|
||||
"surface_ink": "#c0caf5",
|
||||
"surface_soft": "#253452",
|
||||
"tab_on_fill_dark": "#16161e",
|
||||
"text": "#c0caf5",
|
||||
"text_dim": "#9aa5ce",
|
||||
"timeline": "#24283b",
|
||||
"timeline_item": "#24283b",
|
||||
"timeline_item_error": "#4f313f",
|
||||
"timeline_marker": "#9aa5ce",
|
||||
"toolbar": "#1a1b26",
|
||||
"triad": [
|
||||
"#f7768e",
|
||||
"#9ece6a",
|
||||
"#4775ce"
|
||||
],
|
||||
"viewport_bottom": "#151722",
|
||||
"viewport_top": "#151722",
|
||||
"warning": "#e0af68"
|
||||
},
|
||||
"format": "solidcraft-theme/1",
|
||||
"name": "Tokyo Night"
|
||||
}
|
||||
@@ -7,8 +7,13 @@ pub fn state_in_face_anchor_frame<'a>(doc: &Document, state: &'a ModelState, anc
|
||||
let raw = state.body(&anchor.target.body).ok_or_else(|| DocError::Unknown(format!("anchored body `{}`", anchor.target.body)))?;
|
||||
let relative = anchor.source_to_author(doc, author, raw)?;
|
||||
let mut output = crate::state_in_frame_at(doc, state, author, frame).into_owned();
|
||||
let shape = if crate::is_identity(&relative) { raw.body.clone() } else { solidcraft_kernel::transform_matrix(&raw.body, relative)? };
|
||||
let mut body = ModelBody::new(raw.name.clone(), shape, raw.feature);
|
||||
// In place, the body is the model's own, display mesh included: a sketch projecting the
|
||||
// face's edges meshes it once for the features after it too, rather than a copy.
|
||||
let mut body = if crate::is_identity(&relative) {
|
||||
raw.clone()
|
||||
} else {
|
||||
ModelBody::new(raw.name.clone(), solidcraft_kernel::transform_matrix(&raw.body, relative)?, raw.feature)
|
||||
};
|
||||
body.component = raw.component;
|
||||
body.transferred = raw.transferred;
|
||||
body.source_name = raw.source_name.clone();
|
||||
|
||||
@@ -244,9 +244,9 @@ impl Document {
|
||||
m
|
||||
}
|
||||
|
||||
/// World transform of one placement of `component`: the occurrence `path` from the root
|
||||
/// (empty for the root itself). `None` when the path no longer leads to that component.
|
||||
pub fn path_transform(&self, component: u64, path: &[u64]) -> Option<Mat> {
|
||||
/// One placement: the component the occurrence `path` from the root leads to (the root for
|
||||
/// an empty path) and its world transform. `None` when the path no longer exists.
|
||||
pub fn path_placement(&self, path: &[u64]) -> Option<(u64, Mat)> {
|
||||
if path.len() > MAX_PLACEMENT_DEPTH + 1 {
|
||||
return None;
|
||||
}
|
||||
@@ -256,7 +256,7 @@ impl Document {
|
||||
m = mat_mul(&m, &o.transform);
|
||||
c = o.component;
|
||||
}
|
||||
(c == component).then_some(m)
|
||||
Some((c, m))
|
||||
}
|
||||
|
||||
/// Every placement of each component: (component, occurrence path ids, world transform).
|
||||
|
||||
@@ -180,6 +180,9 @@ fn shell_planar_format13_keeps_v12_documents_and_refuses_future() {
|
||||
assert_eq!(current["format"], crate::format::format_string(crate::format::FORMAT));
|
||||
current["format"] = json!("solidcraft/12");
|
||||
crate::format::pieces_in_shell_order(&mut legacy["features"]);
|
||||
for f in legacy["features"].as_array_mut().into_iter().flatten() {
|
||||
f["kernel_piece_order"] = json!(true);
|
||||
}
|
||||
assert_eq!(legacy, current, "the migration preserves every prior field");
|
||||
legacy["format"] = json!(crate::format::format_string(crate::format::FORMAT + 1));
|
||||
assert!(crate::format::read(&legacy.to_string()).is_err());
|
||||
|
||||
@@ -685,8 +685,10 @@ pub enum FeatureKind {
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
combine: bool,
|
||||
/// Where each of `bodies` is mirrored from: the occurrence path of the placement it was
|
||||
/// picked at. The body is mirrored where that placement shows it, as the feature's own
|
||||
/// component sees it. Empty in older designs, which mirror the bodies' own geometry.
|
||||
/// picked at. The body is mirrored where that placement shows it, in the plane of the
|
||||
/// feature's own component, and its copy joins the body's component (or, when that is
|
||||
/// placed more than once, the innermost component around it placed once). Empty in older
|
||||
/// designs, which mirror the bodies' own geometry.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
placements: Vec<Vec<u64>>,
|
||||
},
|
||||
@@ -950,6 +952,15 @@ pub enum FeatureKind {
|
||||
/// Normals the same way instead of facing each other.
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
flip: bool,
|
||||
/// A plane the bodies move with (an origin or construction plane): its origin and normal
|
||||
/// are the `from` point and normal, worked out again whenever the plane moves.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
from_plane: Option<PlaneRef>,
|
||||
/// A plane the bodies move onto: after turning to meet it (when `from` has a normal) the
|
||||
/// `from` point is brought onto the plane along its normal, so the plane may move and the
|
||||
/// bodies follow. Replaces `to`.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
to_plane: Option<PlaneRef>,
|
||||
},
|
||||
/// A helical coil: a circular or square section swept `turns` times about `axis`, rising
|
||||
/// `pitch` per turn, on a helix of `diameter` (the section's centre, or its inside or outside).
|
||||
@@ -2320,6 +2331,12 @@ pub struct Feature {
|
||||
/// where newer ones number them after the first name (`Ribs (4)`, #373).
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
pub shell_piece_order: bool,
|
||||
/// The pieces of a face this feature splits are numbered in the kernel's order and by the
|
||||
/// index of the face they came from, as builds before #3902 numbered them, instead of by
|
||||
/// place. Set on the features of designs saved before then, whose stored `#k` face names
|
||||
/// mean that numbering.
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
pub kernel_piece_order: bool,
|
||||
#[serde(flatten)]
|
||||
pub kind: FeatureKind,
|
||||
}
|
||||
@@ -3021,6 +3038,7 @@ impl Document {
|
||||
face_names: Vec::new(),
|
||||
participants: Vec::new(),
|
||||
shell_piece_order: false,
|
||||
kernel_piece_order: false,
|
||||
kind,
|
||||
};
|
||||
self.place_feature(f);
|
||||
@@ -3209,10 +3227,11 @@ impl Document {
|
||||
if name.len() > 64 {
|
||||
return Some("parameter names are at most 64 characters".into());
|
||||
}
|
||||
if expr::is_reserved(name) {
|
||||
return Some(format!("`{name}` is a unit, constant or function name"));
|
||||
match expr::reserved_kind(name) {
|
||||
Some("constant") => Some(format!("`{name}` is a constant, so it cannot be a parameter name")),
|
||||
Some(kind) => Some(format!("`{name}` is a {kind} name, so it cannot be a parameter name")),
|
||||
None => None,
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Add or change a parameter. Fails if the result would not evaluate. A new parameter
|
||||
|
||||
+676
-87
File diff suppressed because it is too large
Load Diff
@@ -116,19 +116,35 @@ pub(super) fn eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
FeatureKind::Align { bodies, from, to, from_normal, to_normal, flip } => {
|
||||
FeatureKind::Align { bodies, from, to, from_normal, to_normal, flip, from_plane, to_plane } => {
|
||||
// A plane on either side is worked out now, so the bodies follow it when it moves.
|
||||
let (mut from, mut from_normal) = (from.0, from_normal.and_then(|n| n.normalized()));
|
||||
if let Some(p) = from_plane {
|
||||
let pl = doc.resolve_plane_in(vals, p, 0, Some(&*st))?;
|
||||
(from, from_normal) = (pl.origin, Some(pl.normal()));
|
||||
}
|
||||
let target = to_plane.as_ref().map(|p| doc.resolve_plane_in(vals, p, 0, Some(&*st))).transpose()?;
|
||||
let (to, to_normal) = match &target {
|
||||
Some(pl) => (pl.origin, Some(pl.normal())),
|
||||
None => (to.0, to_normal.and_then(|n| n.normalized())),
|
||||
};
|
||||
let mut m = translation(Vec3::ZERO);
|
||||
if let (Some(a), Some(b)) = (from_normal.and_then(|n| n.normalized()), to_normal.and_then(|n| n.normalized())) {
|
||||
if let (Some(a), Some(b)) = (from_normal, to_normal) {
|
||||
// Turn `a` onto −b (faces meet) or b (flipped), about the `from` point.
|
||||
let want = if *flip { b } else { b * -1.0 };
|
||||
let axis = a.cross(want);
|
||||
let ang = axis.len().atan2(a.dot(want));
|
||||
let axis = axis.normalized().unwrap_or_else(|| a.any_perp());
|
||||
if ang.abs() > 1e-12 {
|
||||
m = rotation(from.0, axis, ang);
|
||||
m = rotation(from, axis, ang);
|
||||
}
|
||||
}
|
||||
let d = to.0 - from.0;
|
||||
// Onto a plane the point goes straight along the plane's normal (nothing slides
|
||||
// across it); onto a point it lands on it.
|
||||
let d = match (&target, to_normal) {
|
||||
(Some(_), Some(n)) => n * (to - from).dot(n),
|
||||
_ => to - from,
|
||||
};
|
||||
m[3][0] += d.x;
|
||||
m[3][1] += d.y;
|
||||
m[3][2] += d.z;
|
||||
@@ -611,7 +627,7 @@ pub(super) fn sheet_eval(
|
||||
)));
|
||||
}
|
||||
let sh = st.sheets.get_mut(i).ok_or_else(|| DocError::Invalid("sheet".into()))?;
|
||||
sh.perforations.push(crate::sheet::SheetPerforation { feature: f.id, diameter: d, pitch, margin, square });
|
||||
sh.perforations.push(crate::sheet::SheetPerforation { feature: f.id, diameter: d, pitch, margin, square, from_right: false });
|
||||
Ok(())
|
||||
}
|
||||
FeatureKind::SheetHem { edges, length, gap, flip, body } => {
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
//! Extrudes from a whole face whose outline is no lines and arcs (#2040), checked through the
|
||||
//! tool `whole_face_tool` picks.
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
use serde_json::json;
|
||||
use solidcraft_geom::{Plane, Vec2, Vec3};
|
||||
use solidcraft_kernel as kernel;
|
||||
use solidcraft_sketch::{CurveKind, Sketch};
|
||||
|
||||
use crate::*;
|
||||
|
||||
thread_local! {
|
||||
static SLABS: Cell<usize> = const { Cell::new(0) };
|
||||
}
|
||||
|
||||
/// A cut took a slab over the whole body as its tool, on this thread.
|
||||
pub(super) fn slab_taken() {
|
||||
SLABS.with(|n| n.set(n.get() + 1));
|
||||
}
|
||||
|
||||
/// A closed spline through six points round the origin, on the plane at height `z`.
|
||||
fn spline_sketch(z: f64) -> FeatureKind {
|
||||
let mut sk = Sketch::new();
|
||||
let fit = [(8.0, 0.0), (4.0, 6.0), (-3.0, 7.0), (-7.0, 1.0), (-4.0, -6.0), (3.0, -5.0)];
|
||||
let mut pts: Vec<usize> = fit.iter().map(|&(x, y)| sk.add_point(Vec2::new(x, y), None).unwrap()).collect();
|
||||
pts.push(pts[0]);
|
||||
sk.add_curve(CurveKind::Spline { pts, control: false, degree: 3, periodic: true }, None).unwrap();
|
||||
let plane = Plane { origin: Vec3::new(0.0, 0.0, z), ..Plane::XY };
|
||||
FeatureKind::Sketch { plane: PlaneRef::Custom { plane }, sketch: sk }
|
||||
}
|
||||
|
||||
fn extrude(doc: &mut Document, sketch: u64, extent: serde_json::Value, operation: Operation) {
|
||||
let kind = FeatureKind::Extrude {
|
||||
thin: None,
|
||||
sketch: SketchId(sketch),
|
||||
profiles: ProfileSel::All,
|
||||
extent: serde_json::from_value(extent).unwrap(),
|
||||
operation,
|
||||
targets: vec![],
|
||||
};
|
||||
doc.add_feature(kind, None).unwrap();
|
||||
}
|
||||
|
||||
fn volume(model: &Model) -> f64 {
|
||||
let state = model.state();
|
||||
assert!(model.results.iter().all(|r| r.error.is_none()), "{:?}", model.results.iter().find(|r| r.error.is_some()));
|
||||
assert_eq!(state.bodies.len(), 1);
|
||||
assert!(state.bodies[0].body.validity().is_empty(), "{:?}", state.bodies[0].body.validity());
|
||||
kernel::measure(&state.bodies[0].body).unwrap().volume
|
||||
}
|
||||
|
||||
/// A prism 10 tall on a closed spline, standing on a 40 x 40 x 5 plate, cut 3 into its whole
|
||||
/// top face. The walls only touch the prism, so the cut takes a slab over the whole body from 3
|
||||
/// below the face up: it must take the face's area times 3 and leave the plate alone.
|
||||
#[test]
|
||||
fn slab_cut_into_a_spline_prism() {
|
||||
let mut doc = Document::new("spline prism");
|
||||
let plate = FeatureKind::Box {
|
||||
corner: point_expr(Vec3::new(-20.0, -20.0, 0.0)),
|
||||
length: "40 mm".into(),
|
||||
width: "40 mm".into(),
|
||||
height: "5 mm".into(),
|
||||
operation: Operation::NewBody,
|
||||
};
|
||||
doc.add_feature(plate, None).unwrap();
|
||||
let base = doc.add_feature(spline_sketch(5.0), None).unwrap();
|
||||
extrude(&mut doc, base, json!({"distance": "10 mm"}), Operation::Join);
|
||||
let mut model = Model::new();
|
||||
model.evaluate(&doc);
|
||||
let joined = volume(&model);
|
||||
let area = (joined - 40.0 * 40.0 * 5.0) / 10.0;
|
||||
assert!(area > 100.0, "the prism's area {area}");
|
||||
|
||||
let top = doc.add_feature(spline_sketch(15.0), None).unwrap();
|
||||
extrude(&mut doc, top, json!({"distance": "3 mm", "direction": "negative"}), Operation::Cut);
|
||||
SLABS.with(|n| n.set(0));
|
||||
model.evaluate(&doc);
|
||||
let cut = volume(&model);
|
||||
assert_eq!(SLABS.with(Cell::get), 1, "the cut took the slab");
|
||||
assert!((joined - cut - area * 3.0).abs() < 1e-3 * area, "took {}, expected {}", joined - cut, area * 3.0);
|
||||
let bounds = model.state().bodies[0].mesh().bounds();
|
||||
assert!((bounds.max.z - 12.0).abs() < 1e-6, "top at {}", bounds.max.z);
|
||||
assert!(bounds.min.dist(Vec3::new(-20.0, -20.0, 0.0)) < 1e-6, "plate from {:?}", bounds.min);
|
||||
}
|
||||
+115
-4
@@ -304,9 +304,23 @@ fn is_constant(id: &str) -> bool {
|
||||
CONSTANTS.contains(&id)
|
||||
}
|
||||
|
||||
/// What kind of word the expression language reserves `name` as: `unit`, `constant` or
|
||||
/// `function` (None: free for a parameter).
|
||||
pub fn reserved_kind(name: &str) -> Option<&'static str> {
|
||||
if unit(name).is_some() {
|
||||
Some("unit")
|
||||
} else if is_constant(name) {
|
||||
Some("constant")
|
||||
} else if FUNCTIONS.iter().any(|(f, _)| *f == name) {
|
||||
Some("function")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Is this a name the expression language reserves (unit, constant or function)?
|
||||
pub fn is_reserved(name: &str) -> bool {
|
||||
unit(name).is_some() || is_constant(name) || FUNCTIONS.iter().any(|(f, _)| *f == name)
|
||||
reserved_kind(name).is_some()
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
@@ -330,8 +344,13 @@ 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 == '.' {
|
||||
} else if c.is_ascii_digit() || c == '.' || leading_decimal_comma(&cs, i, depth, out.last()) {
|
||||
let st = i;
|
||||
// A leading decimal comma (`,5` for 0,5, #2966) starts the number as `.5` would.
|
||||
let leading_comma = c == ',';
|
||||
if leading_comma {
|
||||
i += 1;
|
||||
}
|
||||
while cs.get(i).is_some_and(|(_, c)| c.is_ascii_digit() || *c == '.') {
|
||||
i += 1;
|
||||
}
|
||||
@@ -340,7 +359,7 @@ fn lex_spans(s: &str) -> Result<Vec<(Tok, usize, usize)>, DocError> {
|
||||
// them a comma separates a function's 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 && !has_point && digit(i.wrapping_sub(1)) && cs.get(i).is_some_and(|(_, c)| *c == ',') && digit(i + 1) {
|
||||
if depth == 0 && !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;
|
||||
@@ -413,6 +432,22 @@ fn lex_spans(s: &str) -> Result<Vec<(Tok, usize, usize)>, DocError> {
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// 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
|
||||
&& 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(_) => 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
|
||||
@@ -514,6 +549,24 @@ pub fn is_bare_number(s: &str) -> bool {
|
||||
is_literal(s) && s.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
|
||||
/// only; whether the name is free or reserved is `Document::param_name_problem`'s business.
|
||||
fn is_name_shaped(s: &str) -> bool {
|
||||
let mut cs = s.chars();
|
||||
matches!(cs.next(), Some(c) if c.is_alphabetic() || c == '_') && cs.all(|c| c.is_alphanumeric() || c == '_')
|
||||
}
|
||||
|
||||
/// A value typed as a definition, `name = expression`: the name and the text after the first
|
||||
/// `=`, both trimmed (the text may be empty). `None` for anything else, including text whose
|
||||
/// left side is not shaped like a name (`2 = 3`), which stays an ordinary expression and fails
|
||||
/// to parse as before. The dimension input defines a user parameter this way (#2916); other
|
||||
/// value inputs can take the same form later.
|
||||
pub fn definition(s: &str) -> Option<(&str, &str)> {
|
||||
let (name, rhs) = s.split_once('=')?;
|
||||
let name = name.trim();
|
||||
is_name_shaped(name).then(|| (name, rhs.trim()))
|
||||
}
|
||||
|
||||
/// The length unit of a design with these units: `mm` unless they name another length unit.
|
||||
pub fn length_unit_or_mm(units: &str) -> &str {
|
||||
let u = units.trim();
|
||||
@@ -991,11 +1044,24 @@ mod tests {
|
||||
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");
|
||||
// Not between two digits, or a second comma: still an error.
|
||||
for bad in ["2,", ",5", "2 ,5", "2, 5", "1,5,3", "1.5,3"] {
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_leading_decimal_comma_is_a_number() {
|
||||
// #2966: `,5` is 0,5 where `.5` would be a number: at the start or after an operator.
|
||||
assert_eq!(ev(",5", Kind::Unitless).unwrap(), 0.5);
|
||||
assert_eq!(ev(",5 mm", Kind::Length).unwrap(), 0.5);
|
||||
assert_eq!(ev("-,5", Kind::Unitless).unwrap(), -0.5);
|
||||
assert_eq!(ev("2*,5", Kind::Unitless).unwrap(), 1.0);
|
||||
assert_eq!(ev("width - ,25", Kind::Length).unwrap(), 39.75);
|
||||
// Function arguments are still separated by commas.
|
||||
assert_eq!(ev("max(1,5)", Kind::Unitless).unwrap(), 5.0);
|
||||
assert_eq!(ev("max(,5; 2)", Kind::Unitless).ok(), None, "inside parentheses a comma is a separator");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inch_and_foot_marks() {
|
||||
// #1502: `1"` in a millimetre design is an inch, `2'` two feet, straight or as primes.
|
||||
@@ -1170,4 +1236,49 @@ mod tests {
|
||||
assert!(is_bare_number(" -1.5 ") && is_bare_number("+2") && !is_bare_number("20 mm") && !is_bare_number("(2)"));
|
||||
assert_eq!((length_unit_or_mm(" in "), length_unit_or_mm("deg"), length_unit_or_mm("")), ("in", "mm", "mm"));
|
||||
}
|
||||
|
||||
/// `name = expression` splits at the first `=` when the left side is shaped like a name; the
|
||||
/// right side is any text, checked by whoever evaluates it.
|
||||
#[test]
|
||||
fn definitions_split_on_a_name_and_an_equals_sign() {
|
||||
let splits = [
|
||||
("w = 20", "w", "20"),
|
||||
("w=20", "w", "20"),
|
||||
(" w = 20 mm ", "w", "20 mm"),
|
||||
("w = (a + b) / 2", "w", "(a + b) / 2"),
|
||||
("w = 2 * (3 + sin(45 deg))", "w", "2 * (3 + sin(45 deg))"),
|
||||
("w = 1 in - 5.4 mm", "w", "1 in - 5.4 mm"),
|
||||
("_w1 = 1", "_w1", "1"),
|
||||
("szerokość = 20", "szerokość", "20"),
|
||||
// Reserved names split too: the parameter check names the reason.
|
||||
("mm = 5", "mm", "5"),
|
||||
// A second `=` stays in the expression, which then fails to evaluate.
|
||||
("w == 3", "w", "= 3"),
|
||||
("w = a = 3", "w", "a = 3"),
|
||||
// An empty expression is the caller's error to report.
|
||||
("w =", "w", ""),
|
||||
("w = ", "w", ""),
|
||||
];
|
||||
for (text, name, rhs) in splits {
|
||||
assert_eq!(definition(text), Some((name, rhs)), "{text:?}");
|
||||
}
|
||||
for text in ["20", "20 mm", "width / 2", "= 20", "=", "", "2w = 3", "a b = 3", "a-b = 3", "w.x = 3", "(w) = 3", "-w = 3"] {
|
||||
assert_eq!(definition(text), None, "{text:?}");
|
||||
}
|
||||
}
|
||||
|
||||
/// A reserved name is refused by saying what kind of word it is.
|
||||
#[test]
|
||||
fn a_reserved_name_is_refused_by_its_kind() {
|
||||
assert_eq!(
|
||||
(reserved_kind("mm"), reserved_kind("deg"), reserved_kind("pi"), reserved_kind("sin"), reserved_kind("width")),
|
||||
(Some("unit"), Some("unit"), Some("constant"), Some("function"), None)
|
||||
);
|
||||
let problem = |n: &str| crate::Document::param_name_problem(n).unwrap_or_default();
|
||||
assert_eq!(problem("mm"), "`mm` is a unit name, so it cannot be a parameter name");
|
||||
assert_eq!(problem("sin"), "`sin` is a function name, so it cannot be a parameter name");
|
||||
assert_eq!(problem("pi"), "`pi` is a constant, so it cannot be a parameter name");
|
||||
assert_eq!(crate::Document::param_name_problem("width"), None);
|
||||
assert!(problem("2w").contains("not a valid parameter name"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1250,7 +1250,9 @@ mod tests {
|
||||
file["format"] = Value::from("solidcraft/23");
|
||||
let opened = read(&file.to_string()).unwrap();
|
||||
assert_eq!(opened.format, format_string(FORMAT));
|
||||
assert_eq!(opened.features, plain.features);
|
||||
let mut kept = plain.features.clone();
|
||||
kept.iter_mut().for_each(|f| f.kernel_piece_order = true);
|
||||
assert_eq!(opened.features, kept);
|
||||
// One that chooses its relief says so in the file, which is stamped 24 and keeps it.
|
||||
let mut own = Document::new("A");
|
||||
own.add_feature(flange(Some("round"), Some("6 mm")), None).unwrap();
|
||||
@@ -1742,7 +1744,7 @@ mod tests {
|
||||
legacy["format"] = Value::from("solidcraft/6");
|
||||
let original_table = legacy["configs"].clone();
|
||||
let upgraded = read(&legacy.to_string()).unwrap();
|
||||
d.features.iter_mut().for_each(|f| f.shell_piece_order = true);
|
||||
d.features.iter_mut().for_each(|f| (f.shell_piece_order, f.kernel_piece_order) = (true, true));
|
||||
assert_eq!(upgraded, d, "the identity migration preserves the complete design");
|
||||
assert_eq!(serde_json::to_value(&upgraded.configs).unwrap(), original_table);
|
||||
assert_eq!(upgraded.format, format_string(FORMAT));
|
||||
@@ -1796,9 +1798,12 @@ mod tests {
|
||||
legacy["format"] = Value::from("solidcraft/7");
|
||||
let mut features = legacy["features"].clone();
|
||||
pieces_in_shell_order(&mut features);
|
||||
for f in features.as_array_mut().into_iter().flatten() {
|
||||
f["kernel_piece_order"] = Value::Bool(true);
|
||||
}
|
||||
let params = legacy["params"].clone();
|
||||
let upgraded = read(&legacy.to_string()).unwrap();
|
||||
d.features.iter_mut().for_each(|f| f.shell_piece_order = true);
|
||||
d.features.iter_mut().for_each(|f| (f.shell_piece_order, f.kernel_piece_order) = (true, true));
|
||||
assert_eq!(upgraded, d);
|
||||
let upgraded_json: Value = serde_json::from_str(&upgraded.to_json()).unwrap();
|
||||
assert_eq!(upgraded_json["features"], features);
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
//! An Align can name planes (`from_plane`, `to_plane`: an origin, construction or offset plane, or
|
||||
//! an explicit one) instead of a point or a face's point and normal, so the bodies follow the plane
|
||||
//! when it moves. Older designs need no transformation: an Align without them keeps the points and
|
||||
//! normals it was saved with. The last release refuses a design saved with this (the newer format
|
||||
//! number).
|
||||
@@ -0,0 +1,34 @@
|
||||
//! The pieces of a split face are numbered by place (#3902): `#1`, `#2`, … follow the spread of
|
||||
//! their centres, not the kernel's face order or the index of the operand face they came from,
|
||||
//! which can change from one evaluation to the next. Older designs' stored `#k` names mean the
|
||||
//! old numbering, so this step marks each of their features `kernel_piece_order`, which keeps
|
||||
//! it for them. Features added later number by place. The last release refuses a design saved
|
||||
//! with this (the newer format number).
|
||||
use serde_json::Value;
|
||||
|
||||
pub fn step(v: &mut Value) -> crate::Result<()> {
|
||||
let Some(Value::Array(features)) = v.get_mut("features") else {
|
||||
return Ok(());
|
||||
};
|
||||
for feature in features {
|
||||
if let Value::Object(o) = feature {
|
||||
o.insert("kernel_piece_order".into(), Value::Bool(true));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn older_features_keep_their_piece_numbering() {
|
||||
let mut v = json!({"features": [{"type": "sketch"}, {"type": "extrude"}]});
|
||||
super::step(&mut v).unwrap();
|
||||
assert!(v["features"].as_array().unwrap().iter().all(|f| f["kernel_piece_order"] == true));
|
||||
let mut empty = json!({});
|
||||
super::step(&mut empty).unwrap();
|
||||
assert_eq!(empty, json!({}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
//! A sheet's perforation field can run its grid from the flat's right edge (`from_right`): the
|
||||
//! field of a mirrored sheet copy, whose holes are the mirror image of its source's. Older designs
|
||||
//! need no transformation: a field without `from_right` runs from the left as before. The last
|
||||
//! release refuses a design saved with this (the newer format number).
|
||||
@@ -0,0 +1,7 @@
|
||||
//! A Mirror of bodies with `placements` puts each copy in the component of the body it mirrors
|
||||
//! (or, when that component is placed more than once, the innermost component around it placed
|
||||
//! once) instead of the Mirror's own component; the copy is mirrored at the same placement as
|
||||
//! before. Nothing new is saved and older designs need no transformation: a Mirror without
|
||||
//! `placements` mirrors the bodies' own geometry into its own component as before, and one saved
|
||||
//! with `placements` by 0.15 puts its copies beside their bodies when it is opened. The last
|
||||
//! release asks for an update instead of opening a design in this format.
|
||||
@@ -17,6 +17,7 @@
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::sync::{Arc, OnceLock};
|
||||
|
||||
use solidcraft_geom::Vec3;
|
||||
use solidcraft_kernel::history::{FaceOrigin, FaceProv, History};
|
||||
|
||||
use crate::ModelBody;
|
||||
@@ -44,13 +45,14 @@ pub(crate) struct FaceInfo {
|
||||
|
||||
impl Names {
|
||||
/// Names worked out from geometry: the same names, none of them verified, pieces told apart
|
||||
/// by the faces around them (`adjacent`: pairs of faces sharing an edge).
|
||||
pub(crate) fn unverified(names: Vec<String>, adjacent: &[(usize, usize)]) -> Names {
|
||||
/// by the faces around them (`adjacent`: pairs of faces sharing an edge), then numbered by
|
||||
/// place (`centres`, see [`qualify_at`]).
|
||||
pub(crate) fn unverified(names: Vec<String>, adjacent: &[(usize, usize)], centres: Option<&dyn Fn() -> Vec<Vec3>>) -> Names {
|
||||
let named = names
|
||||
.into_iter()
|
||||
.map(|name| Named { name: crate::naming::strip_piece(&name).to_string(), aliases: Vec::new(), verified: false, origins: None })
|
||||
.collect();
|
||||
qualify(named, adjacent)
|
||||
qualify_at(named, adjacent, centres)
|
||||
}
|
||||
|
||||
/// Names given as they are (a moved copy keeps its source's), taken as exact.
|
||||
@@ -95,7 +97,8 @@ pub(crate) fn derive(b: &ModelBody, o: &NamingOrigin, lineage: &Lineage) -> Opti
|
||||
}
|
||||
}
|
||||
let named: Vec<Named> = named.into_iter().collect::<Option<_>>()?;
|
||||
Some(qualify(named, &adjacency(h)))
|
||||
let centres = || crate::naming::face_centres(b);
|
||||
Some(qualify_at(named, &adjacency(h), by_place(&o.feature, ¢res)))
|
||||
}
|
||||
|
||||
/// Pairs of faces that share an edge, from a history.
|
||||
@@ -140,7 +143,8 @@ impl<'a> OperandNames<'a> {
|
||||
.get_or_init(|| {
|
||||
Arc::new(derive(body, o, &stage.lineage).unwrap_or_else(|| {
|
||||
let names = crate::naming::geometric_names(body, o, body.body.face_count());
|
||||
Names::unverified(names, &crate::naming::mesh_adjacency(body))
|
||||
let centres = || crate::naming::face_centres(body);
|
||||
Names::unverified(names, &crate::naming::mesh_adjacency(body), by_place(&o.feature, ¢res))
|
||||
}))
|
||||
})
|
||||
.clone();
|
||||
@@ -266,7 +270,32 @@ pub(crate) fn descends(name: &str, stem: &str) -> bool {
|
||||
|
||||
/// Tell pieces apart: faces sharing a name are named by the faces around them that the others
|
||||
/// lack (`adjacent`: pairs of faces sharing an edge).
|
||||
#[cfg(test)]
|
||||
fn qualify(named: Vec<Named>, adjacent: &[(usize, usize)]) -> Names {
|
||||
qualify_at(named, adjacent, None)
|
||||
}
|
||||
|
||||
/// How the pieces `f` splits a face into are numbered: by place (`centres`), else, for a feature
|
||||
/// saved before that (`Feature::kernel_piece_order`), as the build that saved it numbered them.
|
||||
pub(crate) fn by_place<'a>(f: &crate::Feature, centres: &'a dyn Fn() -> Vec<Vec3>) -> Option<&'a dyn Fn() -> Vec<Vec3>> {
|
||||
(!f.kernel_piece_order).then_some(centres)
|
||||
}
|
||||
|
||||
/// [`qualify`], with `centres` giving each face's centre (worked out only when pieces tie): pieces
|
||||
/// that nothing else tells apart are numbered along their spread, not in the kernel's order,
|
||||
/// which can differ from one evaluation to the next (#3902). Without `centres`, in the kernel's
|
||||
/// order and by the faces they came from, as builds before that numbered them.
|
||||
fn qualify_at(named: Vec<Named>, adjacent: &[(usize, usize)], centres: Option<&dyn Fn() -> Vec<Vec3>>) -> Names {
|
||||
let by_place = centres.is_some();
|
||||
let worked_out = std::cell::OnceCell::new();
|
||||
// Tied pieces in the order of their spread; as they are when there are no centres.
|
||||
let in_order = |tied: Vec<usize>| -> Vec<usize> {
|
||||
let at: &Vec<Vec3> = worked_out.get_or_init(|| centres.map(|c| c()).unwrap_or_default());
|
||||
if at.len() != named.len() {
|
||||
return tied;
|
||||
}
|
||||
crate::naming::along_spread(tied.into_iter().filter_map(|i| Some((i, *at.get(i)?))).collect())
|
||||
};
|
||||
let n = named.len();
|
||||
let mut bounds: Vec<BTreeSet<String>> = vec![BTreeSet::new(); n];
|
||||
for &(a, b) in adjacent {
|
||||
@@ -320,8 +349,20 @@ fn qualify(named: Vec<Named>, adjacent: &[(usize, usize)]) -> Names {
|
||||
let mut tied = tied;
|
||||
let origins: Vec<Option<&Vec<FaceOrigin>>> = tied.iter().map(|i| named.get(*i).and_then(|x| x.origins.as_ref())).collect();
|
||||
let distinct = origins.iter().all(Option::is_some) && origins.iter().collect::<BTreeSet<_>>().len() == origins.len();
|
||||
if distinct {
|
||||
if distinct && !by_place {
|
||||
tied.sort_by_key(|i| named.get(*i).and_then(|x| x.origins.clone()));
|
||||
} else if distinct {
|
||||
// By what they came from, but not by where it stood in its operand's face order:
|
||||
// an operand that came out of a boolean can list its faces in another order on
|
||||
// the next evaluation (#3902). Pieces of faces of one kind are numbered by place.
|
||||
let mut by_kind: BTreeMap<Vec<FaceOrigin>, Vec<usize>> = BTreeMap::new();
|
||||
for i in tied {
|
||||
let kind = named.get(i).and_then(|x| x.origins.as_deref()).unwrap_or_default().iter().map(|o| without_index(*o)).collect();
|
||||
by_kind.entry(kind).or_default().push(i);
|
||||
}
|
||||
tied = by_kind.into_values().flat_map(&in_order).collect();
|
||||
} else {
|
||||
tied = in_order(tied);
|
||||
}
|
||||
for (k, i) in tied.iter().enumerate() {
|
||||
if let Some(slot) = names.get_mut(*i) {
|
||||
@@ -333,17 +374,17 @@ fn qualify(named: Vec<Named>, adjacent: &[(usize, usize)]) -> Names {
|
||||
}
|
||||
}
|
||||
}
|
||||
// A piece's new name can meet a face that already had it: numbered too, never shared.
|
||||
let mut seen: BTreeMap<String, usize> = BTreeMap::new();
|
||||
for name in &names {
|
||||
*seen.entry(name.clone()).or_default() += 1;
|
||||
// A piece's new name can meet a face that already had it: numbered too (along their spread),
|
||||
// never shared.
|
||||
let mut seen: BTreeMap<String, Vec<usize>> = BTreeMap::new();
|
||||
for (i, name) in names.iter().enumerate() {
|
||||
seen.entry(name.clone()).or_default().push(i);
|
||||
}
|
||||
let mut counter: BTreeMap<String, usize> = BTreeMap::new();
|
||||
for (i, name) in names.iter_mut().enumerate() {
|
||||
if seen.get(name.as_str()).is_some_and(|c| *c > 1) {
|
||||
let k = counter.entry(name.clone()).or_default();
|
||||
*k += 1;
|
||||
*name = format!("{name}#{k}");
|
||||
for (name, same) in seen.into_iter().filter(|(_, same)| same.len() > 1) {
|
||||
for (k, i) in in_order(same).into_iter().enumerate() {
|
||||
if let Some(slot) = names.get_mut(i) {
|
||||
*slot = format!("{name}#{}", k + 1);
|
||||
}
|
||||
if let Some(v) = verified.get_mut(i) {
|
||||
*v = false;
|
||||
}
|
||||
@@ -353,6 +394,17 @@ fn qualify(named: Vec<Named>, adjacent: &[(usize, usize)]) -> Names {
|
||||
Names { faces: Arc::new(names), info }
|
||||
}
|
||||
|
||||
/// `origin` with the face or edge it names left out (its kind and operand kept).
|
||||
fn without_index(origin: FaceOrigin) -> FaceOrigin {
|
||||
match origin {
|
||||
FaceOrigin::Face { operand, .. } => FaceOrigin::Face { operand, face: 0 },
|
||||
FaceOrigin::Edge { operand, .. } => FaceOrigin::Edge { operand, edge: 0 },
|
||||
FaceOrigin::Offset { operand, .. } => FaceOrigin::Offset { operand, face: 0 },
|
||||
// A profile's segments come in the sketch's order.
|
||||
FaceOrigin::Profile { .. } | FaceOrigin::Cap { .. } => origin,
|
||||
}
|
||||
}
|
||||
|
||||
/// How a stored face name matches a current face.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub(crate) enum Match {
|
||||
|
||||
@@ -57,11 +57,12 @@ pub use assembly::{
|
||||
rigid,
|
||||
};
|
||||
pub use document::*;
|
||||
pub use eval::KeptResults;
|
||||
pub use eval::{
|
||||
EdgeRef, FeatureResult, Model, ModelBody, ModelState, PlacedBodies, RevolveExtent, SavedModel, SolvedSketch, ThreadInfo, curve_pick_error,
|
||||
edge_names_for, edge_refs, face_point_names, face_point_state, hole_direction_at, import_body_names, metric_thread_sizes, object_height,
|
||||
parse_metric_thread, pattern_matrices, revolve_extent, revolve_extent_with_convention, select_profiles, sketch_hole_direction, sketch_line_plane,
|
||||
state_in_frame, state_in_frame_at, suggested_thread, through_all_distance, world_state, world_state_reusing,
|
||||
state_in_frame, state_in_frame_at, suggested_thread, tangent_face_chain, through_all_distance, world_state, world_state_reusing,
|
||||
};
|
||||
pub use expr::Expr;
|
||||
pub use names::{BodyName, FeatureName, SketchId};
|
||||
|
||||
+46
-36
@@ -241,13 +241,11 @@ pub(crate) fn names(b: &ModelBody) -> Arc<Names> {
|
||||
.get_or_init(|| {
|
||||
// Exact history needs no mesh; geometry does (its face count is the mesh's).
|
||||
let names = match b.names.origin.as_ref() {
|
||||
Some(o) if o.depth < MAX_DEPTH => b
|
||||
.names
|
||||
.lineage
|
||||
.as_ref()
|
||||
.and_then(|l| crate::history_names::derive(b, o, l))
|
||||
.unwrap_or_else(|| Names::unverified(geometric_names(b, o, face_count(b)), &mesh_adjacency(b))),
|
||||
_ => Names::unverified((0..face_count(b)).map(|k| format!("{}:face:{k}", b.name)).collect(), &[]),
|
||||
Some(o) if o.depth < MAX_DEPTH => b.names.lineage.as_ref().and_then(|l| crate::history_names::derive(b, o, l)).unwrap_or_else(|| {
|
||||
let centres = || face_centres(b);
|
||||
Names::unverified(geometric_names(b, o, face_count(b)), &mesh_adjacency(b), crate::history_names::by_place(&o.feature, ¢res))
|
||||
}),
|
||||
_ => Names::unverified((0..face_count(b)).map(|k| format!("{}:face:{k}", b.name)).collect(), &[], None),
|
||||
};
|
||||
Arc::new(names)
|
||||
})
|
||||
@@ -519,35 +517,8 @@ fn number_pieces(names: &mut [String], pts: &[Vec<Vec3>], mark: char) {
|
||||
let ps = pts.get(j).cloned().unwrap_or_default();
|
||||
ps.iter().fold(Vec3::ZERO, |a, p| a + *p) * (1.0 / ps.len().max(1) as f64)
|
||||
};
|
||||
let cs: Vec<(usize, Vec3)> = same.iter().map(|j| (*j, centre(*j))).collect();
|
||||
let ext = cs.iter().map(|(_, c)| *c).collect::<Aabb3>().size();
|
||||
let key = |c: Vec3| {
|
||||
if ext.x >= ext.y && ext.x >= ext.z {
|
||||
(c.x, c.y, c.z)
|
||||
} else if ext.y >= ext.z {
|
||||
(c.y, c.z, c.x)
|
||||
} else {
|
||||
(c.z, c.x, c.y)
|
||||
}
|
||||
};
|
||||
let mut order = cs.clone();
|
||||
// Pieces lying almost level along the sort axis differ only in the last bits of their centre:
|
||||
// the centre is the mean of a tessellated boundary, and that depends on where the loop's
|
||||
// polyline starts, which can differ between processes. Comparing the keys with a tolerance
|
||||
// makes those pieces equal, so the stable sort keeps the order they arrived in and a name
|
||||
// keeps its number across runs (solidcraft#569). total_cmp keeps a NaN deterministic.
|
||||
let tol = ext.x.max(ext.y).max(ext.z).max(1.0) * 1e-9;
|
||||
let bucket = |c: Vec3| {
|
||||
let k = key(c);
|
||||
// Rounding onto the grid, rather than comparing with a tolerance, is what keeps this a
|
||||
// total order: `a` and `b` in one bucket and `b` and `c` in the next must not leave `a`
|
||||
// and `c` in different ones, or the sort has no order to work with. Since Rust 1.81
|
||||
// `sort_by` may panic on a comparator that is not one. Two pieces can still flip when
|
||||
// they straddle a bucket edge; that is the price of a key that ignores their low bits.
|
||||
((k.0 / tol).round() as i64, (k.1 / tol).round() as i64, (k.2 / tol).round() as i64)
|
||||
};
|
||||
order.sort_by_key(|a| bucket(a.1));
|
||||
for (k, (j, _)) in order.iter().enumerate() {
|
||||
let order = along_spread(same.iter().map(|j| (*j, centre(*j))).collect());
|
||||
for (k, j) in order.iter().enumerate() {
|
||||
if let Some(slot) = names.get_mut(*j) {
|
||||
*slot = format!("{n}{mark}{}", k + 1);
|
||||
}
|
||||
@@ -555,6 +526,45 @@ fn number_pieces(names: &mut [String], pts: &[Vec<Vec3>], mark: char) {
|
||||
}
|
||||
}
|
||||
|
||||
/// The items of `cs` (index, centre) ordered along the longest axis of their centres' spread,
|
||||
/// then the next: how the pieces of one face or edge are numbered, the same on every run
|
||||
/// whatever order the kernel gives them in.
|
||||
pub(crate) fn along_spread(cs: Vec<(usize, Vec3)>) -> Vec<usize> {
|
||||
let ext = cs.iter().map(|(_, c)| *c).collect::<Aabb3>().size();
|
||||
let key = |c: Vec3| {
|
||||
if ext.x >= ext.y && ext.x >= ext.z {
|
||||
(c.x, c.y, c.z)
|
||||
} else if ext.y >= ext.z {
|
||||
(c.y, c.z, c.x)
|
||||
} else {
|
||||
(c.z, c.x, c.y)
|
||||
}
|
||||
};
|
||||
let mut order = cs;
|
||||
// Pieces lying almost level along the sort axis differ only in the last bits of their centre:
|
||||
// the centre is the mean of a tessellated boundary, and that depends on where the loop's
|
||||
// polyline starts, which can differ between processes. Comparing the keys with a tolerance
|
||||
// makes those pieces equal, so the stable sort keeps the order they arrived in and a name
|
||||
// keeps its number across runs (solidcraft#569).
|
||||
let tol = ext.x.max(ext.y).max(ext.z).max(1.0) * 1e-9;
|
||||
let bucket = |c: Vec3| {
|
||||
let k = key(c);
|
||||
// Rounding onto the grid, rather than comparing with a tolerance, is what keeps this a
|
||||
// total order: `a` and `b` in one bucket and `b` and `c` in the next must not leave `a`
|
||||
// and `c` in different ones, or the sort has no order to work with. Since Rust 1.81
|
||||
// `sort_by` may panic on a comparator that is not one. Two pieces can still flip when
|
||||
// they straddle a bucket edge; that is the price of a key that ignores their low bits.
|
||||
((k.0 / tol).round() as i64, (k.1 / tol).round() as i64, (k.2 / tol).round() as i64)
|
||||
};
|
||||
order.sort_by_key(|a| bucket(a.1));
|
||||
order.into_iter().map(|(j, _)| j).collect()
|
||||
}
|
||||
|
||||
/// Each face's centre: the mean of the sampled centres of its triangles.
|
||||
pub(crate) fn face_centres(b: &ModelBody) -> Vec<Vec3> {
|
||||
samples(&b.mesh(), face_count(b)).into_iter().map(|ps| ps.iter().fold(Vec3::ZERO, |a, p| a + *p) * (1.0 / ps.len().max(1) as f64)).collect()
|
||||
}
|
||||
|
||||
/// The name of a face new to `f`.
|
||||
fn role_name(f: &Feature, before: &ModelState, ps: &[Vec3], ns: &[Vec3], fi: usize, fid: u64) -> String {
|
||||
let c = ps.iter().fold(Vec3::ZERO, |a, p| a + *p) * (1.0 / ps.len().max(1) as f64);
|
||||
|
||||
+105
-3
@@ -541,6 +541,10 @@ pub struct SheetPerforation {
|
||||
pub pitch: f64,
|
||||
pub margin: f64,
|
||||
pub square: bool,
|
||||
/// The grid runs from the flat's right edge instead of its left: the field of a mirrored
|
||||
/// copy, whose holes are the mirror image of its source's.
|
||||
#[serde(skip_serializing_if = "std::ops::Not::not")]
|
||||
pub from_right: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
@@ -1992,8 +1996,11 @@ impl SheetBody {
|
||||
if y > hi.y - clear + 1e-9 {
|
||||
break;
|
||||
}
|
||||
let mut x = lo.x + clear + if !p.square && row % 2 == 1 { p.pitch / 2.0 } else { 0.0 };
|
||||
while x <= hi.x - clear + 1e-9 {
|
||||
let shift = if !p.square && row % 2 == 1 { p.pitch / 2.0 } else { 0.0 };
|
||||
// Steps across the row from its first hole; a mirrored field counts from the right.
|
||||
let mut step = 0.0;
|
||||
while lo.x + clear + shift + step <= hi.x - clear + 1e-9 {
|
||||
let x = if p.from_right { hi.x - clear - shift - step } else { lo.x + clear + shift + step };
|
||||
let c = Vec2::new(x, y);
|
||||
let (i, j) = idx(x, y);
|
||||
let near = grid.get(j * nx + i).map(Vec::as_slice).unwrap_or(&[]);
|
||||
@@ -2003,7 +2010,7 @@ impl SheetBody {
|
||||
{
|
||||
out.push(c);
|
||||
}
|
||||
x += p.pitch;
|
||||
step += p.pitch;
|
||||
}
|
||||
row += 1;
|
||||
}
|
||||
@@ -4355,6 +4362,101 @@ impl Letters {
|
||||
}
|
||||
}
|
||||
|
||||
/// A flat point mirrored across the flat frame's y axis (x → −x).
|
||||
fn flip_x(p: Vec2) -> Vec2 {
|
||||
Vec2::new(-p.x, p.y)
|
||||
}
|
||||
|
||||
/// A loop mirrored across the flat frame's y axis, exactly (arcs stay arcs, free-form curves
|
||||
/// follow their control points), and run backwards so it keeps its sense: an outer loop stays
|
||||
/// counter-clockwise, a hole clockwise.
|
||||
fn flip_x_loop(l: &Loop2) -> Loop2 {
|
||||
let segs = l
|
||||
.segs
|
||||
.iter()
|
||||
.map(|s| match *s {
|
||||
Seg2::Arc { center, radius, start, sweep } => {
|
||||
Seg2::Arc { center: flip_x(center), radius, start: std::f64::consts::PI - start, sweep: -sweep }
|
||||
}
|
||||
other => map_controls(&other, flip_x),
|
||||
})
|
||||
.collect();
|
||||
Loop2 { segs }.reversed()
|
||||
}
|
||||
|
||||
fn flip_x_region(r: &Region2) -> Region2 {
|
||||
Region2 { outer: flip_x_loop(&r.outer), holes: r.holes.iter().map(flip_x_loop).collect() }
|
||||
}
|
||||
|
||||
impl SheetBody {
|
||||
/// The sheet model of a copy of this body moved by `m` and named `body`: a rigid motion
|
||||
/// moves its frame; a mirror moves its frame too and mirrors everything in the flat, so the
|
||||
/// copy is the opposite hand, with the same thickness side, bends, K-factor and flat
|
||||
/// length. `None` when `m` is not a rigid motion or a mirror (a scaled copy is no sheet).
|
||||
pub fn copied(&self, body: &str, m: &Mat) -> Option<SheetBody> {
|
||||
let axis = |i: usize| m.get(i).map(|c| Vec3::new(c[0], c[1], c[2]));
|
||||
let (x, y, z) = (axis(0)?, axis(1)?, axis(2)?);
|
||||
let unit = |v: Vec3| (v.dot(v) - 1.0).abs() < 1e-6;
|
||||
let isometry = m.iter().flatten().all(|v| v.is_finite())
|
||||
&& unit(x)
|
||||
&& unit(y)
|
||||
&& unit(z)
|
||||
&& x.dot(y).abs() < 1e-6
|
||||
&& x.dot(z).abs() < 1e-6
|
||||
&& y.dot(z).abs() < 1e-6;
|
||||
if !isometry {
|
||||
return None;
|
||||
}
|
||||
let (origin, fx, fy) = (apply_point(m, self.frame.origin), apply_vector(m, self.frame.x), apply_vector(m, self.frame.y));
|
||||
let mut out = self.clone();
|
||||
out.body = body.to_string();
|
||||
if x.cross(y).dot(z) > 0.0 {
|
||||
out.frame = Plane::new(origin, fx, fy)?;
|
||||
} else {
|
||||
// The mirror of flat point (u, v, w) is the point (−u, v, w) of the frame with its x
|
||||
// axis turned round: that frame's normal is the mirrored normal, so the material
|
||||
// stays on the same side of the mirrored base face.
|
||||
out.frame = Plane::new(origin, fx * -1.0, fy)?;
|
||||
out.flip_flat();
|
||||
}
|
||||
Some(out)
|
||||
}
|
||||
|
||||
/// Everything in flat coordinates mirrored across the flat's y axis (x → −x).
|
||||
fn flip_flat(&mut self) {
|
||||
self.base = flip_x_region(&self.base);
|
||||
for f in &mut self.flanges {
|
||||
// The bend line runs the other way, from its far end, so the flange still lies on
|
||||
// the same side of it (`n` turned as `d` is): its ends swap.
|
||||
f.p = flip_x(f.p + f.d * f.len);
|
||||
f.d = Vec2::new(f.d.x, -f.d.y);
|
||||
f.n = flip_x(f.n);
|
||||
f.ext = (f.ext.1, f.ext.0);
|
||||
f.slope = (f.slope.1, f.slope.0);
|
||||
f.tear = (f.tear.1, f.tear.0);
|
||||
if let Some(shape) = &mut f.shape {
|
||||
*shape = shape.iter().rev().map(|p| flip_x(*p)).collect();
|
||||
}
|
||||
if let Some(exact) = &mut f.exact {
|
||||
*exact = flip_x_loop(exact);
|
||||
}
|
||||
}
|
||||
for r in self.holes.iter_mut().chain(self.labels.iter_mut()).chain(self.pockets.iter_mut().map(|p| &mut p.region)) {
|
||||
*r = flip_x_region(r);
|
||||
}
|
||||
for b in &mut self.bores {
|
||||
b.base.x = -b.base.x;
|
||||
b.axis.x = -b.axis.x;
|
||||
}
|
||||
for p in &mut self.perforations {
|
||||
p.from_right = !p.from_right;
|
||||
}
|
||||
for (_, a, b) in &mut self.base_curves {
|
||||
(*a, *b) = (flip_x(*b), flip_x(*a));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Bounded overlap predicate, including crossing-only cuts whose vertices lie outside a panel.
|
||||
pub(crate) fn flat_regions_overlap(a: &Region2, b: &Region2, work: &mut FlatWork) -> Result<bool> {
|
||||
let (a, b) = (planar_region(a)?, planar_region(b)?);
|
||||
|
||||
@@ -103,6 +103,28 @@ fn set_back_reports_restored_features() {
|
||||
assert_eq!((m.last_recomputed, m.last_restored), (0, 0));
|
||||
}
|
||||
|
||||
/// #1989: what an evaluation ahead on a copy of the model computed is kept for the run that
|
||||
/// follows on the model itself, which finds it there even once the evaluation cache has dropped
|
||||
/// it (the cache keeps only a few states of a design with large bodies). Copies keep nothing.
|
||||
#[test]
|
||||
fn results_kept_ahead_outlive_the_cache() {
|
||||
let (mut doc, _) = plate_doc();
|
||||
let mut m = Model::new();
|
||||
m.evaluate(&doc);
|
||||
doc.set_param("width", "50 mm", None, None).unwrap();
|
||||
let kept = std::sync::Arc::new(KeptResults::default());
|
||||
let mut ahead = m.clone();
|
||||
ahead.keep(Some(kept.clone()));
|
||||
assert!(ahead.clone().kept().is_none(), "a copy keeps nothing");
|
||||
ahead.evaluate(&doc);
|
||||
assert_eq!((ahead.last_recomputed, kept.len()), (2, 2));
|
||||
*m.cache.lock().unwrap() = Default::default();
|
||||
m.keep(Some(kept));
|
||||
m.evaluate(&doc);
|
||||
assert_eq!((m.last_recomputed, m.last_restored), (0, 2), "nothing computed again");
|
||||
assert!(rel(measure(&m.state().bodies[0].body).unwrap().volume, 30000.0) < 1e-9);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn box_fillet_cut_timeline() {
|
||||
let (mut doc, _) = plate_doc();
|
||||
|
||||
@@ -1150,3 +1150,76 @@ fn joined_loft_frames_reference_mesh_covers_each_face_once() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The block of #2040 with `extra` run after it.
|
||||
fn rounded_block_and(extra: Value) -> Session {
|
||||
let mut script: Value = serde_json::from_str(include_str!("../../../examples/regressions/extrude_face_rounded_by_fillets.json")).unwrap();
|
||||
script["commands"].as_array_mut().unwrap().push(extra);
|
||||
replay(&script.to_string())
|
||||
}
|
||||
|
||||
/// #2040: the slanted face of a block whose ends fillets round, extruded as a join. Its outline's
|
||||
/// quarter-ellipses come into the face's sketch as splines a hair off the body's edges, and the
|
||||
/// join failed with "boolean Union: no result"; it now sweeps the body's own face. It adds the
|
||||
/// face's area times its height: a 10 x 12 face less four corners of (1 - pi/4) r * r*sqrt(2).
|
||||
#[test]
|
||||
fn extrude_join_from_a_face_rounded_by_fillets() {
|
||||
let at = json!([16.191269086108846, -6.702792508916929, 4.676550460347439]);
|
||||
let before = volume(&replay(include_str!("../../../examples/regressions/extrude_face_rounded_by_fillets.json")), "Body1");
|
||||
let r = 3.8_f64;
|
||||
let area = 10.0 * 12.0 - 4.0 * (1.0 - std::f64::consts::FRAC_PI_4) * r * r * 2f64.sqrt();
|
||||
let joined = rounded_block_and(json!({"command": "solid.extrude", "params": {"face": at, "distance": 5, "operation": "join"}}));
|
||||
let bodies = &joined.model.state().bodies;
|
||||
assert_eq!(bodies.len(), 1, "the join makes no second body");
|
||||
assert!(bodies[0].body.validity().is_empty(), "{:?}", bodies[0].body.validity());
|
||||
let v = volume(&joined, "Body1");
|
||||
assert!((v - (before + area * 5.0)).abs() < 0.05, "join {v} expected {}", before + area * 5.0);
|
||||
}
|
||||
|
||||
/// #2040: a cut 3 mm into the slanted face of the block rounded along its top only (its bottom
|
||||
/// edge left sharp). The walls from the bottom edge go into the body and the others only touch
|
||||
/// it, along edges with curved faces beside them: the cut failed with "boolean Cut: no result".
|
||||
/// In the face's plane the body's section at depth t is the block's section (10 wide, h high, its
|
||||
/// top corners rounded) moved down the face by t: the cut takes
|
||||
/// √2 ∫₀³ (10 (h − t/√2) − 2 (1 − π/4) r²) dt.
|
||||
#[test]
|
||||
fn extrude_cut_into_a_face_beside_rounds() {
|
||||
let at = json!([16.191269086108846, -6.702792508916929, 4.676550460347439]);
|
||||
let top_only = |extra: Option<Value>| {
|
||||
let mut script: Value = serde_json::from_str(include_str!("../../../examples/regressions/extrude_face_rounded_by_fillets.json")).unwrap();
|
||||
let commands = script["commands"].as_array_mut().unwrap();
|
||||
let fillet = commands.last_mut().unwrap();
|
||||
fillet["params"]["edges"] = json!([[10, -10, 8.485281374238571], [10, 0, 8.485281374238571]]);
|
||||
commands.extend(extra);
|
||||
replay(&script.to_string())
|
||||
};
|
||||
let before = volume(&top_only(None), "Body1");
|
||||
let cut = top_only(Some(json!({"command": "solid.extrude", "params": {"face": at, "distance": 3, "direction": "negative", "operation": "cut"}})));
|
||||
let bodies = &cut.model.state().bodies;
|
||||
assert_eq!(bodies.len(), 1);
|
||||
assert!(bodies[0].body.validity().is_empty(), "{:?}", bodies[0].body.validity());
|
||||
let (h, r, d, w) = (8.485281374238571_f64, 3.8_f64, 3.0_f64, 10.0_f64);
|
||||
let corner = (1.0 - std::f64::consts::FRAC_PI_4) * r * r;
|
||||
let taken = 2f64.sqrt() * w * h * d - w * d * d / 2.0 - 2.0 * 2f64.sqrt() * corner * d;
|
||||
let v = before - volume(&cut, "Body1");
|
||||
assert!((v - taken).abs() < 0.05, "took {v}, expected {taken}");
|
||||
}
|
||||
|
||||
/// #2040: a cut 2 mm into the top face of the block rounded at its ends, the strip 10 − 2r wide
|
||||
/// between the rounds. Below the face the rounds stand out past its outline, so a slab over the
|
||||
/// whole body would take them too: the cut takes only what lies over the face, at height z from
|
||||
/// x = 0 to the slanted face at x = x0 + z.
|
||||
#[test]
|
||||
fn extrude_cut_into_a_rounded_block_top() {
|
||||
let (h, r, x0, d) = (8.485281374238571_f64, 3.8_f64, 11.514718625761429_f64, 2.0_f64);
|
||||
let before = volume(&replay(include_str!("../../../examples/regressions/extrude_face_rounded_by_fillets.json")), "Body1");
|
||||
let cut = rounded_block_and(
|
||||
json!({"command": "solid.extrude", "params": {"face": [5, -5, h], "distance": d, "direction": "negative", "operation": "cut"}}),
|
||||
);
|
||||
let bodies = &cut.model.state().bodies;
|
||||
assert_eq!(bodies.len(), 1);
|
||||
assert!(bodies[0].body.validity().is_empty(), "{:?}", bodies[0].body.validity());
|
||||
let taken = (10.0 - 2.0 * r) * (x0 * d + (h * h - (h - d) * (h - d)) / 2.0);
|
||||
let v = before - volume(&cut, "Body1");
|
||||
assert!((v - taken).abs() < 0.05, "took {v}, expected {taken}");
|
||||
}
|
||||
|
||||
@@ -559,18 +559,24 @@ fn rename_body(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let first = f.body_names.first().is_some_and(|n| !n.trim().is_empty());
|
||||
f.body_names.get(i).is_none_or(|n| n.trim().is_empty()) && (f.shell_piece_order || i < f.body_names.len() || !first)
|
||||
};
|
||||
let later: Vec<(u64, Vec<String>)> = match s.doc.features.iter().position(|f| f.id == fid) {
|
||||
Some(at) if s.doc.features.get(at).is_some_and(|f| counted(f, k)) => s
|
||||
// Bodies later features name after this one (`Top (1)`, a split's other piece) keep their
|
||||
// names too: they would otherwise follow it, and break what names them (#3510).
|
||||
let derived = format!("{old} (");
|
||||
let at = s.doc.features.iter().position(|f| f.id == fid);
|
||||
let counts = at.and_then(|at| s.doc.features.get(at)).is_some_and(|f| counted(f, k));
|
||||
let later: Vec<(u64, Vec<String>)> = match at {
|
||||
Some(at) => s
|
||||
.doc
|
||||
.features
|
||||
.iter()
|
||||
.skip(at + 1)
|
||||
.filter_map(|f| {
|
||||
let made: Vec<String> = s.model.result(f.id)?.state.bodies.iter().filter(|x| x.feature == f.id).map(|x| x.name.clone()).collect();
|
||||
(0..made.len()).any(|i| counted(f, i)).then_some((f.id, made))
|
||||
let keep = (counts && (0..made.len()).any(|i| counted(f, i))) || made.iter().any(|n| n.starts_with(&derived));
|
||||
keep.then_some((f.id, made))
|
||||
})
|
||||
.collect(),
|
||||
_ => Vec::new(),
|
||||
None => Vec::new(),
|
||||
};
|
||||
let d = s.doc_mut();
|
||||
for (id, made) in &later {
|
||||
@@ -771,6 +777,26 @@ mod tests {
|
||||
assert_eq!(names(&s)[1], b);
|
||||
}
|
||||
|
||||
/// #3510: each piece of a split is renamed on its own. Renaming the split body leaves the
|
||||
/// other piece, and the features that use it, as they were.
|
||||
#[test]
|
||||
fn split_pieces_are_renamed_on_their_own() {
|
||||
let ok = |s: &Session| s.model.results.iter().all(|r| r.error.is_none());
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "solid.box", json!({"length": 10, "width": 10, "height": 10, "corner": [0, 0, -5], "body_name": "Top"}));
|
||||
run(&mut s, "solid.split_body", json!({"body": "Top", "plane": "XY"}));
|
||||
assert_eq!(names(&s), vec!["Top", "Top (1)"]);
|
||||
run(&mut s, "solid.move", json!({"bodies": ["Top (1)"], "translate": [0, 0, -5]}));
|
||||
run(&mut s, "body.rename", json!({"body": "Top", "name": "Upper"}));
|
||||
assert_eq!(names(&s), vec!["Upper", "Top (1)"]);
|
||||
assert!(ok(&s), "the move still finds the other piece");
|
||||
run(&mut s, "body.rename", json!({"body": "Top (1)", "name": "Lower"}));
|
||||
assert_eq!(names(&s), vec!["Upper", "Lower"]);
|
||||
assert!(ok(&s), "the move follows the renamed piece");
|
||||
run(&mut s, "edit.undo", json!({}));
|
||||
assert_eq!(names(&s), vec!["Upper", "Top (1)"]);
|
||||
}
|
||||
|
||||
/// #1108: a primitive's `targets` are its participants, kept beside its kind; a rename
|
||||
/// follows them, as it follows the names inside the kind (a combine's target and tools).
|
||||
#[test]
|
||||
|
||||
@@ -111,8 +111,9 @@ fn legacy_v9_imported_emboss_anchor_and_ordinary_face_paint_replay_without_renam
|
||||
let mut legacy: Value = serde_json::from_str(&s.doc.to_json()).unwrap();
|
||||
legacy["format"] = json!("solidcraft/9");
|
||||
let reopened = Session::new(solidcraft_doc::Document::from_json(&legacy.to_string()).unwrap());
|
||||
// Saved before format 14, it keeps naming the pieces it makes in the kernel's shell order.
|
||||
let anchored = solidcraft_doc::Feature { shell_piece_order: true, ..anchored };
|
||||
// Saved before format 14, it keeps naming the pieces it makes in the kernel's shell order,
|
||||
// and numbering the pieces of its faces as that build did (#3902).
|
||||
let anchored = solidcraft_doc::Feature { shell_piece_order: true, kernel_piece_order: true, ..anchored };
|
||||
assert_eq!(reopened.doc.feature(anchored.id).unwrap(), &anchored);
|
||||
// Names saved before format 14 keep naming the face they named, marked so they are found as
|
||||
// the build that saved them found them.
|
||||
|
||||
@@ -63,14 +63,30 @@ pub(super) fn face_is_planar(s: &Session, p: Vec3) -> Option<bool> {
|
||||
/// The point is in the frame of the command's other picks: the component's own when
|
||||
/// they were mapped into it, else the world's.
|
||||
pub(super) fn face_ref(s: &Session, v: &Value, body: Option<&str>, cmd: &str) -> Result<Vec3> {
|
||||
match face_ref_form(v).map_err(|why| bad(cmd, why))? {
|
||||
FaceRefForm::Point(p) => Ok(p),
|
||||
FaceRefForm::Name(name) => {
|
||||
let st = if crate::frames::mapped() { s.model.state() } else { s.world_state() };
|
||||
solidcraft_doc::naming::named_face_point(&st, body, name).map_err(|why| bad(cmd, why))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A face reference as given, before it is looked up: see [`face_ref`].
|
||||
pub(crate) enum FaceRefForm<'a> {
|
||||
Point(Vec3),
|
||||
Name(&'a str),
|
||||
}
|
||||
|
||||
/// What [`face_ref`] reads `v` as, without the design: a point, or a name of 1…512 bytes.
|
||||
pub(crate) fn face_ref_form(v: &Value) -> std::result::Result<FaceRefForm<'_>, &'static str> {
|
||||
if let Some(name) = v.as_str() {
|
||||
if name.is_empty() || name.len() > 512 {
|
||||
return Err(bad(cmd, "a face name is 1…512 characters"));
|
||||
return Err("a face name is 1…512 characters");
|
||||
}
|
||||
let st = if crate::frames::mapped() { s.model.state() } else { s.world_state() };
|
||||
return solidcraft_doc::naming::named_face_point(&st, body, name).map_err(|why| bad(cmd, why));
|
||||
return Ok(FaceRefForm::Name(name));
|
||||
}
|
||||
vec3(v).or_else(|| v.get("point").and_then(vec3)).ok_or_else(|| bad(cmd, "a face is a point [x, y, z] on it, or its name from model.faces"))
|
||||
vec3(v).or_else(|| v.get("point").and_then(vec3)).map(FaceRefForm::Point).ok_or("a face is a point [x, y, z] on it, or its name from model.faces")
|
||||
}
|
||||
|
||||
/// The body face under a point: (mesh, face index, distance, body).
|
||||
|
||||
@@ -11,7 +11,8 @@ use solidcraft_geom::Vec3;
|
||||
|
||||
use super::{CommandSpec, not_in_sketch};
|
||||
use crate::params::{bad, bool_, expr, req_expr, str_, string_list, vec2, vec3};
|
||||
use crate::schema::{BoxParams, ExtrudeParams, FeatureResult, TypedContract, output_schema_for};
|
||||
use crate::schema::features::{BoxParams, ExtrudeParams, FeatureResult};
|
||||
use crate::schema::{TypedContract, output_schema_for};
|
||||
use crate::{EngineError, Result, Session};
|
||||
|
||||
const BOX_CONTRACT: TypedContract<BoxParams> = TypedContract::new();
|
||||
@@ -67,7 +68,7 @@ pub static COMMANDS: &[CommandSpec] = &[
|
||||
.at("SOLID", "CREATE")
|
||||
.icon("hole")
|
||||
.key("H")
|
||||
.params("position: [x,y,z] on a face (numbers, or length expressions the hole follows) | sketch + points: [sketch point ids] (one hole each, perpendicular to the sketch, into the material on whichever side of the sketch plane it is) | sketch alone (every sketch point: standalone points and circle centres); flip?: bool (sketch points: drill to the other side); single?: bool (with one sketch point: a Single hole that follows the point, drilled into the face there); direction?: [x,y,z] (default: into the face, against its outward normal; one that leaves the face is mirrored into it; ignored with sketch points unless single); diameter | thread (a tapped hole: the tap drill, major − pitch) | clearance: \"M6\" with fit?: close|normal|loose (ISO 273); depth? (default through all) | to?: [x,y,z] (To Object: drill to that face or point); type?: simple|drilled|counterbore|countersink; tip_angle?; cb_diameter?, cb_depth?; cs_diameter?, cs_angle?; thread?: \"M6\" (cosmetic)"),
|
||||
.params("position: [x,y,z] on a face (numbers, or length expressions the hole follows) | sketch + points: [sketch point ids] (one hole each, perpendicular to the sketch, into the material on whichever side of the sketch plane it is) | sketch alone (every sketch point: standalone points and circle centres); flip?: bool (sketch points: drill to the other side); single?: bool (with one sketch point: a Single hole that follows the point, drilled into the face there); direction?: [x,y,z] (default: into the face, against its outward normal; one that leaves the face is mirrored into it; ignored with sketch points unless single); diameter | thread (a tapped hole: the tap drill, major − pitch) | clearance: \"M6\" with fit?: close|normal|loose (ISO 273); depth? (default through all) | to?: [x,y,z] (To Object: drill to that face or point); type?: simple|drilled|counterbore|countersink; tip_angle?; cb_diameter?, cb_depth?; cs_diameter?, cs_angle?; thread?: \"M6\" (cosmetic); targets?: [body] (the bodies to drill; holes at the points of a sketch on a body's face default to that body, found along the holes' direction on it alone; explicit targets or participants override, [\"*\"] every body)"),
|
||||
CommandSpec::new("solid.pipe", "Pipe", pipe)
|
||||
.at("SOLID", "CREATE")
|
||||
.icon("pipe")
|
||||
@@ -91,7 +92,7 @@ pub static COMMANDS: &[CommandSpec] = &[
|
||||
CommandSpec::new("solid.thread", "Thread", thread)
|
||||
.at("SOLID", "CREATE")
|
||||
.icon("thread")
|
||||
.params("face: [x,y,z] on a cylindrical face; designation?: ISO metric (\"M8\", \"M8x1\"; default: the size that fits); length?: expr (default: the whole face); modeled?: bool (default false). Cosmetic: the model is unchanged, model.threads lists it. Modeled: cuts the thread profile into the body"),
|
||||
.params("face: [x,y,z] on a cylindrical face; designation?: ISO metric (\"M8\", \"M8x1\"; default: the size that fits); length?: expr (default: the whole face); modeled?: bool (default false). Cosmetic: the model is unchanged, model.threads lists it. Modeled: cuts the thread profile into the body; a shaft thicker than the thread is first turned down to its major diameter over the threaded length"),
|
||||
CommandSpec::new("solid.shell", "Shell", shell)
|
||||
.at("SOLID", "MODIFY")
|
||||
.icon("shell")
|
||||
@@ -181,6 +182,16 @@ pub fn auto_operation(s: &Session, p: &Value) -> Option<&'static str> {
|
||||
auto_operation_in(s, p, true)
|
||||
}
|
||||
|
||||
impl Session {
|
||||
/// Whether a cut or intersect that names no bodies takes part in every body of `st`: the
|
||||
/// active component has no body of its own while the model has some (a sketch in the
|
||||
/// assembly's root cutting through its parts, #1234). The command and its preview both ask
|
||||
/// this, so the preview never refuses a cut the command makes (#3085).
|
||||
pub fn cut_takes_every_body(&self, st: &solidcraft_doc::ModelState) -> bool {
|
||||
!st.bodies.is_empty() && !st.bodies.iter().any(|b| self.doc.model_body_component(b) == self.active_component)
|
||||
}
|
||||
}
|
||||
|
||||
fn auto_operation_in(s: &Session, p: &Value, world_faces: bool) -> Option<&'static str> {
|
||||
let symmetric = p.get("to").is_none() && str_(p, "direction").is_some_and(|d| d.eq_ignore_ascii_case("symmetric"));
|
||||
// Imported face groups can belong to a child of an empty active component.
|
||||
@@ -204,13 +215,16 @@ fn auto_operation_in(s: &Session, p: &Value, world_faces: bool) -> Option<&'stat
|
||||
// Match the evaluator's implicit participants: a grouped cut from an empty
|
||||
// assembly component considers every body; otherwise keep the active scope.
|
||||
// Explicit participants override either default.
|
||||
// A component with no body of its own works on those of the components inside it (#3223).
|
||||
let parts = string_list(p, "participants");
|
||||
let implicit_all = grouped.is_some() && !st.bodies.iter().any(|body| s.doc.model_body_component(body) == s.active_component);
|
||||
let bodies: Vec<_> = st
|
||||
.bodies
|
||||
.iter()
|
||||
.filter(|b| if parts.is_empty() { implicit_all || s.doc.model_body_component(b) == s.active_component } else { parts.contains(&b.name) })
|
||||
.collect();
|
||||
let bodies: Vec<_> = if !parts.is_empty() {
|
||||
st.bodies.iter().filter(|b| parts.contains(&b.name)).collect()
|
||||
} else if implicit_all {
|
||||
st.bodies.iter().collect()
|
||||
} else {
|
||||
active_scope(s, &st)
|
||||
};
|
||||
if bodies.is_empty() {
|
||||
return Some("new");
|
||||
}
|
||||
@@ -241,11 +255,7 @@ pub(super) fn operation(p: &Value, cmd: &str) -> Result<Operation> {
|
||||
/// The sketch a feature uses: `sketch` param, the active sketch, or the last sketch.
|
||||
pub(super) fn feature_sketch(s: &Session, p: &Value, cmd: &str) -> Result<u64> {
|
||||
if let Some(v) = p.get("sketch") {
|
||||
let key = match v {
|
||||
Value::Number(n) => n.to_string(),
|
||||
Value::String(x) => x.clone(),
|
||||
_ => return Err(bad(cmd, "`sketch` must be an id or name")),
|
||||
};
|
||||
let key = sketch_key(Some(v)).ok_or_else(|| bad(cmd, "`sketch` must be an id or name"))?;
|
||||
let f = s.doc.find_feature(&key).ok_or_else(|| bad(cmd, format!("no sketch `{key}`")))?;
|
||||
return match f.kind {
|
||||
FeatureKind::Sketch { .. } => Ok(f.id),
|
||||
@@ -497,15 +507,14 @@ fn add_feature_in(s: &mut Session, p: &Value, kind: FeatureKind, world: bool) ->
|
||||
}
|
||||
// A cut or intersect made where the active component has no body of its own (a sketch in
|
||||
// the assembly's root, cutting through the parts, #1234) would have nothing to work on: it
|
||||
// takes part in every body instead, which it changes only where it meets them.
|
||||
// takes part in every body instead, which it changes only where it meets them. An extrude's
|
||||
// own operation counts too: an automatic one that came out as a cut (#3223).
|
||||
let participants = if participants.is_empty()
|
||||
&& !primitive
|
||||
&& (matches!(str_(p, "operation").and_then(Operation::parse), Some(Operation::Cut | Operation::Intersect))
|
||||
|| p.get("faces").is_some() && matches!(&kind, FeatureKind::Extrude { operation: Operation::Cut | Operation::Intersect, .. }))
|
||||
&& {
|
||||
let st = s.model.state();
|
||||
!st.bodies.is_empty() && !st.bodies.iter().any(|b| s.doc.model_body_component(b) == s.active_component)
|
||||
} {
|
||||
|| matches!(&kind, FeatureKind::Extrude { operation: Operation::Cut | Operation::Intersect, .. }))
|
||||
&& s.cut_takes_every_body(&s.model.state())
|
||||
{
|
||||
vec!["*".to_string()]
|
||||
} else {
|
||||
participants
|
||||
@@ -655,14 +664,40 @@ fn extrude(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
}
|
||||
|
||||
/// The body whose face `sketch` is attached to (on the face itself, not offset from it), when
|
||||
/// it is a body of the active component.
|
||||
/// it is one the active component works on ([`active_scope`]).
|
||||
fn sketch_face_body(s: &Session, sketch: u64) -> Option<String> {
|
||||
let FeatureKind::Sketch { plane: solidcraft_doc::PlaneRef::Face { anchor: Some(anchor), .. }, .. } = &s.doc.feature(sketch)?.kind else {
|
||||
return None;
|
||||
};
|
||||
let st = s.model.state();
|
||||
let body = st.body(&anchor.target.body)?;
|
||||
(s.doc.model_body_component(body) == s.active_component).then(|| body.name.clone())
|
||||
active_scope(s, &st).into_iter().find(|b| b.name == anchor.target.body).map(|b| b.name.clone())
|
||||
}
|
||||
|
||||
/// The bodies a feature of the active component works on when it names none: the component's
|
||||
/// own. A component with no body of its own (the root of an opened STEP assembly, whose parts
|
||||
/// are components inside it) works on the bodies of the components inside it whose geometry is
|
||||
/// in its own frame, as an import places it: a profile sketched on such a part's face meets the
|
||||
/// part where it is shown (#3223).
|
||||
fn active_scope<'a>(s: &Session, st: &'a solidcraft_doc::ModelState) -> Vec<&'a solidcraft_doc::ModelBody> {
|
||||
let active = s.active_component;
|
||||
let own: Vec<_> = st.bodies.iter().filter(|b| s.doc.model_body_component(b) == active).collect();
|
||||
if !own.is_empty() {
|
||||
return own;
|
||||
}
|
||||
let Some(into_active) = solidcraft_doc::mat_inverse(&s.doc.component_transform(active)) else { return own };
|
||||
st.bodies
|
||||
.iter()
|
||||
.filter(|b| {
|
||||
let comp = s.doc.model_body_component(b);
|
||||
if !s.doc.component_within(comp, active) {
|
||||
return false;
|
||||
}
|
||||
let frame = s.doc.component_transform(comp);
|
||||
let world = s.doc.model_body_offset(b).map_or(frame, |offset| solidcraft_doc::mat_mul(&frame, offset));
|
||||
let rel = solidcraft_doc::mat_mul(&into_active, &world);
|
||||
rel.iter().flatten().zip(solidcraft_doc::IDENTITY.iter().flatten()).all(|(a, b)| (a - b).abs() < 1e-9)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// A thin extrude's wall from its parameters: the sketch's curves it follows (every drawn
|
||||
@@ -970,45 +1005,64 @@ fn edge_points(s: &Session, p: &Value, cmd: &str) -> Result<Vec<Vec3>> {
|
||||
let mut candidates: Option<Vec<(f64, Vec<solidcraft_kernel::EdgeInfo>)>> = None;
|
||||
let mut out = Vec::new();
|
||||
for e in a {
|
||||
if let Some(pt) = vec3(e).or_else(|| e.get("point").and_then(vec3)) {
|
||||
let bodies = candidates.get_or_insert_with(|| {
|
||||
// Mesh bodies have no edges to pick: rounding one is refused later, saying why.
|
||||
st.bodies
|
||||
.iter()
|
||||
.filter(|b| named.is_none_or(|n| b.name == n) && !b.body.is_mesh())
|
||||
.filter_map(|b| {
|
||||
let size = b.body.size();
|
||||
b.body.edges((size * 2e-3).max(1e-3)).ok().map(|edges| (size, edges))
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
let mut nearest = f64::INFINITY;
|
||||
let on_edge = bodies.iter().any(|(size, edges)| {
|
||||
let d = edges
|
||||
.iter()
|
||||
.flat_map(|e| e.points.windows(2))
|
||||
.filter_map(|w| Some(pt.dist_to_segment(*w.first()?, *w.get(1)?)))
|
||||
.fold(f64::INFINITY, f64::min);
|
||||
nearest = nearest.min(d);
|
||||
d <= size * 0.05 + 1e-3
|
||||
});
|
||||
if !on_edge && !bodies.is_empty() {
|
||||
let far = if nearest.is_finite() { format!("the nearest edge is {nearest:.3} mm away") } else { "there is no edge".to_string() };
|
||||
return Err(bad(cmd, format!("no edge at [{}, {}, {}]: {far}", pt.x, pt.y, pt.z)));
|
||||
let pt = match edge_ref_form(e).map_err(|why| bad(cmd, why))? {
|
||||
EdgeRefForm::Point(pt) => pt,
|
||||
EdgeRefForm::Index { body, index } => {
|
||||
let idx = index as usize;
|
||||
let b = st.body(body).ok_or_else(|| bad(cmd, format!("no body `{body}`")))?;
|
||||
let tol = (b.body.size() * 2e-3).max(1e-3);
|
||||
let edges = b.body.edges(tol)?;
|
||||
out.push(edges.get(idx).ok_or_else(|| bad(cmd, format!("{body} has no edge {idx}")))?.mid);
|
||||
continue;
|
||||
}
|
||||
out.push(pt);
|
||||
continue;
|
||||
};
|
||||
let bodies = candidates.get_or_insert_with(|| {
|
||||
// Mesh bodies have no edges to pick: rounding one is refused later, saying why.
|
||||
st.bodies
|
||||
.iter()
|
||||
.filter(|b| named.is_none_or(|n| b.name == n) && !b.body.is_mesh())
|
||||
.filter_map(|b| {
|
||||
let size = b.body.size();
|
||||
b.body.edges((size * 2e-3).max(1e-3)).ok().map(|edges| (size, edges))
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
let mut nearest = f64::INFINITY;
|
||||
let on_edge = bodies.iter().any(|(size, edges)| {
|
||||
let d = edges
|
||||
.iter()
|
||||
.flat_map(|e| e.points.windows(2))
|
||||
.filter_map(|w| Some(pt.dist_to_segment(*w.first()?, *w.get(1)?)))
|
||||
.fold(f64::INFINITY, f64::min);
|
||||
nearest = nearest.min(d);
|
||||
d <= size * 0.05 + 1e-3
|
||||
});
|
||||
if !on_edge && !bodies.is_empty() {
|
||||
let far = if nearest.is_finite() { format!("the nearest edge is {nearest:.3} mm away") } else { "there is no edge".to_string() };
|
||||
return Err(bad(cmd, format!("no edge at [{}, {}, {}]: {far}", pt.x, pt.y, pt.z)));
|
||||
}
|
||||
let body = e.get("body").and_then(Value::as_str).ok_or_else(|| bad(cmd, "an edge is a point [x,y,z], {point} or {body, index}"))?;
|
||||
let idx = e.get("index").and_then(Value::as_u64).ok_or_else(|| bad(cmd, "edge `index` must be a number"))? as usize;
|
||||
let b = st.body(body).ok_or_else(|| bad(cmd, format!("no body `{body}`")))?;
|
||||
let tol = (b.body.size() * 2e-3).max(1e-3);
|
||||
let edges = b.body.edges(tol)?;
|
||||
out.push(edges.get(idx).ok_or_else(|| bad(cmd, format!("{body} has no edge {idx}")))?.mid);
|
||||
out.push(pt);
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// An edge reference as given, before it is looked up: see [`edge_points`].
|
||||
pub(crate) enum EdgeRefForm<'a> {
|
||||
Point(Vec3),
|
||||
Index { body: &'a str, index: u64 },
|
||||
}
|
||||
|
||||
/// What [`edge_points`] reads one edge as, without the design: a point on it (`[x, y, z]` or
|
||||
/// `{point}`), else `{body, index}`.
|
||||
pub(crate) fn edge_ref_form(e: &Value) -> std::result::Result<EdgeRefForm<'_>, &'static str> {
|
||||
if let Some(pt) = vec3(e).or_else(|| e.get("point").and_then(vec3)) {
|
||||
return Ok(EdgeRefForm::Point(pt));
|
||||
}
|
||||
let body = e.get("body").and_then(Value::as_str).ok_or("an edge is a point [x,y,z], {point} or {body, index}")?;
|
||||
let index = e.get("index").and_then(Value::as_u64).ok_or("edge `index` must be a number")?;
|
||||
Ok(EdgeRefForm::Index { body, index })
|
||||
}
|
||||
|
||||
/// Press Pull: a face moves along its normal (an extrude that joins outward or cuts inward); an
|
||||
/// edge is rounded (a fillet).
|
||||
fn press_pull(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
@@ -1435,22 +1489,26 @@ fn mirror(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
|
||||
/// Bodies picked where the viewport shows them: each world name ("Body1", or "Body1 (Occ:2)"
|
||||
/// for a further instance) becomes its component's body name, with the occurrence path of the
|
||||
/// placement it was picked at. Names stay as they are (and no placement is kept) while every
|
||||
/// component is in place, or when one of them is not a body.
|
||||
/// placement it was picked at (where the copy goes). Names stay as they are, and no placement is
|
||||
/// kept, while every body is in the active component and every component in place, or when one
|
||||
/// of them is not a body.
|
||||
fn placed_bodies(s: &Session, names: Vec<String>, cmd: &str) -> Result<(Vec<String>, Vec<Vec<u64>>)> {
|
||||
if names.is_empty() || !crate::frames::placed(s) {
|
||||
if names.is_empty() || s.doc.occurrences.is_empty() {
|
||||
return Ok((names, Vec::new()));
|
||||
}
|
||||
let frames = s.doc.body_frames_with_source(&s.model.state());
|
||||
let mut picked = Vec::with_capacity(names.len());
|
||||
for name in &names {
|
||||
let Some((_, own, _, path, _)) = frames.iter().find(|(world, ..)| world == name) else { return Ok((names, Vec::new())) };
|
||||
if path.iter().any(|id| s.pending_moves.contains_key(id)) {
|
||||
return Err(bad(cmd, format!("`{name}` has a position that is not captured yet: Capture Position (component.capture_position) first")));
|
||||
}
|
||||
picked.push((own.clone(), path.clone()));
|
||||
let Some((_, own, component, path, _)) = frames.iter().find(|(world, ..)| world == name) else { return Ok((names, Vec::new())) };
|
||||
picked.push((own.clone(), *component, path.clone()));
|
||||
}
|
||||
Ok(picked.into_iter().unzip())
|
||||
if !crate::frames::placed(s) && picked.iter().all(|(_, component, _)| *component == s.active_component) {
|
||||
return Ok((names, Vec::new()));
|
||||
}
|
||||
if let Some((name, ..)) = picked.iter().find(|(_, _, path)| path.iter().any(|id| s.pending_moves.contains_key(id))) {
|
||||
return Err(bad(cmd, format!("`{name}` has a position that is not captured yet: Capture Position (component.capture_position) first")));
|
||||
}
|
||||
Ok(picked.into_iter().map(|(own, _, path)| (own, path)).unzip())
|
||||
}
|
||||
|
||||
/// The other name of an origin plane (`Top` for `XY`, `XY` for `Top`...), if `name` is one.
|
||||
@@ -1807,15 +1865,25 @@ fn hole(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
Some(v) => Some(vec3(v).ok_or_else(|| bad(cmd, "`to` must be a point [x, y, z] on the face to drill to"))?),
|
||||
None => None,
|
||||
};
|
||||
// A hole picked on a body of another component (a moved one, shown where it is placed) cuts
|
||||
// that body: the pick is already in that body's frame, and the component's own bodies alone
|
||||
// would leave nothing to cut.
|
||||
let mut p = p.clone();
|
||||
if points.is_none()
|
||||
&& p.get("participants").is_none()
|
||||
&& let Some(body) = body_of_other_component_at(s, position)
|
||||
{
|
||||
p["participants"] = json!([body]);
|
||||
let targets = string_list(&p, "targets");
|
||||
if p.get("participants").is_none() {
|
||||
// The bodies to drill, as `targets` (like an extrude's): its participants.
|
||||
if !targets.is_empty() {
|
||||
p["participants"] = json!(targets);
|
||||
} else if let Some(body) = points.as_ref().filter(|(sp, _, _)| !sp.single).and_then(|(sp, _, _)| sketch_face_body(s, sp.sketch.0)) {
|
||||
// Holes at the points of a sketch on a body's face drill that body (#2882): a body
|
||||
// further along, past the face, doesn't take them. Stored, so recomputes and
|
||||
// reopening keep the same scope.
|
||||
p["participants"] = json!([body]);
|
||||
} else if points.is_none()
|
||||
&& let Some(body) = body_of_other_component_at(s, position)
|
||||
{
|
||||
// A hole picked on a body of another component (a moved one, shown where it is
|
||||
// placed) cuts that body: the pick is already in that body's frame, and the
|
||||
// component's own bodies alone would leave nothing to cut.
|
||||
p["participants"] = json!([body]);
|
||||
}
|
||||
}
|
||||
add_feature(
|
||||
s,
|
||||
@@ -1920,17 +1988,22 @@ fn draft(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
}
|
||||
|
||||
pub(super) fn sketch_id(s: &Session, v: Option<&Value>, cmd: &str, what: &str) -> Result<u64> {
|
||||
let key = match v {
|
||||
Some(Value::Number(n)) => n.to_string(),
|
||||
Some(Value::String(x)) => x.clone(),
|
||||
_ => return Err(bad(cmd, format!("`{what}` must be a sketch id or name"))),
|
||||
};
|
||||
let key = sketch_key(v).ok_or_else(|| bad(cmd, format!("`{what}` must be a sketch id or name")))?;
|
||||
match s.doc.find_feature(&key) {
|
||||
Some(f) if matches!(f.kind, FeatureKind::Sketch { .. }) => Ok(f.id),
|
||||
_ => Err(bad(cmd, format!("no sketch `{key}`"))),
|
||||
}
|
||||
}
|
||||
|
||||
/// The key [`sketch_id`] and [`feature_sketch`] look a sketch up by: an id (a number) or a name.
|
||||
pub(crate) fn sketch_key(v: Option<&Value>) -> Option<String> {
|
||||
match v {
|
||||
Some(Value::Number(n)) => Some(n.to_string()),
|
||||
Some(Value::String(x)) => Some(x.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn sweep(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let cmd = "solid.sweep";
|
||||
let sketch = feature_sketch(s, p, cmd)?;
|
||||
|
||||
@@ -41,7 +41,7 @@ pub static COMMANDS: &[CommandSpec] = &[
|
||||
CommandSpec::new("solid.align", "Align", align)
|
||||
.at("SOLID", "MODIFY")
|
||||
.icon("align")
|
||||
.params("bodies: [names]; from: [x,y,z] | {snap: vertex|edge_mid|face_center|circle_center, at: [x,y,z]} | from_face: [x,y,z]; to: (the same) | to_face: [x,y,z] (faces also turn to meet); flip?: bool"),
|
||||
.params("bodies: [names]; from: [x,y,z] | \"origin\" | {snap: vertex|edge_mid|face_center|circle_center, at: [x,y,z]} | from_face: [x,y,z] | from_plane: XY|XZ|YZ|construction or offset plane name or id|{origin, normal}; to: (the same) | to_face: [x,y,z] | to_plane: (a plane as from_plane; the point or face goes onto it along its normal, and the plane may move later: the bodies follow); faces and planes also turn to meet; flip?: bool"),
|
||||
CommandSpec::new("solid.remove", "Remove", remove).at("SOLID", "MODIFY").icon("delete").params("bodies: [names] (removed from here on in the timeline)"),
|
||||
];
|
||||
|
||||
@@ -385,23 +385,46 @@ fn align(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
return Err(bad(cmd, format!("no body `{b}`")));
|
||||
}
|
||||
}
|
||||
let pick = |s: &Session, pk: &str, fk: &str| -> Result<(Vec3, Option<Vec3>)> {
|
||||
// A side is a plane (`from_plane`/`to_plane`: XY|XZ|YZ, a construction or offset plane, or
|
||||
// {origin, normal}), kept as a plane so the bodies follow it when it moves; or a point and, for
|
||||
// a face, its normal.
|
||||
let plane = |s: &Session, key: &str| -> Result<Option<solidcraft_doc::PlaneRef>> {
|
||||
p.get(key).map(|_| super::features::plane_param(s, p.get(key), cmd)).transpose()
|
||||
};
|
||||
let (from_plane, to_plane) = (plane(s, "from_plane")?, plane(s, "to_plane")?);
|
||||
let pick = |s: &Session, pk: &str, fk: &str, plane: &Option<solidcraft_doc::PlaneRef>| -> Result<(Vec3, Option<Vec3>)> {
|
||||
if plane.is_some() {
|
||||
if p.get(pk).is_some() || p.get(fk).is_some() {
|
||||
return Err(bad(cmd, format!("give `{pk}_plane` or `{pk}`/`{fk}`, not both")));
|
||||
}
|
||||
// Worked out from the plane when the feature is evaluated.
|
||||
return Ok((Vec3::ZERO, None));
|
||||
}
|
||||
if let Some(fp) = p.get(fk).and_then(vec3) {
|
||||
let n = super::face::face_normal(s, fp).ok_or_else(|| bad(cmd, format!("`{fk}` must be on a planar face")))?;
|
||||
return Ok((fp, Some(n)));
|
||||
}
|
||||
// The origin point.
|
||||
if p.get(pk).and_then(Value::as_str).is_some_and(|o| o.eq_ignore_ascii_case("origin")) {
|
||||
return Ok((Vec3::ZERO, None));
|
||||
}
|
||||
// A snap point: {snap: vertex|edge_mid|face_center|circle_center, at: [x,y,z]}.
|
||||
if let Some(o) = p.get(pk).filter(|v| v.is_object()) {
|
||||
let at = o.get("at").and_then(vec3).ok_or_else(|| bad(cmd, format!("`{pk}.at` must be [x, y, z]")))?;
|
||||
let kind = o.get("snap").and_then(Value::as_str).unwrap_or("vertex");
|
||||
return snap_point(s, kind, at).map(|q| (q, None)).ok_or_else(|| bad(cmd, format!("no {kind} to snap to near `{pk}`")));
|
||||
}
|
||||
p.get(pk).and_then(vec3).map(|v| (v, None)).ok_or_else(|| bad(cmd, format!("give `{pk}` or `{fk}` as [x, y, z]")))
|
||||
p.get(pk)
|
||||
.and_then(vec3)
|
||||
.map(|v| (v, None))
|
||||
.ok_or_else(|| bad(cmd, format!("give `{pk}`, `{fk}` or `{pk}_plane` ([x, y, z], \"origin\", or a plane)")))
|
||||
};
|
||||
let (from, from_normal) = pick(s, "from", "from_face")?;
|
||||
let (to, to_normal) = pick(s, "to", "to_face")?;
|
||||
let (from, from_normal) = pick(s, "from", "from_face", &from_plane)?;
|
||||
let (to, to_normal) = pick(s, "to", "to_face", &to_plane)?;
|
||||
// Faces turn only to meet another face or a plane; a plane's normal is read when evaluated.
|
||||
let (from_normal, to_normal) = match (from_normal, to_normal) {
|
||||
(Some(a), Some(b)) => (Some(a), Some(b)),
|
||||
(a, b) if from_plane.is_some() || to_plane.is_some() => (a, b),
|
||||
_ => (None, None),
|
||||
};
|
||||
let flip = bool_(p, "flip").unwrap_or(false);
|
||||
@@ -415,6 +438,8 @@ fn align(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
from_normal: from_normal.map(Dir),
|
||||
to_normal: to_normal.map(Dir),
|
||||
flip,
|
||||
from_plane,
|
||||
to_plane,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -540,6 +540,57 @@ fn replace_face_align_remove() {
|
||||
assert_eq!(measure(&mut s)["body_count"], 1);
|
||||
}
|
||||
|
||||
/// The z range and the x minimum of body `name`.
|
||||
fn z_x(s: &mut Session, name: &str) -> (f64, f64, f64) {
|
||||
let m = measure(s);
|
||||
let b = m["bodies"].as_array().unwrap().iter().find(|b| b["name"] == name).unwrap().clone();
|
||||
let g = |k: &str, i: usize| b["bbox"][k][i].as_f64().unwrap_or(f64::NAN);
|
||||
(g("min", 2), g("max", 2), g("min", 0))
|
||||
}
|
||||
|
||||
/// Align onto planes and the origin: a face goes flush onto an origin plane (turning to meet it),
|
||||
/// onto an offset plane, a vertex onto the origin point, and a plane the bodies are aligned to
|
||||
/// is followed when it moves.
|
||||
#[test]
|
||||
fn align_takes_planes_and_the_origin() {
|
||||
let boxed = || {
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "solid.box", json!({"length": 10, "width": 10, "height": 10, "corner": [50, 0, 20], "body_name": "C"}));
|
||||
s
|
||||
};
|
||||
// The bottom face onto XY: it already faces down, so it only drops; nothing slides across.
|
||||
let mut s = boxed();
|
||||
run(&mut s, "solid.align", json!({"bodies": ["C"], "from_face": [55, 5, 20], "to_plane": "XY"}));
|
||||
let (lo, hi, x) = z_x(&mut s, "C");
|
||||
assert!(lo.abs() < 1e-6 && (hi - 10.0).abs() < 1e-6 && (x - 50.0).abs() < 1e-6, "{lo} {hi} {x}");
|
||||
// The top face onto XY: the body turns over (the face now faces down) and sits on the plane.
|
||||
let mut s = boxed();
|
||||
run(&mut s, "solid.align", json!({"bodies": ["C"], "from_face": [55, 5, 30], "to_plane": "XY"}));
|
||||
let (lo, hi, _) = z_x(&mut s, "C");
|
||||
assert!(lo.abs() < 1e-6 && (hi - 10.0).abs() < 1e-6, "{lo} {hi}");
|
||||
assert!(measure(&mut s)["bodies"][0]["bbox"]["max"][2].as_f64().is_some());
|
||||
// A vertex onto the origin point.
|
||||
let mut s = boxed();
|
||||
run(&mut s, "solid.align", json!({"bodies": ["C"], "from": {"snap": "vertex", "at": [50, 0, 20]}, "to": "origin"}));
|
||||
let (lo, _, x) = z_x(&mut s, "C");
|
||||
assert!(lo.abs() < 1e-6 && x.abs() < 1e-6, "{lo} {x}");
|
||||
// Onto an offset plane, and the bodies follow it when its distance changes.
|
||||
let mut s = boxed();
|
||||
run(&mut s, "parameters.add", json!({"name": "lift", "expression": "30 mm"}));
|
||||
run(&mut s, "construct.plane.offset", json!({"base": "XY", "offset": "lift", "name": "Shelf"}));
|
||||
run(&mut s, "solid.align", json!({"bodies": ["C"], "from_face": [55, 5, 20], "to_plane": "Shelf"}));
|
||||
assert!((z_x(&mut s, "C").0 - 30.0).abs() < 1e-6);
|
||||
run(&mut s, "parameters.change", json!({"name": "lift", "expression": "45 mm"}));
|
||||
assert!((z_x(&mut s, "C").0 - 45.0).abs() < 1e-6, "the bodies follow the plane");
|
||||
// A plane as the source: the bodies move with it onto a point.
|
||||
let mut s = boxed();
|
||||
run(&mut s, "solid.align", json!({"bodies": ["C"], "from_plane": "XY", "to": [0, 0, 7]}));
|
||||
assert!((z_x(&mut s, "C").0 - 27.0).abs() < 1e-6);
|
||||
// One side is a plane or a point, not both; an unknown plane is refused.
|
||||
assert!(s.execute("solid.align", &json!({"bodies": ["C"], "from_plane": "XY", "from": [0, 0, 0], "to": [0, 0, 7]})).is_err());
|
||||
assert!(s.execute("solid.align", &json!({"bodies": ["C"], "from": [0, 0, 0], "to_plane": "Nowhere"})).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coil_and_loft_to_a_point() {
|
||||
use std::f64::consts::{PI, TAU};
|
||||
|
||||
@@ -20,11 +20,11 @@ pub static COMMANDS: &[CommandSpec] = &[
|
||||
.icon("open")
|
||||
.key("Ctrl+O")
|
||||
.noundo()
|
||||
.params("path: .solidcraft design, or a .step/.stp/.igs/.fcstd/.3dm/.3mf/.stl/.obj/.glb/.gltf file (STEP, IGES, FreeCAD, Rhino, 3MF, STL, OBJ or glTF; opens as a new design holding the file as a component named after it, as file.insert_step and file.insert_mesh make); up?: z|y for a STEP, IGES, FreeCAD, Rhino or glTF file (see file.insert_step and file.insert_mesh); names?: solid|product|instance for a STEP, IGES, FreeCAD or Rhino file (see file.insert_step)"),
|
||||
.params("path: .solidcraft design, or a .step/.stp/.igs/.fcstd/.3dm/.3mf/.stl/.obj/.glb/.gltf file (STEP, IGES, FreeCAD, Rhino, 3MF, STL, OBJ or glTF; opens as a new design holding the file as a component named after it, as file.insert_step and file.insert_mesh make); up?: z|y for a STEP, IGES, FreeCAD, Rhino or glTF file (see file.insert_step and file.insert_mesh); names?: auto|solid|product|instance for a STEP, IGES, FreeCAD or Rhino file (see file.insert_step)"),
|
||||
CommandSpec::new("file.insert_step", "Insert STEP", insert_step)
|
||||
.at("SOLID", "INSERT")
|
||||
.icon("import")
|
||||
.params("path: STEP, IGES, FreeCAD .FCStd or Rhino .3dm file (its bodies join the design as an Import base feature in a new component named after the file, placed in the active component; an assembly's products become nested components, a repeated part one component with an occurrence per placement, all held as one rigid group until component.unlock → component, component_name, occurrence, subcomponents?, rigid_joints?); name? (the feature's and the component's); up?: z|y (default z: the file's axes are kept; y: the file has Y up, so the import's component is turned a quarter turn about X, Y becoming Z → stand_up: its occurrence); names?: solid|product|instance (what the bodies are named after: the solid's own name, else the product's (default); the name of the product (part) that holds the body; the name of the assembly occurrence (instance), else the product's); matrix?: [12 or 16 numbers] (where the new component's occurrence sits in the active component, after `up`: a rigid row-major 3x4 rotation-and-translation or 4x4 matrix, flat or as rows, as occurrence.move takes it; a scale, shear or mirror is refused before the file is read); tolerance? (see occurrence.move)"),
|
||||
.params("path: STEP, IGES, FreeCAD .FCStd or Rhino .3dm file (its bodies join the design as an Import base feature in a new component named after the file, placed in the active component; an assembly's products become nested components, a repeated part one component with an occurrence per placement, all held as one rigid group until component.unlock → component, component_name, occurrence, subcomponents?, rigid_joints?); name? (the feature's and the component's); up?: z|y (default z: the file's axes are kept; y: the file has Y up, so the import's component is turned a quarter turn about X, Y becoming Z → stand_up: its occurrence); names?: auto|solid|product|instance (default auto: keep meaningful solid names, replace placeholders with the nearest named product, else instance; solid: the solid's own name; product: the part's name; instance: the occurrence's name, else the product's); matrix?: [12 or 16 numbers] (where the new component's occurrence sits in the active component, after `up`: a rigid row-major 3x4 rotation-and-translation or 4x4 matrix, flat or as rows, as occurrence.move takes it; a scale, shear or mirror is refused before the file is read); tolerance? (see occurrence.move)"),
|
||||
CommandSpec::new("file.insert_mesh", "Insert Mesh", insert_mesh)
|
||||
.at("SOLID", "INSERT")
|
||||
.icon("import")
|
||||
@@ -89,9 +89,10 @@ fn step_options(p: &Value, cmd: &str) -> Result<StepOptions> {
|
||||
};
|
||||
let names = match p.get("names") {
|
||||
None | Some(Value::Null) => solidcraft_io::StepNames::default(),
|
||||
Some(v) => {
|
||||
v.as_str().and_then(solidcraft_io::StepNames::parse).ok_or_else(|| bad(cmd, "`names` must be \"solid\", \"product\" or \"instance\""))?
|
||||
}
|
||||
Some(v) => v
|
||||
.as_str()
|
||||
.and_then(solidcraft_io::StepNames::parse)
|
||||
.ok_or_else(|| bad(cmd, "`names` must be \"auto\", \"solid\", \"product\" or \"instance\""))?,
|
||||
};
|
||||
Ok(StepOptions { y_up, names })
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ pub static COMMANDS: &[CommandSpec] = &[
|
||||
.noundo()
|
||||
.doc_only()
|
||||
.contract(&DESCRIBE_CONTRACT)
|
||||
.output(crate::schema::output_schema_for::<crate::schema::DescribeResult>)
|
||||
.output(crate::schema::output_schema_for::<crate::schema::inspect::DescribeResult>)
|
||||
.params("command: a command id. Returns {id, label, params, input, output} (and param_warnings in the warn parameter-check mode): the command's parameter documentation and, once the command has a typed contract, the JSON Schemas (2020-12) of its parameters and of a successful result (null otherwise). Read-only."),
|
||||
];
|
||||
|
||||
@@ -326,13 +326,13 @@ fn model_faces(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
Ok(json!({"body": b.name, "faces": faces}))
|
||||
}
|
||||
|
||||
const DESCRIBE_CONTRACT: crate::schema::TypedContract<crate::schema::DescribeParams> = crate::schema::TypedContract::new();
|
||||
const DESCRIBE_CONTRACT: crate::schema::TypedContract<crate::schema::inspect::DescribeParams> = crate::schema::TypedContract::new();
|
||||
|
||||
/// A command's prose documentation and, for converted commands, its schemas.
|
||||
fn describe(_s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let id = p.get("command").and_then(Value::as_str).ok_or_else(|| crate::params::bad("engine.describe", "`command` must be a command id"))?;
|
||||
let spec = super::find_command(id).ok_or_else(|| crate::EngineError::UnknownCommand(id.to_string()))?;
|
||||
let reply = crate::schema::DescribeResult {
|
||||
let reply = crate::schema::inspect::DescribeResult {
|
||||
id: spec.id.to_string(),
|
||||
label: spec.label.to_string(),
|
||||
params: spec.params.to_string(),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user